fix: show reasoning on the reasoning stream so thoughts render - #868
Conversation
|
Hey @addyCooks - nice diagnosis - the batch-flush ordering is the real root cause, and the Requested changeRouting is still driven by start/end markers rather than by the delta type.
Deriving the flag from the delta itself makes it ordering-independent and reduces the start/end handlers to pure flush points: case 'reasoning-delta':
if (!isReasoning) {
flushPending();
isReasoning = true;
}
accumulatedReasoning += chunk.text;
tokenBuffer += chunk.text;
...
case 'text-delta':
if (isReasoning) {
flushPending();
isReasoning = false;
}
...This also neutralises Smaller itemsLive and replay disagree on leading whitespace. Whitespace-only reasoning is still persisted. The user-visible piece has no coverage. Test shape nit. |
40c6e27 to
19c81a9
Compare
|
Thanks @will-lamerton all four addressed in Routing by delta type. Whitespace. Fixed at the source instead of on replay: the callback accumulates only what it emits ( chat-panel.js coverage. Your premise was right when you wrote it, but Test shape. Tokens now stream from inside Manual pass worth being precise: not a live extension session. I drove the real So the thoughts were never lost they were printing as the assistant's reply, and the empty bubble was the leading I ticked Tested with OpenAI-compatible API on that basis with an inline caveat. Happy to untick if you'd rather that box mean a live session only that pass is still outstanding. CI is green, full suite included. |
Streamed tokens are batched behind a 150ms timer, but flushBuffer read isReasoning at flush time instead of at buffer time. A provider that opens the next stream inside that window — the Responses API defers reasoning-end until the reasoning item completes, and openai-compatible reopens reasoning without closing text — flipped the flag first, so the batch went out on the wrong callback: reasoning surfaced as assistant text and the thought view stayed empty. Flush pending tokens before the switch, and drop whitespace-only reasoning from ACP thought chunks and from the webview's thought section, since marked renders it to nothing and leaves a bare "Thought for 0s" bubble behind.
Review follow-up to the batch-flush fix. isReasoning was still only assigned in the *-start / *-end cases, so a reasoning delta arriving with no preceding reasoning-start - or after a provider deferred reasoning-end past text-start - kept landing on onToken. The delta type is now the source of truth and the four start/end cases collapse into a single flush point, which makes routing independent of provider ordering and removes the isReasoning = false on reasoning-end. The ACP reasoning callback now accumulates only what it emits, so the stored reasoning is exactly what the live stream showed and replaySessionHistory can re-send it verbatim; whitespace-only reasoning is no longer persisted on the message either. The webview whitespace guard gains coverage in source/vscode/chat-panel-thoughts.spec.ts, the harness main picked up since this branched.
19c81a9 to
c2e8e11
Compare
|
Nice work @addyCooks :) |
|
Thanks @will-lamerton! : ) |
Closes #853.
Description
The thought dropdown rendered but expanded to nothing. Three changes:
source/ai-sdk-client/chat/chat-handler.tsstreamed tokens are batched behind a 150ms timer, but the reasoning/text routing flag was driven by the*-start/*-endmarkers around a batch rather than by the deltas that filled it. The delta type is now the source of truth:reasoning-delta/text-deltaflush the pending batch before flipping the flag, and the four start/end cases collapse into a single flush-only case. Routing is now independent of provider ordering.source/acp/acp-conversation.ts/acp-agent.tsleading whitespace-only reasoning no longer emits anagent_thought_chunk, is no longer stored on the message, and is skipped on session-history replay. The callback accumulates only what it emits, soreplaySessionHistoryre-sending the stored value renders identically to the live stream.plugins/vscode/media/chat-panel.jsa thought section is only opened once there is non-whitespace text, andendCurrentTextBlock()moved inside that guard so an empty chunk can't split the answer.Upstream references for the orderings this has to survive:
@ai-sdk/openaiemitsreasoning-endonpart.doneonly whenstoreis set, otherwise it defers tooutput_item.done;@ai-sdk/openai-compatibleemitsreasoning-startwithout ever closing an active text part. Some providers emit reasoning deltas with no start marker at all.Type of Change
Changeset
pnpm changeset) describing this change for the changelogTesting
Automated Tests
.spec.ts/tsxfilespnpm test:allcompletes successfully)Manual Testing
--acpserver → realchat-panel.js), not a hosted provider; see the review reply for exactly what was coveredChecklist