feat(tui): interleave tool rows into live assistant turns

Live turn rendering used to show the streaming assistant text as one
blob with tool calls pooled in a separate section below, so the live
view drifted from the reload view (which threads tool rows inline via
toTranscriptMessages). Model now mirrors reload:

- turnStore gains streamSegments (completed assistant chunks, each
  with any tool rows that landed between its predecessor and itself)
  and streamPendingTools (tool rows waiting for the next chunk)
- turnController.flushStreamingSegment() seals the current bufRef into
  a segment when a new tool.start fires; pending tools get attached to
  that next chunk so order matches reload hydration
- recordMessageComplete returns finalMessages instead of one payload,
  so appendMessage gets the same shape for live-ending turns as for
  reloaded ones
- appLayout renders segments before the progress/streaming area, and
  the streaming message + pending-tools fallback carry whatever tools
  arrived after the last assistant chunk
This commit is contained in:
Brooklyn Nicholson 2026-04-17 11:33:29 -05:00
parent f53250b5e1
commit bedbeebbc8
6 changed files with 85 additions and 22 deletions

View file

@ -31,6 +31,10 @@ const StreamingAssistant = memo(function StreamingAssistant({
return (
<>
{progress.streamSegments.map((msg, i) => (
<MessageLine cols={cols} compact={compact} detailsMode={detailsMode} key={`seg:${i}`} msg={msg} t={t} />
))}
{progress.showProgressArea && (
<Box flexDirection="column" marginBottom={progress.showStreamingArea ? 1 : 0}>
<ToolTrail
@ -57,7 +61,21 @@ const StreamingAssistant = memo(function StreamingAssistant({
compact={compact}
detailsMode={detailsMode}
isStreaming
msg={{ role: 'assistant', text: progress.streaming }}
msg={{
role: 'assistant',
text: progress.streaming,
...(progress.streamPendingTools.length && { tools: progress.streamPendingTools })
}}
t={t}
/>
)}
{!progress.showStreamingArea && !!progress.streamPendingTools.length && (
<MessageLine
cols={cols}
compact={compact}
detailsMode={detailsMode}
msg={{ kind: 'trail', role: 'system', text: '', tools: progress.streamPendingTools }}
t={t}
/>
)}