Skip to content

feat: stream claude-code tokens (reland #6664) + fix the orphan reasoning-end that killed runs - #6670

Merged
pedrofrxncx merged 2 commits into
mainfrom
fix/claude-code-stream-block-ordering
Aug 28, 2026
Merged

feat: stream claude-code tokens (reland #6664) + fix the orphan reasoning-end that killed runs#6670
pedrofrxncx merged 2 commits into
mainfrom
fix/claude-code-stream-block-ordering

Conversation

@pedrofrxncx

@pedrofrxncx pedrofrxncx commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Relands #6664 (reverted in #6669) with the bug that forced the revert fixed.

Stacked on #6669 — base is the revert branch, so this merges as: revert lands, then #6664 comes back fixed. Retarget to main if #6669 merges first.

What broke

A streamed text/reasoning block was ended after the finish-step that the next assistant message pushes (claude-code.ts pushed the step boundary before translator.translate(message), and translateAssistant is where the open block was closed). The AI SDK reducer clears its open text/reasoning parts on finish-step (process-ui-message-stream.ts:770-774), so the end arrived for a part it no longer knew:

[Decopilot] stream pump error for thread thrd_T9FUqcMjz9KaeubUZJfMi:
Received reasoning-end for missing reasoning part with ID "stream-2".

That throw kills the pump mid-run. No finish chunk ever reaches the JetStream log, so resolveCleanRunStatus(undefined) reads the truncated stream as a clean end: the thread is stored completed while the sandbox agent is still working, and the thread-finish hook advances the task board card to In Review. Observed in prod on board_x5gk0kjwXRfgmf3TFZ_JS — the thread's finish part carries no usage, unlike healthy runs.

The fix

Close whatever stream_event left open before pushing finish-step. closeOpenStreamBlocks() becomes public for that; translateAssistant still calls it (now a no-op on that path), so a block closed by content_block_stop is unaffected and the assistant restatement never emits a second end.

Testing

  • bun test packages/harness-runner — 69 pass, incl. a new case: a block left open at the step boundary is closed once, and the assistant restatement adds nothing.
  • bun run --cwd=packages/harness-runner check, bun run fmt.

Summary by cubic

Relands token-level streaming for Claude Code runs: text now arrives as the model writes it instead of one large frame after the turn finishes, and the block-ordering bug that made the first merge unsafe is fixed — an interrupted stream no longer throws, kills the run mid-stream, and records the thread completed while the agent keeps working. Also adds a live elapsed readout for long-running tool calls and two-minute working-tree checkpoints in the sandbox daemon.

Claude Code streaming

  • Enables includePartialMessages; the translator turns raw stream_event messages into text/reasoning start-delta-end chunks and skips the assistant message's restatement of blocks already streamed.
  • A coalescer concatenates adjacent deltas and flushes at ~200 characters, so token streaming does not become per-token frames through the daemon, JetStream, or SSE.
  • Blocks still open at a step boundary are now closed before finish-step; an end emitted after that lands on a part the AI SDK reducer has already cleared.

UI and sandbox durability

  • Any still-running tool call shows a live "Running for Ns" counter once it passes 10 seconds, replacing the static "Preparing…" that made long builds look frozen.
  • The sandbox daemon checkpoints a run's working tree to its branch every two minutes, so a hard pod death loses at most that window of work.
  • Checkpointed publishes never commit .env variants anywhere in the tree; .envrc is still committed.

Written for commit ef3975d. Summary will update on new commits.

Review in cubic

Base automatically changed from revert-6664-feat/stream-reconnect-autocommit to main August 28, 2026 01:41
Pedro França added 2 commits August 27, 2026 23:21
A streamed text/reasoning block was ended AFTER the `finish-step` the next
assistant message pushes. The AI SDK reducer clears its open text/reasoning
parts on `finish-step`, so that end arrived for a part it no longer knew and
threw `Received reasoning-end for missing reasoning part with ID "stream-2"`.

That throw killed the stream pump mid-run: no `finish` chunk ever reached the
log, so `resolveCleanRunStatus(undefined)` read the truncated stream as a clean
end and stored the thread `completed` while the sandbox kept working — the task
board then advanced the card to In Review under a still-running agent.
@pedrofrxncx
pedrofrxncx force-pushed the fix/claude-code-stream-block-ordering branch from 287cd32 to ef3975d Compare August 28, 2026 02:21
@pedrofrxncx
pedrofrxncx merged commit d1c3f40 into main Aug 28, 2026
33 checks passed
@pedrofrxncx
pedrofrxncx deleted the fix/claude-code-stream-block-ordering branch August 28, 2026 11:48
decocms Bot pushed a commit that referenced this pull request Aug 28, 2026
PR: #6670 feat: stream claude-code tokens (reland #6664) + fix the orphan reasoning-end that killed runs
Bump type: minor

- decocms (apps/api/package.json): 4.288.1 -> 4.289.0
- @decocms/native (apps/native/package.json): 4.288.1 -> 4.289.0
- @decocms/harness-runner (packages/harness-runner/package.json): 0.8.1 -> 0.9.0
- @decocms/sandbox (packages/sandbox/package.json): 1.59.1 -> 1.60.0
- deploy/helm/sandbox-env (chart 0.16.29) (deploy/helm/sandbox-env/values.yaml deploy/helm/sandbox-env/Chart.yaml): image.tag/appVersion -> 1.60.0

Deploy-Scope: web
pedrofrxncx added a commit that referenced this pull request Aug 28, 2026
… error (#6675)

fail() emitted finish-step without closing whatever stream_event had left
open, unlike the assistant-message step boundary a few lines above, which
closes it first (added earlier in #6670 for the same orphan-end hazard). An
SDK throw mid-block leaves that text/reasoning part without its -end chunk,
so it reads as still streaming forever after the run has already failed.
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.

1 participant