From 71cf8d2d62fd946da4e7b7b98bdbcdced656c0f6 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Sun, 5 Apr 2026 11:55:24 -0400 Subject: [PATCH] fix: handle both assistant and delta events without duplicates --- frontend/src/App.jsx | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 6f2d0b2..d8c0bdb 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -171,9 +171,9 @@ const App = () => { } // Claude stream-json events - // NOTE: We intentionally skip the 'assistant' event type. - // It contains the full message text that was ALREADY streamed via - // content_block_delta events, so handling it would create a duplicate bubble. + // We handle BOTH content_block_delta (streaming) and assistant (full message). + // Short responses may arrive as assistant-only without any deltas. + // To prevent duplicates: assistant events only render if no deltas were received. if (type === 'content_block_delta') { const delta = data.delta; @@ -184,14 +184,30 @@ const App = () => { } if (type === 'content_block_stop') { - // Block done, finalize if (pendingAssistantRef.current) { upsertStreamingMessage(pendingAssistantRef.current, false); } } + if (type === 'assistant') { + // Full assistant message — only use if we didn't get streaming deltas + if (!pendingAssistantRef.current) { + const content = data.message?.content; + let text = ''; + if (Array.isArray(content)) { + text = content.filter(b => b.type === 'text').map(b => b.text).join(''); + } else if (typeof content === 'string') { + text = content; + } + if (text) { + pendingAssistantRef.current = text; + upsertStreamingMessage(text, false); + } + } + // If deltas already built the text, skip — no duplicate + } + if (type === 'result') { - // Turn complete const sub = data.subtype; if (sub === 'success' || sub === 'error_max_turns') { if (pendingAssistantRef.current) { @@ -200,7 +216,7 @@ const App = () => { } if (data.session_id) setClaudeSessionId(data.session_id); setChatWaiting(false); - fetchChatSessions(); // refresh session list + fetchChatSessions(); } if (sub === 'error' || sub === 'error_during_execution') { pendingAssistantRef.current = '';