From 664fc0e109a8584105cefd5df0430e4f12f6bece Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Fri, 13 Feb 2026 00:41:23 +0000 Subject: [PATCH] feat: display thinking/reasoning content during streaming Extract thinking blocks from delta events and render them inline using the existing ThinkingBlock component. Previously, thinking content was only visible after the stream finished (via history reload). Now it appears in real-time as the agent reasons. Closes feedback #57. --- src/hooks/useGateway.ts | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/hooks/useGateway.ts b/src/hooks/useGateway.ts index a02443f..c260e01 100644 --- a/src/hooks/useGateway.ts +++ b/src/hooks/useGateway.ts @@ -6,7 +6,7 @@ import { isSystemEvent } from '../lib/systemEvent'; import type { ChatMessage, MessageBlock, ConnectionStatus, Session, AgentIdentity } from '../types'; interface ChatPayloadMessage { - content?: string | Array<{ type: string; text?: string }>; + content?: string | Array<{ type: string; text?: string; thinking?: string }>; } function extractText(message: ChatPayloadMessage | undefined): string { @@ -22,6 +22,16 @@ function extractText(message: ChatPayloadMessage | undefined): string { return ''; } +function extractThinking(message: ChatPayloadMessage | undefined): string { + if (!message) return ''; + const content = message.content; + if (!Array.isArray(content)) return ''; + return content + .filter((b) => b.type === 'thinking') + .map((b) => b.thinking || b.text || '') + .join('\n'); +} + export function useGateway() { const clientRef = useRef(null); const [status, setStatus] = useState('disconnected'); @@ -304,6 +314,7 @@ export function useGateway() { if (state === 'delta') { const text = extractText(message); + const thinking = extractThinking(message); currentRunIdRef.current = runId; setMessages(prev => { @@ -311,16 +322,24 @@ export function useGateway() { if (last && last.role === 'assistant' && last.isStreaming && last.runId === runId) { const updated = { ...last }; updated.content = text; - const nonTextBlocks = updated.blocks.filter(b => b.type !== 'text'); - updated.blocks = [...nonTextBlocks, { type: 'text' as const, text }]; + // Preserve tool blocks, rebuild text + thinking blocks from latest delta + const toolBlocks = updated.blocks.filter(b => b.type === 'tool_use' || b.type === 'tool_result'); + const newBlocks: MessageBlock[] = []; + if (thinking) newBlocks.push({ type: 'thinking' as const, text: thinking }); + newBlocks.push(...toolBlocks); + newBlocks.push({ type: 'text' as const, text }); + updated.blocks = newBlocks; return [...prev.slice(0, -1), updated]; } + const blocks: MessageBlock[] = []; + if (thinking) blocks.push({ type: 'thinking' as const, text: thinking }); + blocks.push({ type: 'text' as const, text }); const msg: ChatMessage = { id: runId + '-' + Date.now(), role: 'assistant', content: text, timestamp: Date.now(), - blocks: [{ type: 'text', text }], + blocks, isStreaming: true, runId, };