Skip to content

feat(protocol): carry reasoning, and show it in the desktop - #230

Merged
oratis merged 2 commits into
mainfrom
feat/protocol-reasoning
Aug 3, 2026
Merged

feat(protocol): carry reasoning, and show it in the desktop#230
oratis merged 2 commits into
mainfrom
feat/protocol-reasoning

Conversation

@oratis

@oratis oratis commented Aug 3, 2026

Copy link
Copy Markdown
Owner

The protocol carried reasoningTokens and nothing else. DeepSeek's reasoner produced its most distinctive output and every client dropped it — the CLI got this in #218, but the desktop couldn't, because there was nothing on the wire to render.

A new transient event, not a flag on item.delta

export interface ReasoningDeltaEvent {
  type: 'reasoning.delta';
  threadId: string; turnId: string; itemId: string; delta: string;
}

Separate on purpose: reasoning is not the answer, it is never persisted as a completed item, and a client that doesn't understand the type must be able to drop it rather than accidentally render it as assistant text. Gated by a new reasoningDeltas capability, so a client can tell whether silence means "no reasoning" or "server too old".

The app-server forwards the agent loop's thinking_delta events. VS Code and the LSP bridge forward protocol events unchanged, so they receive it without any change.

Desktop

Reasoning lands on the assistant turn as a distinct reasoning field — deliberately not concatenated into text, which is how it would get lost again — and renders as a collapsed block above the answer:

▸ thinking · 2 lines

Collapsed because reasoner output is long and is not the response. The line count is in the summary because while a turn is streaming it is often the only thing there is to look at. appendReasoningDelta can open a turn on its own, since reasoning usually arrives before any answer text.

Verification

4 unit tests on the stream reducer (opening a turn from reasoning alone, accumulating without touching the answer, starting a fresh turn after one finishes, no leakage into text), the capability assertion updated in the protocol runtime test, and a Playwright case that asserts the block is present, collapsed, and reveals the reasoning on click. Screenshotted the rendered result.

pnpm typecheck · lint · format:check clean; protocol 24 · core 750/16 skipped · desktop 94 · server 41 · cli 208 · vscode 12 · lsp 13 · scripts 21; playwright test → 6 passed.

🤖 Generated with Claude Code

t and others added 2 commits August 3, 2026 13:45
The protocol carried reasoningTokens and nothing else, so DeepSeek's reasoner
produced its most distinctive output and every client dropped it. The CLI got
this in #218; the desktop could not, because there was nothing on the wire to
render.

Adds a `reasoning.delta` transient event behind a `reasoningDeltas` capability.
Separate from `item.delta` rather than a flag on it: reasoning is not the
answer, it is never persisted as a completed item, and a client that doesn't
understand the type has to be able to drop it rather than accidentally render
it as assistant text.

The app-server forwards the agent loop's thinking_delta events; the desktop
projects them onto the assistant turn as a distinct `reasoning` field — kept
out of `text` precisely so it can be rendered as its own channel — and shows a
collapsed `▸ thinking · N lines` block above the answer. Collapsed because
reasoner output is long and is not the response; the line count is there
because while a turn streams it is often the only thing to look at.

VS Code and the LSP bridge forward protocol events unchanged, so they receive
the new event without changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The conflict boundary fell inside a describe(), so the file lost its closing
braces and esbuild refused it. Same shape as the CSS seam in #220 — resolving
a conflict by keeping both sides needs a structural check, not just "no
markers left".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@oratis
oratis force-pushed the feat/protocol-reasoning branch from 59e847e to c929642 Compare August 3, 2026 05:47
@oratis
oratis merged commit 7fc8f88 into main Aug 3, 2026
5 checks passed
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