Skip to content

fix(opencode): emit a done record at the end of JSON runs - #50341

Closed
oldantique wants to merge 1 commit into
anomalyco:devfrom
oldantique:fix/run-json-done
Closed

oldantique wants to merge 1 commit into
anomalyco:devfrom
oldantique:fix/run-json-done

Conversation

@oldantique

Copy link
Copy Markdown

Issue for this PR

Closes #36413

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

opencode run --format json has no record that marks the end of a run, so a consumer can't tell a finished run from one that died after its last event. And an auto-rejected permission leaves nothing in the JSON stream (the warning only goes to stderr).

This adds one final record, written once the primary session goes idle and the prompt/command request has returned:

{"type":"done","timestamp":…,"sessionID":"ses_…","reason":"idle","rejected":["bash"]}

rejected lists the permissions the CLI auto-rejected (deduped, [] if none). done means the run completed, not that it succeeded: earlier error records and the exit code keep their meaning.

It's written in finish() rather than at the idle break, because the request can still fail after idle is seen, and then done would not be the last line. If the event stream ends without idle, or the loop throws, there's no done. Exit codes, text output and --attach are unchanged (attach returns before the loop result today; I left that alone).

On #44966: ending the loop on step_finish with reason: "stop" isn't safe, because some providers return stop together with tool calls and SessionPrompt keeps going (see "loop continues when finish is stop but assistant has tool parts" in prompt.test.ts). The new test "waits for continuation after stop with tool calls" covers this. With #44966's change applied, the run stops after the first step_finish and the continuation text is lost.

How did you verify your code works?

From packages/opencode:

  • bun test test/cli/run/run-process.test.ts: 16 pass (13 before, plus 3 new tests: stop followed by a tool continuation, empty init command output, auto-rejected permission listed in done). The interrupt test now also checks that no done is written.
  • With only the run.ts change reverted, the 6 JSON-format tests fail, so the tests depend on the change.
  • bun test test/cli: 371 pass, 0 fail.
  • bun typecheck: clean.

Screenshots / recordings

N/A

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation cleanly gates done emission to JSON mode and idle completion, and the added subprocess tests cover the new terminal record and continuation edge cases.

Review effort: Lite
Findings: None

What changed in this PR

This PR improves the machine-readable contract of opencode run --format json by emitting a final terminal "done" record once the primary session reaches idle and the prompt/command request has completed, allowing consumers to reliably distinguish “completed” runs from truncated streams and to see any CLI auto-rejected permissions.

Changes:

  • Add tracking of auto-rejected permissions during the run event loop and emit a final JSON "done" record on idle completion.
  • Update JSON subprocess integration tests to assert the "done" record, validate timestamp/sessionID invariants, and cover continuation-after-stop with tool calls.
  • Extend interrupt coverage to ensure "done" is not emitted when the run is interrupted.
File Description
packages/​opencode/​src/​cli/​cmd/​run.ts Track auto-rejected permissions and emit a final done JSON record after the run completes and the session is idle.
packages/​opencode/​test/​cli/​run/​run-process.test.ts Add/adjust subprocess tests to assert the new done record and cover key JSON-stream edge cases.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@thdxr

thdxr commented Oct 10, 2026

Copy link
Copy Markdown
Member

This branch has been upgraded to V2. We're no longer taking PRs for V1. If you think this is still relevant, please port it over and open it for V2.

— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

opencode run exits 0 with empty stdout when a tool call is auto-rejected and the model produces no final message

3 participants