Background subprocesses/subagents do not wake the calling agent on completion

Open 💬 18 comments Opened Mar 25, 2026 by zoox101
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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:

  1. Block the message thread by continuously polling for a result; or
  2. 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.

View original on GitHub ↗

18 Comments

github-actions[bot] contributor · 5 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #14314
  • #14303
  • #15461

Powered by Codex Action

JaysonGeng · 4 months ago

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:

  1. 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.
  2. 2. Sub-agent completion notifications used trigger_turn = false, so an idle parent never woke up.

Patch summary:

  1. The async watcher now claims unseen background completions, injects a synthetic function_call_output into the next turn, and wakes the idle session.
  2. 2. write_stdin and the watcher coordinate with an atomic claim to avoid double delivery.
  3. 3. 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.

This should also cover #15461, #14314, and #14303.

cipher982 · 4 months ago

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.

janbernloehr · 3 months ago

See also #13733 for the token cost angle

Necmttn · 2 months ago

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 · 1 month ago

+1

StatPan · 1 month ago

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.

15230745073 · 1 month ago

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.

StatPan · 1 month ago

Consolidated PoC update for an explicit, one-shot subagent completion callback:

The PoC adds a separate register_completion_callback function. 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 explicit wait_agent polling.

The public-path integration test covers:

spawn child → register callback → parent becomes idle
→ child completes → one parent continuation starts without user input

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_agent schema 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.

StatPan · 1 month ago

Follow-up after implementing and live-testing the PoC: this supersedes the explicit register_completion_callback approach 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:

  • A terminal subagent or opted-in background exec produces one bounded completion receipt.
  • Delivery is deduplicated and starts exactly one continuation when the owning session is idle.
  • If the session is still busy, delivery waits for a safe turn boundary.
  • write_stdin and the async watcher share a claim state so polling cannot race with, or duplicate, automatic delivery.
  • Reserved collaboration tool schemas remain unchanged.

For unified exec, the PoC uses an explicit on_exit: wake policy. 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:

(sleep 10; echo 'SHELL BACKGROUND CALLBACK DONE') & echo "PID=$!"

The validated sequence is:

PID/session returned promptly -> parent turn completes
-> no polling and no user input
-> shell background job finishes
-> exactly one parent continuation starts with the completion result

The implementation detects the shell background operator syntactically, rather than using a string match, so &&, quoted ampersands, redirections such as 2>&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, or setsid; 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 no wait_agent/write_stdin polling. 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.

chrisgeo · 28 days ago

Confirmed on the current combined ChatGPT/Codex macOS app with an event-ordered local trace.

Environment:

  • ChatGPT/Codex app 26.727.40816 (build 6067)
  • Bundled codex-cli 0.146.0-alpha.9.2
  • macOS 26.6 (25G72), arm64
  • MultiAgentV2 collaboration tools

Sanitized timeline:

2026-07-30T23:39:26.774Z  parent calls collaboration.spawn_agent
2026-07-30T23:39:27.044Z  parent records sub_agent_activity kind=started
2026-07-30T23:39:36.097Z  parent emits task_complete and becomes idle

2026-07-31T00:00:15.815Z  child calls collaboration.send_message(target=/root)
2026-07-31T00:00:15.820Z  child records parent sub_agent_activity kind=interacted
2026-07-31T00:00:24.837Z  child emits task_complete successfully

2026-07-31T00:00:54.954Z  user manually prompts parent after noticing job is done
2026-07-31T00:01:03.026Z  parent calls collaboration.list_agents
2026-07-31T00:01:03.114Z  list_agents returns the completed child result
2026-07-31T00:01:03.132Z  child messages finally materialize in parent with trigger_turn=false

There was no parent task_started event between the child's explicit send_message/task_complete and 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_agents retrieves the result, and the imported inter-agent messages are marked trigger_turn:false.

Sanitized identifiers, in case they are useful for internal telemetry correlation:

  • Parent thread: 019fb43b-291c-74e1-8357-f6f0c9ecb159
  • Child thread: 019fb565-ab13-7b00-a688-f2b862f8f9f2
  • Parent spawn turn: 019fb565-4a64-7f52-a362-a870786f5ad1
  • Child completion turn: 019fb565-abc4-74a0-adb3-8eb046a7384c

The same user-visible failure was also observed on July 28, before build 6067 was 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.

Bruce-anle · 22 days ago

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.

Bruce-anle · 21 days ago

Update (2026-08-07; live MultiAgentV2/app-server smoke on Codex 0.146.1):

  • A parent started two real children with a concurrency limit of 2, then ended its turn.
  • The parent used no wait_agent, polling, or user input.
  • After the children reached terminal state, their results were delivered and the idle parent resumed automatically.
  • After capacity was released, the parent created a third child, ended its turn again, and resumed automatically when the late child completed.
  • The full sequence completed without a 429.

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 main still constructs the MultiAgentV2 completion communication with trigger_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.

ikerrrrrrrrrrr · 16 days ago

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:

  • wakes an idle thread when a background terminal or subagent completes;
  • queues the completion when a model turn or tool call is active, then injects it at the next safe boundary;
  • avoids polling and does not interrupt active model reasoning;
  • keeps terminal completion context bounded by line and token limits;
  • allows non-consuming tail inspection through write_stdin;
  • suppresses automatic wakeups for user-initiated termination.

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.

SlopSurfer4444 · 15 days ago

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:

parent calls join(all, exact direct-child turns)
  -> Armed
child terminal results
  -> durable Receipt(s)
all receipts present
  -> one FIFO-admitted parent continuation
  -> Acknowledged
user/newer work/cancellation
  -> Cancelled (fail closed)

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.

starSumi · 14 days ago

One source-level refinement to the opt-in direction, based on current main at 636e505c. 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, where trigger_turn is currently hard-coded to false. 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 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?

krzkraw · 10 days ago

Reproduced an intermittent active-wait variant on Codex Desktop.

Environment:

  • Session-reported runtime: codex-cli 0.148.0-alpha.9
  • MultiAgentV2
  • Direct child reused through followup_task

Observed behavior:

  • 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.

Codex feedback ID: 01a00cb4-a2a7-7f32-9fcb-a446e0d3b749

SlopSurfer4444 · 10 days ago

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?