Background terminal (unified_exec) exit while the session is idle never produces a follow-up turn — the completed task is silently ignored
Summary
When a command started via the experimental unified exec tool is returned to the model as still running (backgrounded), and the process then exits while the agent session is idle, nothing brings the session back. The exit-watcher emits an outbound client event (ExecCommandEnd), but the idle TUI drops it, and no turn is ever started to let the model observe the result. The practical effect: "start a long build/test in the background and tell me when it's done" ends with the agent never coming back — in our original observation, a ~6-minute background task finished and the session sat idle indefinitely, unaware.
Related reports of the agent failing to notice background-command completion: #14314, #22957, #12033. Adjacent: #17737 (feature request for background-terminal monitoring — evidence of user demand for completion awareness).
Reproduction
Verified still present as of rust-v0.144.5 (and rust-v0.145.0-alpha.20); first characterized on rust-v0.144.4, macOS arm64.
- Start a codex session with
use_experimental_unified_exec_tool = true. - Prompt: "Run
sleep 90 && echo donein the background and report the result when it finishes." - The model calls
exec_command; the process outlives the yield window and is returned as still-running; the turn completes; the session goes idle. - Wait for the process to exit (~90s).
- Observe: the exit produces an
ExecCommandEndclient event only. No new turn starts; the model never sees the exit; the session stays idle indefinitely.
Any exit that lands after the turn completes reproduces this; longer sleeps just make the idle state unambiguous.
Where the gap is (code anchors at rust-v0.144.5)
- The drop site: the TUI completion handler early-returns when no task is running —
tui/src/chatwidget/command_lifecycle.rs:154,if !self.bottom_pane.is_task_running() { return; }— so the deliveredExecCommandEndis discarded exactly in the idle case. - The exit-watcher tail (
core/src/unified_exec/) emits that terminal client event and stops — there is no path from "background process exited while idle" into the session's turn machinery. - The stock idle-start path (
maybe_start_turn_for_pending_work) is driven by mailbox sources only; a background exit is not one of them, and nothing else submits work for it. - Interactively (session busy), the exit is surfaced in the next poll/tool result — the gap is specifically the idle case, which is also the headline use-case for backgrounding.
Expected behavior
A background terminal exit while idle should wake the session and let the model process the exit as input (bounded, attributable to a natural exit rather than a kill), or at minimum offer a supported hook for a client to trigger that continuation.
Context
We (a fork running codex as a multi-agent orchestrator) hit this daily: panel/CI-style dispatches that background long commands and rely on the agent noticing completion. We have a working implementation of the wake path behind a default-off flag (routed through the serialized submission loop as an internal op, so aborts/capacity compose; extensive test matrix), and we intend to open it as a small stacked series once our production soak completes. Happy to share the design notes in the meantime.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗