fix(server): a Cursor command you stop shows as interrupted - #13806
Conversation
When a turn is stopped mid-tool, Cursor never sends tool-call-completed for the running command. finalizeTurn closed those tools as "completed", so the cut-short command looked like it finished. Unfinished tools now take the turn's terminal status (interrupted, failed, or cancelled), as Pi and Codex already report them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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. |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a small, self-contained server bug fix that correctly marks unfinished Cursor commands with the enclosing turn’s terminal status. Existing completed-tool behavior is preserved, and the interrupted-mid-tool regression is explicitly tested. You can add or adjust custom eligibility rules. Learn more. |
When you stop a Cursor turn while a command is running, the command is saved as
completed, as if it had finished. The run and provider turn are correctlyinterrupted.Evidence
Seen live on the V2 head with
gpt-5.4-nano. The prompt ranfor i in $(seq 1 60); do echo tick $i; sleep 1; doneand was stopped about 17s into the command. The provider log has atool-call-startedfor the shell call, thenrun.cancel, thenrun.completedwithstatus: "cancelled". It never has atool-call-completed. The projection then held:Pi and Codex report a stopped command as
interrupted, and Claude reports it asfailed.Fix
finalizeTurncloses any tool that never got atool-call-completed. It used to close them ascompleted. Now, when the turn endsinterrupted,failed, orcancelled, those tools take that status. Tools on a turn that completes normally are unchanged.Web keeps stopped rows visible once #13790 lands. Without that PR, web hides them, the same as for Codex.
Verification
turn_interrupt_mid_tool/cursor_output.tsnow assertscommandItem.status === "interrupted". The recorded transcript already stops mid-command without atool-call-completed.vp test run src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts -t cursor:expected 'completed' to equal 'interrupted'(1 failed, 9 passed).vp test run src/orchestration-v2/Adapters/CursorAdapterV2.test.ts: 11 passed.vp exec tsc --noEmit -p .inapps/server: noerror TSorwarning TS.vp run knip:check: clean.vp linton the two touched files reports one warning. It is the unusedlayerat the end ofCursorAdapterV2.ts, which is already on the base and is not touched here.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code