Repository navigation
Feature request: deliver external/async events as turns into a live (visible) session — today only headless remote-control threads are reachable #33556
Description
Summary
Codex has no supported way for an external, asynchronous event (a file-watcher signal, a webhook, an incoming message/notification) to wake a running, user-visible session and inject a turn into it. External injection through the remote-control daemon only drives headless threads, which never surface in the desktop app window or a foreground codex TUI the user is actually watching.
The transport pieces already exist (remote-control + thread/start + turn/start) — they just can't target the session a human has open.
What I observed (why it isn't possible today)
macOS (Apple Silicon), Codex CLI 0.140.x with managed app-server 0.142.x, verified on a real machine via live process inspection + WS-over-unix RPC:
- The visible desktop app's backing app-server runs as a private stdio subprocess with no listening socket:
/Applications/Codex.app/Contents/Resources/codex app-server --listen stdio://
→ nothing external can connect to it. - The only externally reachable endpoint is a separate remote-control daemon:
codex … --remote-control --listen unix://…/app-server-control/app-server-control.sock thread/start+turn/startover WS-over-unix to that control socket does wake an idle headless thread and run a full turn (observedturn/started→ streameditem/agentMessage/delta→turn/completed, real assistant output).- But that headless thread lives in a different process / thread-store than the desktop window (confirmed with distinct live PIDs for the window's app-server vs. the injectable daemon). The injected turn never appears in the window the user is looking at.
Net: you can wake a headless agent, but not the session you have open. There is no --listen/attach option that both (a) serves a visible interactive session and (b) is reachable by an external turn/start.
What I'm asking for
Any one of these would close the gap:
- An opt-in flag to expose a live interactive session (desktop app or
codexTUI) on the remote-control endpoint, so an externalturn/startis delivered as a new turn in that session. - Let the desktop app / TUI attach to the remote-control daemon's thread-store (shared threads), so a headless-injected turn surfaces in the visible window.
- A first-class "external wake" primitive — the model Claude Code's
Monitortool already provides: you register a long-lived watcher process, and each line it emits on stdout becomes an asynchronous user-turn in the live session. Codex has the injection transport (turn/start) but no equivalent way to bind a watcher to the session the user is watching.
Motivation
Event-driven automation often needs to wake the session the user is actively watching when something happens outside Codex — a file changes, a webhook fires, a message arrives — and have the agent pick it up as a new turn without the user re-prompting. Today the only externally wakeable target is an invisible headless thread, so any such flow has to run where the user can't see it. A supported wake path for live sessions would remove that limitation.
Environment
- Codex CLI 0.140.x, managed app-server 0.142.x
- macOS (Apple Silicon)
- Observed 2026-07 on a real machine.
Activity
github-actions commentedon Jul 16, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
ctut commentedon Jul 26, 2026
Adding a concrete macOS accessibility/productivity use case that matches this request.
The user wants a bare Fn push-to-talk helper: holding Fn records locally, releasing it transcribes, and the resulting text is delivered into the same visible Codex Desktop thread they are already using. If any other key or modifier is pressed, capture is abandoned and the original key combination continues to its normal macOS/application handler unchanged.
The capture and transcription side can be built without UI automation. The blocker is safe delivery into the live Desktop session. A separate app-server can create or resume its own control context, but that does not prove delivery into the exact in-flight session visible to the user.
For this workflow, a supported bridge would ideally provide these properties:
We have intentionally held activation of the otherwise feasible capture/transcription half because presenting it as complete without a supported live-session bridge would be misleading. An opt-in live-session attachment or fixed-target external-wake primitive would unblock this use case cleanly.
PMP57 commentedon Aug 30, 2026
Adding a cross-vendor agent-coordination use case and recent sandbox measurements that reinforce this request.
For ORCH, an external hub daemon needs to wake an already-open Codex Desktop task only when an addressed message arrives. The quiet-period requirement is zero model invocations and zero model tokens. A headless watcher can launch or resume a headless Codex process, but that does not keep the existing visible Desktop task in the loop; top-of-turn polling also cannot reach a task while it remains idle.
Comparable product mechanisms show that the requested lifecycle is not hypothetical. Claude Code channels push messages, alerts, and webhooks from MCP servers into a running session, while Claude Code plugin monitors keep a background process for the session lifetime and deliver its stdout lines as notifications. Google Antigravity sidecars are host-managed background processes that can use
agentapi send-messageto address an existing conversation; Antigravity subagents automatically wake from idle when messaged and retain prior context. Antigravity artifact feedback also continues the associated agent workflow. These mechanisms are not identical contracts, and the Antigravity documentation does not state the same quiet-token accounting, but they demonstrate that event-driven wake and continuation are established product patterns.In an isolated August 2026 app-server queue probe, we observed:
queue/addauto-started before the client could issuequeue/startin 8/8 repeats.This suggests that useful queue mechanics exist, but the product still needs a supported, opt-in bridge into the visible Desktop task: atomic active-versus-idle admission, durable idempotency/cursor semantics, pause and watcher-death visibility, narrow authentication/permissions, and zero model activity while quiet.
That would let an external message bus wake the task the user is already watching, rather than creating a separate invisible headless seat.
hueniverse commentedon Sep 2, 2026
Our workflow relies on hacks and workaround for this limitation to enable us to run a Codex and Claude Code sessions in pairs, talking to each other. On the Claude side we use the monitor feature, but on Codex we need to override the cli settings to hijack traffic.
saksham10arora-dotcom commentedon Oct 18, 2026
Adding a concrete finding that might make this smaller to ship than it looks: the desktop app already contains a wake path for visible threads. What is missing is only a way to put a message into a thread's queue from outside.
Use case
I run Claude Code and Codex Desktop as a pair on the same project. They talk through a shared local mailbox (an append-only JSONL file; the tool is public at https://github.com/saksham10arora-dotcom/agent-sync). Claude Code can watch that file with its
Monitortool, so it reacts to a new message within a second. Codex can only check the mailbox when a scheduled automation fires (every 20 minutes for me) or when I type. So every exchange where Codex has to answer costs up to 20 minutes, and an idle Codex task still spends tokens on each scheduled check that finds nothing.This is the same gap described above: the event exists outside, and the visible Codex thread has no way to hear it.
What the desktop app already has
Inspected read-only on macOS, ChatGPT desktop app 26.1013.31256 (the Codex tab), in
Contents/Resources/app.asar. The app registers thecodexURL scheme (CFBundleURLSchemesinInfo.plist), and its deep link parser has thesethreadsroutes:codex://threads/newwithprompt,mode,path,browserUrlororiginUrlquery parameters opens a new thread with the composer pre-filled. In my test it did not send, so a person still has to press Send.codex://threads/<id>opens an existing local conversation.codex://threads/wake/<conversationId>is parsed into{ kind: "wakeQueuedThread", conversationId }. The parser only accepts exactly two path segments with no query string and no fragment. The handler deduplicates repeated wakes for the same conversation and retries up to 3 times with a 1 second backoff (it logs "Failed to wake queued thread" on failure).As far as I can tell, route 3 processes follow-up messages that are already queued on a thread (for example a message typed while a turn was running). It does not accept a payload, so an outside process cannot add anything for it to process. I found no supported way to enqueue a follow-up from outside the app, which matches the
remote-controlfindings in the issue body: the injectable daemon only reaches headless threads.Suggested minimal change
Since the queue and the visible-thread wake already exist, one of these would close the gap without a new transport:
codex://threads/wake/<conversationId>?prompt=<urlencoded text>, which appends the text as a queued follow-up and then runs the existing wake.codex://threads/<conversationId>/enqueue?prompt=...route that only enqueues, keepingwakeunchanged.Safety
Deep links can be opened by any local app or web page, so this should be off by default:
Workarounds today
codex://threads/new?prompt=...plus a human pressing Send.Happy to test a build or share more detail on the pairing setup if that helps.