A React Native app for the ultimate thinking partner.

fix(tool-calls): pair tool calls with results for tight display

- Tool calls now find and render with their corresponding tool return
- Tool returns are skipped if they follow a tool call (avoid duplication)
- Orphaned tool returns render standalone with "(orphaned)" label
- Result section visible even when tool call is collapsed
- Enables two-line tight display: call followed by result

+21 -9
+19 -7
App.tsx
··· 1496 1496 ); 1497 1497 } 1498 1498 1499 - // Handle tool calls 1499 + // Handle tool calls - find and render with their result 1500 1500 if (isToolCall) { 1501 + // Find the corresponding tool return (next message in the list) 1502 + const msgIndex = displayMessages.findIndex(m => m.id === msg.id); 1503 + const nextMsg = msgIndex >= 0 && msgIndex < displayMessages.length - 1 ? displayMessages[msgIndex + 1] : null; 1504 + const toolReturn = nextMsg && nextMsg.message_type === 'tool_return_message' ? nextMsg : null; 1505 + 1501 1506 return ( 1502 1507 <View style={styles.messageContainer}> 1503 1508 <ToolCallItem 1504 1509 callText={msg.content} 1505 - resultText={undefined} 1506 - reasoning={undefined} 1507 - hasResult={toolCallHasResult.get(msg.id) || false} 1510 + resultText={toolReturn?.content} 1511 + reasoning={msg.reasoning} 1512 + hasResult={!!toolReturn} 1508 1513 /> 1509 1514 </View> 1510 1515 ); 1511 1516 } 1512 1517 1513 - // Handle tool returns - just show the result text 1518 + // Skip tool returns - they're rendered with their tool call 1514 1519 if (isToolReturn) { 1520 + // Check if previous message is a tool call 1521 + const msgIndex = displayMessages.findIndex(m => m.id === msg.id); 1522 + const prevMsg = msgIndex > 0 ? displayMessages[msgIndex - 1] : null; 1523 + if (prevMsg && prevMsg.message_type === 'tool_call_message') { 1524 + return null; // Already rendered with the tool call 1525 + } 1526 + 1527 + // Orphaned tool return (no matching tool call) - render it standalone 1515 1528 const isExpanded = expandedToolReturns.has(msg.id); 1516 - 1517 1529 return ( 1518 1530 <View style={styles.messageContainer}> 1519 1531 <View style={styles.toolReturnContainer}> ··· 1527 1539 size={12} 1528 1540 color={darkTheme.colors.text.tertiary} 1529 1541 /> 1530 - <Text style={styles.toolReturnLabel}>Result</Text> 1542 + <Text style={styles.toolReturnLabel}>Result (orphaned)</Text> 1531 1543 </TouchableOpacity> 1532 1544 {isExpanded && ( 1533 1545 <View style={styles.toolReturnContent}>
+2 -2
src/components/ToolCallItem.tsx
··· 150 150 style={styles.chevron} 151 151 /> 152 152 </TouchableOpacity> 153 - {expanded && !!resultText && ( 153 + {!!resultText && ( 154 154 <TouchableOpacity 155 155 style={[styles.resultHeader, resultExpanded && styles.resultHeaderExpanded]} 156 156 onPress={() => setResultExpanded((e) => !e)} ··· 165 165 <Text style={styles.resultLabel}>Result</Text> 166 166 </TouchableOpacity> 167 167 )} 168 - {expanded && resultExpanded && !!resultText && ( 168 + {resultExpanded && !!resultText && ( 169 169 <View style={styles.resultBox}> 170 170 <Text style={styles.resultText}>{formattedResult}</Text> 171 171 </View>