You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note: the original body of this issue was accidentally replaced with the text of RFC #121 at filing time, so its content was lost. This is a reconstruction from the title and surrounding context ("Re-awakening idle Claude Code sessions — a webhook→injection bridge to consume the dormant push subsystem"). Corrections welcome if the original intent differed.
Summary
The bus has a complete push subsystem — register_webhook with channel/event-type filters, HMAC signing, and retries — but nothing consumes it. Meanwhile, the delivery model for events to Claude Code sessions is entirely pull: an idle session doesn't see a DM or a help_needed until its human happens to submit a prompt (the UserPromptSubmit hook drain). This RFC proposes the missing consumer: a small local bridge daemon that subscribes via webhook and injects a wake-up into the idle session, closing the loop from "event published" to "agent acts on it" without a human in the middle.
Motivation
DMs today rely on the human-as-router pattern: the server notifies the human via macOS notification, the human switches terminals and types "check the event bus." That works, but only when the human is present and watching.
The webhook subsystem already exists and is tested — the marginal cost is one consumer process.
Design sketch
publish_event(channel="session:X", ...)
│
▼
agent-event-bus ──POST──▶ bridge daemon (localhost)
│ filter: min_level=actionable
▼
injection into session X
On session start (SessionStart hook), the bridge registers a webhook scoped to that session: register_webhook(url="http://127.0.0.1:<port>/wake/<session>", channel="session:<id>", ...) plus optionally the broadcast channels at actionable level only.
On receipt, the bridge decides whether the target session is idle (no in-flight turn) and injects.
Injection mechanism options
tmux send-keys (when sessions run under tmux): the bridge types "check the event bus" into the pane. Crude, robust, visible to the human.
FIFO/spool + hook drain: the bridge appends to a per-session spool file; Stop / UserPromptSubmit hooks already drain the bus and could also drain the spool. This only wakes the session at hook boundaries, so it's "sooner," not "immediate" — but it needs no terminal automation.
claude --resume/headless invocation: the bridge starts a non-interactive turn in the session's context. Most powerful, most invasive; needs care with concurrency and permissions.
Recommendation: start with (1) behind a config flag, keep (2) as the portable fallback.
Webhook payloads now carry correlation_id, title, tags, and signal_level, so the bridge can render a meaningful wake-up prompt without a second round-trip.
Open questions
Idle detection: how does the bridge know a session is mid-turn? Injecting into an active turn is at best noise, at worst interleaves with tool output.
Loop prevention: a woken session may publish events that wake other sessions — is actionable-only filtering sufficient back-pressure, or does the bridge need a cooldown per session?
Scope: does the bridge live in this repo (it's the natural consumer of the bus) or in dotfiles next to the hooks that would cooperate with it?
Security: localhost-only listener + HMAC secret per webhook seems sufficient; anything else needed once Tailscale-remote sessions enter the picture?
Summary
The bus has a complete push subsystem —
register_webhookwith channel/event-type filters, HMAC signing, and retries — but nothing consumes it. Meanwhile, the delivery model for events to Claude Code sessions is entirely pull: an idle session doesn't see a DM or ahelp_neededuntil its human happens to submit a prompt (theUserPromptSubmithook drain). This RFC proposes the missing consumer: a small local bridge daemon that subscribes via webhook and injects a wake-up into the idle session, closing the loop from "event published" to "agent acts on it" without a human in the middle.Motivation
Design sketch
register_webhook(url="http://127.0.0.1:<port>/wake/<session>", channel="session:<id>", ...)plus optionally the broadcast channels atactionablelevel only.Injection mechanism options
send-keys(when sessions run under tmux): the bridge types "check the event bus" into the pane. Crude, robust, visible to the human.Stop/UserPromptSubmithooks already drain the bus and could also drain the spool. This only wakes the session at hook boundaries, so it's "sooner," not "immediate" — but it needs no terminal automation.claude --resume/headless invocation: the bridge starts a non-interactive turn in the session's context. Most powerful, most invasive; needs care with concurrency and permissions.Recommendation: start with (1) behind a config flag, keep (2) as the portable fallback.
What this builds on (now available)
min_level=actionablefiltering shipped (Server-side signal-level tagging (lifecycle/info/actionable) to replace client-side denylists #129) — the bridge can subscribe to exactly "things aimed at someone" instead of maintaining a denylist.correlation_idshipped (RFC RFC: Event payload schema and the bus's drift from coordination-bus to shared knowledge-store #121 substrate) — a woken session can fetch the whole request thread withget_events(correlation_id=...).correlation_id,title,tags, andsignal_level, so the bridge can render a meaningful wake-up prompt without a second round-trip.Open questions
actionable-only filtering sufficient back-pressure, or does the bridge need a cooldown per session?Relationship to other issues