hermes-agent/ui-tui/src/app/turnStore.ts
Brooklyn Nicholson bedbeebbc8 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
2026-04-17 11:33:29 -05:00

44 lines
1.1 KiB
TypeScript

import { atom } from 'nanostores'
import type { ActiveTool, ActivityItem, Msg, SubagentProgress } from '../types.js'
const buildTurnState = (): TurnState => ({
activity: [],
outcome: '',
reasoning: '',
reasoningActive: false,
reasoningStreaming: false,
reasoningTokens: 0,
streamPendingTools: [],
streamSegments: [],
streaming: '',
subagents: [],
toolTokens: 0,
tools: [],
turnTrail: []
})
export const $turnState = atom<TurnState>(buildTurnState())
export const getTurnState = () => $turnState.get()
export const patchTurnState = (next: Partial<TurnState> | ((state: TurnState) => TurnState)) =>
$turnState.set(typeof next === 'function' ? next($turnState.get()) : { ...$turnState.get(), ...next })
export const resetTurnState = () => $turnState.set(buildTurnState())
export interface TurnState {
activity: ActivityItem[]
outcome: string
reasoning: string
reasoningActive: boolean
reasoningStreaming: boolean
reasoningTokens: number
streamPendingTools: string[]
streamSegments: Msg[]
streaming: string
subagents: SubagentProgress[]
toolTokens: number
tools: ActiveTool[]
turnTrail: string[]
}