Repository navigation
fix(codex): correct rollout tail ownership so the committed feed keeps growing - #9
Merged
Merged
Conversation
For client-executed tools (MCP especially), Codex issues the follow-up /v1/responses request carrying the function_call_output before the proxy observes the previous socket's response-end — which on long-lived / broken transports may never arrive. Holding activeFlowId until response-end demoted the legitimate follow-up flow to 'secondary', so the renderer saw only phase changes and no block/text events for it. Add a 'completed' attribution state: response.completed (the upstream semantic terminal of a Responses turn) releases activeFlowId and marks the flow completed, so the next model turn can claim the active slot while stray tail chunks from the finished flow are still suppressed. Refs Juliusolsson05/agent-code#159 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The committed-transcript channel could go permanently silent after bootstrap. CodexHeadless.start() pinned tailFile() to a single rollout JSONL; when Codex wrote the resumed conversation into a different (reconstructed / forked) file, the tailer kept reading the dead one. The renderer still received live proxy semantics, so the feed appeared to "clear" every couple of turns while runtime.entries stayed frozen at the bootstrap snapshot. See Juliusolsson05/agent-code#159. Resume path (tailResumeRolloutFile / watchResumeForkRollout): - After attaching to the resume file, watch the sessions dir for a bounded window and switch the committed tail to a forked rollout if one appears. - A candidate must match BOTH our cwd AND share copied-history item ids with the resumed file (isResumeForkCandidate + readRolloutLineageIds / collectRolloutLineageIds). cwd alone is not enough: orchestration branches spawn sibling Codex agents in the same directory, so a cwd-only match would latch a sibling agent's transcript. Lineage is matched by opaque item id VALUE (msg_/fc_/ rs_/call_/turn ids), which survives any re-serialisation a copied history might introduce. - A same-cwd candidate with no shared history is rejected with a diagnostic rather than silently adopted. Fresh path (tailNewRolloutFile): - Keeps "first new rollout wins", but now emits a diagnostic when additional new rollout files appear during startup, surfacing the wrong-session-attach ambiguity instead of failing silently. Refs Juliusolsson05/agent-code#159 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review feedback on the rollout-tail-ownership fix: - watchResumeForkRollout: replace the prime-then-watch design with a single ignoreInitial:false watcher. The two-phase version recorded a fork created during the priming scan as "pre-existing" and never evaluated it — defeating the fix in exactly its target case. Freshness is now decided by the isResumeForkCandidate mtime guard, not by watch ordering. - isResumeForkCandidate: compare cwd canonically (normalizeCwd — realpath resolves macOS symlink/case, resolve handles `..`/trailing slash) so a symlinked or non-canonical cwd no longer rejects a real fork. - Drop the empty-lineage cwd-only fallback: if the resumed file has no fingerprintable history we can no longer prove a same-cwd candidate is ours, so fail closed rather than risk adopting a sibling agent's transcript. - Widen the candidate read cap 256 KiB -> 4 MiB so large copied bootstrap entries cannot push shared item ids out of range. - Document the intentional open-before-close tail overlap in switchTo. - CodexResponsesAdapter: a 'completed' flow now drops late chunks instead of growing flow.buffer and refreshing lastEventAt — the latter previously kept the watchdog from ever reaping a finished flow on a keepalive-heavy transport. Refs Juliusolsson05/agent-code#159 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Owner
Author
|
Thanks — review addressed in Blocking
Should Fix
Nits
|
Owner
Author
|
Follow-up work from review tracked as bookkeeping issues (not blocking this PR):
|
Juliusolsson05
added a commit
to Juliusolsson05/agent-code
that referenced
this pull request
May 16, 2026
Bumps packages/codex-headless to include the rollout-tail-ownership fix (Juliusolsson05/codex-headless#9). The committed-transcript tailer could go permanently silent after bootstrap: it pinned to a single rollout JSONL and, when a resumed Codex session's conversation was written into a forked rollout file, kept reading the dead one. The renderer still received live proxy semantics, so the feed appeared to "clear" every couple of turns during MCP / tool-call runs while runtime.entries stayed frozen. The submodule fix makes the tailer follow a forked rollout — gated by cwd plus copied-history lineage so sibling orchestration agents in the same directory are not latched — and releases the proxy flow slot on response.completed so MCP follow-up turns are not starved. Root-cause analysis: #159. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Juliusolsson05
added a commit
to Juliusolsson05/agent-code
that referenced
this pull request
May 16, 2026
Bumps packages/codex-headless to include the rollout-tail-ownership fix (Juliusolsson05/codex-headless#9). The committed-transcript tailer could go permanently silent after bootstrap: it pinned to a single rollout JSONL and, when a resumed Codex session's conversation was written into a forked rollout file, kept reading the dead one. The renderer still received live proxy semantics, so the feed appeared to "clear" every couple of turns during MCP / tool-call runs while runtime.entries stayed frozen. The submodule fix makes the tailer follow a forked rollout — gated by cwd plus copied-history lineage so sibling orchestration agents in the same directory are not latched — and releases the proxy flow slot on response.completed so MCP follow-up turns are not starved. Root-cause analysis: #159. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
During MCP / client-tool agentic runs the Agent Code feed appears to "clear" every couple of turns and stops growing —
runtime.entriesstays frozen at the bootstrap snapshot while the proxy semantic stream keeps producing turns. Traced in Juliusolsson05/agent-code#159.Root cause is here, not in the renderer: the committed-transcript channel goes permanently silent after bootstrap.
CodexHeadless.start()pinstailFile()to a single rollout JSONL. When Codex writes the resumed conversation into a different (reconstructed / forked) rollout file, the tailer keeps reading the dead one — so no liverollout-entryevents ever land.activeFlowIduntil the socket'sresponse-end. Codex issues the follow-up/v1/responses(carryingfunction_call_output) before that end is observed — and on long-lived transports it may never arrive — so the legitimate follow-up flow was demoted tosecondaryand never published.Fix
CodexResponsesAdapter— release the flow slot onresponse.completed. New'completed'attribution state: the upstream semantic terminal releasesactiveFlowIdso the next model turn can claim the active slot. A'completed'flow drops late chunks instead of growing its buffer / refreshinglastEventAt, so it is reaped rather than leaked.CodexHeadless— correct rollout tail ownership...-normalised) and share copied-history item ids with the resumed file. cwd alone is unsafe — orchestration branches spawn sibling Codex agents in the same directory. Lineage is matched by opaque item id value (msg_/fc_/rs_/call_/turn ids), which survives re-serialisation.tailNewRolloutFileemits a diagnostic when additional new rollout files appear during startup, surfacing wrong-session-attach ambiguity.Verification
npm run build(tsc) passes clean.response.completed.Resolves
Root cause of Juliusolsson05/agent-code#159 (paired with the submodule-pointer PR Juliusolsson05/agent-code#160).
Follow-up issues (bookkeeping — not blocking)
rollout-error)🤖 Generated with Claude Code