Skip to content

fix(cli): emit text_delta events in run --format json#38670

Closed
weiconghe wants to merge 1 commit into
anomalyco:devfrom
weiconghe:fix/run-json-text-delta
Closed

fix(cli): emit text_delta events in run --format json#38670
weiconghe wants to merge 1 commit into
anomalyco:devfrom
weiconghe:fix/run-json-text-delta

Conversation

@weiconghe

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #38638

Type of change

  • Bug fix

What does this PR do?

opencode run --format json only emitted the complete text event after generation finished, and silently dropped every message.part.delta event the server already produces in real time. Consumers parsing the JSON stream could not show streaming/progress output — for a long-running prompt they'd see step_start, then nothing for tens of seconds, then the whole response and step_finish at once (#38638).

The run event loop in packages/opencode/src/cli/cmd/run.ts handled message.part.updated but had no case for message.part.delta. This adds one: under --format json, each streaming text chunk is mirrored as a text_delta event ({ messageID, partID, delta }) ahead of the final complete text event, which is still emitted for backward compatibility. Non-json mode is unchanged — its text rendering is driven by the complete part.

Worth flagging: a message.part.delta event carries field but not the part type, and reasoning deltas also use field: "text"processor.ts updates the reasoning part's text via the same updatePartDelta({ field: "text", ... }) call. Filtering on field === "text" alone would therefore leak reasoning content into text_delta. The fix tracks each part's type from message.part.updated in a partTypes map and only emits text_delta for parts whose type is text, mirroring how run/session-data.ts resolves a delta's kind before rendering it.

How did you verify your code works?

  • bun run typecheck (tsgo --noEmit) on the opencode package: 0 errors.
  • Extended the existing subprocess integration tests in test/cli/run/run-process.test.ts (real CLI binary against an in-process TestLLMServer): updated the three existing --format json cases to assert text_delta now precedes text, and added a regression case for opencode run --format json does not emit message.part.delta events in real time #38638 that sends two text chunks on one reply and asserts the text_delta events concatenate to the full text, carry messageID/partID, precede the final text, and that the complete text event is still emitted.
  • bun test test/cli/run/run-process.test.ts --test-name-pattern "format json": 4/5 of the affected cases pass locally. The remaining case (records partial output for an unknown stream finish) fails on Windows before this PR as well — confirmed by git stash-ing the changes and re-running on unmodified dev, where it fails the same way (the second step_start/step_finish is not emitted). It is a Windows cross-spawn exit-timing issue with the mid-stream llm.fail, not a regression; the Linux CI run is the source of truth.

Screenshots / recordings

N/A — pure CLI/JSON output change.

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:

I found a potentially related PR:

PR #38504: feat(cli): stream incremental reasoning/text deltas in run --format json
#38504

This appears to be closely related as it addresses streaming reasoning/text deltas in the run --format json command. The current PR #38670 specifically fixes the text_delta event emission, while #38504 also involves streaming deltas. These may be addressing overlapping concerns or competing implementations for the same feature.

@weiconghe

Copy link
Copy Markdown
Contributor Author

Closing as duplicate of #38504, which I missed when searching before opening this. #38504 landed the same core idea a day earlier — the same partTypes map (partID mapped to part.type via message.part.updated) to attribute message.part.delta events — and is more complete (streams reasoning + text as part_delta; mine only emitted text).

I did add subprocess integration tests (test/cli/run/run-process.test.ts: delta ordering, multi-chunk concatenation, reasoning-leak guard) — happy to contribute them to #38504 if useful. Apologies for the noise.

@weiconghe weiconghe closed this Jul 24, 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.

opencode run --format json does not emit message.part.delta events in real time

1 participant