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
{{ message }}
Repository navigation
Background subprocesses/subagents do not wake the calling agent on completion #15723
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.
GitHub shows the branch as cleanly mergeable, but this repo is invitation-only for PRs from non-collaborators.
Root cause:
Background unified_exec completions were only surfaced through write_stdin, so if the originating turn had already ended and nobody polled, the session stayed idle forever.
Sub-agent completion notifications used trigger_turn = false, so an idle parent never woke up.
Patch summary:
The async watcher now claims unseen background completions, injects a synthetic function_call_output into the next turn, and wakes the idle session.
write_stdin and the watcher coordinate with an atomic claim to avoid double delivery.
Sub-agent completion notifications now use trigger_turn = true.
Local validation: targeted unified-exec/subagent tests, just fmt, just fix -p codex-core, and git diff --check.
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:
the parent turn had already emitted task_complete
afterward, the child completion was still injected back into the parent
thread as a <subagent_notification>
but no new assistant turn was started to consume or synthesize that result
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.
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.
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 terminal Result to its parent with trigger_turn=false, while the model-facing coordination primitive remains timeout-oriented wait_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:
direct children only; no sibling or grandchild targeting;
exact child-turn ownership rather than thread-name matching;
a fast child that completes immediately before join admission is accepted through its retained exact terminal receipt;
all requested children coalesce into one continuation;
duplicate terminal delivery is idempotent;
new user input or newer parent work wins over synthetic continuation;
nested joins wake only the direct parent at each level;
restart recovery is durable;
no heartbeat, polling daemon, lifecycle hook, or arbitrary cross-thread send capability.
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 main rather 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_agents tool or a non-timeout suspension mode on wait_agent, and whether durable restart semantics should land atomically or as a gated stacked change.
A backward-compatible per-spawn contract could be:
completion_delivery: queue_only | resume_parent, defaulting to queue_only;
persist the selected policy in SubAgentSource::ThreadSpawn (or equivalent durable spawn provenance), rather than only in ephemeral spawn arguments;
let the active V2 terminal sink set trigger_turn only for resume_parent;
route that mail through the existing pending-work scheduler, which already waits for an idle parent and drains trigger-turn mail together. This avoids adding another watcher, debounce timer, or parallel wake state machine.
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_parent is 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::ThreadSpawn or a separate runtime-owned registration, given that the collaboration tool schema is reserved?
The parent triggered a direct child with followup_task, then entered wait_agent.
The child reached terminal completion while the parent was actively waiting.
The parent wait did not wake and eventually timed out.
A later one-shot list_agents call 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_agent should receive one durable, deduplicated terminal completion event for its direct child and wake immediately. A one-shot list_agents recovery query can recover the retained result, but it only mitigates the stall.
The affected session and diagnostics were submitted privately through Codex /feedback.
Local 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), and git range-diff classified 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?
A concrete ML-training use case where this matters a lot:
Suppose Codex launches a model training job that is expected to take ~10 hours. I want to be able to tell Codex something like:
Start the training, then wake up in 10 hours and inspect the logs / metrics / checkpoints. If training finished earlier, inspect it when the process exits.
Today, to approximate this kind of autonomous follow-up, the agent effectively has to stay in a continuous monitoring / goal-style loop and poll repeatedly. For a multi-hour training job, that can waste a large amount of model tokens even though nothing useful is happening during almost all of that time.
It would be much better if Codex had a lightweight scheduled wake-up / timer primitive, ideally alongside event-driven process completion notifications. The agent could suspend completely, consume no inference tokens while waiting, and then resume at either:
a specified time (wake me in 10h / wake at 08:00), or
a subprocess completion event (wake when PID/job exits).
On wake-up, it should retain the task context and be able to execute the next step itself, e.g. read train.log, inspect TensorBoard/JSON metrics, check the final checkpoint, decide whether the run succeeded, and possibly launch the next experiment.
This seems especially valuable for ML/research workflows where training/evaluation jobs commonly take many hours. The key benefit is not just convenience; it avoids spending tokens on unnecessary polling while still enabling truly autonomous long-running workflows.
Still reproducible on Codex Desktop for Windows 26.901.6511.0 with codex-cli 0.153.4 (reproduced 2026-09-07).
A parent task spawned a Luna subagent, told the user it would review the result, and then emitted its final answer while the child was still running. The child completed normally; the parent log recorded the child-completed activity immediately afterward, but no parent continuation was started. The parent processed the result only after the user sent another message.
I saw the same user-visible failure repeatedly in late August: the child’s completed state was visible in the sidebar/log while the parent remained idle until a status operation or user prompt occurred.
This is the same lifecycle gap described here, not a worker failure. Our current workaround is a global instruction requiring the parent to keep its turn alive with repeated wait_agent calls, then review and integrate the result before returning. That mitigates the abandoned result, but it does not fix event-driven wake-up and can consume unnecessary model turns/tokens while waiting.
Current Windows/Desktop evidence shows this is still reproducible on codex-cli 0.153.4 / Desktop 26.901.6511.0, and the latest MultiAgentV2 path explains why.
I audited one canonical parent rollout (sanitized counts/ordinals only; no private prompt or child payload copied). It has 7,483 contiguous records. Earlier in the same root turn, 9 successful GLM child completions followed the healthy sequence:
The newest child (/root/plan5_texture_runtime) completed after the parent turn had already terminated on a usage-limit error. The child rollout itself is complete (351 records, final assistant result persisted, then task_complete). In the parent rollout the terminal ordering is:
There is no subsequent inter_agent_communication_metadata or parent agent_message for that completed child. So the child did finish and persist its result; the parent simply never materialized/read the completion after becoming idle.
Current default-branch source matches this behavior. The active V2 terminal-turn forwarding constructs the parent completion communication with trigger_turn: false. In session/handlers.rs, inter_agent_communication() enqueues the mailbox item but only calls maybe_start_turn_for_pending_work_with_sub_id() when trigger_turn is true (or durable sleep is outstanding). This is therefore not a routed-model/GLM-specific loss; the same GLM children delivered normally while the parent turn was alive.
This also complements #43221's late-subagent ordering: that report demonstrated analytics loss after parent termination; this rollout shows the user-visible completion can remain unread by the parent as well.
I would avoid a blanket trigger_turn: true because #33061 documents the opposite duplicate-response failure. A focused regression should exercise the production V2 path: parent turn becomes terminal while a child is still running -> child completes -> terminal receipt remains durable and is either consumed by an explicit wait/join policy or wakes an idle owner exactly once under a defined completion-wake policy. At minimum, the result should not require an unrelated user message to become visible to the owner.
Additional current-version evidence for the background-process side of this lifecycle family, distinct from my earlier late-subagent example:
Environment:
Windows Codex Desktop AppX 26.903.8094.0
codex-cli 0.153.4
Observed event ordering, sanitized:
T0 parent starts a long-running local process and receives a session handle
T0..+10m parent polls that session while the process continues
20:09:02Z parent emits task_complete(error: usage_limit_exceeded)
no later parent task_started / agent_message appears
20:39:13Z the still-running process writes its selected checkpoint
20:45:57Z it writes a complete 50-epoch history
20:45:58Z it writes its final curve artifact
Read-only follow-up found no process still alive. The post-terminal artifacts are internally consistent rather than obviously partial: the history parses with 50 epoch rows, the selected checkpoint loads with weights_only=True, contains the expected model-state tensor set, and its stored best epoch/validation metadata agrees with the history. The parent rollout is structurally valid and its projection is caught up exactly to EOF; there is simply no continuation record after the terminal usage-limit event to consume/report the later process completion.
So in this case the useful child process was neither cancelled with the parent turn nor visibly adopted by an owner after the parent became terminal. It completed roughly 37 minutes later with no parent wake/result synthesis.
I am not claiming this necessarily uses the same internal completion path as MultiAgentV2 subagents. The shared lifecycle requirement seems to be: once a turn that owns asynchronous work becomes terminal, the runtime needs an explicit contract—cancel the work, or durably adopt its terminal result and deliver/wake exactly once. Silent survival plus an unreachable completion leaves externally useful work in an ambiguous state.
No prompt content, repository/project name, thread/session ID, local path, model artifact, account identifier, or credential is included here.
Reproduced on Codex CLI 0.154.0 (Linux, interactive CLI, MultiAgentV2). This is a delivered result without parent continuation, rather than a child failing to finish.
Sanitized reproduction
Our workflow is: background job watcher -> failure event -> subagent collects evidence and diagnoses -> parent reviews and handles recovery -> resume observation.
In one observed case:
The parent had finished its turn while the watcher subagent remained active.
At T+0, the external observer published a failure-related event.
At approximately T+5s, the child's event reader returned.
At approximately T+14s, the child began handling the incident, saved evidence and performed bounded diagnosis.
At approximately T+90s, it returned its report and recorded task_complete.
The parent runtime received three communications (preliminary report, diagnosis report and final completion), all with trigger_turn=false.
No subsequent parent handling turn appeared during the observation window, including more than four minutes after child completion. The result was present, but review/recovery did not start.
The child was correctly waiting for parent review rather than making an uncertain repair or resuming observation prematurely. Independent monitoring/protection continued, but the agent response chain stopped at the parent.
wait_agent is not a workaround for our requirement
The main agent must remain available to do other useful work while the background watcher runs. Blocking its execution in wait_agent (or sleep), or keeping it alive through repeated timeout/wait cycles, does not meet that requirement. It also does not solve continuation after the parent has finished a turn.
We need asynchronous, event-driven parent handling:
If the parent is doing other work, deliver the actionable report at a safe boundary so it can reprioritize.
If the parent is idle, start one continuation to review the result, without a user prompt.
Deduplicate preliminary/final notifications and respect explicit cancellation.
We are not asking for unconditional new turns for every child message. We need a supported completion/incident notification contract that allows background delegation without blocking the parent or abandoning the result. For this workflow, the issue remains unresolved.
No internal project names, job/session IDs, infrastructure addresses, local paths or raw logs are included.
Follow-up to my earlier report: we implemented local, opt-in idle-parent wake support, and found a second failure mode after a new parent turn has actually started.
1. Local support for triggering a new parent turn
Our local patch on top of Codex CLI 0.156.0 adds an opt-in subagent completion policy (resume_parent_when_idle). A terminal child result is delivered through the parent mailbox and the existing pending-work scheduler starts a parent continuation when idle. Ordinary queue-only delivery remains the default. This is a local modification, not a claim that upstream already supports this behavior.
2. The newly triggered turn can intermittently return an empty final
Observed sequence:
parent finishes its previous turn and becomes idle
-> child returns an actionable FINAL_ANSWER report
-> runtime delivers the report and starts a new parent turn
-> parent sends a model request with its prior context and child reports
-> upstream completes with an assistant final whose text is empty
-> parent turn ends without handling the pending task
We saw this in two real sessions. Their original raw transport responses were not retained. We then reproduced an empty final in an isolated real-model WebSocket replay: generate an actual preceding parent final, then send the original child reports in a new turn using previous_response_id. In that reproduction, the raw upstream events already contained empty final text, no text deltas and no tool calls; the response completed without an error/incomplete event. Thus, for this reproduction, the blank was present before Codex's response parser/UI.
This is intermittent: another run of the same lifecycle continued normally. Successful requests also contain the exact literal Message Type: FINAL_ANSWER, so that label alone is not a sufficient explanation. A full-input control with the same logical context also continued, but the sample is too small and backend/sampling state differs; this does not establish a previous_response_id defect. Request construction, continuation state and model/backend behavior still need causal isolation.
3. Workaround: append one explicit continuation message
We preserved the original child report, including its FINAL_ANSWER envelope, and appended one separate role: "user" / input_text message instructing the parent to review the returned result against the original task and continue. This is an automatic host notice, not a manual user prompt. The long-context WebSocket probes retained previous_response_id; we did not need to replace continuation with a full request to add the notice.
A neutral form, suitable for either successful or failed child reports, is:
Automatic notification: a subagent has returned a result. Review it together with the conversation context and original task, then determine and perform the next step. If the original task is complete, summarize the result for the user. This notification grants no additional authority beyond the original task.
Two qualifying long-context reminder probes produced evidence-inspection tool calls (those tools were deliberately not executed). Separately, a short synthetic native idle-wake test over SSE, with the notice inserted by a proxy, actually read a local evidence fixture and produced a correct constrained final. That test had only one initial user turn submission; child completion triggered the second parent turn automatically. The proxy notice was not persisted in native history, which is a limitation of that test harness.
Update: the bounded automatic notice is now implemented in the native idle-completion-wake path and persisted in session history. Focused notification integration tests cover idle success/failure, deferred completion, active delivery, and persistence across resume. A freshly built package also passed the short real-model SSE lifecycle with an observation-only proxy: native and forwarded request bodies matched, the original child report was preserved, one notice was persisted, and the awakened parent actually read the evidence fixture before producing the constrained final. This native test still does not validate long-context WebSocket reliability. These small experiments demonstrate feasibility, not a measured reliability guarantee or a proven root cause. They also suggest that waking the parent and getting it to resume useful processing need separate acceptance checks.
No private prompts, child report contents, job/session identifiers, infrastructure addresses, credentials or raw logs are included here.
Additional observed case on Codex Desktop for Windows (2026-10-08, Asia/Taipei). Reporting under this existing issue rather than creating a duplicate.
Environment and models
Installed Windows Appx package: OpenAI.Codex 26.1002.7124.0 (queried from installed package metadata, not the About dialog).
Windows: Microsoft Windows NT 10.0.22621.0, x64.
ChatGPT Plus authentication.
Parent: gpt-6.1-sol, reasoning effort medium, verified from recent local turn_context records.
Direct child implementation and independent review seats: gpt-6-luna, reasoning effort medium, explicitly passed to collaboration.spawn_agent with fork_turns="none". These were native Codex subagents, not external model CLI jobs.
Observed sequence
Parent delegated implementation to one Luna child and ended its turn with a user-facing status response.
User later asked why work had stopped. collaboration.list_agents reported that child as completed with its final result. The parent collected it only during this user-triggered turn.
Parent launched an independent Luna review and again ended its turn. User later asked why completed work had not been reported. list_agents again returned a completed result, and the parent then received/displayed the review findings.
In a later closeout, the parent explicitly stayed in the same active turn using collaboration.wait_agent. Child progress and final messages were received while waiting, and the parent collected implementation plus independent acceptance and reported them without another user prompt.
Implementation and review results existed and were retrievable; the observed failure was the lack of an automatic parent continuation/user-facing report after the parent had already ended its turn. The user repeatedly had to prompt the parent to collect completed work.
Expected / requested investigation
Please clarify the supported lifecycle contract for a direct native child completing after its parent turn ends: should it start an authorized parent continuation, enqueue an event for the next active turn, show a completion notification only, or require the parent to remain in wait_agent? If automatic continuation is supported, please investigate the idle-parent completion transition and user-visible reporting.
Cause and limits
The established failure point is completed child -> idle parent continuation/user-visible report. The underlying cause is NOT established. Parent/model premature turn termination is an observed contributor; event delivery, scheduling, desktop notification, prompt interactions, and intended product semantics have not been isolated. A result becoming visible after list_agents does not prove list_agents caused delivery. Receipt of messages during an active wait does not prove idle-parent wakeup works.
This is an incident report, not a newly run controlled reproduction. It is not evidence that Luna itself failed, nor that external harness return failures have the same cause. No raw project files, transcripts, credentials, account identifiers, private paths, or thread IDs are attached. No Codex runtime patches or private-interface workaround were applied; the user requests an official investigation rather than a local fix.
Activity
github-actions commentedon Mar 25, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
JaysonGeng commentedon Mar 31, 2026
Candidate fix pushed in my fork. Compare link: 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.
cipher982 commentedon Apr 6, 2026
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.
janbernloehr commentedon May 15, 2026
See also #13733 for the token cost angle
Necmttn commentedon Jun 21, 2026
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.
ilyes-i-ben commentedon Jul 2, 2026
+1
docs: 비전 노트에 책임자/실무 계층 + codex wake 부재(openai/codex#15723) 반영
8 remaining items
SlopSurfer4444 commentedon Aug 13, 2026
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.starSumi commentedon Aug 14, 2026
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?KRZ303 commentedon Aug 17, 2026
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-a446e0d3b749SlopSurfer4444 commentedon Aug 17, 2026
Local 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?
Jason-Wang313 commentedon Aug 28, 2026
A concrete ML-training use case where this matters a lot:
Suppose Codex launches a model training job that is expected to take ~10 hours. I want to be able to tell Codex something like:
Today, to approximate this kind of autonomous follow-up, the agent effectively has to stay in a continuous monitoring / goal-style loop and poll repeatedly. For a multi-hour training job, that can waste a large amount of model tokens even though nothing useful is happening during almost all of that time.
It would be much better if Codex had a lightweight scheduled wake-up / timer primitive, ideally alongside event-driven process completion notifications. The agent could suspend completely, consume no inference tokens while waiting, and then resume at either:
wake me in 10h/wake at 08:00), orwake when PID/job exits).On wake-up, it should retain the task context and be able to execute the next step itself, e.g. read
train.log, inspect TensorBoard/JSON metrics, check the final checkpoint, decide whether the run succeeded, and possibly launch the next experiment.This seems especially valuable for ML/research workflows where training/evaluation jobs commonly take many hours. The key benefit is not just convenience; it avoids spending tokens on unnecessary polling while still enabling truly autonomous long-running workflows.
Xp2510 commentedon Sep 8, 2026
Still reproducible on Codex Desktop for Windows 26.901.6511.0 with codex-cli 0.153.4 (reproduced 2026-09-07).
A parent task spawned a Luna subagent, told the user it would review the result, and then emitted its final answer while the child was still running. The child completed normally; the parent log recorded the child-completed activity immediately afterward, but no parent continuation was started. The parent processed the result only after the user sent another message.
I saw the same user-visible failure repeatedly in late August: the child’s completed state was visible in the sidebar/log while the parent remained idle until a status operation or user prompt occurred.
This is the same lifecycle gap described here, not a worker failure. Our current workaround is a global instruction requiring the parent to keep its turn alive with repeated
wait_agentcalls, then review and integrate the result before returning. That mitigates the abandoned result, but it does not fix event-driven wake-up and can consume unnecessary model turns/tokens while waiting.PracticalSwan commentedon Sep 8, 2026
Current Windows/Desktop evidence shows this is still reproducible on
codex-cli 0.153.4/ Desktop26.901.6511.0, and the latest MultiAgentV2 path explains why.I audited one canonical parent rollout (sanitized counts/ordinals only; no private prompt or child payload copied). It has 7,483 contiguous records. Earlier in the same root turn, 9 successful GLM child completions followed the healthy sequence:
The newest child (
/root/plan5_texture_runtime) completed after the parent turn had already terminated on a usage-limit error. The child rollout itself is complete (351 records, final assistant result persisted, thentask_complete). In the parent rollout the terminal ordering is:There is no subsequent
inter_agent_communication_metadataor parentagent_messagefor that completed child. So the child did finish and persist its result; the parent simply never materialized/read the completion after becoming idle.Current default-branch source matches this behavior. The active V2 terminal-turn forwarding constructs the parent completion communication with
trigger_turn: false. Insession/handlers.rs,inter_agent_communication()enqueues the mailbox item but only callsmaybe_start_turn_for_pending_work_with_sub_id()whentrigger_turnis true (or durable sleep is outstanding). This is therefore not a routed-model/GLM-specific loss; the same GLM children delivered normally while the parent turn was alive.This also complements #43221's late-subagent ordering: that report demonstrated analytics loss after parent termination; this rollout shows the user-visible completion can remain unread by the parent as well.
I would avoid a blanket
trigger_turn: truebecause #33061 documents the opposite duplicate-response failure. A focused regression should exercise the production V2 path: parent turn becomes terminal while a child is still running -> child completes -> terminal receipt remains durable and is either consumed by an explicit wait/join policy or wakes an idle owner exactly once under a defined completion-wake policy. At minimum, the result should not require an unrelated user message to become visible to the owner.PracticalSwan commentedon Sep 9, 2026
Additional current-version evidence for the background-process side of this lifecycle family, distinct from my earlier late-subagent example:
Environment:
26.903.8094.0codex-cli 0.153.4Observed event ordering, sanitized:
Read-only follow-up found no process still alive. The post-terminal artifacts are internally consistent rather than obviously partial: the history parses with 50 epoch rows, the selected checkpoint loads with
weights_only=True, contains the expected model-state tensor set, and its stored best epoch/validation metadata agrees with the history. The parent rollout is structurally valid and its projection is caught up exactly to EOF; there is simply no continuation record after the terminal usage-limit event to consume/report the later process completion.So in this case the useful child process was neither cancelled with the parent turn nor visibly adopted by an owner after the parent became terminal. It completed roughly 37 minutes later with no parent wake/result synthesis.
I am not claiming this necessarily uses the same internal completion path as MultiAgentV2 subagents. The shared lifecycle requirement seems to be: once a turn that owns asynchronous work becomes terminal, the runtime needs an explicit contract—cancel the work, or durably adopt its terminal result and deliver/wake exactly once. Silent survival plus an unreachable completion leaves externally useful work in an ambiguous state.
No prompt content, repository/project name, thread/session ID, local path, model artifact, account identifier, or credential is included here.
Document why Codex loses background completions
Document why Codex loses background completions
dengoswei commentedon Sep 16, 2026
Reproduced on Codex CLI
0.154.0(Linux, interactive CLI, MultiAgentV2). This is a delivered result without parent continuation, rather than a child failing to finish.Sanitized reproduction
Our workflow is: background job watcher -> failure event -> subagent collects evidence and diagnoses -> parent reviews and handles recovery -> resume observation.
In one observed case:
task_complete.trigger_turn=false.The child was correctly waiting for parent review rather than making an uncertain repair or resuming observation prematurely. Independent monitoring/protection continued, but the agent response chain stopped at the parent.
wait_agentis not a workaround for our requirementThe main agent must remain available to do other useful work while the background watcher runs. Blocking its execution in
wait_agent(or sleep), or keeping it alive through repeated timeout/wait cycles, does not meet that requirement. It also does not solve continuation after the parent has finished a turn.We need asynchronous, event-driven parent handling:
We are not asking for unconditional new turns for every child message. We need a supported completion/incident notification contract that allows background delegation without blocking the parent or abandoning the result. For this workflow, the issue remains unresolved.
No internal project names, job/session IDs, infrastructure addresses, local paths or raw logs are included.
ilyes-i-ben commentedon Sep 21, 2026
that's getting ridiculous, a lot of potential is blocked by this issue!
dengoswei commentedon Sep 24, 2026
Follow-up to my earlier report: we implemented local, opt-in idle-parent wake support, and found a second failure mode after a new parent turn has actually started.
1. Local support for triggering a new parent turn
Our local patch on top of Codex CLI
0.156.0adds an opt-in subagent completion policy (resume_parent_when_idle). A terminal child result is delivered through the parent mailbox and the existing pending-work scheduler starts a parent continuation when idle. Ordinary queue-only delivery remains the default. This is a local modification, not a claim that upstream already supports this behavior.2. The newly triggered turn can intermittently return an empty final
Observed sequence:
We saw this in two real sessions. Their original raw transport responses were not retained. We then reproduced an empty final in an isolated real-model WebSocket replay: generate an actual preceding parent final, then send the original child reports in a new turn using
previous_response_id. In that reproduction, the raw upstream events already contained empty final text, no text deltas and no tool calls; the response completed without an error/incomplete event. Thus, for this reproduction, the blank was present before Codex's response parser/UI.This is intermittent: another run of the same lifecycle continued normally. Successful requests also contain the exact literal
Message Type: FINAL_ANSWER, so that label alone is not a sufficient explanation. A full-input control with the same logical context also continued, but the sample is too small and backend/sampling state differs; this does not establish aprevious_response_iddefect. Request construction, continuation state and model/backend behavior still need causal isolation.3. Workaround: append one explicit continuation message
We preserved the original child report, including its
FINAL_ANSWERenvelope, and appended one separaterole: "user"/input_textmessage instructing the parent to review the returned result against the original task and continue. This is an automatic host notice, not a manual user prompt. The long-context WebSocket probes retainedprevious_response_id; we did not need to replace continuation with a full request to add the notice.A neutral form, suitable for either successful or failed child reports, is:
Two qualifying long-context reminder probes produced evidence-inspection tool calls (those tools were deliberately not executed). Separately, a short synthetic native idle-wake test over SSE, with the notice inserted by a proxy, actually read a local evidence fixture and produced a correct constrained final. That test had only one initial user turn submission; child completion triggered the second parent turn automatically. The proxy notice was not persisted in native history, which is a limitation of that test harness.
Update: the bounded automatic notice is now implemented in the native idle-completion-wake path and persisted in session history. Focused notification integration tests cover idle success/failure, deferred completion, active delivery, and persistence across resume. A freshly built package also passed the short real-model SSE lifecycle with an observation-only proxy: native and forwarded request bodies matched, the original child report was preserved, one notice was persisted, and the awakened parent actually read the evidence fixture before producing the constrained final. This native test still does not validate long-context WebSocket reliability. These small experiments demonstrate feasibility, not a measured reliability guarantee or a proven root cause. They also suggest that waking the parent and getting it to resume useful processing need separate acceptance checks.
No private prompts, child report contents, job/session identifiers, infrastructure addresses, credentials or raw logs are included here.
docs: explain temporary cli-run workaround (openai/codex#42908, opena…
KyleLokK commentedon Oct 8, 2026
Additional observed case on Codex Desktop for Windows (2026-10-08, Asia/Taipei). Reporting under this existing issue rather than creating a duplicate.
Environment and models
Observed sequence
Implementation and review results existed and were retrievable; the observed failure was the lack of an automatic parent continuation/user-facing report after the parent had already ended its turn. The user repeatedly had to prompt the parent to collect completed work.
Expected / requested investigation
Please clarify the supported lifecycle contract for a direct native child completing after its parent turn ends: should it start an authorized parent continuation, enqueue an event for the next active turn, show a completion notification only, or require the parent to remain in wait_agent? If automatic continuation is supported, please investigate the idle-parent completion transition and user-visible reporting.
Cause and limits
The established failure point is completed child -> idle parent continuation/user-visible report. The underlying cause is NOT established. Parent/model premature turn termination is an observed contributor; event delivery, scheduling, desktop notification, prompt interactions, and intended product semantics have not been isolated. A result becoming visible after list_agents does not prove list_agents caused delivery. Receipt of messages during an active wait does not prove idle-parent wakeup works.
This is an incident report, not a newly run controlled reproduction. It is not evidence that Luna itself failed, nor that external harness return failures have the same cause. No raw project files, transcripts, credentials, account identifiers, private paths, or thread IDs are attached. No Codex runtime patches or private-interface workaround were applied; the user requests an official investigation rather than a local fix.