import type { Message } from '@/stores/hermes/chat' // Group only within an uninterrupted turn. Tool rows remain available after // the combined answer; user, command and system messages are hard boundaries. export function groupRunMessages(messages: Message[], runActive: boolean): Message[] { const result: Message[] = [] let assistants: Message[] = [] let tools: Message[] = [] let marker: string | null = null function flush(completed: boolean) { if (assistants.length) { const first = assistants[0] result.push({ ...first, toolMessages: tools.length ? tools : undefined, contentSegments: assistants.map(m => m.content).filter(text => text.trim()), id: assistants[assistants.length - 1].id, content: assistants.map(m => m.content).filter(text => text.trim()).join('\n\n'), reasoning: assistants.map(m => m.reasoning).filter(Boolean).join('\n\n') || undefined, attachments: assistants.flatMap(m => m.attachments || []), isStreaming: assistants.some(m => m.isStreaming), }) } if (tools.length && !assistants.length) { result.push({ id: `tool-group-${tools[0].id}`, role: 'system', content: '', timestamp: tools[0].timestamp, systemType: 'tool-group', toolMessages: tools }) } if (completed && assistants.length) { result.push({ id: `run-end-${assistants[0].id}`, role: 'system', content: '', timestamp: assistants[assistants.length - 1].timestamp, systemType: 'run-end' }) } assistants = [] tools = [] marker = null } for (const message of messages) { if (message.role !== 'assistant' && message.role !== 'tool') { flush(true) result.push(message) continue } if (marker && message.runMarker && marker !== message.runMarker) flush(true) marker ||= message.runMarker || null if (message.role === 'assistant') assistants.push(message) else tools.push(message) } flush(!runActive) return result }