fix(server): Grok monitor updates stay running until Grok says they finished - #13557
Conversation
| readonly output?: string; | ||
| }) { | ||
| const context = yield* Ref.get(activeTurn); | ||
| if (context === null || context.finalized || !context.promptSettled) return; |
There was a problem hiding this comment.
🟠 High Adapters/AcpAdapterV2.ts:3596
When task_completed arrives before promptSettled is set, the registered tool row remains inProgress, so deferred finalization stays blocked and the run never completes. finishRegisteredBackgroundTool returns on !context.promptSettled, while applyLateBackgroundMutation has already cleared the running-task set; allow this terminal mutation to finish the row before the completion callback settles the prompt.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts around line 3596:
When `task_completed` arrives before `promptSettled` is set, the registered tool row remains `inProgress`, so deferred finalization stays blocked and the run never completes. `finishRegisteredBackgroundTool` returns on `!context.promptSettled`, while `applyLateBackgroundMutation` has already cleared the running-task set; allow this terminal mutation to finish the row before the completion callback settles the prompt.
| const rawOutput = unknownRecord(withTitle.data.rawOutput); | ||
| const outputType = nonEmptyString(rawOutput?.type)?.toLowerCase(); | ||
| if (outputType === "bash") { | ||
| const reportedRunning = withTitle.status === "inProgress" || withTitle.status === "pending"; |
There was a problem hiding this comment.
🟠 High acp/XAiAcpExtension.ts:510
A status-less terminal Bash re-report after an earlier inProgress update remains inProgress, so its exit_code is never applied and deferred finalization leaves the turn hanging. mergeToolCallState preserves the previous status via next.status ?? previous.status, causing reportedRunning to misclassify the current update; preserve whether status was present in the incoming update and terminalize status-less Bash results from their exit_code.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/provider/acp/XAiAcpExtension.ts around line 510:
A status-less terminal Bash re-report after an earlier `inProgress` update remains `inProgress`, so its `exit_code` is never applied and deferred finalization leaves the turn hanging. `mergeToolCallState` preserves the previous status via `next.status ?? previous.status`, causing `reportedRunning` to misclassify the current update; preserve whether `status` was present in the incoming update and terminalize status-less Bash results from their `exit_code`.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
| if (status !== "pending" && status !== "running") return; | ||
| context.awaitingBackgroundHydration.delete(mutation.taskId); | ||
| const finished = { ...tool, status: mutation.status }; | ||
| yield* emitTool( |
There was a problem hiding this comment.
This changes how a settled, deferred turn finishes its registered monitor tool, but the tests do not assert that a task_completed mutation terminalizes that row with the snapshot output and lets the turn settle without a TaskOutput update. Could you add a focused adapter regression test for that path (including the failed exit-code case)?
Posted via Macroscope — Effect Service Conventions
ApprovabilityVerdict: Would Approve Macroscope's review found this PR approvable — This is a focused Grok lifecycle bug fix that keeps monitor rows running through intermediate ticks and terminalizes them from the structured completion snapshot, including failure status and final output. The adapter-level deferred-finalization edge cases and missing integration regression coverage remain notable risks to verify. Not approved because:
Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more. |
…inished Grok 1.0.41 streams every monitor tick as a tool_call_update with status "in_progress" and a Bash-shaped rawOutput whose exit_code is already 0. normalizeXAiAcpToolCallState read that exit_code as a finished command, so the monitor row completed at its first tick and the held root turn settled while the monitor was still running. An explicit in_progress or pending status from Grok now wins; the exit_code inference only applies when Grok sends no status. Grok never sends a completed status for a monitor. Its end is the structured `_x.ai/task_completed`, which now carries the snapshot's final output and exit code and finishes the registered monitor row while a settled turn is held open for it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
c92af70 to
7fd0e1d
Compare
…inished (#13557) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Live Grok 1.0.41 recordings (#13537) showed that a Grok Monitor row completes at its first tick. The root run then settles while the monitored command is still running.
Cause
Grok streams every monitor tick as a
tool_call_updatewithstatus: "in_progress", and its rawOutput is shaped like a finished shell result ({ type: "Bash", exit_code: 0, output_for_prompt: "tick 1\n" }).normalizeXAiAcpToolCallStateinferred "completed" from any Bash rawOutput withexit_code: 0and some text, ignoring Grok's own status. Recorded sequence forfor i in 1 2 3; do sleep 8; echo tick $i; done:tool_callmonitor→tool_call_updatevariant: "Monitor"→ start ACK{ type: "Monitor", taskId, persistent: false }, plus_x.ai/task_backgroundedin_progressBash-shaped update per tick, with a new_x.ai/monitor_event {task_id, event_text}for each_x.ai/task_completed {task_snapshot: {task_id, output, exit_code, kind: "monitor"}}. Grok never sends a completed status for the monitor tool itself, and the old<monitor-event …>andMonitor … endedtext forms never appear asuser_message_chunks.Fix
in_progressorpendingstatus from Grok now wins. Theexit_codeinference applies only when Grok sends no status._x.ai/task_completedis now authoritative: the lifecycle mutation carries the snapshot's final output and exit code (non-zero means failed). When a settled root turn is held open by deferred finalize for that task, the registered row finishes with that output and the turn can settle. While the prompt is still open, the existing TaskOutput hydration and mid-turn continuation paths are unchanged.Live check through the real adapter after the fix: the monitor row stays
runningthrough tick 1 and tick 2, completes withtick 1\ntick 2\ntick 3\nwhentask_completedarrives, and the run completes after the 3 s quiet window. Before the fix it completed at tick 1.Tests
XAiAcpExtensiontests, fed the exact frames recorded from Grok 1.0.41 through the adapter's parse path: the start update merged with the tick-1in_progressupdate staysinProgress, and the recordedtask_completedsnapshot maps to a completed mutation with the final output (a non-zero exit code maps to failed). Without the fix the tick test fails withexpected 'completed' to be 'inProgress'.inProgressand expectedcompleted. It now covers the case the inference is still for, a Bash result with no status.task_completed, so replay has to hold them until the held turn settles. The fixture is planned for the gates PR that stacks on this one.Verification
vp test run src/provider/acp/XAiAcpExtension.test.ts src/orchestration-v2/Adapters/AcpAdapterV2.test.ts src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts: 249 passed.vp exec tsc --noEmit -p .in apps/server: 0error TS/warning TS.vp run knip:check: clean.vp linton the touched files: no new warnings (two existing unused-symbol warnings in these files are on the base).Stacked on #13537.
Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code