Background subprocesses/subagents do not wake the calling agent on completion
What version of Codex CLI is running?
codex-cli 0.116.0
What subscription do you have?
API Key
Which model were you using?
gpt-5.3-codex medium
What platform is your computer?
Linux 6.8.0-101-generic x86_64 x86_64
What terminal emulator and version are you using (if applicable)?
tmux
What issue are you seeing?
Long-running background sub-processes and sub-agents do not inform the calling agent when they complete. Instead the calling agent must either:
- Block the message thread by continuously polling for a result; or
- Wait for the user to request an update
What steps can reproduce the bug?
In interactive mode, prompt the agent with:
Use `exec_command` to run the following script in a background terminal: `sleep 15; echo "print the whale emoji"`
or
Run a subagent in the background with the following prompt: `wait for 15 seconds then respond with the whale emoji"`
In both cases, the agent spins up the sub-task, responds to the user, and then does nothing after the sub-task returns.
What is the expected behavior?
Agents should be informed when long-running sub-processes return so they can take autonomous action.
Additional information
In other agent harnesses, watcher scripts can be used to automate agent actions. An agent can run a script in the background that polls an external endpoint without blocking the main interaction loop. When the script returns, the agent is able to respond autonomously without user intervention. Codex agents cannot implement this pattern because sub-processes do not appear to queue a message for the agent.
18 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Candidate fix pushed in my fork. Compare link: https://github.com/openai/codex/compare/main...JaysonGeng:codex:fix/auto-wake-on-async-completion
GitHub shows the branch as cleanly mergeable, but this repo is invitation-only for PRs from non-collaborators.
Root cause:
Patch summary:
Local validation: targeted unified-exec/subagent tests, just fmt, just fix -p codex-core, and git diff --check.
This should also cover #15461, #14314, and #14303.
I hit what looks like the same issue, and my logs make the boundary a bit clearer.
In my case, the child subagents did complete successfully. The important detail was:
task_completethread as a
<subagent_notification>So from the user side it looked like: the child finished and the parent technically received the result, but nothing visibly happened until I manually prompted the parent again
That makes this feel like more than “background work has no notification”. The completion did land, but the calling agent was not really resumed/woken in a user-visible way. I think the ideal agent UX is that the parent gets triggered to msg a response so i dont sit there going 'hey check again plz' over and over.
See also #13733 for the token cost angle
The wake should be runtime-owned, not model-discovered.
When a background process or subagent completes, the runtime can enqueue a completion receipt: origin id, parent turn id, exit status, output ref, finished_at, and whether it should trigger a new assistant turn. The model should consume that receipt only after something changed.
That keeps completion separate from polling. A child finishing should be able to wake the parent once, while a still-running child with no new output stays local and costs no extra model turn.
---
_Generated with ax._
+1
A completion-driven continuation needs a little more than a notification. Completion notifications should be delivered as durable, deduplicated session events and trigger exactly one continuation turn only when the owning session is idle. If the session is busy, defer and coalesce completion events until a safe turn boundary.
This avoids both polling and a burst of redundant model turns when several background processes or subagents finish close together. A receipt should at least preserve the origin ID, parent turn/session ID, terminal status, completion time, and a bounded result or output reference so delivery can be safely recovered after reconnect or resume.
Adding a Codex Desktop / ChatGPT integration perspective: I have encountered the same orchestration gap in multi-step, DAG-style workflows.
Sub-agent delegation itself works well, but if the parent agent finishes its turn or becomes idle while a child is still running, the child’s terminal event does not reliably resume the parent workflow. This affects successful completion, timeout, failure/exception, and abnormal termination. The child result may be available, but the next dependent step is not scheduled until the user sends another message.
For reliable multi-agent workflows, terminal child-task events should durably notify and, when appropriate, wake the owning parent/orchestrator so it can evaluate dependency/join conditions and continue without manual intervention. This should be idempotent to avoid duplicate downstream execution.
Claude Code’s event-driven background-task lifecycle handling is a useful UX reference here: completion, timeout, failure, and sub-agent events promptly return control to the coordinating agent. Comparable behavior in Codex would make long-running and DAG-based workflows much more reliable.
Consolidated PoC update for an explicit, one-shot subagent completion callback:
StatPan/codex:poc/multi-agent-v2-completion-callback-queue30ba5885ccopenai/codex:mainThe PoC adds a separate
register_completion_callbackfunction. It returns immediately; when the watched subagent reaches a terminal state, its existing bounded completion envelope is queued for the registering parent and the existing pending-work scheduler starts exactly one continuation once that parent is idle. This avoids both unconditional wake-up on every child completion and explicitwait_agentpolling.The public-path integration test covers:
A live TUI/service smoke test also completed that sequence without calling
wait_agent.Validation caught an important issue in the first draft: it modified the reserved
collaboration.wait_agentschema and the live API rejected the request with HTTP 400. That change was removed. The corrected branch preserves all reserved collaboration tool schemas and exposes callback registration as an ordinary function.Current scope is deliberately limited to the direct parent/subagent completion path. Registration is still in memory and does not survive restart/resume, so this is a behavior PoC rather than a production-ready durability design. No upstream PR has been opened.
Follow-up after implementing and live-testing the PoC: this supersedes the explicit
register_completion_callbackapproach described in my previous comment.The cleaner contract is runtime-owned completion delivery rather than asking the model to discover and call a separate registration tool:
write_stdinand the async watcher share a claim state so polling cannot race with, or duplicate, automatic delivery.For unified exec, the PoC uses an explicit
on_exit: wakepolicy. This keeps callbacks optional, while making the requested callback a runtime guarantee rather than a prompt to poll again.One important correctness case is ordinary POSIX background syntax. When the model honors a background-work request by emitting a normal
cmd &, the outer shell can exit before the actual job. Watching only the outer shell therefore loses the meaningful completion boundary and produces surprising behavior: a foreground long command wakes correctly, while the equivalent background command does not.The callback contract should therefore include the ordinary
&form that the model naturally uses:The validated sequence is:
The implementation detects the shell background operator syntactically, rather than using a string match, so
&&, quoted ampersands, redirections such as2>&1, and arithmetic expressions are not misclassified.This is not intended to supervise processes that the user explicitly fully detaches with mechanisms such as
nohup,disown, orsetsid; those need a different ownership contract. The scope here is the normal background syntax Codex itself is likely to generate while promising a completion callback.The direct subagent flow, unified-exec completion flow, and the raw POSIX
&flow all passed integration coverage and a live TUI test with nowait_agent/write_stdinpolling. Restart/resume durability is still outside this PoC, so I would stage an upstream implementation as runtime receipt/wake semantics first, unified-exec opt-in delivery second, and POSIX&lifecycle correctness as acceptance coverage for that exec contract.No upstream PR has been opened.
Confirmed on the current combined ChatGPT/Codex macOS app with an event-ordered local trace.
Environment:
26.727.40816(build6067)codex-cli 0.146.0-alpha.9.226.6(25G72), arm64Sanitized timeline:
There was no parent
task_startedevent between the child's explicitsend_message/task_completeand the user's manual prompt. The completion was retained and discoverable, but neither the message nor the terminal transition scheduled the idle parent. This is therefore not a macOS notification-permission problem; it is the collaboration completion-to-parent-wake bridge.Expected: when an owned child sends a result and reaches a terminal state while its parent is idle, the runtime should durably deliver the result and start exactly one parent continuation turn without polling or user intervention.
Actual: the parent remains dormant. A user prompt followed by
list_agentsretrieves the result, and the imported inter-agent messages are markedtrigger_turn:false.Sanitized identifiers, in case they are useful for internal telemetry correlation:
019fb43b-291c-74e1-8357-f6f0c9ecb159019fb565-ab13-7b00-a688-f2b862f8f9f2019fb565-4a64-7f52-a362-a870786f5ad1019fb565-abc4-74a0-adb3-8eb046a7384cThe same user-visible failure was also observed on July 28, before build
6067was installed, so this does not appear to have been introduced solely by the current July 30 build. It may predate it within the combined-app rollout.Additional current MultiAgentV2 reproduction (2026-08-06): In Codex 0.146.1, an explicitly started parallel child batch can finish while the parent is idle. Completion watcher queues parent mailbox receipts with trigger_turn=false, so no parent continuation starts until a later user turn or status/read operation. wait_agent is mailbox-activity wait, not a wait-all barrier; repeated waits add model boundaries and context/token cost. Desired contract: runtime-owned batch receipt and one coalesced parent continuation after the batch reaches a terminal/deadline state; parent remains final admission/integration/delivery owner; do not globally change all notifications to trigger_turn=true; preserve distinct completed/error/timeout/interrupted/shutdown/missing outcomes. This is evidence refining #15723, not a separate issue. Related prior art: #9837, #18400, #34468, #35259, #35108, #24951, #31178, #16386.
Update (2026-08-07; live MultiAgentV2/app-server smoke on Codex 0.146.1):
wait_agent, polling, or user input.This validates the idle-parent completion-to-wake path and the capacity-release continuation path in a local patch. It is not a claim that restart/resume durability, timeout policy, or every topology is solved.
The current
mainstill constructs the MultiAgentV2 completion communication withtrigger_turn=false:https://github.com/openai/codex/blob/4ee41929eaf4fc1e5662c9b4befd05230688ca62/codex-rs/core/src/session/mod.rs#L1941-L1976
I would not change every notification to
trigger_turn=true; #33061 shows that a late completion can create a second user-visible response. The intended fix should remain runtime-owned, deduplicated/coalesced, and scoped to a parent that still owns pending work.Would a maintainer confirm whether this direction is acceptable and, if so, invite a focused PR? I can then rebase a minimal patch onto current
main, narrow the diff, and add the regression coverage for this idle-parent/capacity-release sequence.The runtime-owned, coalesced direction described above matches what I found in practice.
I’ve been investigating this issue in the context of long-running training jobs. The practical failure mode is that a two-day training process may terminate after four hours, but the agent remains idle and the failure is not handled until the user returns much later.
I built and tested a proof of concept that:
The implementation and comparison are available here:
https://github.com/openai/codex/compare/main...ikerrrrrrrrrrr:codex:feature/runtime-wake-events
This overlaps with the event-driven behavior proposed in #32188, while also covering late subagent completion.
The current PoC is rebased onto the latest main; the protocol suite passes 286/286 tests and the focused background-terminal suite passes 10/10 tests.
This branch is intended as a validated PoC rather than a proposal to review the entire 50-file diff as one PR.
Would the maintainers be interested in this architectural direction? If so, I’m happy to split the implementation into smaller reviewable stages and submit an invited PR.
I tested a different contract after reading the callback and runtime-owned wake PoCs above: an explicit owned join barrier. This is intentionally not another global “wake on every child completion” patch.
I reproduced the subagent gap on current
main(902bd9e06b3ecb32cbf7f8e64cd23b956be3e7fe). Multi-Agent V2 still sends a direct child's terminalResultto its parent withtrigger_turn=false, while the model-facing coordination primitive remains timeout-orientedwait_agent.I also tested the tempting smaller change—unconditionally using
trigger_turn=true. It did wake the parent, but in four independent live direct-child cases the resulting parent turn completed with zero assistant/tool/action output. It also creates one-turn-per-child pressure for fan-out. That suggests “every terminal event wakes” is too broad even when delivery itself is deduplicated.The alternative prototype makes the dependency explicit:
The key semantic difference from automatic wake is that an unarmed terminal result remains queue-only and creates no parent model turn. The parent is resumed only after it has explicitly declared that later work depends on those children.
The prototype contract is:
Natural dogfood was useful because it exposed three non-obvious gaps before the final hardening: pre-admission completion, sequential re-arm inside a resumed continuation, and premature logical completion when a child was itself suspended on a nested join. Those cases are now covered alongside empty-success ACK, process restart, terminal dedupe, and intervening-user-turn priority.
Current verification includes 15/15 lifecycle tests, 5/5 focused join tests, a nested direct-parent integration test, full format/clippy, and a native Windows build. Live hardened-runtime dogfood passed one child, two-child join-all, and child→grandchild routing with no polling/heartbeat and no lost, duplicate, empty, or wrong-parent continuation observed.
This is deliberately scoped to explicit subagent completion dependency. It does not attempt to solve general in-flight progress or background subprocess ownership. The local implementation is design evidence, not an unsolicited PR, and I would port the agreed behavior cleanly onto current
mainrather than propose the accumulated lab branch as-is.Would an explicit owned join fit the intended Multi-Agent V2 direction alongside, or instead of, runtime-owned automatic wake? If so, would a maintainer be open to inviting a focused PR? I would also appreciate guidance on whether the preferred model surface is a new
join_agentstool or a non-timeout suspension mode onwait_agent, and whether durable restart semantics should land atomically or as a gated stacked change.One source-level refinement to the opt-in direction, based on current
mainat636e505c. This is design analysis, not a tested patch.Normal Multi-Agent V2 spawn explicitly skips the detached completion watcher. Its active terminal path is
Session::maybe_notify_parent_of_terminal_turn->forward_child_completion_to_parent, wheretrigger_turnis currently hard-coded tofalse. Therefore, changing the stale V2 branch in the detached watcher would not change the normal V2 completion path.A backward-compatible per-spawn contract could be:
completion_delivery: queue_only | resume_parent, defaulting toqueue_only;SubAgentSource::ThreadSpawn(or equivalent durable spawn provenance), rather than only in ephemeral spawn arguments;trigger_turnonly forresume_parent;The focused regression matrix would cover: default compatibility; one idle opt-in child producing one continuation; a busy parent deferring without interruption; two opt-in completions coalescing into one drain; restart preserving the policy; newer user work/cancellation winning; and nested completion targeting only the direct parent.
This would not replace an explicit all-of join.
resume_parentis per-child completion delivery; a join remains the stronger contract when later work depends on an exact set of children and durable barrier receipts.Would a backward-compatible per-spawn delivery option fit alongside the explicit join direction, and should its durable owner be
SubAgentSource::ThreadSpawnor a separate runtime-owned registration, given that the collaboration tool schema is reserved?Reproduced an intermittent active-wait variant on Codex Desktop.
Environment:
codex-cli 0.148.0-alpha.9followup_taskObserved behavior:
followup_task, then enteredwait_agent.list_agentscall returned the already-completed child result.This occurred twice during the same session. A third comparable direct-child completion woke the parent wait in approximately 35 ms. The failure is therefore intermittent, and the child result is retained rather than lost.
Expected behavior:
An active owning-parent
wait_agentshould receive one durable, deduplicated terminal completion event for its direct child and wake immediately. A one-shotlist_agentsrecovery query can recover the retained result, but it only mitigates the stall.The affected session and diagnostics were submitted privately through Codex
/feedback.Codex feedback ID:
01a00cb4-a2a7-7f32-9fcb-a446e0d3b749Local dogfood and rebase are now complete for the explicit owned-join direction described above. The two logical commits are rebased without conflicts onto current
main(a4f37a5b7f), andgit range-diffclassified both patches as exact equivalents. Post-rebase lifecycle and user-priority integration tests pass; the hardened Windows runtime also passed one-child, two-child all-of, and child-to-grandchild live scenarios without polling or duplicate/empty/wrong-parent continuation.The implementation is deliberately not published as an unsolicited PR. The full restart-safe contract is larger than the model-facing tool because it includes exact-turn receipts, FIFO admission, restart rebind, rollback/fork/paginated-history safety, and dedupe. If this direction aligns with the intended Multi-Agent V2 design, would a maintainer be willing to invite a PR and advise whether the durability layer should be reviewed atomically or as a stacked sequence?