Expose non-interrupt queued delivery for cross-thread send_message_to_thread
Feature request
Codex Desktop/App already has user-facing Queue vs Steer concepts, but the app/tool surface exposed to agents for cross-thread orchestration appears to only provide send_message_to_thread without an explicit delivery mode.
For parent/worker thread orchestration, we need a way for a worker thread to send a completion notice back to a parent thread without interrupting the parent thread's current turn. The desired behavior is:
delivery: "queued"orinterrupt: falseforsend_message_to_thread/ equivalent cross-thread send- the message is accepted immediately as pending input for the target thread
- it is processed only after the target thread's current active turn completes
- it should not be treated as
turn/steeror an urgent interruption - it should be durable/visible enough that the parent does not need to poll child threads with
read_thread - optionally expose status such as queued, delivered, canceled, failed
Why this matters
A common Codex workflow is a durable parent/router thread with multiple worker threads. Worker completion notices currently have two bad options:
- Use direct
send_message_to_thread, which can interrupt or interleave with the parent thread's active reasoning and muddy decision-making. - Do not send a notice and require the parent to poll/read worker threads, which is easy to miss and makes the workflow stall.
A non-interrupt queued cross-thread send would let workers report completion reliably while preserving the parent thread's current reasoning turn.
Related/adjacent issues found
- #23615 Queue / Steer / Stash UX concepts
- #23100 queued messages not processing until project focus
- #14923 cross-thread orchestration over existing thread/turn primitives
Those are related, but the specific request here is an explicit non-interrupt delivery mode for cross-thread programmatic sends, suitable for agent/worker completion notices.
Suggested API shape
send_message_to_thread({
threadId,
prompt,
delivery: "queued" // or interrupt: false
})
or app-server equivalent:
{
"method": "thread/message/send",
"params": {
"threadId": "...",
"input": [{"type":"text","text":"WORKER_NOTICE ..."}],
"delivery": "queued"
}
}
If an active turn exists, this should enqueue. If no active turn exists, it can start normally or remain queued depending on product semantics, but the delivery mode should be explicit and observable.
2 Comments
I strongly support this, especially for parent/worker thread workflows.
The important distinction for me is that cross-thread communication should not always mean steering the target thread. If a worker finishes while the parent/router thread is still actively reasoning or executing, I want the worker's completion report to be queued as the parent's next input, not injected into or interleaved with the active turn.
A useful shape would be a configurable delivery mode on cross-thread sends, for example:
delivery: "queued"/interrupt: false: enqueue for after the current active turndelivery: "steer"/interrupt: true: urgent guidance for the current active turnThis would make multi-thread orchestration much calmer: workers can report completion reliably, but the parent thread keeps its current task boundary intact unless the sender explicitly chooses to interrupt.
Non-interrupt queued delivery would help, but worker completion needs a slightly stronger app-server transaction to avoid both polling and duplicate/partial wake behavior.
Suggested scoped operation on the Desktop host that already owns the coordinator's app-server connection:
The append and wake should be serialized per parent thread. Duplicate nonce is a no-op. Unknown lineage, oversized payload, busy/non-wakeable coordinator, or any failed precondition must append nothing and start no turn.
This cannot be safely emulated by attaching workers to the ChatGPT-owned stdio transport, launching a second app-server, separate
thread/inject_itemsplusturn/startcalls, rollout edits, direct steering, polling, a watcher, or a local queue: none can atomically target the already-running Desktop task with exactly-once semantics.Useful acceptance fixtures: schema round-trip; valid append-plus-one-turn; duplicate nonce; wrong lineage; unknown thread; invalid/oversized payload; busy coordinator; rollback on precondition failure; and a Desktop-host integration proving one visible completion plus one coordinator turn on the owning connection. A negative fixture should show that a separately launched app-server cannot claim to wake the original Desktop task.