Feature request: native event-driven session wake primitive
Summary
Codex is turn-driven and has no native primitive for waking an idle session on an external event. This blocks real-time reaction patterns where an agent should respond to inbound activity (chat mentions, queue messages, file changes, MCP resource pushes) while idle, not only at the next user turn.
Requesting a configurable inbound event source that delivers structured input into an idle session and starts a new turn, under the session's existing sandbox/approval mode.
Shape
A watcher declared in config (e.g. ~/.codex/config.toml) naming:
- source — process to spawn, file to tail, SSE URL, MCP resource, or local unix socket
- delivery — line-delimited JSON, each record injected as a user-message-frame equivalent (not as an exec-tool invocation)
- cursor — position that survives crash, reconnect, and session resume so events are neither dropped nor duplicated
- session scope — multiple Codex agents on one host can subscribe to disjoint streams without cross-talk
Lifecycle to specify
- idle session — wake triggers a new turn
- mid-turn arrival — queued, coalesced, or dropped (any choice, just defined)
- restart — cursor resumes from last delivered record
- shutdown — defined drain semantics
Non-goals
- Not asking Codex to host the bus, broker, or persistence layer.
- Not asking Codex to bypass approval/sandbox. The primitive triggers a turn; the turn still obeys the existing safety model.
Why
Claude Code exposes this via its Monitor tool: each stdout line from a watched process is delivered as a session notification and wakes the model. Same operational shape in Codex would let event sources — message buses, webhooks, queue consumers, MCP push subscriptions — drive Codex agents in real time while preserving Codex's safety semantics.
Concrete use case driving this: yaklog (https://github.com/jrtorrez31337/yaklog), a small SSE-fanout message bus we use for multi-agent coordination. Claude Code agents wire Monitor to tail -F events.ndjson and react to @mentions in ~500ms. Codex agents on the same bus can only catch up at turn start via a drain helper — real-time reaction while idle isn't currently possible.
9 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Thanks for the cross-link. #18929 is related but distinct in scope: it asks for a persistent in-workspace daemon-agent architecture (Codex hosts the daemon, observes state, pushes to the main agent). This issue is narrower — a single primitive: a configurable external event source (file tail, SSE, MCP resource event, local socket) that delivers structured input and wakes the idle session, with no opinion on what produces the events.
They're complementary: if this primitive lands, #18929's "daemon → main agent push" half could ride on it. Leaving open.
Adding a clarifying second example to widen the source-shape side of the request, prompted by an internal ask from a sibling agent setting up an scp-based inbox/courier flow:
Second concrete use case — cross-host artifact courier on file creation:
Producer scp's a tarball +
.sha256sidecar into a watched inbox directory on the consumer host. The consumer's Codex agent should wake on file-creation in that directory (inotify CREATE on Linux /fs.watchon macOS), verify the sha, copy into the working tree, and ack — no separate message bus involved. This is event-driven workflow continuation triggered purely by filesystem state, complementary to the message-arrival reaction the issue already covers.Mapping back to the requested shape: where the original example specifies "file to tail" (which reads as appended content via
tail -Fsemantics), this second arm requests "directory to watch for file-creation" (inotify/fs.watch semantics). Both are filesystem-event sources; they differ in whether the agent reacts to new lines in an existing file vs new files in a directory.Acceptance criteria stay the same — wake the session, run one turn, support backoff/dedupe. Just want to make sure the implementation doesn't ship tail-only and leave directory-watch out by accident, since the courier pattern is structurally distinct from the announce-on-bus pattern.
this is the right primitive to ask for, and the cursor-survives-resume detail is the part most implementations skip.
a working reference if useful: repowire wakes idle sessions on external events today. peer asks, telegram/slack messages and scheduled jobs all start a turn in an otherwise-idle session. the source shapes you list (file tail, SSE, MCP push, unix socket) map onto the same external-event --> structured-input --> new-turn contract.
one design note from running it: i deliberately went hook/turn-mediated rather than interrupting a truly-idle process at the kernel level. the wake rides in on the next turn boundary, which keeps sandbox/approval semantics intact (a kernel interrupt mid-turn gets messy fast). that lines up with your under-the-existing-approval-mode requirement, worth deciding that boundary explicitly if it gets built natively. https://github.com/prassanna-ravishankar/repowire
Related: this patch implements a narrow MCP notification wake/ingress path for active CLI sessions. It does not try to solve the broader generic wake primitive, but may be a useful lower-level building block.
Patch: https://github.com/openai/codex/compare/main...alexfrmn:upstream-pr-mcp-surface-notifications?expand=1
This proposal maps closely to LS work on durable event streams, causal identity, replay, and governed agent coordination.
A useful distinction may be:
event delivered
≠ event trusted
≠ event requires action
≠ action authorized
The wake primitive should start a turn, but the inbound event should still pass through validation before it can influence shared state or trigger side effects.
A possible inbound event envelope:
{
"schema_version": "agent-inbound-event/v0.1",
"event_id": "evt-000184",
"stream_id": "coordination-bus",
"cursor": "partition-2:184",
"source": {
"type": "SSE",
"identity": "yaklog",
"channel": "project-alpha"
},
"target": {
"session_id": "codex-thread-123",
"trajectory_id": "project-alpha-migration"
},
"event_type": "MENTION",
"payload": {
"sender": "agent-reviewer",
"message": "Migration validation completed"
},
"received_at": "2026-06-24T11:42:18Z",
"delivery": {
"attempt": 1,
"deduplication_key": "yaklog:partition-2:184"
},
"verification_status": "UNVERIFIED"
}
A safe processing path could be:
receive
→ authenticate source
→ validate session scope
→ deduplicate by event identity
→ persist cursor and receipt
→ wake session
→ classify event
→ verify load-bearing claims
→ propose action
→ existing approval and sandbox gates
The core invariant would be:
«An inbound event may wake an agent, but it must not inherit execution authority from the event source.»
For example, this event:
{
"event_type": "COMMAND",
"payload": {
"action": "deploy production"
}
}
should not become an authorized tool call simply because it arrived from a configured watcher.
The receiving turn should instead record:
{
"event_ref": "evt-000184",
"classification": "ACTION_REQUEST",
"decision": "HOLD",
"reason": "Explicit approval required",
"execution_authorized": false
}
The cursor semantics are also important.
A cursor should represent delivery progress, not proof that the event was successfully processed:
source cursor
→ event durably received
→ processing receipt
→ resulting turn
→ outcome
This suggests separate fields such as:
That separation helps prevent both event loss and false acknowledgements.
A deterministic conformance fixture could test:
A compact processing receipt could look like:
{
"event_id": "evt-000184",
"session_id": "codex-thread-123",
"continuation_id": "continuation-after-wake-09",
"status": "PROCESSED",
"turn_ref": "turn-991",
"decision": "NO_ACTION",
"outcome_ref": null
}
This would also support multi-agent learning safely:
inbound event
→ observed response
→ verified outcome
→ contribution signal
Only the verified outcome—not mere event arrival or agent agreement—should update future routing merit.
Related LS work:
https://github.com/safal207/LS/issues/594
https://github.com/safal207/LS/issues/595
https://github.com/safal207/LS/issues/597
https://github.com/safal207/LS/pull/651
Would a small vendor-neutral "AgentInboundEvent" and "EventProcessingReceipt" fixture help test wake-up, deduplication, cursor recovery, session scoping, and approval preservation?
The wake primitive is useful if it is defined as ingress, not authority.
I would keep the contract small: an external event can wake an idle session and create a new turn, but it cannot authorize any side effect by arriving from a configured source.
For the event envelope, the minimum useful fields seem to be:
The key invariant is that delivery progress is not the same as processing success. A cursor can prove that an event was received durably, but it should not prove that the event was trusted, acted on, or acknowledged as complete.
I would pin these conformance cases first:
That keeps the primitive useful for real-time coordination without turning the event bus into a hidden permission channel.
Boundary: architecture and test feedback only; no claim about using this project or running its code.
Author's note: I'm a Korean speaker; I directed this comment and made the calls, and the English was written by the AI I operate (Claude and Codex).
Operational data point — lessons learned, not an implementation recipe. I'm not a software developer; I run a small renewable-energy company and operate a multi-session, multi-machine agent setup (Codex and Claude sessions mixed, roughly a dozen across three machines) for real business work. We run a homegrown version of what this issue asks for as a desktop-local pilot: a local event log per machine, a small classifier routing signals into per-session streams, and a dispatcher that starts turns in idle Codex desktop sessions. The lifecycle this issue proposes to specify is one we live with daily — here is how that experience bears on the specifics:
For what it's worth, the sibling discussion in anthropics/claude-code#60943 is converging on a similar shape — stable alias resolution plus typed receipts (accepted/rejected/no_target/deduped, never silent drop). Two ecosystems appear to be discovering the absence of the same primitive independently.
Happy to share more detail on any of the above if useful.
The operational data point makes the acceptance boundary sharper: a wake primitive is ingress, not identity continuity and not authority.
I would test it as four receipts:
Regression cases:
The invariant I would keep is: external input can create a turn, but it cannot assign work, choose tools, or authorize side effects merely because it arrived from a configured source. Every wake should end with a typed outcome and an auditable cursor decision, including no_target and deduped cases, so the system never fails as a silent drop.