fix(orchestrator): Close and drain OpenCode 1.x tool items - #4786
fix(orchestrator): Close and drain OpenCode 1.x tool items#4786mwolson wants to merge 6 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a targeted bug fix that ensures OpenCode tool items are properly closed when turns end mid-flight, preventing UI spinners from being stuck forever. The ~150 lines of logic changes are well-tested with 8+ replay fixtures covering various edge cases (error cleanup, interrupts, unscoped errors), the author has recent commits on this exact code area, and all previous review comments have been addressed in the head commit. You can add or adjust custom eligibility rules. Learn more. |
0069b28 to
8c7575d
Compare
a3b3a5d to
c49d36e
Compare
a543fd4 to
378615b
Compare
c49d36e to
ddaffdf
Compare
e24b59c to
4213ac5
Compare
ddaffdf to
88e61c5
Compare
25de21d to
0af2a6e
Compare
88e61c5 to
50bc97c
Compare
22bd872 to
a27c1cc
Compare
50bc97c to
641e99b
Compare
a186d64 to
5b1a115
Compare
641e99b to
0af7ffd
Compare
519c42a to
4c55679
Compare
0af7ffd to
db55c22
Compare
CI Check fails on current CTM because layerTest now surfaces ServerSettingsError while the ProviderSessionManager test layer requires never. orDie matches the other test layers.
`finalizeTurn` flushed only `text` and `reasoning` parts, so a tool still mid-flight when the turn ended kept whatever state it was last observed in. OpenCode's `session.abort` stops the run without reporting a final state for the running tool, so nothing ever closed the item: after a Stop, the interrupted run's `command_execution` stayed `running` permanently and the row spun forever in the UI. Sweeps non-terminal tool parts during finalize and emits them with the turn's terminal status, threaded through `emitToolPart` and `emitSubagent` as an optional override so the existing projection logic is reused rather than duplicated. `OrchestrationV2TurnItemStatus` and `OrchestrationV2Subagent` declare the same status literals, so an interrupted subagent stays interrupted rather than collapsing to failed. Found by the new `opencode-interrupt-direct-stop` live pack, which reproduced it as `command_execution:running` surviving on an `interrupted` run. The same pack now reports `command_execution:interrupted` with zero running commands.
The existing tests covered only the pure status mapping, so they would still pass with the sweep deleted, applied to already-terminal tools, or failing to forward the override. Codex review of pingdotgg#4759 called that out. Adds an OpenCode provider to the `turn_interrupt_mid_tool` replay fixture, which drives the real adapter and asserts the projection. The transcript reproduces the ordering from a live run rather than an idealised one: the tool goes running, the abort goes out, then `session.error` and idle arrive, and only afterwards does OpenCode's cleanup emit its own tool part update. That late update lands on an already-finalized turn and is ignored, so the item's `interrupted` status has to come from the sweep. Verified the fixture catches the regression: with the sweep commented out it fails with `expected 'running' to equal 'interrupted'`, and nothing else in the suite breaks.
db55c22 to
5842bb0
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5842bb0. Configure here.
|
Closing this as superseded. OpenCode 1.x is retired on the v2 line in favor of native OpenCode 2. The drain-before-finalize work here is specific to 1.x |

Summary
session.errorcleanup so a tool that finishes during OpenCode's 250ms cleanup window can land its real result before finalize.This branch is a deliberate multi-commit consolidation onto current
t3code/codex-turn-mapping. Each commit is one concern. It does not include OpenCode 2.Problem and Fix
runningbecausefinalizeTurnflushed text and reasoning but not tool parts.session.errorfinalized immediately, so later cleanup part updates were dropped and the tool lost its real exit/output.Validation
vp check: 0 errors, 22 inherited warningst3code/codex-turn-mapping(feat(contracts): track thread title regeneration)Absorbed PRs
Superseded
Note
Medium Risk
Changes core OpenCode turn finalization and error/idle event ordering in orchestration-v2; wrong drain timing could leave turns open or drop late cleanup results, but behavior is heavily fixture-tested.
Overview
Fixes OpenCode 1.x turns that leave tool rows stuck on
runningafter interrupt, abort, orsession.error, and cases where cleanup emits late tool parts after the adapter would have already finalized.Turn finalization sweep:
finalizeTurnnow re-emits any tool stillpending/running(and interrupted cleanup tool errors) using a newterminalToolStatusmapping so item status matches the turn (completed,interrupted,cancelled,failed).emitToolPart/ subagent projection accept an optional forced terminal status and only synthesizecompletedAtwhen the native part never terminalized.Error cleanup drain: On scoped
session.errorwith in-flight tools, finalization is deferred viapendingErrorCleanupuntil OpenCode’s cleanup can land—tracking assistant message IDs, tolerating pre-cleanup idle pairs, and settling on completed assistant messages or follow-up idle when publishers omit the usual signals. Unscoped errors or interrupts without cleanup still finalize immediately; subagent statuses now follow turn-item literals (e.g. interrupted stays interrupted).Tests: Unit coverage for
terminalToolStatusplus orchestrator replay fixtures for drain, fallback, multi-message, unscoped error, and mid-tool interrupt (including OpenCode onturn_interrupt_mid_tool).Reviewed by Cursor Bugbot for commit fdb35c8. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Fix OpenCode 1.x tool items to close and drain on session error
OpenCodeAdapterV2: when asession.erroroccurs with in-flight tools, finalization is held until cleanup tool completions arrive rather than closing the turn immediately.pendingErrorCleanupstate onActiveOpenCodeTurnto track which tool message IDs are still outstanding and whether a pre-cleanup idle has been seen.emitToolPartandemitSubagentto accept a terminal turn status, closing any pending/running tool items with a matching terminal status and synthesizedcompletedAtwhen no native completion time exists.finalizeTurnnow emits non-terminal tool parts (including interrupted-error parts) before closing the turn, preventing indefinitely running items in the projection.Macroscope summarized fdb35c8.