Skip to content

fix(cli): flush missed parts when run goes idle - #50453

Closed
BoYanZh wants to merge 1 commit into
anomalyco:devfrom
BoYanZh:fix/run-attach-flush-final-parts
Closed

BoYanZh wants to merge 1 commit into
anomalyco:devfrom
BoYanZh:fix/run-attach-flush-final-parts

Conversation

@BoYanZh

@BoYanZh BoYanZh commented Sep 21, 2026

Copy link
Copy Markdown

Problem

Non-interactive opencode run --format json intermittently exits 0 with empty stdout even though the backend recorded output. Observed with run --attach (also reproducible without it): a short assistant reply such as OK is stored server-side (verified in the session database), but the CLI prints nothing — downstream consumers see an empty turn.

Root cause in loop() (packages/opencode/src/cli/cmd/run.ts):

  1. Text is printed only on message.part.updated with part.time.end set.
  2. The loop breaks the moment session.status turns idle.

If idle wins the race against the final part update (typical for instant, short answers — long streamed replies almost always win), the client exits before printing anything. The codebase already knows transports can drop status events (stream.transport.ts: "some transports can miss status events"); this is the inverse direction with no mitigation at all.

Fix

  • Track mirrored part IDs in the live loop.
  • On idle, re-fetch the session timeline via client.session.messages() and mirror finished text / reasoning (when --thinking) / completed-or-errored tool parts that were never streamed, reusing the exact same rendering path (outputPart()), so live and flushed output are identical.
  • Best-effort: fetch failures keep the previous behavior.
  • The live-loop branches are refactored to call the shared outputPart() with no behavior change.

Verification

  • New unit tests: packages/opencode/test/cli/run/flush-missed-parts.test.ts (9 tests: missed finished parts, already-emitted skip, unfinished skip, thinking gate, tool statuses, user-message exclusion, synthetic-signal exclusion, empty timeline).
  • Existing test/cli/run/run-process.test.ts subprocess suite: 13/13 pass (covers the refactor).
  • tsgo --noEmit clean; oxlint reports no new warnings on touched files.
  • Manual: patched CLI run against a live server returns the expected step_start + text JSON lines repeatedly with no duplication.

Notes

  • Completed tool calls were already executed server-side; the flush only renders them, never re-runs.
  • session.messages() lookup is by session ID only (same as the existing session.get usage), so this works for attach and local runs alike.

Non-interactive \
un\ prints text only on message.part.updated with

time.end set, and exits the moment session.status turns idle. If idle

wins the race against the final part update (typical for instant,

short answers), the command exits 0 with empty output despite the

backend having recorded it.

Track mirrored part IDs; on idle, re-fetch the session timeline and

mirror finished text/reasoning/tool parts the stream never delivered.

Best-effort: fetch failures keep the previous behavior. Live-loop

rendering is refactored into a shared outputPart() with no behavior

change (covered by existing run-process subprocess tests).
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Sep 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.

Feel free to open a new pull request that follows our guidelines.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Sep 22, 2026
@github-actions github-actions Bot closed this Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant