A React Native app for the ultimate thinking partner.

fix(streaming): reset reasoning between messages and add (co thought) indicator

- Reset reasoning when transitioning to new message (assistant msg or tool call)
- Reset assistant message when new reasoning starts after previous content
- Add '(co thought)' status indicator when displaying reasoning content
- Prevents reasoning accumulation across multiple message cycles
- Fixes missing status indicator for streamed reasoning blocks

+45 -21
+39 -19
App.tsx
··· 479 479 return; 480 480 } 481 481 482 - // Simple accumulation - no message creation during streaming 482 + // Simple accumulation - reset accumulators when switching to a new message type 483 483 if (chunk.message_type === 'reasoning_message' && chunk.reasoning) { 484 - // Accumulate reasoning 485 - setCurrentStream(prev => ({ 486 - ...prev, 487 - reasoning: prev.reasoning + chunk.reasoning 488 - })); 484 + // Accumulate reasoning, but reset if we're coming from assistant message or tool call 485 + setCurrentStream(prev => { 486 + // If we have assistant message or tool calls, this is a NEW reasoning block - reset 487 + if (prev.assistantMessage || prev.toolCalls.length > 0) { 488 + return { 489 + reasoning: chunk.reasoning, 490 + toolCalls: [], 491 + assistantMessage: '' 492 + }; 493 + } 494 + // Otherwise, continue accumulating reasoning 495 + return { 496 + ...prev, 497 + reasoning: prev.reasoning + chunk.reasoning 498 + }; 499 + }); 489 500 } else if ((chunk.message_type === 'tool_call_message' || chunk.message_type === 'tool_call') && chunk.tool_call) { 490 501 // Add tool call to list (deduplicate by ID) 491 502 const callObj = chunk.tool_call.function || chunk.tool_call; ··· 531 542 } 532 543 533 544 if (contentText) { 534 - setCurrentStream(prev => ({ 535 - ...prev, 536 - assistantMessage: prev.assistantMessage + contentText 537 - })); 545 + setCurrentStream(prev => { 546 + // If we have reasoning but no assistant message, this is a NEW assistant message - clear reasoning 547 + if (prev.reasoning && !prev.assistantMessage) { 548 + return { 549 + reasoning: '', 550 + toolCalls: [], 551 + assistantMessage: contentText 552 + }; 553 + } 554 + // Otherwise, continue accumulating assistant message 555 + return { 556 + ...prev, 557 + assistantMessage: prev.assistantMessage + contentText 558 + }; 559 + }); 538 560 } 539 561 } else if (chunk.message_type === 'tool_return_message' || chunk.message_type === 'tool_response') { 540 562 // Ignore tool returns during streaming - we'll get them from server ··· 2207 2229 <Animated.View style={[styles.assistantFullWidthContainer, { minHeight: spacerHeightAnim, opacity: statusFadeAnim }]}> 2208 2230 {/* Streaming Block - show all current stream content */} 2209 2231 2210 - {/* Show thinking indicator while reasoning is streaming (no other content yet) */} 2211 - {currentStream.reasoning && !currentStream.assistantMessage && currentStream.toolCalls.length === 0 && ( 2212 - <LiveStatusIndicator status="thinking" /> 2213 - )} 2214 - 2215 - {/* Show reasoning content if we have it (always visible once it starts) */} 2232 + {/* Show reasoning with status indicator if we have it */} 2216 2233 {currentStream.reasoning && ( 2217 - <View style={styles.reasoningStreamingContainer}> 2218 - <Text style={styles.reasoningStreamingText}>{currentStream.reasoning}</Text> 2219 - </View> 2234 + <> 2235 + <LiveStatusIndicator status="thought" /> 2236 + <View style={styles.reasoningStreamingContainer}> 2237 + <Text style={styles.reasoningStreamingText}>{currentStream.reasoning}</Text> 2238 + </View> 2239 + </> 2220 2240 )} 2221 2241 2222 2242 {/* Show tool calls if we have any */}
+6 -2
src/components/LiveStatusIndicator.tsx
··· 3 3 import { darkTheme } from '../theme'; 4 4 5 5 interface LiveStatusIndicatorProps { 6 - status: string; // e.g., "thinking", "searching the web", "saying" 6 + status: string; // e.g., "thinking", "searching the web", "saying", "thought" 7 7 } 8 8 9 9 const LiveStatusIndicator: React.FC<LiveStatusIndicatorProps> = ({ status }) => { ··· 23 23 return () => animation.stop(); 24 24 }, []); 25 25 26 + // Special cases for past tense verbs (don't use "is") 27 + const isPastTense = status === 'thought'; 28 + const displayText = isPastTense ? status : `is ${status}`; 29 + 26 30 return ( 27 31 <View style={styles.container}> 28 32 <Text style={styles.text}>(</Text> ··· 39 43 > 40 44 co 41 45 </Animated.Text> 42 - <Text style={styles.text}> is {status})</Text> 46 + <Text style={styles.text}> {displayText})</Text> 43 47 </View> 44 48 ); 45 49 };