@tanstack/ai 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/activities/chat/index.js +17 -1
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.d.ts +21 -1
- package/dist/esm/activities/chat/messages.js +50 -1
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +17 -0
- package/dist/esm/activities/chat/stream/processor.js +27 -0
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/adapter-configuration/SKILL.md +1 -1
- package/skills/ai-core/media-generation/SKILL.md +22 -12
- package/src/activities/chat/index.ts +23 -0
- package/src/activities/chat/messages.ts +60 -0
- package/src/activities/chat/stream/processor.ts +31 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processor.js","names":[],"sources":["../../../../../src/activities/chat/stream/processor.ts"],"sourcesContent":["/**\n * Unified Stream Processor\n *\n * Core stream processing engine that manages the full UIMessage[] conversation.\n * Single source of truth for message state.\n *\n * Handles:\n * - Full conversation management (UIMessage[])\n * - Text content accumulation with configurable chunking strategies\n * - Parallel tool calls with lifecycle state tracking\n * - Tool results and approval flows\n * - Thinking/reasoning content\n * - Recording/replay for testing\n * - Event-driven architecture for UI updates\n * - Per-message stream state tracking for multi-message sessions\n *\n * @see docs/chat-architecture.md — Canonical reference for AG-UI chunk ordering,\n * adapter contract, single-shot flows, and expected UIMessage output.\n */\nimport {\n aguiSnapshotMessageToUIMessage,\n coerceCreatedAt,\n generateMessageId,\n uiMessageToModelMessages,\n} from '../messages.js'\nimport { runErrorEventToError } from '../../../utilities/errors'\nimport { isProviderExecutedToolCall } from '../../../utilities/provider-executed'\nimport {\n mergeMetadata,\n tanstackMetadata,\n} from '../../../utilities/merge-metadata'\nimport { getChunkRunId } from '../../../utilities/chunk-ids'\nimport type { AdapterYieldChunk } from '../../../utilities/adapter-yield-chunk'\nimport { normalizeToolResult } from '../../../utilities/tool-result'\nimport { defaultJSONParser } from './json-parser'\nimport {\n appendStructuredOutputDelta,\n completeStructuredOutputPart,\n errorStructuredOutputPart,\n updateTextPart,\n updateThinkingPart,\n updateToolCallApproval,\n updateToolCallApprovalResponse,\n updateToolCallPart,\n updateToolCallWithOutput,\n updateToolResultPart,\n} from './message-updaters'\nimport { ImmediateStrategy } from './strategies'\nimport { INTERRUPT_BINDING_METADATA_KEY } from '../../../interrupt-resume'\nimport type {\n ChunkRecording,\n ChunkStrategy,\n InternalToolCallState,\n MessageStreamState,\n ProcessorResult,\n ProcessorState,\n ToolCallState,\n ToolResultState,\n} from './types'\nimport type {\n ContentPart,\n Interrupt,\n MessagePart,\n ModelMessage,\n StreamChunk,\n ThinkingPart,\n ToolCall,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n UIResourceEvent,\n UIResourcePart,\n} from '../../../types'\n\n/**\n * Events emitted by the StreamProcessor\n */\nexport interface StreamProcessorEvents {\n // State events - full array on any change\n onMessagesChange?: (messages: Array<UIMessage>) => void\n\n // Lifecycle events\n onStreamStart?: () => void\n onStreamEnd?: (message: UIMessage) => void\n onError?: (error: Error) => void\n\n // Interaction events - client must handle these\n onToolCall?: (args: {\n toolCallId: string\n toolName: string\n input: any\n }) => void\n onApprovalRequest?: (args: {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n }) => void\n\n // Custom events from server-side tools\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n\n // Granular events for UI optimization (character-by-character, state tracking)\n onTextUpdate?: (messageId: string, content: string) => void\n onToolCallStateChange?: (\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n args: string,\n ) => void\n onThinkingUpdate?: (\n messageId: string,\n stepId: string,\n content: string,\n ) => void\n onStructuredOutputChange?: (args: {\n phase: 'start' | 'update' | 'complete' | 'error'\n messageId: string\n status: 'streaming' | 'complete' | 'error'\n raw: string\n partial?: unknown\n data?: unknown\n reasoning?: string\n errorMessage?: string\n delta?: string\n }) => void\n}\n\n/**\n * Options for StreamProcessor\n */\nexport interface StreamProcessorOptions {\n chunkStrategy?: ChunkStrategy\n /** Event-driven handlers */\n events?: StreamProcessorEvents\n jsonParser?: {\n parse: (jsonString: string) => any\n }\n /** Enable recording for replay testing */\n recording?: boolean\n /** Initial messages to populate the processor */\n initialMessages?: Array<UIMessage>\n}\n\nconst STRUCTURED_OUTPUT_UPDATE_BATCH_SIZE = 12\n\nfunction interruptBatchHasGeneric(interrupts: Array<Interrupt>): boolean {\n return interrupts.some((interrupt) => {\n const metadata = interrupt.metadata\n if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {\n return false\n }\n const binding = metadata[INTERRUPT_BINDING_METADATA_KEY]\n return (\n binding !== null &&\n typeof binding === 'object' &&\n !Array.isArray(binding) &&\n binding.kind === 'generic'\n )\n })\n}\n\n/**\n * StreamProcessor - State machine for processing AI response streams\n *\n * Manages the full UIMessage[] conversation and emits events on changes.\n * Trusts the adapter contract: adapters emit clean AG-UI events in the\n * correct order.\n *\n * State tracking:\n * - Full message array\n * - Per-message stream state (text, tool calls, thinking)\n * - Multiple concurrent message streams\n * - Tool call completion via TOOL_CALL_END events\n *\n * @see docs/chat-architecture.md#streamprocessor-internal-state — State field reference\n * @see docs/chat-architecture.md#adapter-contract — What this class expects from adapters\n */\nexport class StreamProcessor {\n private readonly chunkStrategy: ChunkStrategy\n private readonly events: StreamProcessorEvents\n private readonly jsonParser: { parse: (jsonString: string) => any }\n private recordingEnabled: boolean\n\n // Message state\n private messages: Array<UIMessage> = []\n\n // Per-message stream state\n private readonly messageStates: Map<string, MessageStreamState> = new Map()\n private readonly activeMessageIds: Set<string> = new Set()\n private readonly toolCallToMessage: Map<string, string> = new Map()\n private pendingManualMessageId: string | null = null\n private pendingThinkingStepId: string | null = null\n\n private readonly structuredMessageIds: Set<string> = new Set()\n private readonly structuredOutputUpdateBatches = new Map<\n string,\n {\n delta: string\n chunkCount: number\n }\n >()\n\n // Run tracking (for concurrent run safety)\n private readonly activeRuns = new Set<string>()\n\n // Shared stream state\n private finishReason: string | null = null\n private hasError = false\n private isDone = false\n private streamEndEmitted = false\n\n // Recording\n private recording: ChunkRecording | null = null\n private recordingStartTime = 0\n\n constructor(options: StreamProcessorOptions = {}) {\n this.chunkStrategy = options.chunkStrategy || new ImmediateStrategy()\n this.events = options.events || {}\n this.jsonParser = options.jsonParser || defaultJSONParser\n this.recordingEnabled = options.recording ?? false\n\n // Initialize with provided messages\n if (options.initialMessages) {\n this.messages = [...options.initialMessages]\n }\n }\n\n // ============================================\n // Message Management Methods\n // ============================================\n\n /**\n * Set the messages array (e.g., from persisted state)\n */\n setMessages(messages: Array<UIMessage>): void {\n this.messages = [...messages]\n this.emitMessagesChange()\n }\n\n /**\n * Add a user message to the conversation.\n * Supports both simple string content and multimodal content arrays.\n *\n * @param content - The message content (string or array of content parts)\n * @param id - Optional custom message ID (generated if not provided)\n * @param metadata - Optional AG-UI metadata bag\n * @returns The created UIMessage\n *\n * @example\n * ```ts\n * // Simple text message\n * processor.addUserMessage('Hello!')\n *\n * // Multimodal message with image\n * processor.addUserMessage([\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ])\n *\n * // With custom ID\n * processor.addUserMessage('Hello!', 'custom-id-123')\n * ```\n */\n addUserMessage(\n content: string | Array<ContentPart>,\n id?: string,\n metadata?: UIMessage['metadata'],\n ): UIMessage {\n // Convert content to message parts\n const parts: Array<MessagePart> =\n typeof content === 'string'\n ? [{ type: 'text', content }]\n : content.map((part) => {\n // ContentPart types (text, image, audio, video, document) are compatible with MessagePart\n return part\n })\n\n const userMessage: UIMessage = {\n id: id ?? generateMessageId(),\n role: 'user',\n parts,\n createdAt: new Date(),\n ...(metadata != null ? { metadata } : {}),\n }\n\n this.messages = [...this.messages, userMessage]\n this.emitMessagesChange()\n\n return userMessage\n }\n\n /**\n * Prepare for a new assistant message stream.\n * Does NOT create the message immediately -- the message is created lazily\n * when the first content-bearing chunk arrives via ensureAssistantMessage().\n * This prevents empty assistant messages from flickering in the UI when\n * auto-continuation produces no content.\n */\n prepareAssistantMessage(): void {\n // Reset stream state for new message\n this.resetStreamState()\n }\n\n /**\n * @deprecated Use prepareAssistantMessage() instead. This eagerly creates\n * an assistant message which can cause empty message flicker.\n */\n startAssistantMessage(messageId?: string): string {\n this.prepareAssistantMessage()\n const { messageId: id } = this.ensureAssistantMessage(messageId)\n this.pendingManualMessageId = id\n return id\n }\n\n /**\n * Get the current assistant message ID (if one has been created).\n * Returns null if prepareAssistantMessage() was called but no content\n * has arrived yet.\n */\n getCurrentAssistantMessageId(): string | null {\n // Scan all message states (not just active) for the last assistant.\n // After finalizeStream() clears activeMessageIds, messageStates retains entries.\n // After reset() / resetStreamState(), messageStates is cleared → returns null.\n let lastId: string | null = null\n for (const [id, state] of this.messageStates) {\n if (state.role === 'assistant') {\n lastId = id\n }\n }\n return lastId\n }\n\n /**\n * Add a tool result (called by client after handling onToolCall)\n */\n addToolResult(toolCallId: string, output: any, error?: string): void {\n // Find the message containing this tool call\n const messageWithToolCall = this.messages.find((msg) =>\n msg.parts.some(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n ),\n )\n\n if (!messageWithToolCall) {\n console.warn(\n `[StreamProcessor] Could not find message with tool call ${toolCallId}`,\n )\n return\n }\n\n // Step 1: Update the tool-call part's output field (for UI rendering)\n let updatedMessages = updateToolCallWithOutput(\n this.messages,\n toolCallId,\n output,\n error ? 'error' : undefined,\n error,\n )\n\n // Step 2: Create a tool-result part (for LLM conversation history)\n const content = normalizeToolResult(output)\n const toolResultState: ToolResultState = error ? 'error' : 'complete'\n\n updatedMessages = updateToolResultPart(\n updatedMessages,\n messageWithToolCall.id,\n toolCallId,\n content,\n toolResultState,\n error,\n )\n\n this.messages = updatedMessages\n this.emitMessagesChange()\n }\n\n /**\n * Add an approval response (called by client after handling onApprovalRequest)\n */\n addToolApprovalResponse(approvalId: string, approved: boolean): void {\n this.messages = updateToolCallApprovalResponse(\n this.messages,\n approvalId,\n approved,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Get the conversation as ModelMessages (for sending to LLM)\n */\n toModelMessages(): Array<ModelMessage> {\n const modelMessages: Array<ModelMessage> = []\n for (const msg of this.messages) {\n modelMessages.push(...uiMessageToModelMessages(msg))\n }\n return modelMessages\n }\n\n /**\n * Get current messages\n */\n getMessages(): Array<UIMessage> {\n return this.messages\n }\n\n /**\n * Check if all tool calls in the last assistant message are complete\n * Useful for auto-continue logic\n */\n areAllToolsComplete(): boolean {\n const lastAssistant = this.messages.findLast(\n (m: UIMessage) => m.role === 'assistant',\n )\n\n if (!lastAssistant) return true\n\n const toolParts = lastAssistant.parts.filter(\n (p): p is ToolCallPart => p.type === 'tool-call',\n )\n\n if (toolParts.length === 0) return true\n\n // Get tool result parts to check for server tool completion\n const toolResultIds = new Set(\n lastAssistant.parts\n .filter((p): p is ToolResultPart => p.type === 'tool-result')\n .map((p) => p.toolCallId),\n )\n\n // All tool calls must be in a terminal state\n // A tool call is complete if:\n // 1. It was approved/denied (approval-responded state)\n // 2. It has an output field set (client tool completed via addToolResult)\n // 3. It has a corresponding tool-result part (server tool completed)\n // 4. It is provider-executed (e.g. Anthropic web_search) — already run by\n // the provider, so there is no client result to wait for.\n return toolParts.every(\n (part) =>\n part.state === 'complete' ||\n part.state === 'approval-responded' ||\n (part.output !== undefined && !part.approval) ||\n toolResultIds.has(part.id) ||\n isProviderExecutedToolCall(part),\n )\n }\n\n /**\n * Remove messages after a certain index (for reload/retry)\n */\n removeMessagesAfter(index: number): void {\n const keptIds = new Set(this.messages.slice(0, index + 1).map((m) => m.id))\n // Drop routing state for messages that no longer exist; otherwise a\n // resumed stream (`reload()` or a server that reuses messageIds across\n // runs) could land deltas / tool args on stale map entries and corrupt\n // the new assistant message's parts. Mirror the four routing maps that\n // key on messageId: structuredMessageIds (custom-event routing),\n // messageStates (per-message stream state), toolCallToMessage (tool\n // args → message), and activeMessageIds (finalize / completeAllToolCalls\n // iteration targets — must not include phantoms).\n for (const id of this.structuredMessageIds) {\n if (!keptIds.has(id)) this.structuredMessageIds.delete(id)\n }\n for (const id of this.structuredOutputUpdateBatches.keys()) {\n if (!keptIds.has(id)) this.structuredOutputUpdateBatches.delete(id)\n }\n for (const id of this.messageStates.keys()) {\n if (!keptIds.has(id)) this.messageStates.delete(id)\n }\n for (const [toolCallId, msgId] of this.toolCallToMessage) {\n if (!keptIds.has(msgId)) this.toolCallToMessage.delete(toolCallId)\n }\n for (const id of this.activeMessageIds) {\n if (!keptIds.has(id)) this.activeMessageIds.delete(id)\n }\n this.messages = this.messages.slice(0, index + 1)\n this.emitMessagesChange()\n }\n\n /**\n * Clear all messages\n */\n clearMessages(): void {\n this.messages = []\n this.messageStates.clear()\n this.activeMessageIds.clear()\n this.toolCallToMessage.clear()\n this.structuredMessageIds.clear()\n this.structuredOutputUpdateBatches.clear()\n this.pendingManualMessageId = null\n this.emitMessagesChange()\n }\n\n // ============================================\n // Stream Processing Methods\n // ============================================\n\n /**\n * Process a stream and emit events through handlers\n */\n async process(stream: AsyncIterable<any>): Promise<ProcessorResult> {\n // Reset stream state (but keep messages)\n this.resetStreamState()\n\n // Start recording if enabled\n if (this.recordingEnabled) {\n this.startRecording()\n }\n\n // Process each chunk\n for await (const chunk of stream) {\n this.processChunk(chunk)\n }\n\n // Stream ended - finalize everything\n this.finalizeStream()\n\n // Finalize recording\n if (this.recording) {\n this.recording.result = this.getResult()\n }\n\n return this.getResult()\n }\n\n /**\n * Process a single chunk from the stream.\n *\n * Central dispatch for all AG-UI events. Each event type maps to a specific\n * handler. Events not listed in the switch are intentionally ignored\n * (STEP_STARTED, STATE_SNAPSHOT, STATE_DELTA).\n *\n * @see docs/chat-architecture.md#adapter-contract — Expected event types and ordering\n */\n processChunk(chunk: StreamChunk): void {\n // Record chunk if enabled\n if (this.recording) {\n this.recording.chunks.push({\n chunk,\n timestamp: Date.now(),\n index: this.recording.chunks.length,\n })\n }\n\n // Cast needed: @ag-ui/core Zod passthrough types add `& { [k: string]: unknown }`\n // which prevents TypeScript from narrowing the `type` discriminant in switch.\n const c = chunk\n // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- AG-UI EventType enum members vs string-literal case labels; default branch handles untraced events.\n switch (c.type) {\n // AG-UI Events\n case 'TEXT_MESSAGE_START':\n this.handleTextMessageStartEvent(\n chunk as Extract<StreamChunk, { type: 'TEXT_MESSAGE_START' }>,\n )\n break\n\n case 'TEXT_MESSAGE_CONTENT':\n this.handleTextMessageContentEvent(\n chunk as Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,\n )\n break\n\n case 'TEXT_MESSAGE_END':\n this.handleTextMessageEndEvent(\n chunk as Extract<StreamChunk, { type: 'TEXT_MESSAGE_END' }>,\n )\n break\n\n case 'TOOL_CALL_START':\n this.handleToolCallStartEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_START' }>,\n )\n break\n\n case 'TOOL_CALL_ARGS':\n this.handleToolCallArgsEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_ARGS' }>,\n )\n break\n\n case 'TOOL_CALL_END':\n this.handleToolCallEndEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_END' }>,\n )\n break\n\n case 'RUN_FINISHED':\n this.handleRunFinishedEvent(\n chunk as Extract<StreamChunk, { type: 'RUN_FINISHED' }>,\n )\n break\n\n case 'RUN_ERROR':\n this.handleRunErrorEvent(\n chunk as Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n )\n break\n\n case 'STEP_FINISHED':\n this.handleStepFinishedEvent(\n chunk as Extract<StreamChunk, { type: 'STEP_FINISHED' }>,\n )\n break\n\n case 'MESSAGES_SNAPSHOT':\n this.handleMessagesSnapshotEvent(\n chunk as Extract<StreamChunk, { type: 'MESSAGES_SNAPSHOT' }>,\n )\n break\n\n case 'CUSTOM':\n this.handleCustomEvent(\n chunk as Extract<StreamChunk, { type: 'CUSTOM' }>,\n )\n break\n\n case 'RUN_STARTED':\n this.handleRunStartedEvent(\n chunk as Extract<StreamChunk, { type: 'RUN_STARTED' }>,\n )\n break\n\n case 'REASONING_START':\n case 'REASONING_MESSAGE_START':\n case 'REASONING_MESSAGE_END':\n case 'REASONING_END':\n // No special handling needed\n break\n\n case 'REASONING_MESSAGE_CONTENT':\n this.handleReasoningMessageContentEvent(\n chunk as Extract<StreamChunk, { type: 'REASONING_MESSAGE_CONTENT' }>,\n )\n break\n\n case 'REASONING_ENCRYPTED_VALUE':\n this.handleReasoningEncryptedValueEvent(\n chunk as Extract<StreamChunk, { type: 'REASONING_ENCRYPTED_VALUE' }>,\n )\n break\n\n case 'TOOL_CALL_RESULT':\n this.handleToolCallResultEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_RESULT' }>,\n )\n break\n\n case 'STEP_STARTED':\n this.handleStepStartedEvent(\n chunk as Extract<StreamChunk, { type: 'STEP_STARTED' }>,\n )\n break\n\n default:\n // STATE_SNAPSHOT, STATE_DELTA - no special handling needed\n break\n }\n }\n\n // ============================================\n // Per-Message State Helpers\n // ============================================\n\n /**\n * Create a new MessageStreamState for a message\n */\n private createMessageState(\n messageId: string,\n role: 'user' | 'assistant' | 'system',\n ): MessageStreamState {\n const state: MessageStreamState = {\n id: messageId,\n role,\n totalTextContent: '',\n currentSegmentText: '',\n lastEmittedText: '',\n hasSeenReasoningEvents: false,\n thinkingSteps: new Map(),\n thinkingStepSignatures: new Map(),\n thinkingStepOrder: [],\n currentThinkingStepId: null,\n toolCalls: new Map(),\n toolCallOrder: [],\n hasToolCallsSinceTextStart: false,\n isComplete: false,\n }\n this.messageStates.set(messageId, state)\n return state\n }\n\n /**\n * Get the MessageStreamState for a message\n */\n private getMessageState(messageId: string): MessageStreamState | undefined {\n return this.messageStates.get(messageId)\n }\n\n /**\n * Promote a pending stepId from a STEP_STARTED that fired before the\n * assistant message existed onto the given message state, so the next\n * thinking event (STEP_FINISHED or REASONING_MESSAGE_CONTENT) attributes\n * to the correct step.\n */\n private consumePendingThinkingStep(state: MessageStreamState): void {\n if (!this.pendingThinkingStepId) return\n const stepId = this.pendingThinkingStepId\n state.currentThinkingStepId = stepId\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, '')\n state.thinkingStepOrder.push(stepId)\n }\n this.pendingThinkingStepId = null\n }\n\n /**\n * Get the most recent active assistant message ID.\n * Used as fallback for events that don't include a messageId.\n */\n private getActiveAssistantMessageId(): string | null {\n // Set iteration is insertion-order; reverse-iterate to search from the end\n const ids = Array.from(this.activeMessageIds).reverse()\n for (const id of ids) {\n const state = this.messageStates.get(id)\n if (state && state.role === 'assistant') {\n return id\n }\n }\n // finalizeStream() clears activeMessageIds but keeps messageStates.\n // Leftover reasoning after an early RUN_FINISHED must resume that\n // assistant. A new user turn calls prepareAssistantMessage(), which\n // clears messageStates first.\n for (const [id, state] of [...this.messageStates].reverse()) {\n if (state.role === 'assistant') {\n return id\n }\n }\n return null\n }\n\n private resumeAssistantState(id: string, state: MessageStreamState): void {\n this.activeMessageIds.add(id)\n if (state.isComplete || this.isDone) {\n state.isComplete = false\n this.isDone = false\n }\n }\n\n /**\n * Ensure an active assistant message exists, creating one if needed.\n * Used for backward compat when events arrive without prior TEXT_MESSAGE_START.\n *\n * On reconnect/resume, a TEXT_MESSAGE_CONTENT may arrive for a message that\n * already exists in this.messages (e.g. from initialMessages or a prior\n * MESSAGES_SNAPSHOT) but whose transient state was cleared. In that case we\n * hydrate state from the existing message rather than creating a duplicate.\n */\n private ensureAssistantMessage(preferredId?: string): {\n messageId: string\n state: MessageStreamState\n } {\n // Try to find state by preferred ID\n if (preferredId) {\n const state = this.getMessageState(preferredId)\n if (state) {\n this.resumeAssistantState(preferredId, state)\n return { messageId: preferredId, state }\n }\n }\n\n // Try active assistant message\n const activeId = this.getActiveAssistantMessageId()\n if (activeId) {\n const state = this.getMessageState(activeId)\n if (state) {\n this.resumeAssistantState(activeId, state)\n return { messageId: activeId, state }\n }\n }\n\n // Check if a message with preferredId already exists (reconnect/resume case).\n // Hydrate transient state from the existing message instead of duplicating it.\n if (preferredId) {\n const existingMsg = this.messages.find((m) => m.id === preferredId)\n if (existingMsg) {\n const state = this.createMessageState(preferredId, existingMsg.role)\n this.activeMessageIds.add(preferredId)\n\n // Seed segment text from the existing last text part so that\n // incoming deltas append correctly and updateTextPart produces\n // the right content (existing text + new delta).\n const lastPart =\n existingMsg.parts.length > 0\n ? existingMsg.parts[existingMsg.parts.length - 1]\n : null\n if (lastPart && lastPart.type === 'text') {\n state.currentSegmentText = lastPart.content\n state.lastEmittedText = lastPart.content\n state.totalTextContent = lastPart.content\n }\n\n return { messageId: preferredId, state }\n }\n }\n\n // Auto-create an assistant message (backward compat for process() without TEXT_MESSAGE_START)\n const id = preferredId || generateMessageId()\n const assistantMessage: UIMessage = {\n id,\n role: 'assistant',\n parts: [],\n createdAt: new Date(),\n }\n this.messages = [...this.messages, assistantMessage]\n const state = this.createMessageState(id, 'assistant')\n this.activeMessageIds.add(id)\n this.pendingManualMessageId = id\n this.events.onStreamStart?.()\n this.emitMessagesChange()\n return { messageId: id, state }\n }\n\n // ============================================\n // Event Handlers\n // ============================================\n\n /**\n * Merge event metadata onto a UIMessage. `tanstack` is deep-merged so a\n * later delta does not wipe `tanstack.model`. High-frequency leftover\n * keys (`content`, `args`) never stamp onto the message.\n * Rebuilds `createdAt` when `tanstack.createdAt` is an ISO string.\n */\n private mergeMessageMetadata(messageId: string, incoming: unknown): void {\n if (\n incoming == null ||\n typeof incoming !== 'object' ||\n Array.isArray(incoming)\n ) {\n return\n }\n const message = this.messages.find((msg) => msg.id === messageId)\n if (!message) return\n\n const incomingRecord = incoming as NonNullable<UIMessage['metadata']>\n const incomingTanstack = tanstackMetadata(incomingRecord)\n const toMerge =\n incomingTanstack != null &&\n ('content' in incomingTanstack || 'args' in incomingTanstack)\n ? {\n ...incomingRecord,\n tanstack: Object.fromEntries(\n Object.entries(incomingTanstack).filter(\n ([key]) => key !== 'content' && key !== 'args',\n ),\n ),\n }\n : incomingRecord\n const metadata = mergeMetadata(message.metadata, toMerge)\n const createdAt = coerceCreatedAt(\n tanstackMetadata(incomingRecord)?.createdAt,\n )\n const createdAtValid = createdAt !== undefined\n this.messages = this.messages.map((msg) =>\n msg.id === messageId\n ? {\n ...msg,\n ...(metadata !== undefined ? { metadata } : {}),\n ...(createdAtValid ? { createdAt } : {}),\n }\n : msg,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Handle TEXT_MESSAGE_START event\n */\n private handleTextMessageStartEvent(\n chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_START' }>,\n ): void {\n const { messageId, role } = chunk\n\n // Map 'tool' and 'developer' roles to 'assistant' for both UIMessage and MessageStreamState\n // (UIMessage doesn't support 'tool'/'developer' role, and lookups like\n // getActiveAssistantMessageId() check state.role === 'assistant')\n const uiRole: 'system' | 'user' | 'assistant' =\n role === 'user' || role === 'system' ? role : 'assistant'\n\n // Case 1: A manual message was created via startAssistantMessage()\n if (this.pendingManualMessageId) {\n const pendingId = this.pendingManualMessageId\n this.pendingManualMessageId = null\n\n if (pendingId !== messageId) {\n // Update the message's ID in the messages array\n this.messages = this.messages.map((msg) =>\n msg.id === pendingId ? { ...msg, id: messageId } : msg,\n )\n\n // Move state to the new key\n const existingState = this.messageStates.get(pendingId)\n if (existingState) {\n existingState.id = messageId\n this.messageStates.delete(pendingId)\n this.messageStates.set(messageId, existingState)\n }\n\n // Update activeMessageIds\n this.activeMessageIds.delete(pendingId)\n this.activeMessageIds.add(messageId)\n\n // TOOL_CALL_ARGS/END route through toolCallToMessage. Keep those\n // entries on the remapped id so later args still accumulate\n // (interleaved text can arrive as a full START/CONTENT/END block).\n for (const [toolCallId, mappedMessageId] of this.toolCallToMessage) {\n if (mappedMessageId === pendingId) {\n this.toolCallToMessage.set(toolCallId, messageId)\n }\n }\n }\n\n // Ensure state exists\n let pendingState = this.messageStates.get(messageId)\n if (!pendingState) {\n pendingState = this.createMessageState(messageId, uiRole)\n this.activeMessageIds.add(messageId)\n } else if (pendingState.hasToolCallsSinceTextStart) {\n // A tool call (e.g. TOOL_CALL_START with parentMessageId) marked\n // this message before its \"real\" TEXT_MESSAGE_START arrived — same\n // reset Case 2 performs, so the segment accumulator doesn't carry\n // stale tool-call state into the text that follows.\n if (pendingState.currentSegmentText !== pendingState.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n pendingState.currentSegmentText = ''\n pendingState.lastEmittedText = ''\n pendingState.hasToolCallsSinceTextStart = false\n }\n\n this.mergeMessageMetadata(messageId, chunk.metadata)\n this.emitMessagesChange()\n return\n }\n\n // Case 2: Message already exists (dedup)\n const existingMsg = this.messages.find((m) => m.id === messageId)\n if (existingMsg) {\n this.activeMessageIds.add(messageId)\n const existingState = this.messageStates.get(messageId)\n if (!existingState) {\n this.createMessageState(messageId, uiRole)\n } else {\n // If tool calls happened since last text, this TEXT_MESSAGE_START\n // signals a new text segment — reset segment accumulation\n if (existingState.hasToolCallsSinceTextStart) {\n if (\n existingState.currentSegmentText !== existingState.lastEmittedText\n ) {\n this.emitTextUpdateForMessage(messageId)\n }\n existingState.currentSegmentText = ''\n existingState.lastEmittedText = ''\n existingState.hasToolCallsSinceTextStart = false\n }\n }\n this.mergeMessageMetadata(messageId, chunk.metadata)\n return\n }\n\n // Case 3: New message from the stream\n const newMessage: UIMessage = {\n id: messageId,\n role: uiRole,\n parts: [],\n createdAt: new Date(),\n }\n\n this.messages = [...this.messages, newMessage]\n this.createMessageState(messageId, uiRole)\n this.activeMessageIds.add(messageId)\n\n this.mergeMessageMetadata(messageId, chunk.metadata)\n this.events.onStreamStart?.()\n this.emitMessagesChange()\n }\n\n /**\n * Handle TEXT_MESSAGE_END event\n */\n private handleTextMessageEndEvent(\n chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_END' }>,\n ): void {\n const { messageId } = chunk\n this.mergeMessageMetadata(messageId, chunk.metadata)\n const state = this.getMessageState(messageId)\n if (!state) return\n if (state.isComplete) return\n\n // Emit any pending text for this message\n if (state.currentSegmentText !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n }\n\n /**\n * Handle MESSAGES_SNAPSHOT event\n */\n private handleMessagesSnapshotEvent(\n chunk: Extract<StreamChunk, { type: 'MESSAGES_SNAPSHOT' }>,\n ): void {\n this.resetStreamState()\n // Normalize AG-UI snapshot messages to UIMessage[] so every message has a\n // `parts` array. AG-UI messages carry `content` but no `parts`, so casting\n // them directly to UIMessage[] is unsafe and causes \"Cannot read properties\n // of undefined (reading 'find')\" when code later reads message.parts (e.g.\n // the onToolCallStateChange devtools handler).\n //\n // The AG-UI `MESSAGES_SNAPSHOT` wire shape cannot reconstruct client-side\n // tool-call metadata a server may omit: a `role: 'tool'` message only carries\n // `toolCallId` + `content`, and an assistant message in the snapshot may\n // drop `toolCalls` the client already observed via `TOOL_CALL_*` events.\n // Without the matching `tool-call` part, later `addToolResult(toolCallId)`\n // calls cannot locate the call and warn + no-op (see #859). To keep the\n // UI representation consistent with the streaming fan-out and preserve the\n // unreconstructable metadata, reconcile the normalized snapshot against\n // the pre-snapshot state; see `reconcileSnapshotToolCalls`.\n const prevMessages = this.messages\n const prevById = new Map(prevMessages.map((msg) => [msg.id, msg]))\n const normalized = this.mergeReasoningFanOut(\n chunk.messages.map(aguiSnapshotMessageToUIMessage),\n )\n this.messages = this.reconcileSnapshotToolCalls(\n normalized,\n prevMessages,\n ).map((msg) => {\n if (msg.metadata != null) return msg\n const prev = prevById.get(msg.id)\n if (prev?.metadata == null) return msg\n return { ...msg, metadata: prev.metadata }\n })\n this.emitMessagesChange()\n }\n\n /**\n * Reconcile a freshly normalized snapshot with the pre-snapshot message\n * state so unreconstructable tool-call metadata is preserved.\n *\n * Post-pass (a): anchor `tool-result`-only assistant messages (the shape\n * `aguiSnapshotMessageToUIMessage` emits for AG-UI `role: 'tool'` wire\n * messages) into the message containing the matching `tool-call` part, or —\n * when the snapshot supplies no such part — the nearest earlier anchorable\n * assistant message, matching the in-stream fan-out shape\n * `assistant: [text, tool-call, tool-result, ...]`. Detached messages with\n * no earlier anchorable assistant are kept verbatim.\n *\n * Post-pass (b): when a `tool-result` part references a `toolCallId` whose\n * `tool-call` part is absent from the snapshot, carry the `tool-call` part\n * forward from the pre-snapshot state (state and output untouched) so a\n * subsequent `addToolResult(toolCallId)` can still locate the call.\n *\n * Post-pass (c): AG-UI wire snapshots rebuild `tool-call` parts as\n * `input-complete` without `output` (ModelMessage has no result field on\n * the call). After anchoring results, copy each `tool-result` onto its\n * matching `tool-call` (and prefer pre-snapshot complete/output when the\n * snapshot is poorer) so server tools keep the same UI shape as client tools.\n */\n /**\n * Wire order is reasoning fan-outs, then the assistant anchor.\n * Snapshot conversion turns each reasoning row into its own assistant\n * message. Fold leading thinking-only messages into the next real\n * assistant. Do not fold into a tool-result-only message (`role: 'tool'`\n * on the wire). `reconcileSnapshotToolCalls` anchors those results.\n */\n private mergeReasoningFanOut(messages: Array<UIMessage>): Array<UIMessage> {\n const out: Array<UIMessage> = []\n let pending: Array<UIMessage> = []\n const thinkingParts = (msg: UIMessage) =>\n msg.parts.filter((part): part is ThinkingPart => part.type === 'thinking')\n const isThinkingOnly = (msg: UIMessage) =>\n msg.role === 'assistant' &&\n msg.parts.length > 0 &&\n msg.parts.every((part) => part.type === 'thinking')\n const isToolResultOnly = (msg: UIMessage) =>\n msg.role === 'assistant' &&\n msg.parts.some((part) => part.type === 'tool-result') &&\n msg.parts.every(\n (part) => part.type === 'tool-result' || part.type === 'ui-resource',\n )\n const flushPending = () => {\n out.push(...pending)\n pending = []\n }\n for (const msg of messages) {\n if (isThinkingOnly(msg)) {\n pending.push(msg)\n continue\n }\n if (\n msg.role === 'assistant' &&\n pending.length > 0 &&\n !isToolResultOnly(msg)\n ) {\n out.push({\n ...msg,\n parts: [...pending.flatMap(thinkingParts), ...msg.parts],\n })\n pending = []\n continue\n }\n flushPending()\n out.push(msg)\n }\n flushPending()\n return out\n }\n\n private reconcileSnapshotToolCalls(\n snapshot: Array<UIMessage>,\n prevMessages: Array<UIMessage>,\n ): Array<UIMessage> {\n // Index tool-call parts observed before the snapshot by id so we can\n // restore metadata the snapshot cannot re-emit. Duplicate ids resolve\n // last-write-wins: the same tool call can appear in multiple messages\n // across reconnects, and the most recent part carries the freshest state.\n const prevToolCalls = new Map<string, ToolCallPart>()\n for (const msg of prevMessages) {\n for (const part of msg.parts) {\n if (part.type === 'tool-call') {\n prevToolCalls.set(part.id, part)\n }\n }\n }\n // Index tool-call parts already present in the snapshot so (b) only fills\n // genuine gaps rather than duplicating a tool-call the snapshot supplies.\n const snapshotToolCallIds = new Set<string>()\n for (const msg of snapshot) {\n for (const part of msg.parts) {\n if (part.type === 'tool-call') {\n snapshotToolCallIds.add(part.id)\n }\n }\n }\n\n const reconciled: Array<UIMessage> = []\n for (const msg of snapshot) {\n const toolResultParts = msg.parts.filter(\n (part): part is ToolResultPart => part.type === 'tool-result',\n )\n const toolResultPart =\n msg.role === 'assistant' &&\n toolResultParts.length === 1 &&\n msg.parts.every(\n (part) => part.type === 'tool-result' || part.type === 'ui-resource',\n )\n ? toolResultParts[0]\n : undefined\n\n if (!toolResultPart) {\n reconciled.push(msg)\n continue\n }\n\n // Prefer the message that actually contains the matching tool-call\n // part. AG-UI `reasoning`/`activity` messages also normalize to\n // `role: 'assistant'`, so anchoring into the nearest assistant alone\n // could separate a result from its call (and a later\n // `addToolResult(toolCallId)` would then append a duplicate result\n // next to the call).\n const target =\n reconciled.findLast((m) =>\n m.parts.some(\n (p) => p.type === 'tool-call' && p.id === toolResultPart.toolCallId,\n ),\n ) ??\n reconciled.findLast(\n (m) =>\n m.role === 'assistant' &&\n !(m.parts.length === 1 && m.parts[0]?.type === 'tool-result'),\n )\n\n if (!target) {\n // No assistant to anchor into — keep the detached message intact.\n if (!snapshotToolCallIds.has(toolResultPart.toolCallId)) {\n console.warn(\n `[StreamProcessor] MESSAGES_SNAPSHOT contains a tool-result for \"${toolResultPart.toolCallId}\" but no matching tool-call exists in the snapshot, and there is no assistant message to anchor into; addToolResult(\"${toolResultPart.toolCallId}\") will not be able to locate this call`,\n )\n }\n reconciled.push(msg)\n continue\n }\n\n const parts = [...target.parts]\n // (b) Fill in a missing tool-call part from the pre-snapshot state when\n // the snapshot references its id via a tool-result but supplies no\n // tool-call metadata of its own.\n if (\n !snapshotToolCallIds.has(toolResultPart.toolCallId) &&\n !parts.some(\n (p) => p.type === 'tool-call' && p.id === toolResultPart.toolCallId,\n )\n ) {\n const prev = prevToolCalls.get(toolResultPart.toolCallId)\n if (prev) {\n // Insert the carried-over tool-call before its tool-result (pushed\n // below) so call→result ordering matches the streaming fan-out.\n parts.push({ ...prev })\n snapshotToolCallIds.add(prev.id)\n } else {\n console.warn(\n `[StreamProcessor] MESSAGES_SNAPSHOT contains a tool-result for \"${toolResultPart.toolCallId}\" but no matching tool-call exists in the snapshot or the pre-snapshot state; addToolResult(\"${toolResultPart.toolCallId}\") will not be able to locate this call`,\n )\n }\n }\n parts.push(...msg.parts)\n // Replace rather than push into `target.parts`: a snapshot message that\n // arrived already carrying `parts` (TanStack server echoing UIMessages)\n // shares its array with the incoming chunk, and mutating it in place\n // would corrupt the caller's event object.\n target.parts = parts\n }\n\n return this.enrichSnapshotToolCallsFromResults(reconciled, prevToolCalls)\n }\n\n /**\n * Post-pass (c): fold `tool-result` content into sibling `tool-call` parts\n * and prefer pre-snapshot complete/output when the snapshot rebuilt a\n * poorer `input-complete` call (AG-UI ModelMessage has no result on calls).\n */\n private enrichSnapshotToolCallsFromResults(\n messages: Array<UIMessage>,\n prevToolCalls: Map<string, ToolCallPart>,\n ): Array<UIMessage> {\n const resultsByCallId = new Map<string, ToolResultPart>()\n for (const msg of messages) {\n for (const part of msg.parts) {\n if (part.type === 'tool-result') {\n resultsByCallId.set(part.toolCallId, part)\n }\n }\n }\n\n return messages.map((msg) => {\n const parts = msg.parts.map((part) => {\n if (part.type !== 'tool-call') return part\n\n const prev = prevToolCalls.get(part.id)\n const result = resultsByCallId.get(part.id)\n let next: ToolCallPart = part\n\n // Prefer a pre-snapshot call that already carried output/complete —\n // the client observed TOOL_CALL_END/RESULT before the snapshot wipe.\n if (\n prev &&\n (prev.output !== undefined ||\n prev.state === 'complete' ||\n prev.state === 'error') &&\n (part.output === undefined ||\n part.state === 'input-complete' ||\n part.state === 'input-streaming' ||\n part.state === 'awaiting-input')\n ) {\n next = {\n ...part,\n ...(prev.output !== undefined ? { output: prev.output } : {}),\n state: prev.state,\n ...(prev.approval !== undefined ? { approval: prev.approval } : {}),\n ...(prev.metadata !== undefined ? { metadata: prev.metadata } : {}),\n }\n }\n\n // Apply sibling tool-result when the call still has no output.\n if (result && next.output === undefined) {\n let output: unknown\n if (Array.isArray(result.content)) {\n output = result.content\n } else {\n try {\n output = JSON.parse(result.content)\n } catch {\n output = result.content\n }\n }\n const errorText =\n result.state === 'error'\n ? this.extractToolResultError(output)\n : undefined\n next = {\n ...next,\n output: errorText ? { error: errorText } : output,\n state: result.state === 'error' ? 'error' : 'complete',\n }\n }\n\n return next\n })\n // Rebuild only when a part object identity changed.\n const partsChanged = parts.some(\n (part, index) => part !== msg.parts[index],\n )\n return partsChanged ? { ...msg, parts } : msg\n })\n }\n\n /**\n * Handle TEXT_MESSAGE_CONTENT event.\n *\n * Accumulates delta into both currentSegmentText (for UI emission) and\n * totalTextContent (for ProcessorResult). Lazily creates the assistant\n * UIMessage on first content. Uses updateTextPart() which replaces the\n * last TextPart or creates a new one depending on part ordering.\n *\n * @see docs/chat-architecture.md#single-shot-text-response — Text accumulation step-by-step\n * @see docs/chat-architecture.md#uimessage-part-ordering-invariants — Replace vs. push logic\n */\n private handleTextMessageContentEvent(\n chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,\n ): void {\n const { messageId, state } = this.ensureAssistantMessage(chunk.messageId)\n this.mergeMessageMetadata(messageId, chunk.metadata)\n\n if (this.structuredMessageIds.has(messageId)) {\n const delta = chunk.delta || ''\n if (delta !== '') {\n this.messages = appendStructuredOutputDelta(\n this.messages,\n messageId,\n delta,\n )\n state.totalTextContent += delta\n this.queueStructuredOutputUpdate(messageId, delta)\n this.emitMessagesChange()\n }\n return\n }\n\n const previousSegment = state.currentSegmentText\n\n // Detect if this is a NEW text segment (after tool calls) vs continuation\n const isNewSegment =\n state.hasToolCallsSinceTextStart &&\n previousSegment.length > 0 &&\n this.isNewTextSegment(chunk, previousSegment)\n\n if (isNewSegment) {\n // Emit any accumulated text before starting new segment\n if (previousSegment !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n // Reset SEGMENT text accumulation for the new text segment after tool calls\n state.currentSegmentText = ''\n state.lastEmittedText = ''\n state.hasToolCallsSinceTextStart = false\n }\n\n // A segment begins at its first delta, not its second. Left set, the flag\n // makes the second delta look like a further segment: the accumulation\n // resets and updateTextPart writes over the part the first delta created.\n // A later tool call sets it again in handleToolCallStartEvent.\n state.hasToolCallsSinceTextStart = false\n\n const currentText = state.currentSegmentText\n const delta = chunk.delta || ''\n const nextText = delta !== '' ? currentText + delta : currentText\n\n // Calculate the delta for totalTextContent\n const textDelta = nextText.slice(currentText.length)\n state.currentSegmentText = nextText\n state.totalTextContent += textDelta\n\n const chunkPortion = chunk.delta || ''\n const shouldEmit = this.chunkStrategy.shouldEmit(\n chunkPortion,\n state.currentSegmentText,\n )\n if (shouldEmit && state.currentSegmentText !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n }\n\n /**\n * Handle TOOL_CALL_START event.\n *\n * Creates a new InternalToolCallState entry in the toolCalls Map and appends\n * a ToolCallPart to the UIMessage. Duplicate toolCallId is a no-op.\n *\n * CRITICAL: This MUST be received before any TOOL_CALL_ARGS for the same\n * toolCallId. Args for unknown IDs are silently dropped.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — Tool call state transitions\n * @see docs/chat-architecture.md#parallel-tool-calls-single-shot — Parallel tracking by ID\n * @see docs/chat-architecture.md#adapter-contract — Ordering requirements\n */\n private handleToolCallStartEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_START' }>,\n ): void {\n // Determine the message this tool call belongs to\n const targetMessageId =\n chunk.parentMessageId ?? this.getActiveAssistantMessageId()\n const { messageId, state } = this.ensureAssistantMessage(\n targetMessageId ?? undefined,\n )\n\n // Mark that we've seen tool calls since the last text segment\n state.hasToolCallsSinceTextStart = true\n\n const toolCallId = chunk.toolCallId\n const existingToolCall = state.toolCalls.get(toolCallId)\n\n if (!existingToolCall) {\n // New tool call starting\n const initialState: ToolCallState = 'awaiting-input'\n\n const toolName = chunk.toolCallName\n\n // Capture provider metadata that arrived on TOOL_CALL_START so it\n // round-trips back through the assistant message on the next turn\n // (e.g. Gemini's thoughtSignature).\n const chunkMetadata = chunk.metadata\n\n const newToolCall: InternalToolCallState = {\n id: chunk.toolCallId,\n name: toolName,\n arguments: '',\n state: initialState,\n parsedArguments: undefined,\n index: state.toolCalls.size,\n ...(chunkMetadata !== undefined && { metadata: chunkMetadata }),\n }\n\n state.toolCalls.set(toolCallId, newToolCall)\n state.toolCallOrder.push(toolCallId)\n\n // Store mapping for TOOL_CALL_ARGS/END routing\n this.toolCallToMessage.set(toolCallId, messageId)\n\n // Update UIMessage\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: chunk.toolCallId,\n name: toolName,\n arguments: '',\n state: initialState,\n ...(chunkMetadata !== undefined && { metadata: chunkMetadata }),\n })\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onToolCallStateChange?.(\n messageId,\n chunk.toolCallId,\n initialState,\n '',\n )\n }\n }\n\n /**\n * Handle TOOL_CALL_ARGS event.\n *\n * Appends the delta to the tool call's accumulated arguments string.\n * Transitions state from awaiting-input → input-streaming on first non-empty delta.\n * Attempts partial JSON parse on each update for UI preview.\n *\n * If toolCallId is not found in the Map (no preceding TOOL_CALL_START),\n * this event is silently dropped.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — Step-by-step tool call processing\n */\n private handleToolCallArgsEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_ARGS' }>,\n ): void {\n const toolCallId = chunk.toolCallId\n const messageId = this.toolCallToMessage.get(toolCallId)\n if (!messageId) return\n\n const state = this.getMessageState(messageId)\n if (!state) return\n\n const existingToolCall = state.toolCalls.get(toolCallId)\n if (!existingToolCall) return\n\n const wasAwaitingInput = existingToolCall.state === 'awaiting-input'\n\n // Accumulate arguments from delta\n existingToolCall.arguments += chunk.delta || ''\n\n // Update state\n if (wasAwaitingInput && chunk.delta) {\n existingToolCall.state = 'input-streaming'\n }\n\n // Try to parse the updated arguments\n existingToolCall.parsedArguments = this.jsonParser.parse(\n existingToolCall.arguments,\n )\n\n // Update UIMessage\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: existingToolCall.id,\n name: existingToolCall.name,\n arguments: existingToolCall.arguments,\n state: existingToolCall.state,\n })\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onToolCallStateChange?.(\n messageId,\n existingToolCall.id,\n existingToolCall.state,\n existingToolCall.arguments,\n )\n }\n\n /**\n * Handle TOOL_CALL_END event — arguments are finalized (input-complete).\n * Tool output arrives on TOOL_CALL_RESULT, not on this event.\n *\n * If TOOL_CALL_END carries parsed `input`, use it as the canonical arguments:\n * back-fill the accumulated string when no TOOL_CALL_ARGS deltas were seen\n * (adapters that deliver the whole input on END — e.g. Anthropic\n * server_tool_use / web_search — issue #839) and override the rendered part's\n * `input` with the canonical value.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — End-to-end flow\n */\n private handleToolCallEndEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_END' }>,\n ): void {\n const messageId = this.toolCallToMessage.get(chunk.toolCallId)\n if (!messageId) return\n\n const msgState = this.getMessageState(messageId)\n if (!msgState) return\n\n // The parsed input can ride on the spec `input` field or, for adapters\n // that only stamp it into TanStack metadata, on `metadata.tanstack.input`.\n const input =\n chunk.input !== undefined\n ? chunk.input\n : (tanstackMetadata(chunk)?.input as unknown)\n\n // Transition the tool call to input-complete (the authoritative completion signal)\n const existingToolCall = msgState.toolCalls.get(chunk.toolCallId)\n if (existingToolCall && existingToolCall.state !== 'input-complete') {\n // Back-fill the arguments string from the parsed input when no\n // TOOL_CALL_ARGS deltas were received, so completeToolCall's strict parse\n // surfaces the correct value on the ToolCallPart.\n if (input !== undefined && !existingToolCall.arguments) {\n try {\n existingToolCall.arguments = JSON.stringify(input)\n } catch {\n // circular refs, BigInt, etc. — leave arguments empty rather than\n // aborting stream processing\n }\n }\n\n const index = msgState.toolCallOrder.indexOf(chunk.toolCallId)\n this.completeToolCall(messageId, index, existingToolCall)\n\n // Canonicalize on the parsed input: overrides the accumulated-args parse\n // that completeToolCall wrote (adapters may coerce values differently\n // between streamed args and the final structured input).\n if (input !== undefined) {\n existingToolCall.parsedArguments = input\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: existingToolCall.id,\n name: existingToolCall.name,\n arguments: existingToolCall.arguments,\n state: 'input-complete',\n input,\n ...(existingToolCall.metadata !== undefined && {\n metadata: existingToolCall.metadata,\n }),\n })\n this.emitMessagesChange()\n }\n }\n }\n\n private extractToolResultError(output: unknown): string {\n if (\n output &&\n typeof output === 'object' &&\n 'error' in output &&\n typeof output.error === 'string'\n ) {\n return output.error\n }\n return typeof output === 'string' ? output : 'Tool execution failed'\n }\n\n /**\n * Handle TOOL_CALL_RESULT event (AG-UI spec).\n *\n * Creates a tool-result part and updates the tool-call output field,\n * mirroring the logic from TOOL_CALL_END when it carries a result.\n * This is the spec-compliant path for delivering tool results to the client.\n */\n private handleToolCallResultEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_RESULT' }>,\n ): void {\n // A resume stream delivers TOOL_CALL_RESULT for a tool call that was\n // started in a PRIOR run. A preceding MESSAGES_SNAPSHOT resets stream state\n // (clearing `toolCallToMessage`), so fall back to locating the message that\n // owns the tool-call part — matching `addToolResult`/`handleInterrupts`.\n // Without this the result is dropped, the follow-up request omits the tool\n // message, and the still-\"pending\" tool call re-interrupts (issue #532).\n const messageId =\n this.toolCallToMessage.get(chunk.toolCallId) ??\n this.messages.find((m) =>\n m.parts.some(\n (p): p is ToolCallPart =>\n p.type === 'tool-call' && p.id === chunk.toolCallId,\n ),\n )?.id\n if (!messageId) return\n\n const extra = chunk as AdapterYieldChunk\n const isOutputError =\n extra.state === 'output-error' ||\n tanstackMetadata(chunk)?.state === 'output-error'\n\n // Step 1: Update the tool-call part's output field\n let output: unknown\n try {\n output = JSON.parse(chunk.content)\n } catch {\n output = chunk.content\n }\n this.messages = updateToolCallWithOutput(\n this.messages,\n chunk.toolCallId,\n output,\n isOutputError ? 'error' : undefined,\n )\n\n // Step 2: Create/update the tool-result part\n const resultState: ToolResultState = isOutputError ? 'error' : 'complete'\n this.messages = updateToolResultPart(\n this.messages,\n messageId,\n chunk.toolCallId,\n chunk.content,\n resultState,\n resultState === 'error' ? this.extractToolResultError(output) : undefined,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Handle RUN_STARTED event.\n *\n * Registers the run so that RUN_FINISHED can determine whether other\n * runs are still active before finalizing.\n */\n private handleRunStartedEvent(\n chunk: Extract<StreamChunk, { type: 'RUN_STARTED' }>,\n ): void {\n this.activeRuns.add(chunk.runId)\n }\n\n /**\n * Handle RUN_FINISHED event.\n *\n * Records the finishReason and removes the run from activeRuns.\n * Only finalizes when no more runs are active, so that concurrent\n * runs don't interfere with each other.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — finishReason semantics\n * @see docs/chat-architecture.md#adapter-contract — Why RUN_FINISHED is mandatory\n */\n private handleRunFinishedEvent(\n chunk: Extract<StreamChunk, { type: 'RUN_FINISHED' }>,\n ): void {\n const extra = chunk as AdapterYieldChunk\n this.finishReason =\n extra.finishReason !== undefined\n ? extra.finishReason\n : (tanstackMetadata(chunk)?.finishReason ?? null)\n this.activeRuns.delete(chunk.runId)\n\n if (chunk.outcome?.type === 'interrupt') {\n this.handleInterrupts(chunk.outcome.interrupts)\n }\n\n if (this.activeRuns.size === 0) {\n this.completeAllToolCalls()\n const isIntermediateToolTurn =\n this.finishReason === 'tool_calls' &&\n chunk.outcome?.type !== 'interrupt'\n if (isIntermediateToolTurn) {\n return\n }\n this.isDone = true\n this.finalizeStream()\n }\n }\n\n private handleInterrupts(interrupts: Array<Interrupt>): void {\n const hasGeneric = interruptBatchHasGeneric(interrupts)\n for (const interrupt of interrupts) {\n const metadata =\n interrupt.metadata && typeof interrupt.metadata === 'object'\n ? interrupt.metadata\n : {}\n const kind = typeof metadata.kind === 'string' ? metadata.kind : undefined\n const toolCallId = interrupt.toolCallId\n if (!toolCallId) continue\n\n const toolName =\n typeof metadata.toolName === 'string'\n ? metadata.toolName\n : this.findToolCallName(toolCallId)\n const input = Object.hasOwn(metadata, 'input') ? metadata.input : {}\n\n if (kind === 'approval' || interrupt.reason === 'approval_required') {\n // An interrupt terminal arrives after MESSAGES_SNAPSHOT, which may have\n // rebuilt the message list without the live active-message/tool-call\n // maps. Fall back to locating the assistant message that actually owns\n // the tool-call part so the approval UI (part.state) still updates.\n const resolvedMessageId =\n this.getActiveAssistantMessageId() ??\n this.toolCallToMessage.get(toolCallId) ??\n this.messages.find(\n (m) =>\n m.role === 'assistant' &&\n m.parts.some(\n (p) => p.type === 'tool-call' && p.id === toolCallId,\n ),\n )?.id\n if (resolvedMessageId) {\n this.messages = updateToolCallApproval(\n this.messages,\n resolvedMessageId,\n toolCallId,\n interrupt.id,\n )\n this.emitMessagesChange()\n }\n\n this.events.onApprovalRequest?.({\n toolCallId,\n toolName,\n input,\n approvalId: interrupt.id,\n })\n continue\n }\n\n if (kind === 'client_tool' || interrupt.reason === 'client_tool_input') {\n // Generic interrupts in the same batch decide `toolResume`. Do not\n // run client tools until that policy is `continue`.\n if (hasGeneric) continue\n this.events.onToolCall?.({\n toolCallId,\n toolName,\n input,\n })\n }\n }\n }\n\n private findToolCallName(toolCallId: string): string {\n for (const state of this.messageStates.values()) {\n const toolCall = state.toolCalls.get(toolCallId)\n if (toolCall) return toolCall.name\n }\n return ''\n }\n\n /**\n * Handle RUN_ERROR event\n */\n private handleRunErrorEvent(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n ): void {\n this.hasError = true\n const runId = getChunkRunId(chunk)\n if (runId) {\n this.activeRuns.delete(runId)\n } else {\n this.activeRuns.clear()\n }\n const { messageId } = this.ensureAssistantMessage()\n // Prefer spec field `message`; fall back to deprecated `error.message`.\n // If neither is set, the chunk still carries debug context (provider\n // error codes, request ids, etc.) — log it so the failure isn't silent.\n const errorMessage = chunk.message || 'An error occurred'\n if (!chunk.message) {\n console.error(\n '[StreamProcessor] RUN_ERROR with no message; original chunk:',\n chunk,\n )\n }\n\n if (this.structuredMessageIds.has(messageId)) {\n this.flushStructuredOutputUpdate(messageId)\n this.messages = errorStructuredOutputPart(\n this.messages,\n messageId,\n errorMessage,\n )\n this.structuredMessageIds.delete(messageId)\n this.emitStructuredOutputChange(messageId, 'error')\n this.emitMessagesChange()\n }\n\n // Attach the provider's structured error body (`rawEvent`) and `code` to\n // the surfaced Error so consumers can recover the upstream detail that the\n // RUN_ERROR's `message` alone discards. Both are optional and added only\n // when present, keeping the Error backward compatible.\n this.events.onError?.(runErrorEventToError(chunk))\n }\n\n /**\n * Handle STEP_STARTED event (for thinking/reasoning content).\n *\n * Records the stepId so later REASONING_MESSAGE_CONTENT deltas accumulate\n * into their own ThinkingPart. Does not create a message — the message\n * is lazily created when the first REASONING_MESSAGE_CONTENT arrives.\n */\n private handleStepStartedEvent(\n chunk: Extract<StreamChunk, { type: 'STEP_STARTED' }>,\n ): void {\n const stepId = chunk.stepName || generateMessageId()\n const activeId = this.getActiveAssistantMessageId()\n if (activeId) {\n const state = this.getMessageState(activeId)\n if (state) {\n state.currentThinkingStepId = stepId\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, '')\n state.thinkingStepOrder.push(stepId)\n }\n // Clear any pending stepId from a prior STEP_STARTED that fired\n // before the assistant message existed. Now that we're tracking\n // the step directly on message state, the pending value is stale\n // and must not leak into the next REASONING_MESSAGE_CONTENT.\n this.pendingThinkingStepId = null\n return\n }\n }\n\n // No active message yet — defer until ensureAssistantMessage in\n // REASONING_MESSAGE_CONTENT\n this.pendingThinkingStepId = stepId\n }\n\n /**\n * Handle STEP_FINISHED event.\n *\n * Thinking *content* comes from REASONING_MESSAGE_CONTENT, not STEP_FINISHED.\n * But some adapters (e.g. BytePlus thinking-summary) carry the provider\n * signature blob ONLY on the STEP_FINISHED event, so still extract that here\n * and attach it to the thinking step the reasoning events already built.\n */\n private handleStepFinishedEvent(\n chunk: Extract<StreamChunk, { type: 'STEP_FINISHED' }>,\n ): void {\n const extra = chunk as AdapterYieldChunk\n const signature = extra.signature\n if (!signature) return\n\n const { messageId, state } = this.ensureAssistantMessage(\n this.getActiveAssistantMessageId() ?? undefined,\n )\n const stepId = state.currentThinkingStepId ?? extra.stepId\n if (!stepId) return\n const thinking = state.thinkingSteps.get(stepId)\n if (thinking === undefined) return\n\n state.thinkingStepSignatures.set(stepId, signature)\n this.messages = updateThinkingPart(\n this.messages,\n messageId,\n stepId,\n thinking,\n signature,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Handle REASONING_MESSAGE_CONTENT event (AG-UI reasoning protocol).\n *\n * Accumulates reasoning delta into thinking content and updates the\n * corresponding ThinkingPart in the UIMessage.\n */\n private handleReasoningMessageContentEvent(\n chunk: Extract<StreamChunk, { type: 'REASONING_MESSAGE_CONTENT' }>,\n ): void {\n const { messageId, state } = this.ensureAssistantMessage(\n this.getActiveAssistantMessageId() ?? undefined,\n )\n\n state.hasSeenReasoningEvents = true\n const delta = chunk.delta || ''\n\n this.consumePendingThinkingStep(state)\n\n const stepId = state.currentThinkingStepId ?? chunk.messageId\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, '')\n state.thinkingStepOrder.push(stepId)\n state.currentThinkingStepId = stepId\n }\n\n const nextThinking = (state.thinkingSteps.get(stepId) ?? '') + delta\n state.thinkingSteps.set(stepId, nextThinking)\n\n this.messages = updateThinkingPart(\n this.messages,\n messageId,\n stepId,\n nextThinking,\n state.thinkingStepSignatures.get(stepId),\n )\n this.emitMessagesChange()\n\n this.events.onThinkingUpdate?.(messageId, stepId, nextThinking)\n }\n\n /**\n * Attach a provider signature blob from REASONING_ENCRYPTED_VALUE.\n * `subtype: 'message'` updates ThinkingPart.signature.\n * `subtype: 'tool-call'` stores Gemini thoughtSignature on the tool-call part.\n */\n private handleReasoningEncryptedValueEvent(\n chunk: Extract<StreamChunk, { type: 'REASONING_ENCRYPTED_VALUE' }>,\n ): void {\n const encryptedValue = chunk.encryptedValue\n if (typeof encryptedValue !== 'string' || encryptedValue === '') return\n\n if (chunk.subtype === 'tool-call') {\n this.attachToolCallSignature(chunk.entityId, encryptedValue)\n return\n }\n\n const { messageId, state } = this.ensureAssistantMessage(\n this.getActiveAssistantMessageId() ?? undefined,\n )\n const stepId = state.currentThinkingStepId ?? chunk.entityId\n state.thinkingStepSignatures.set(stepId, encryptedValue)\n const content = state.thinkingSteps.get(stepId) ?? ''\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, content)\n state.thinkingStepOrder.push(stepId)\n }\n this.messages = updateThinkingPart(\n this.messages,\n messageId,\n stepId,\n content,\n encryptedValue,\n )\n this.emitMessagesChange()\n }\n\n private attachToolCallSignature(\n toolCallId: string,\n thoughtSignature: string,\n ): void {\n this.messages = this.messages.map((msg) => {\n let changed = false\n const parts = msg.parts.map((part) => {\n if (part.type !== 'tool-call' || part.id !== toolCallId) return part\n changed = true\n return {\n ...part,\n metadata: {\n ...(part.metadata != null && typeof part.metadata === 'object'\n ? part.metadata\n : {}),\n thoughtSignature,\n },\n }\n })\n return changed ? { ...msg, parts } : msg\n })\n for (const state of this.messageStates.values()) {\n const call = state.toolCalls.get(toolCallId)\n if (!call) continue\n call.metadata = {\n ...(call.metadata ?? {}),\n thoughtSignature,\n }\n }\n this.emitMessagesChange()\n }\n\n /**\n * Handle CUSTOM event.\n *\n * Handles custom events consumed by the processor:\n * - 'tool-input-available': Legacy/replay-compatible input for client tool\n * execution. Fires onToolCall.\n * - 'approval-requested': Legacy/replay-compatible input for tool approval.\n * Updates tool-call part state and fires onApprovalRequest.\n *\n * Current core streams represent user-actionable waits through\n * RUN_FINISHED.outcome.type === 'interrupt'; these custom events are not the\n * source of truth for new emissions.\n *\n * @see docs/chat-architecture.md#client-tools-and-approval-flows — Full flow details\n */\n private handleCustomEvent(\n chunk: Extract<StreamChunk, { type: 'CUSTOM' }>,\n ): void {\n const messageId = this.getActiveAssistantMessageId()\n\n if (chunk.name === 'structured-output.start' && chunk.value) {\n const v = chunk.value as { messageId?: string }\n const { messageId: targetId } = this.ensureAssistantMessage(\n v.messageId ?? messageId ?? undefined,\n )\n if (targetId) {\n this.structuredMessageIds.add(targetId)\n this.structuredOutputUpdateBatches.delete(targetId)\n this.events.onStructuredOutputChange?.({\n phase: 'start',\n messageId: targetId,\n status: 'streaming',\n raw: '',\n })\n }\n return\n }\n\n if (chunk.name === 'structured-output.complete' && chunk.value) {\n const v = chunk.value as {\n object: unknown\n raw?: string\n reasoning?: string\n messageId?: string\n }\n const { messageId: targetId } = this.ensureAssistantMessage(\n v.messageId ?? messageId ?? undefined,\n )\n if (targetId) {\n this.flushStructuredOutputUpdate(targetId)\n this.messages = completeStructuredOutputPart(\n this.messages,\n targetId,\n v.object,\n v.raw ?? '',\n v.reasoning,\n )\n this.structuredMessageIds.delete(targetId)\n this.emitStructuredOutputChange(targetId, 'complete')\n this.emitMessagesChange()\n }\n // Fall through so user `onCustomEvent` callbacks still observe the event.\n }\n\n // Handle client tool input availability - trigger client-side execution\n if (chunk.name === 'tool-input-available' && chunk.value) {\n const { toolCallId, toolName, input } = chunk.value as {\n toolCallId: string\n toolName: string\n input: any\n }\n\n // Emit onToolCall event for the client to execute the tool\n this.events.onToolCall?.({\n toolCallId,\n toolName,\n input,\n })\n return\n }\n\n // Handle approval requests\n if (chunk.name === 'approval-requested' && chunk.value) {\n const { toolCallId, toolName, input, approval } = chunk.value as {\n toolCallId: string\n toolName: string\n input: any\n approval: { id: string; needsApproval: boolean }\n }\n\n // Resolve the message containing this tool call. After RUN_FINISHED,\n // activeMessageIds is cleared, so fall back to the toolCallToMessage map\n // which is populated during TOOL_CALL_START and preserved across finalize.\n const resolvedMessageId =\n messageId ?? this.toolCallToMessage.get(toolCallId)\n if (resolvedMessageId) {\n this.messages = updateToolCallApproval(\n this.messages,\n resolvedMessageId,\n toolCallId,\n approval.id,\n )\n this.emitMessagesChange()\n }\n\n // Emit approval request event\n this.events.onApprovalRequest?.({\n toolCallId,\n toolName,\n input,\n approvalId: approval.id,\n })\n return\n }\n\n // Handle MCP Apps ui-resource events — materialize a UIResourcePart on the\n // active assistant message. Never falls through to onCustomEvent because\n // ui-resource is a system event, not a user-defined custom event.\n if (chunk.name === 'ui-resource' && chunk.value) {\n const v: UIResourceEvent['value'] = chunk.value\n // Resolve the target assistant message. When a toolCallId is present, the\n // tool call's OWNER message is authoritative, so prefer it first; fall\n // back to the active assistant id only if the tool call isn't mapped.\n // This avoids misattaching the widget to a different active message in a\n // multi-message session.\n const resolvedMessageId =\n this.toolCallToMessage.get(v.toolCallId) ?? messageId\n if (resolvedMessageId) {\n const part: UIResourcePart = {\n type: 'ui-resource',\n resource: v.resource,\n toolCallId: v.toolCallId,\n toolName: v.toolName,\n ...(v.serverId !== undefined && { serverId: v.serverId }),\n ...(v.meta !== undefined && { meta: v.meta }),\n }\n this.messages = this.messages.map((msg) =>\n msg.id === resolvedMessageId\n ? { ...msg, parts: [...msg.parts, part] }\n : msg,\n )\n this.emitMessagesChange()\n } else {\n // No owner message and no active assistant id — the server read and\n // streamed a widget that has nowhere to attach (e.g. a toolCallId never\n // registered, or the event arrived after the run cleared its active\n // ids). Drop fail-soft, but warn: a vanished widget is otherwise\n // undebuggable from the client.\n console.warn(\n `[mcp-apps] dropped ui-resource: no target message for toolCallId \"${v.toolCallId}\" (toolName \"${v.toolName}\")`,\n )\n }\n return\n }\n\n // Forward non-system custom events to onCustomEvent callback\n if (this.events.onCustomEvent) {\n const toolCallId =\n chunk.value && typeof chunk.value === 'object'\n ? chunk.value.toolCallId\n : undefined\n this.events.onCustomEvent(chunk.name, chunk.value, { toolCallId })\n }\n }\n\n // ============================================\n // Internal Helpers\n // ============================================\n\n /**\n * Detect if an incoming content chunk represents a NEW text segment\n */\n private isNewTextSegment(\n _chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,\n _previous: string,\n ): boolean {\n return true\n }\n\n /**\n * Complete all tool calls across all active messages — safety net for stream termination.\n *\n * Called by RUN_FINISHED and finalizeStream(). Force-transitions any tool call\n * not yet in input-complete state. Handles cases where TOOL_CALL_END was\n * missed (adapter bug, network error, aborted stream).\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — Safety net behavior\n */\n private completeAllToolCalls(): void {\n for (const messageId of this.activeMessageIds) {\n this.completeAllToolCallsForMessage(messageId)\n }\n }\n\n /**\n * Complete all tool calls for a specific message\n */\n private completeAllToolCallsForMessage(messageId: string): void {\n const state = this.getMessageState(messageId)\n if (!state) return\n\n state.toolCalls.forEach((toolCall, id) => {\n if (toolCall.state !== 'input-complete') {\n const index = state.toolCallOrder.indexOf(id)\n this.completeToolCall(messageId, index, toolCall)\n }\n })\n }\n\n /**\n * Mark a tool call as complete and emit event\n */\n private completeToolCall(\n messageId: string,\n _index: number,\n toolCall: InternalToolCallState,\n ): void {\n // Finalize the internal bookkeeping: the call's input arguments ARE\n // complete regardless of whether execution later failed, so the call still\n // counts as a completed tool call in getCompletedToolCalls()/getState().\n toolCall.state = 'input-complete'\n\n // Only surface `input` from a strict parse. The streaming partial-JSON\n // parser closes unterminated strings, so truncated arguments would become\n // a plausible but wrong object (GitHub issue #1017). If parse fails,\n // `input` stays unset and consumers use the raw `arguments` string.\n let strictParseSucceeded = false\n try {\n toolCall.parsedArguments = JSON.parse(toolCall.arguments)\n strictParseSucceeded = true\n } catch {\n toolCall.parsedArguments = undefined\n }\n\n // Don't downgrade the rendered part of a call that already reached the\n // terminal 'error' state (e.g. an output-error TOOL_CALL_RESULT arrived\n // without a preceding TOOL_CALL_END). The RUN_FINISHED / finalizeStream\n // safety net must not clobber a failed call back to 'input-complete'.\n if (this.isToolCallPartErrored(toolCall.id)) {\n return\n }\n\n // RUN_FINISHED interrupt handling can mark the rendered part as waiting on\n // user action before the completion safety net runs. Keep the parsed\n // argument bookkeeping above, but do not downgrade that visible state.\n if (this.isToolCallPartAwaitingUserAction(toolCall.id)) {\n return\n }\n\n // Update UIMessage. The arguments are complete now, so surface the parsed\n // input on the part from the accumulated TOOL_CALL_ARGS deltas.\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: 'input-complete',\n ...(strictParseSucceeded && { input: toolCall.parsedArguments }),\n ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),\n })\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onToolCallStateChange?.(\n messageId,\n toolCall.id,\n 'input-complete',\n toolCall.arguments,\n )\n }\n\n private isToolCallPartAwaitingUserAction(toolCallId: string): boolean {\n return this.messages.some((msg) =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `parts` is typed as required, but seeded ModelMessage-shaped messages can lack it at runtime.\n msg.parts?.some(\n (part) =>\n part.type === 'tool-call' &&\n part.id === toolCallId &&\n (part.state === 'approval-requested' ||\n part.state === 'approval-responded'),\n ),\n )\n }\n\n /**\n * Whether the rendered tool-call part for the given id has reached the\n * terminal 'error' state. Used to prevent the completion safety net from\n * downgrading a failed call back to 'input-complete'.\n */\n private isToolCallPartErrored(toolCallId: string): boolean {\n // `initialMessages` may be ModelMessage-shaped (no `parts`) — e.g. the\n // common pattern of seeding a processor with the same messages passed to\n // `chat()`. Guard the access so iterating them never throws.\n return this.messages.some((msg) =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `parts` is typed as required, but seeded ModelMessage-shaped messages can lack it at runtime.\n msg.parts?.some(\n (part) =>\n part.type === 'tool-call' &&\n part.id === toolCallId &&\n part.state === 'error',\n ),\n )\n }\n\n /**\n * Emit pending text update for a specific message.\n *\n * Calls updateTextPart() which has critical append-vs-replace logic:\n * - If last UIMessage part is TextPart → replaces its content (same segment).\n * - If last part is anything else → pushes new TextPart (new segment after tools).\n *\n * @see docs/chat-architecture.md#uimessage-part-ordering-invariants — Replace vs. push logic\n */\n private emitTextUpdateForMessage(messageId: string): void {\n const state = this.getMessageState(messageId)\n if (!state) return\n\n state.lastEmittedText = state.currentSegmentText\n\n // Update UIMessage\n this.messages = updateTextPart(\n this.messages,\n messageId,\n state.currentSegmentText,\n )\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onTextUpdate?.(messageId, state.currentSegmentText)\n }\n\n private queueStructuredOutputUpdate(messageId: string, delta: string): void {\n const existing = this.structuredOutputUpdateBatches.get(messageId)\n const next = {\n delta: `${existing?.delta ?? ''}${delta}`,\n chunkCount: (existing?.chunkCount ?? 0) + 1,\n }\n\n this.structuredOutputUpdateBatches.set(messageId, next)\n\n if (next.chunkCount >= STRUCTURED_OUTPUT_UPDATE_BATCH_SIZE) {\n this.flushStructuredOutputUpdate(messageId)\n }\n }\n\n private flushStructuredOutputUpdate(messageId: string): void {\n const batch = this.structuredOutputUpdateBatches.get(messageId)\n if (!batch || batch.chunkCount === 0) return\n\n this.structuredOutputUpdateBatches.delete(messageId)\n this.emitStructuredOutputChange(messageId, 'update', batch.delta)\n }\n\n private emitStructuredOutputChange(\n messageId: string,\n phase: 'update' | 'complete' | 'error',\n delta?: string,\n ): void {\n const part = this.messages\n .find((message) => message.id === messageId)\n ?.parts.find(\n (\n messagePart,\n ): messagePart is Extract<MessagePart, { type: 'structured-output' }> =>\n messagePart.type === 'structured-output',\n )\n if (!part) return\n\n this.events.onStructuredOutputChange?.({\n phase,\n messageId,\n status: part.status,\n raw: part.raw,\n ...(part.partial !== undefined ? { partial: part.partial } : {}),\n ...(part.data !== undefined ? { data: part.data } : {}),\n ...(part.reasoning !== undefined ? { reasoning: part.reasoning } : {}),\n ...(part.errorMessage !== undefined\n ? { errorMessage: part.errorMessage }\n : {}),\n ...(delta !== undefined ? { delta } : {}),\n })\n }\n\n /**\n * Emit messages change event\n */\n private emitMessagesChange(): void {\n this.events.onMessagesChange?.([...this.messages])\n }\n\n /**\n * Finalize the stream — complete all pending operations.\n *\n * Called when the async iterable ends (stream closed). Acts as the final\n * safety net: completes any remaining tool calls, flushes un-emitted text,\n * and fires onStreamEnd.\n *\n * @see docs/chat-architecture.md#single-shot-text-response — Finalization step\n */\n finalizeStream(): void {\n this.isDone = true\n let lastAssistantMessage: UIMessage | undefined\n\n // Finalize ALL active messages\n for (const messageId of this.activeMessageIds) {\n const state = this.getMessageState(messageId)\n if (!state) continue\n\n // Complete any remaining tool calls\n this.completeAllToolCallsForMessage(messageId)\n\n // Emit any pending text if not already emitted\n if (state.currentSegmentText !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n\n state.isComplete = true\n\n const msg = this.messages.find((m) => m.id === messageId)\n if (msg && msg.role === 'assistant') {\n lastAssistantMessage = msg\n }\n }\n\n // The stream closed but one or more structured-output runs never sent\n // their terminal `structured-output.complete`. Snap each lingering\n // streaming part to error so the UI doesn't appear to stream forever,\n // and drop the routing entries so a subsequent run on the same\n // processor instance (long-lived `subscribe()` mode) doesn't reuse\n // the stale ids.\n //\n // The iteration is unconditional w.r.t. `this.hasError` — RUN_ERROR\n // already removed its target messageId from `structuredMessageIds`\n // before reaching finalize, so anything still in the set is by\n // definition a non-errored, never-completed run (the multi-run case:\n // run-A errors, run-B is still streaming when finalize fires).\n for (const messageId of this.structuredMessageIds) {\n this.flushStructuredOutputUpdate(messageId)\n this.messages = errorStructuredOutputPart(\n this.messages,\n messageId,\n 'Stream ended without structured-output.complete',\n )\n this.emitStructuredOutputChange(messageId, 'error')\n }\n this.structuredMessageIds.clear()\n this.structuredOutputUpdateBatches.clear()\n\n this.activeMessageIds.clear()\n\n // Remove whitespace-only assistant messages (handles models like Gemini\n // that sometimes return just \"\\n\" during auto-continuation).\n // Preserve the message on errors so the UI can show error state.\n if (lastAssistantMessage && !this.hasError) {\n if (this.isWhitespaceOnlyMessage(lastAssistantMessage)) {\n this.messages = this.messages.filter(\n (m) => m.id !== lastAssistantMessage.id,\n )\n this.emitMessagesChange()\n return\n }\n }\n\n // Emit stream end for the last assistant message\n if (lastAssistantMessage && !this.streamEndEmitted) {\n this.streamEndEmitted = true\n this.events.onStreamEnd?.(lastAssistantMessage)\n }\n }\n\n /**\n * Get completed tool calls in API format (aggregated across all messages)\n */\n private getCompletedToolCalls(): Array<ToolCall> {\n const result: Array<ToolCall> = []\n for (const state of this.messageStates.values()) {\n for (const tc of state.toolCalls.values()) {\n if (tc.state === 'input-complete') {\n result.push({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.name,\n arguments: tc.arguments,\n },\n // Preserve provider metadata (e.g. Gemini thoughtSignature) on\n // ProcessorResult.toolCalls so callers using process()/getResult()\n // get the same round-trip support as the streaming UI path.\n ...(tc.metadata !== undefined && { metadata: tc.metadata }),\n })\n }\n }\n }\n return result\n }\n\n /**\n * Get current result (aggregated across all messages)\n */\n private getResult(): ProcessorResult {\n const toolCalls = this.getCompletedToolCalls()\n let content = ''\n let thinking = ''\n\n for (const state of this.messageStates.values()) {\n content += state.totalTextContent\n for (const stepId of state.thinkingStepOrder) {\n thinking += state.thinkingSteps.get(stepId) ?? ''\n }\n }\n\n return {\n content,\n thinking: thinking || undefined,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n finishReason: this.finishReason,\n }\n }\n\n /**\n * Get current processor state (aggregated across all messages)\n */\n getState(): ProcessorState {\n let content = ''\n let thinking = ''\n const toolCalls = new Map<string, InternalToolCallState>()\n const toolCallOrder: Array<string> = []\n\n for (const state of this.messageStates.values()) {\n content += state.totalTextContent\n for (const stepId of state.thinkingStepOrder) {\n thinking += state.thinkingSteps.get(stepId) ?? ''\n }\n for (const [id, tc] of state.toolCalls) {\n toolCalls.set(id, tc)\n }\n toolCallOrder.push(...state.toolCallOrder)\n }\n\n return {\n content,\n thinking,\n toolCalls,\n toolCallOrder,\n finishReason: this.finishReason,\n done: this.isDone,\n }\n }\n\n /**\n * Start recording chunks\n */\n startRecording(): void {\n this.recordingEnabled = true\n this.recordingStartTime = Date.now()\n this.recording = {\n version: '1.0',\n timestamp: this.recordingStartTime,\n chunks: [],\n }\n }\n\n /**\n * Get the current recording\n */\n getRecording(): ChunkRecording | null {\n return this.recording\n }\n\n /**\n * Reset stream state (but keep messages)\n */\n private resetStreamState(): void {\n this.messageStates.clear()\n this.activeMessageIds.clear()\n this.activeRuns.clear()\n this.toolCallToMessage.clear()\n this.structuredMessageIds.clear()\n this.structuredOutputUpdateBatches.clear()\n this.pendingManualMessageId = null\n this.pendingThinkingStepId = null\n this.finishReason = null\n this.hasError = false\n this.isDone = false\n this.streamEndEmitted = false\n this.chunkStrategy.reset?.()\n }\n\n /**\n * Full reset (including messages)\n */\n reset(): void {\n this.resetStreamState()\n this.messages = []\n }\n\n /**\n * Check if a message contains only whitespace text and no other meaningful parts\n * (no tool calls, tool results, thinking, etc.)\n */\n private isWhitespaceOnlyMessage(message: UIMessage): boolean {\n if (message.parts.length === 0) return false\n return message.parts.every(\n (part) => part.type === 'text' && part.content.trim() === '',\n )\n }\n\n /**\n * Replay a recording through the processor\n */\n static async replay(\n recording: ChunkRecording,\n options?: StreamProcessorOptions,\n ): Promise<ProcessorResult> {\n const processor = new StreamProcessor(options)\n return processor.process(createReplayStream(recording))\n }\n}\n\n/**\n * Create an async iterable from a recording\n */\nexport function createReplayStream(\n recording: ChunkRecording,\n): AsyncIterable<StreamChunk> {\n return {\n // eslint-disable-next-line @typescript-eslint/require-await -- async generator required by AsyncIterable contract; body has no await\n async *[Symbol.asyncIterator]() {\n for (const { chunk } of recording.chunks) {\n yield chunk\n }\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"processor.js","names":[],"sources":["../../../../../src/activities/chat/stream/processor.ts"],"sourcesContent":["/**\n * Unified Stream Processor\n *\n * Core stream processing engine that manages the full UIMessage[] conversation.\n * Single source of truth for message state.\n *\n * Handles:\n * - Full conversation management (UIMessage[])\n * - Text content accumulation with configurable chunking strategies\n * - Parallel tool calls with lifecycle state tracking\n * - Tool results and approval flows\n * - Thinking/reasoning content\n * - Recording/replay for testing\n * - Event-driven architecture for UI updates\n * - Per-message stream state tracking for multi-message sessions\n *\n * @see docs/chat-architecture.md — Canonical reference for AG-UI chunk ordering,\n * adapter contract, single-shot flows, and expected UIMessage output.\n */\nimport {\n aguiSnapshotMessageToUIMessage,\n coerceCreatedAt,\n generateMessageId,\n uiMessageToModelMessages,\n} from '../messages.js'\nimport { runErrorEventToError } from '../../../utilities/errors'\nimport { isProviderExecutedToolCall } from '../../../utilities/provider-executed'\nimport {\n mergeMetadata,\n tanstackMetadata,\n} from '../../../utilities/merge-metadata'\nimport { getChunkRunId } from '../../../utilities/chunk-ids'\nimport type { AdapterYieldChunk } from '../../../utilities/adapter-yield-chunk'\nimport { normalizeToolResult } from '../../../utilities/tool-result'\nimport { defaultJSONParser } from './json-parser'\nimport {\n appendStructuredOutputDelta,\n completeStructuredOutputPart,\n errorStructuredOutputPart,\n updateTextPart,\n updateThinkingPart,\n updateToolCallApproval,\n updateToolCallApprovalResponse,\n updateToolCallPart,\n updateToolCallWithOutput,\n updateToolResultPart,\n} from './message-updaters'\nimport { ImmediateStrategy } from './strategies'\nimport { INTERRUPT_BINDING_METADATA_KEY } from '../../../interrupt-resume'\nimport type {\n ChunkRecording,\n ChunkStrategy,\n InternalToolCallState,\n MessageStreamState,\n ProcessorResult,\n ProcessorState,\n ToolCallState,\n ToolResultState,\n} from './types'\nimport type {\n ContentPart,\n Interrupt,\n MessagePart,\n ModelMessage,\n StreamChunk,\n ThinkingPart,\n ToolCall,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n UIResourceEvent,\n UIResourcePart,\n} from '../../../types'\n\n/**\n * Events emitted by the StreamProcessor\n */\nexport interface StreamProcessorEvents {\n // State events - full array on any change\n onMessagesChange?: (messages: Array<UIMessage>) => void\n\n // Lifecycle events\n onStreamStart?: () => void\n onStreamEnd?: (message: UIMessage) => void\n onError?: (error: Error) => void\n\n // Interaction events - client must handle these\n onToolCall?: (args: {\n toolCallId: string\n toolName: string\n input: any\n }) => void\n onApprovalRequest?: (args: {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n }) => void\n\n // Custom events from server-side tools\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n\n // Granular events for UI optimization (character-by-character, state tracking)\n onTextUpdate?: (messageId: string, content: string) => void\n onToolCallStateChange?: (\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n args: string,\n ) => void\n onThinkingUpdate?: (\n messageId: string,\n stepId: string,\n content: string,\n ) => void\n onStructuredOutputChange?: (args: {\n phase: 'start' | 'update' | 'complete' | 'error'\n messageId: string\n status: 'streaming' | 'complete' | 'error'\n raw: string\n partial?: unknown\n data?: unknown\n reasoning?: string\n errorMessage?: string\n delta?: string\n }) => void\n}\n\n/**\n * Options for StreamProcessor\n */\nexport interface StreamProcessorOptions {\n chunkStrategy?: ChunkStrategy\n /** Event-driven handlers */\n events?: StreamProcessorEvents\n jsonParser?: {\n parse: (jsonString: string) => any\n }\n /** Enable recording for replay testing */\n recording?: boolean\n /** Initial messages to populate the processor */\n initialMessages?: Array<UIMessage>\n}\n\nconst STRUCTURED_OUTPUT_UPDATE_BATCH_SIZE = 12\n\nfunction interruptBatchHasGeneric(interrupts: Array<Interrupt>): boolean {\n return interrupts.some((interrupt) => {\n const metadata = interrupt.metadata\n if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {\n return false\n }\n const binding = metadata[INTERRUPT_BINDING_METADATA_KEY]\n return (\n binding !== null &&\n typeof binding === 'object' &&\n !Array.isArray(binding) &&\n binding.kind === 'generic'\n )\n })\n}\n\n/**\n * StreamProcessor - State machine for processing AI response streams\n *\n * Manages the full UIMessage[] conversation and emits events on changes.\n * Trusts the adapter contract: adapters emit clean AG-UI events in the\n * correct order.\n *\n * State tracking:\n * - Full message array\n * - Per-message stream state (text, tool calls, thinking)\n * - Multiple concurrent message streams\n * - Tool call completion via TOOL_CALL_END events\n *\n * @see docs/chat-architecture.md#streamprocessor-internal-state — State field reference\n * @see docs/chat-architecture.md#adapter-contract — What this class expects from adapters\n */\nexport class StreamProcessor {\n private readonly chunkStrategy: ChunkStrategy\n private readonly events: StreamProcessorEvents\n private readonly jsonParser: { parse: (jsonString: string) => any }\n private recordingEnabled: boolean\n\n // Message state\n private messages: Array<UIMessage> = []\n\n // Per-message stream state\n private readonly messageStates: Map<string, MessageStreamState> = new Map()\n private readonly activeMessageIds: Set<string> = new Set()\n private readonly toolCallToMessage: Map<string, string> = new Map()\n private pendingManualMessageId: string | null = null\n private pendingThinkingStepId: string | null = null\n\n private readonly structuredMessageIds: Set<string> = new Set()\n private readonly structuredOutputUpdateBatches = new Map<\n string,\n {\n delta: string\n chunkCount: number\n }\n >()\n\n // Run tracking (for concurrent run safety)\n private readonly activeRuns = new Set<string>()\n\n // Shared stream state\n private finishReason: string | null = null\n private hasError = false\n private isDone = false\n private streamEndEmitted = false\n\n // Recording\n private recording: ChunkRecording | null = null\n private recordingStartTime = 0\n\n constructor(options: StreamProcessorOptions = {}) {\n this.chunkStrategy = options.chunkStrategy || new ImmediateStrategy()\n this.events = options.events || {}\n this.jsonParser = options.jsonParser || defaultJSONParser\n this.recordingEnabled = options.recording ?? false\n\n // Initialize with provided messages\n if (options.initialMessages) {\n this.messages = [...options.initialMessages]\n }\n }\n\n // ============================================\n // Message Management Methods\n // ============================================\n\n /**\n * Set the messages array (e.g., from persisted state)\n */\n setMessages(messages: Array<UIMessage>): void {\n this.messages = [...messages]\n this.emitMessagesChange()\n }\n\n /**\n * Put older UI messages at the front of the conversation.\n *\n * Skip a message if its id is already in the list. Keep the existing message.\n * Then emit the same messages-change event as `setMessages`.\n *\n * Use this for older history pages. The first hydrate window uses `setMessages`.\n *\n * @param messages Older UI messages in insertion order. The first item is the oldest.\n *\n * @example\n * ```ts\n * processor.setMessages([newest])\n * processor.prependMessages([oldest])\n * ```\n */\n prependMessages(messages: Array<UIMessage>) {\n const existingIds = new Set(this.messages.map((message) => message.id))\n const olderMessages: Array<UIMessage> = []\n for (const message of messages) {\n const isDuplicate = existingIds.has(message.id)\n if (isDuplicate) {\n continue\n }\n existingIds.add(message.id)\n olderMessages.push(message)\n }\n this.messages = [...olderMessages, ...this.messages]\n this.emitMessagesChange()\n }\n\n /**\n * Add a user message to the conversation.\n * Supports both simple string content and multimodal content arrays.\n *\n * @param content - The message content (string or array of content parts)\n * @param id - Optional custom message ID (generated if not provided)\n * @param metadata - Optional AG-UI metadata bag\n * @returns The created UIMessage\n *\n * @example\n * ```ts\n * // Simple text message\n * processor.addUserMessage('Hello!')\n *\n * // Multimodal message with image\n * processor.addUserMessage([\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ])\n *\n * // With custom ID\n * processor.addUserMessage('Hello!', 'custom-id-123')\n * ```\n */\n addUserMessage(\n content: string | Array<ContentPart>,\n id?: string,\n metadata?: UIMessage['metadata'],\n ): UIMessage {\n // Convert content to message parts\n const parts: Array<MessagePart> =\n typeof content === 'string'\n ? [{ type: 'text', content }]\n : content.map((part) => {\n // ContentPart types (text, image, audio, video, document) are compatible with MessagePart\n return part\n })\n\n const userMessage: UIMessage = {\n id: id ?? generateMessageId(),\n role: 'user',\n parts,\n createdAt: new Date(),\n ...(metadata != null ? { metadata } : {}),\n }\n\n this.messages = [...this.messages, userMessage]\n this.emitMessagesChange()\n\n return userMessage\n }\n\n /**\n * Prepare for a new assistant message stream.\n * Does NOT create the message immediately -- the message is created lazily\n * when the first content-bearing chunk arrives via ensureAssistantMessage().\n * This prevents empty assistant messages from flickering in the UI when\n * auto-continuation produces no content.\n */\n prepareAssistantMessage(): void {\n // Reset stream state for new message\n this.resetStreamState()\n }\n\n /**\n * @deprecated Use prepareAssistantMessage() instead. This eagerly creates\n * an assistant message which can cause empty message flicker.\n */\n startAssistantMessage(messageId?: string): string {\n this.prepareAssistantMessage()\n const { messageId: id } = this.ensureAssistantMessage(messageId)\n this.pendingManualMessageId = id\n return id\n }\n\n /**\n * Get the current assistant message ID (if one has been created).\n * Returns null if prepareAssistantMessage() was called but no content\n * has arrived yet.\n */\n getCurrentAssistantMessageId(): string | null {\n // Scan all message states (not just active) for the last assistant.\n // After finalizeStream() clears activeMessageIds, messageStates retains entries.\n // After reset() / resetStreamState(), messageStates is cleared → returns null.\n let lastId: string | null = null\n for (const [id, state] of this.messageStates) {\n if (state.role === 'assistant') {\n lastId = id\n }\n }\n return lastId\n }\n\n /**\n * Add a tool result (called by client after handling onToolCall)\n */\n addToolResult(toolCallId: string, output: any, error?: string): void {\n // Find the message containing this tool call\n const messageWithToolCall = this.messages.find((msg) =>\n msg.parts.some(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n ),\n )\n\n if (!messageWithToolCall) {\n console.warn(\n `[StreamProcessor] Could not find message with tool call ${toolCallId}`,\n )\n return\n }\n\n // Step 1: Update the tool-call part's output field (for UI rendering)\n let updatedMessages = updateToolCallWithOutput(\n this.messages,\n toolCallId,\n output,\n error ? 'error' : undefined,\n error,\n )\n\n // Step 2: Create a tool-result part (for LLM conversation history)\n const content = normalizeToolResult(output)\n const toolResultState: ToolResultState = error ? 'error' : 'complete'\n\n updatedMessages = updateToolResultPart(\n updatedMessages,\n messageWithToolCall.id,\n toolCallId,\n content,\n toolResultState,\n error,\n )\n\n this.messages = updatedMessages\n this.emitMessagesChange()\n }\n\n /**\n * Add an approval response (called by client after handling onApprovalRequest)\n */\n addToolApprovalResponse(approvalId: string, approved: boolean): void {\n this.messages = updateToolCallApprovalResponse(\n this.messages,\n approvalId,\n approved,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Get the conversation as ModelMessages (for sending to LLM)\n */\n toModelMessages(): Array<ModelMessage> {\n const modelMessages: Array<ModelMessage> = []\n for (const msg of this.messages) {\n modelMessages.push(...uiMessageToModelMessages(msg))\n }\n return modelMessages\n }\n\n /**\n * Get current messages\n */\n getMessages(): Array<UIMessage> {\n return this.messages\n }\n\n /**\n * Check if all tool calls in the last assistant message are complete\n * Useful for auto-continue logic\n */\n areAllToolsComplete(): boolean {\n const lastAssistant = this.messages.findLast(\n (m: UIMessage) => m.role === 'assistant',\n )\n\n if (!lastAssistant) return true\n\n const toolParts = lastAssistant.parts.filter(\n (p): p is ToolCallPart => p.type === 'tool-call',\n )\n\n if (toolParts.length === 0) return true\n\n // Get tool result parts to check for server tool completion\n const toolResultIds = new Set(\n lastAssistant.parts\n .filter((p): p is ToolResultPart => p.type === 'tool-result')\n .map((p) => p.toolCallId),\n )\n\n // All tool calls must be in a terminal state\n // A tool call is complete if:\n // 1. It was approved/denied (approval-responded state)\n // 2. It has an output field set (client tool completed via addToolResult)\n // 3. It has a corresponding tool-result part (server tool completed)\n // 4. It is provider-executed (e.g. Anthropic web_search) — already run by\n // the provider, so there is no client result to wait for.\n return toolParts.every(\n (part) =>\n part.state === 'complete' ||\n part.state === 'approval-responded' ||\n (part.output !== undefined && !part.approval) ||\n toolResultIds.has(part.id) ||\n isProviderExecutedToolCall(part),\n )\n }\n\n /**\n * Remove messages after a certain index (for reload/retry)\n */\n removeMessagesAfter(index: number): void {\n const keptIds = new Set(this.messages.slice(0, index + 1).map((m) => m.id))\n // Drop routing state for messages that no longer exist; otherwise a\n // resumed stream (`reload()` or a server that reuses messageIds across\n // runs) could land deltas / tool args on stale map entries and corrupt\n // the new assistant message's parts. Mirror the four routing maps that\n // key on messageId: structuredMessageIds (custom-event routing),\n // messageStates (per-message stream state), toolCallToMessage (tool\n // args → message), and activeMessageIds (finalize / completeAllToolCalls\n // iteration targets — must not include phantoms).\n for (const id of this.structuredMessageIds) {\n if (!keptIds.has(id)) this.structuredMessageIds.delete(id)\n }\n for (const id of this.structuredOutputUpdateBatches.keys()) {\n if (!keptIds.has(id)) this.structuredOutputUpdateBatches.delete(id)\n }\n for (const id of this.messageStates.keys()) {\n if (!keptIds.has(id)) this.messageStates.delete(id)\n }\n for (const [toolCallId, msgId] of this.toolCallToMessage) {\n if (!keptIds.has(msgId)) this.toolCallToMessage.delete(toolCallId)\n }\n for (const id of this.activeMessageIds) {\n if (!keptIds.has(id)) this.activeMessageIds.delete(id)\n }\n this.messages = this.messages.slice(0, index + 1)\n this.emitMessagesChange()\n }\n\n /**\n * Clear all messages\n */\n clearMessages(): void {\n this.messages = []\n this.messageStates.clear()\n this.activeMessageIds.clear()\n this.toolCallToMessage.clear()\n this.structuredMessageIds.clear()\n this.structuredOutputUpdateBatches.clear()\n this.pendingManualMessageId = null\n this.emitMessagesChange()\n }\n\n // ============================================\n // Stream Processing Methods\n // ============================================\n\n /**\n * Process a stream and emit events through handlers\n */\n async process(stream: AsyncIterable<any>): Promise<ProcessorResult> {\n // Reset stream state (but keep messages)\n this.resetStreamState()\n\n // Start recording if enabled\n if (this.recordingEnabled) {\n this.startRecording()\n }\n\n // Process each chunk\n for await (const chunk of stream) {\n this.processChunk(chunk)\n }\n\n // Stream ended - finalize everything\n this.finalizeStream()\n\n // Finalize recording\n if (this.recording) {\n this.recording.result = this.getResult()\n }\n\n return this.getResult()\n }\n\n /**\n * Process a single chunk from the stream.\n *\n * Central dispatch for all AG-UI events. Each event type maps to a specific\n * handler. Events not listed in the switch are intentionally ignored\n * (STEP_STARTED, STATE_SNAPSHOT, STATE_DELTA).\n *\n * @see docs/chat-architecture.md#adapter-contract — Expected event types and ordering\n */\n processChunk(chunk: StreamChunk): void {\n // Record chunk if enabled\n if (this.recording) {\n this.recording.chunks.push({\n chunk,\n timestamp: Date.now(),\n index: this.recording.chunks.length,\n })\n }\n\n // Cast needed: @ag-ui/core Zod passthrough types add `& { [k: string]: unknown }`\n // which prevents TypeScript from narrowing the `type` discriminant in switch.\n const c = chunk\n // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- AG-UI EventType enum members vs string-literal case labels; default branch handles untraced events.\n switch (c.type) {\n // AG-UI Events\n case 'TEXT_MESSAGE_START':\n this.handleTextMessageStartEvent(\n chunk as Extract<StreamChunk, { type: 'TEXT_MESSAGE_START' }>,\n )\n break\n\n case 'TEXT_MESSAGE_CONTENT':\n this.handleTextMessageContentEvent(\n chunk as Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,\n )\n break\n\n case 'TEXT_MESSAGE_END':\n this.handleTextMessageEndEvent(\n chunk as Extract<StreamChunk, { type: 'TEXT_MESSAGE_END' }>,\n )\n break\n\n case 'TOOL_CALL_START':\n this.handleToolCallStartEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_START' }>,\n )\n break\n\n case 'TOOL_CALL_ARGS':\n this.handleToolCallArgsEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_ARGS' }>,\n )\n break\n\n case 'TOOL_CALL_END':\n this.handleToolCallEndEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_END' }>,\n )\n break\n\n case 'RUN_FINISHED':\n this.handleRunFinishedEvent(\n chunk as Extract<StreamChunk, { type: 'RUN_FINISHED' }>,\n )\n break\n\n case 'RUN_ERROR':\n this.handleRunErrorEvent(\n chunk as Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n )\n break\n\n case 'STEP_FINISHED':\n this.handleStepFinishedEvent(\n chunk as Extract<StreamChunk, { type: 'STEP_FINISHED' }>,\n )\n break\n\n case 'MESSAGES_SNAPSHOT':\n this.handleMessagesSnapshotEvent(\n chunk as Extract<StreamChunk, { type: 'MESSAGES_SNAPSHOT' }>,\n )\n break\n\n case 'CUSTOM':\n this.handleCustomEvent(\n chunk as Extract<StreamChunk, { type: 'CUSTOM' }>,\n )\n break\n\n case 'RUN_STARTED':\n this.handleRunStartedEvent(\n chunk as Extract<StreamChunk, { type: 'RUN_STARTED' }>,\n )\n break\n\n case 'REASONING_START':\n case 'REASONING_MESSAGE_START':\n case 'REASONING_MESSAGE_END':\n case 'REASONING_END':\n // No special handling needed\n break\n\n case 'REASONING_MESSAGE_CONTENT':\n this.handleReasoningMessageContentEvent(\n chunk as Extract<StreamChunk, { type: 'REASONING_MESSAGE_CONTENT' }>,\n )\n break\n\n case 'REASONING_ENCRYPTED_VALUE':\n this.handleReasoningEncryptedValueEvent(\n chunk as Extract<StreamChunk, { type: 'REASONING_ENCRYPTED_VALUE' }>,\n )\n break\n\n case 'TOOL_CALL_RESULT':\n this.handleToolCallResultEvent(\n chunk as Extract<StreamChunk, { type: 'TOOL_CALL_RESULT' }>,\n )\n break\n\n case 'STEP_STARTED':\n this.handleStepStartedEvent(\n chunk as Extract<StreamChunk, { type: 'STEP_STARTED' }>,\n )\n break\n\n default:\n // STATE_SNAPSHOT, STATE_DELTA - no special handling needed\n break\n }\n }\n\n // ============================================\n // Per-Message State Helpers\n // ============================================\n\n /**\n * Create a new MessageStreamState for a message\n */\n private createMessageState(\n messageId: string,\n role: 'user' | 'assistant' | 'system',\n ): MessageStreamState {\n const state: MessageStreamState = {\n id: messageId,\n role,\n totalTextContent: '',\n currentSegmentText: '',\n lastEmittedText: '',\n hasSeenReasoningEvents: false,\n thinkingSteps: new Map(),\n thinkingStepSignatures: new Map(),\n thinkingStepOrder: [],\n currentThinkingStepId: null,\n toolCalls: new Map(),\n toolCallOrder: [],\n hasToolCallsSinceTextStart: false,\n isComplete: false,\n }\n this.messageStates.set(messageId, state)\n return state\n }\n\n /**\n * Get the MessageStreamState for a message\n */\n private getMessageState(messageId: string): MessageStreamState | undefined {\n return this.messageStates.get(messageId)\n }\n\n /**\n * Promote a pending stepId from a STEP_STARTED that fired before the\n * assistant message existed onto the given message state, so the next\n * thinking event (STEP_FINISHED or REASONING_MESSAGE_CONTENT) attributes\n * to the correct step.\n */\n private consumePendingThinkingStep(state: MessageStreamState): void {\n if (!this.pendingThinkingStepId) return\n const stepId = this.pendingThinkingStepId\n state.currentThinkingStepId = stepId\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, '')\n state.thinkingStepOrder.push(stepId)\n }\n this.pendingThinkingStepId = null\n }\n\n /**\n * Get the most recent active assistant message ID.\n * Used as fallback for events that don't include a messageId.\n */\n private getActiveAssistantMessageId(): string | null {\n // Set iteration is insertion-order; reverse-iterate to search from the end\n const ids = Array.from(this.activeMessageIds).reverse()\n for (const id of ids) {\n const state = this.messageStates.get(id)\n if (state && state.role === 'assistant') {\n return id\n }\n }\n // finalizeStream() clears activeMessageIds but keeps messageStates.\n // Leftover reasoning after an early RUN_FINISHED must resume that\n // assistant. A new user turn calls prepareAssistantMessage(), which\n // clears messageStates first.\n for (const [id, state] of [...this.messageStates].reverse()) {\n if (state.role === 'assistant') {\n return id\n }\n }\n return null\n }\n\n private resumeAssistantState(id: string, state: MessageStreamState): void {\n this.activeMessageIds.add(id)\n if (state.isComplete || this.isDone) {\n state.isComplete = false\n this.isDone = false\n }\n }\n\n /**\n * Ensure an active assistant message exists, creating one if needed.\n * Used for backward compat when events arrive without prior TEXT_MESSAGE_START.\n *\n * On reconnect/resume, a TEXT_MESSAGE_CONTENT may arrive for a message that\n * already exists in this.messages (e.g. from initialMessages or a prior\n * MESSAGES_SNAPSHOT) but whose transient state was cleared. In that case we\n * hydrate state from the existing message rather than creating a duplicate.\n */\n private ensureAssistantMessage(preferredId?: string): {\n messageId: string\n state: MessageStreamState\n } {\n // Try to find state by preferred ID\n if (preferredId) {\n const state = this.getMessageState(preferredId)\n if (state) {\n this.resumeAssistantState(preferredId, state)\n return { messageId: preferredId, state }\n }\n }\n\n // Try active assistant message\n const activeId = this.getActiveAssistantMessageId()\n if (activeId) {\n const state = this.getMessageState(activeId)\n if (state) {\n this.resumeAssistantState(activeId, state)\n return { messageId: activeId, state }\n }\n }\n\n // Check if a message with preferredId already exists (reconnect/resume case).\n // Hydrate transient state from the existing message instead of duplicating it.\n if (preferredId) {\n const existingMsg = this.messages.find((m) => m.id === preferredId)\n if (existingMsg) {\n const state = this.createMessageState(preferredId, existingMsg.role)\n this.activeMessageIds.add(preferredId)\n\n // Seed segment text from the existing last text part so that\n // incoming deltas append correctly and updateTextPart produces\n // the right content (existing text + new delta).\n const lastPart =\n existingMsg.parts.length > 0\n ? existingMsg.parts[existingMsg.parts.length - 1]\n : null\n if (lastPart && lastPart.type === 'text') {\n state.currentSegmentText = lastPart.content\n state.lastEmittedText = lastPart.content\n state.totalTextContent = lastPart.content\n }\n\n return { messageId: preferredId, state }\n }\n }\n\n // Auto-create an assistant message (backward compat for process() without TEXT_MESSAGE_START)\n const id = preferredId || generateMessageId()\n const assistantMessage: UIMessage = {\n id,\n role: 'assistant',\n parts: [],\n createdAt: new Date(),\n }\n this.messages = [...this.messages, assistantMessage]\n const state = this.createMessageState(id, 'assistant')\n this.activeMessageIds.add(id)\n this.pendingManualMessageId = id\n this.events.onStreamStart?.()\n this.emitMessagesChange()\n return { messageId: id, state }\n }\n\n // ============================================\n // Event Handlers\n // ============================================\n\n /**\n * Merge event metadata onto a UIMessage. `tanstack` is deep-merged so a\n * later delta does not wipe `tanstack.model`. High-frequency leftover\n * keys (`content`, `args`) never stamp onto the message.\n * Rebuilds `createdAt` when `tanstack.createdAt` is an ISO string.\n */\n private mergeMessageMetadata(messageId: string, incoming: unknown): void {\n if (\n incoming == null ||\n typeof incoming !== 'object' ||\n Array.isArray(incoming)\n ) {\n return\n }\n const message = this.messages.find((msg) => msg.id === messageId)\n if (!message) return\n\n const incomingRecord = incoming as NonNullable<UIMessage['metadata']>\n const incomingTanstack = tanstackMetadata(incomingRecord)\n const toMerge =\n incomingTanstack != null &&\n ('content' in incomingTanstack || 'args' in incomingTanstack)\n ? {\n ...incomingRecord,\n tanstack: Object.fromEntries(\n Object.entries(incomingTanstack).filter(\n ([key]) => key !== 'content' && key !== 'args',\n ),\n ),\n }\n : incomingRecord\n const metadata = mergeMetadata(message.metadata, toMerge)\n const createdAt = coerceCreatedAt(\n tanstackMetadata(incomingRecord)?.createdAt,\n )\n const createdAtValid = createdAt !== undefined\n this.messages = this.messages.map((msg) =>\n msg.id === messageId\n ? {\n ...msg,\n ...(metadata !== undefined ? { metadata } : {}),\n ...(createdAtValid ? { createdAt } : {}),\n }\n : msg,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Handle TEXT_MESSAGE_START event\n */\n private handleTextMessageStartEvent(\n chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_START' }>,\n ): void {\n const { messageId, role } = chunk\n\n // Map 'tool' and 'developer' roles to 'assistant' for both UIMessage and MessageStreamState\n // (UIMessage doesn't support 'tool'/'developer' role, and lookups like\n // getActiveAssistantMessageId() check state.role === 'assistant')\n const uiRole: 'system' | 'user' | 'assistant' =\n role === 'user' || role === 'system' ? role : 'assistant'\n\n // Case 1: A manual message was created via startAssistantMessage()\n if (this.pendingManualMessageId) {\n const pendingId = this.pendingManualMessageId\n this.pendingManualMessageId = null\n\n if (pendingId !== messageId) {\n // Update the message's ID in the messages array\n this.messages = this.messages.map((msg) =>\n msg.id === pendingId ? { ...msg, id: messageId } : msg,\n )\n\n // Move state to the new key\n const existingState = this.messageStates.get(pendingId)\n if (existingState) {\n existingState.id = messageId\n this.messageStates.delete(pendingId)\n this.messageStates.set(messageId, existingState)\n }\n\n // Update activeMessageIds\n this.activeMessageIds.delete(pendingId)\n this.activeMessageIds.add(messageId)\n\n // TOOL_CALL_ARGS/END route through toolCallToMessage. Keep those\n // entries on the remapped id so later args still accumulate\n // (interleaved text can arrive as a full START/CONTENT/END block).\n for (const [toolCallId, mappedMessageId] of this.toolCallToMessage) {\n if (mappedMessageId === pendingId) {\n this.toolCallToMessage.set(toolCallId, messageId)\n }\n }\n }\n\n // Ensure state exists\n let pendingState = this.messageStates.get(messageId)\n if (!pendingState) {\n pendingState = this.createMessageState(messageId, uiRole)\n this.activeMessageIds.add(messageId)\n } else if (pendingState.hasToolCallsSinceTextStart) {\n // A tool call (e.g. TOOL_CALL_START with parentMessageId) marked\n // this message before its \"real\" TEXT_MESSAGE_START arrived — same\n // reset Case 2 performs, so the segment accumulator doesn't carry\n // stale tool-call state into the text that follows.\n if (pendingState.currentSegmentText !== pendingState.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n pendingState.currentSegmentText = ''\n pendingState.lastEmittedText = ''\n pendingState.hasToolCallsSinceTextStart = false\n }\n\n this.mergeMessageMetadata(messageId, chunk.metadata)\n this.emitMessagesChange()\n return\n }\n\n // Case 2: Message already exists (dedup)\n const existingMsg = this.messages.find((m) => m.id === messageId)\n if (existingMsg) {\n this.activeMessageIds.add(messageId)\n const existingState = this.messageStates.get(messageId)\n if (!existingState) {\n this.createMessageState(messageId, uiRole)\n } else {\n // If tool calls happened since last text, this TEXT_MESSAGE_START\n // signals a new text segment — reset segment accumulation\n if (existingState.hasToolCallsSinceTextStart) {\n if (\n existingState.currentSegmentText !== existingState.lastEmittedText\n ) {\n this.emitTextUpdateForMessage(messageId)\n }\n existingState.currentSegmentText = ''\n existingState.lastEmittedText = ''\n existingState.hasToolCallsSinceTextStart = false\n }\n }\n this.mergeMessageMetadata(messageId, chunk.metadata)\n return\n }\n\n // Case 3: New message from the stream\n const newMessage: UIMessage = {\n id: messageId,\n role: uiRole,\n parts: [],\n createdAt: new Date(),\n }\n\n this.messages = [...this.messages, newMessage]\n this.createMessageState(messageId, uiRole)\n this.activeMessageIds.add(messageId)\n\n this.mergeMessageMetadata(messageId, chunk.metadata)\n this.events.onStreamStart?.()\n this.emitMessagesChange()\n }\n\n /**\n * Handle TEXT_MESSAGE_END event\n */\n private handleTextMessageEndEvent(\n chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_END' }>,\n ): void {\n const { messageId } = chunk\n this.mergeMessageMetadata(messageId, chunk.metadata)\n const state = this.getMessageState(messageId)\n if (!state) return\n if (state.isComplete) return\n\n // Emit any pending text for this message\n if (state.currentSegmentText !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n }\n\n /**\n * Handle MESSAGES_SNAPSHOT event\n */\n private handleMessagesSnapshotEvent(\n chunk: Extract<StreamChunk, { type: 'MESSAGES_SNAPSHOT' }>,\n ): void {\n this.resetStreamState()\n // Normalize AG-UI snapshot messages to UIMessage[] so every message has a\n // `parts` array. AG-UI messages carry `content` but no `parts`, so casting\n // them directly to UIMessage[] is unsafe and causes \"Cannot read properties\n // of undefined (reading 'find')\" when code later reads message.parts (e.g.\n // the onToolCallStateChange devtools handler).\n //\n // The AG-UI `MESSAGES_SNAPSHOT` wire shape cannot reconstruct client-side\n // tool-call metadata a server may omit: a `role: 'tool'` message only carries\n // `toolCallId` + `content`, and an assistant message in the snapshot may\n // drop `toolCalls` the client already observed via `TOOL_CALL_*` events.\n // Without the matching `tool-call` part, later `addToolResult(toolCallId)`\n // calls cannot locate the call and warn + no-op (see #859). To keep the\n // UI representation consistent with the streaming fan-out and preserve the\n // unreconstructable metadata, reconcile the normalized snapshot against\n // the pre-snapshot state; see `reconcileSnapshotToolCalls`.\n const prevMessages = this.messages\n const prevById = new Map(prevMessages.map((msg) => [msg.id, msg]))\n const normalized = this.mergeReasoningFanOut(\n chunk.messages.map(aguiSnapshotMessageToUIMessage),\n )\n this.messages = this.reconcileSnapshotToolCalls(\n normalized,\n prevMessages,\n ).map((msg) => {\n if (msg.metadata != null) return msg\n const prev = prevById.get(msg.id)\n if (prev?.metadata == null) return msg\n return { ...msg, metadata: prev.metadata }\n })\n this.emitMessagesChange()\n }\n\n /**\n * Reconcile a freshly normalized snapshot with the pre-snapshot message\n * state so unreconstructable tool-call metadata is preserved.\n *\n * Post-pass (a): anchor `tool-result`-only assistant messages (the shape\n * `aguiSnapshotMessageToUIMessage` emits for AG-UI `role: 'tool'` wire\n * messages) into the message containing the matching `tool-call` part, or —\n * when the snapshot supplies no such part — the nearest earlier anchorable\n * assistant message, matching the in-stream fan-out shape\n * `assistant: [text, tool-call, tool-result, ...]`. Detached messages with\n * no earlier anchorable assistant are kept verbatim.\n *\n * Post-pass (b): when a `tool-result` part references a `toolCallId` whose\n * `tool-call` part is absent from the snapshot, carry the `tool-call` part\n * forward from the pre-snapshot state (state and output untouched) so a\n * subsequent `addToolResult(toolCallId)` can still locate the call.\n *\n * Post-pass (c): AG-UI wire snapshots rebuild `tool-call` parts as\n * `input-complete` without `output` (ModelMessage has no result field on\n * the call). After anchoring results, copy each `tool-result` onto its\n * matching `tool-call` (and prefer pre-snapshot complete/output when the\n * snapshot is poorer) so server tools keep the same UI shape as client tools.\n */\n /**\n * Wire order is reasoning fan-outs, then the assistant anchor.\n * Snapshot conversion turns each reasoning row into its own assistant\n * message. Fold leading thinking-only messages into the next real\n * assistant. Do not fold into a tool-result-only message (`role: 'tool'`\n * on the wire). `reconcileSnapshotToolCalls` anchors those results.\n */\n private mergeReasoningFanOut(messages: Array<UIMessage>): Array<UIMessage> {\n const out: Array<UIMessage> = []\n let pending: Array<UIMessage> = []\n const thinkingParts = (msg: UIMessage) =>\n msg.parts.filter((part): part is ThinkingPart => part.type === 'thinking')\n const isThinkingOnly = (msg: UIMessage) =>\n msg.role === 'assistant' &&\n msg.parts.length > 0 &&\n msg.parts.every((part) => part.type === 'thinking')\n const isToolResultOnly = (msg: UIMessage) =>\n msg.role === 'assistant' &&\n msg.parts.some((part) => part.type === 'tool-result') &&\n msg.parts.every(\n (part) => part.type === 'tool-result' || part.type === 'ui-resource',\n )\n const flushPending = () => {\n out.push(...pending)\n pending = []\n }\n for (const msg of messages) {\n if (isThinkingOnly(msg)) {\n pending.push(msg)\n continue\n }\n if (\n msg.role === 'assistant' &&\n pending.length > 0 &&\n !isToolResultOnly(msg)\n ) {\n out.push({\n ...msg,\n parts: [...pending.flatMap(thinkingParts), ...msg.parts],\n })\n pending = []\n continue\n }\n flushPending()\n out.push(msg)\n }\n flushPending()\n return out\n }\n\n private reconcileSnapshotToolCalls(\n snapshot: Array<UIMessage>,\n prevMessages: Array<UIMessage>,\n ): Array<UIMessage> {\n // Index tool-call parts observed before the snapshot by id so we can\n // restore metadata the snapshot cannot re-emit. Duplicate ids resolve\n // last-write-wins: the same tool call can appear in multiple messages\n // across reconnects, and the most recent part carries the freshest state.\n const prevToolCalls = new Map<string, ToolCallPart>()\n for (const msg of prevMessages) {\n for (const part of msg.parts) {\n if (part.type === 'tool-call') {\n prevToolCalls.set(part.id, part)\n }\n }\n }\n // Index tool-call parts already present in the snapshot so (b) only fills\n // genuine gaps rather than duplicating a tool-call the snapshot supplies.\n const snapshotToolCallIds = new Set<string>()\n for (const msg of snapshot) {\n for (const part of msg.parts) {\n if (part.type === 'tool-call') {\n snapshotToolCallIds.add(part.id)\n }\n }\n }\n\n const reconciled: Array<UIMessage> = []\n for (const msg of snapshot) {\n const toolResultParts = msg.parts.filter(\n (part): part is ToolResultPart => part.type === 'tool-result',\n )\n const toolResultPart =\n msg.role === 'assistant' &&\n toolResultParts.length === 1 &&\n msg.parts.every(\n (part) => part.type === 'tool-result' || part.type === 'ui-resource',\n )\n ? toolResultParts[0]\n : undefined\n\n if (!toolResultPart) {\n reconciled.push(msg)\n continue\n }\n\n // Prefer the message that actually contains the matching tool-call\n // part. AG-UI `reasoning`/`activity` messages also normalize to\n // `role: 'assistant'`, so anchoring into the nearest assistant alone\n // could separate a result from its call (and a later\n // `addToolResult(toolCallId)` would then append a duplicate result\n // next to the call).\n const target =\n reconciled.findLast((m) =>\n m.parts.some(\n (p) => p.type === 'tool-call' && p.id === toolResultPart.toolCallId,\n ),\n ) ??\n reconciled.findLast(\n (m) =>\n m.role === 'assistant' &&\n !(m.parts.length === 1 && m.parts[0]?.type === 'tool-result'),\n )\n\n if (!target) {\n // No assistant to anchor into — keep the detached message intact.\n if (!snapshotToolCallIds.has(toolResultPart.toolCallId)) {\n console.warn(\n `[StreamProcessor] MESSAGES_SNAPSHOT contains a tool-result for \"${toolResultPart.toolCallId}\" but no matching tool-call exists in the snapshot, and there is no assistant message to anchor into; addToolResult(\"${toolResultPart.toolCallId}\") will not be able to locate this call`,\n )\n }\n reconciled.push(msg)\n continue\n }\n\n const parts = [...target.parts]\n // (b) Fill in a missing tool-call part from the pre-snapshot state when\n // the snapshot references its id via a tool-result but supplies no\n // tool-call metadata of its own.\n if (\n !snapshotToolCallIds.has(toolResultPart.toolCallId) &&\n !parts.some(\n (p) => p.type === 'tool-call' && p.id === toolResultPart.toolCallId,\n )\n ) {\n const prev = prevToolCalls.get(toolResultPart.toolCallId)\n if (prev) {\n // Insert the carried-over tool-call before its tool-result (pushed\n // below) so call→result ordering matches the streaming fan-out.\n parts.push({ ...prev })\n snapshotToolCallIds.add(prev.id)\n } else {\n console.warn(\n `[StreamProcessor] MESSAGES_SNAPSHOT contains a tool-result for \"${toolResultPart.toolCallId}\" but no matching tool-call exists in the snapshot or the pre-snapshot state; addToolResult(\"${toolResultPart.toolCallId}\") will not be able to locate this call`,\n )\n }\n }\n parts.push(...msg.parts)\n // Replace rather than push into `target.parts`: a snapshot message that\n // arrived already carrying `parts` (TanStack server echoing UIMessages)\n // shares its array with the incoming chunk, and mutating it in place\n // would corrupt the caller's event object.\n target.parts = parts\n }\n\n return this.enrichSnapshotToolCallsFromResults(reconciled, prevToolCalls)\n }\n\n /**\n * Post-pass (c): fold `tool-result` content into sibling `tool-call` parts\n * and prefer pre-snapshot complete/output when the snapshot rebuilt a\n * poorer `input-complete` call (AG-UI ModelMessage has no result on calls).\n */\n private enrichSnapshotToolCallsFromResults(\n messages: Array<UIMessage>,\n prevToolCalls: Map<string, ToolCallPart>,\n ): Array<UIMessage> {\n const resultsByCallId = new Map<string, ToolResultPart>()\n for (const msg of messages) {\n for (const part of msg.parts) {\n if (part.type === 'tool-result') {\n resultsByCallId.set(part.toolCallId, part)\n }\n }\n }\n\n return messages.map((msg) => {\n const parts = msg.parts.map((part) => {\n if (part.type !== 'tool-call') return part\n\n const prev = prevToolCalls.get(part.id)\n const result = resultsByCallId.get(part.id)\n let next: ToolCallPart = part\n\n // Prefer a pre-snapshot call that already carried output/complete —\n // the client observed TOOL_CALL_END/RESULT before the snapshot wipe.\n if (\n prev &&\n (prev.output !== undefined ||\n prev.state === 'complete' ||\n prev.state === 'error') &&\n (part.output === undefined ||\n part.state === 'input-complete' ||\n part.state === 'input-streaming' ||\n part.state === 'awaiting-input')\n ) {\n next = {\n ...part,\n ...(prev.output !== undefined ? { output: prev.output } : {}),\n state: prev.state,\n ...(prev.approval !== undefined ? { approval: prev.approval } : {}),\n ...(prev.metadata !== undefined ? { metadata: prev.metadata } : {}),\n }\n }\n\n // Apply sibling tool-result when the call still has no output.\n if (result && next.output === undefined) {\n let output: unknown\n if (Array.isArray(result.content)) {\n output = result.content\n } else {\n try {\n output = JSON.parse(result.content)\n } catch {\n output = result.content\n }\n }\n const errorText =\n result.state === 'error'\n ? this.extractToolResultError(output)\n : undefined\n next = {\n ...next,\n output: errorText ? { error: errorText } : output,\n state: result.state === 'error' ? 'error' : 'complete',\n }\n }\n\n return next\n })\n // Rebuild only when a part object identity changed.\n const partsChanged = parts.some(\n (part, index) => part !== msg.parts[index],\n )\n return partsChanged ? { ...msg, parts } : msg\n })\n }\n\n /**\n * Handle TEXT_MESSAGE_CONTENT event.\n *\n * Accumulates delta into both currentSegmentText (for UI emission) and\n * totalTextContent (for ProcessorResult). Lazily creates the assistant\n * UIMessage on first content. Uses updateTextPart() which replaces the\n * last TextPart or creates a new one depending on part ordering.\n *\n * @see docs/chat-architecture.md#single-shot-text-response — Text accumulation step-by-step\n * @see docs/chat-architecture.md#uimessage-part-ordering-invariants — Replace vs. push logic\n */\n private handleTextMessageContentEvent(\n chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,\n ): void {\n const { messageId, state } = this.ensureAssistantMessage(chunk.messageId)\n this.mergeMessageMetadata(messageId, chunk.metadata)\n\n if (this.structuredMessageIds.has(messageId)) {\n const delta = chunk.delta || ''\n if (delta !== '') {\n this.messages = appendStructuredOutputDelta(\n this.messages,\n messageId,\n delta,\n )\n state.totalTextContent += delta\n this.queueStructuredOutputUpdate(messageId, delta)\n this.emitMessagesChange()\n }\n return\n }\n\n const previousSegment = state.currentSegmentText\n\n // Detect if this is a NEW text segment (after tool calls) vs continuation\n const isNewSegment =\n state.hasToolCallsSinceTextStart &&\n previousSegment.length > 0 &&\n this.isNewTextSegment(chunk, previousSegment)\n\n if (isNewSegment) {\n // Emit any accumulated text before starting new segment\n if (previousSegment !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n // Reset SEGMENT text accumulation for the new text segment after tool calls\n state.currentSegmentText = ''\n state.lastEmittedText = ''\n state.hasToolCallsSinceTextStart = false\n }\n\n // A segment begins at its first delta, not its second. Left set, the flag\n // makes the second delta look like a further segment: the accumulation\n // resets and updateTextPart writes over the part the first delta created.\n // A later tool call sets it again in handleToolCallStartEvent.\n state.hasToolCallsSinceTextStart = false\n\n const currentText = state.currentSegmentText\n const delta = chunk.delta || ''\n const nextText = delta !== '' ? currentText + delta : currentText\n\n // Calculate the delta for totalTextContent\n const textDelta = nextText.slice(currentText.length)\n state.currentSegmentText = nextText\n state.totalTextContent += textDelta\n\n const chunkPortion = chunk.delta || ''\n const shouldEmit = this.chunkStrategy.shouldEmit(\n chunkPortion,\n state.currentSegmentText,\n )\n if (shouldEmit && state.currentSegmentText !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n }\n\n /**\n * Handle TOOL_CALL_START event.\n *\n * Creates a new InternalToolCallState entry in the toolCalls Map and appends\n * a ToolCallPart to the UIMessage. Duplicate toolCallId is a no-op.\n *\n * CRITICAL: This MUST be received before any TOOL_CALL_ARGS for the same\n * toolCallId. Args for unknown IDs are silently dropped.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — Tool call state transitions\n * @see docs/chat-architecture.md#parallel-tool-calls-single-shot — Parallel tracking by ID\n * @see docs/chat-architecture.md#adapter-contract — Ordering requirements\n */\n private handleToolCallStartEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_START' }>,\n ): void {\n // Determine the message this tool call belongs to\n const targetMessageId =\n chunk.parentMessageId ?? this.getActiveAssistantMessageId()\n const { messageId, state } = this.ensureAssistantMessage(\n targetMessageId ?? undefined,\n )\n\n // Mark that we've seen tool calls since the last text segment\n state.hasToolCallsSinceTextStart = true\n\n const toolCallId = chunk.toolCallId\n const existingToolCall = state.toolCalls.get(toolCallId)\n\n if (!existingToolCall) {\n // New tool call starting\n const initialState: ToolCallState = 'awaiting-input'\n\n const toolName = chunk.toolCallName\n\n // Capture provider metadata that arrived on TOOL_CALL_START so it\n // round-trips back through the assistant message on the next turn\n // (e.g. Gemini's thoughtSignature).\n const chunkMetadata = chunk.metadata\n\n const newToolCall: InternalToolCallState = {\n id: chunk.toolCallId,\n name: toolName,\n arguments: '',\n state: initialState,\n parsedArguments: undefined,\n index: state.toolCalls.size,\n ...(chunkMetadata !== undefined && { metadata: chunkMetadata }),\n }\n\n state.toolCalls.set(toolCallId, newToolCall)\n state.toolCallOrder.push(toolCallId)\n\n // Store mapping for TOOL_CALL_ARGS/END routing\n this.toolCallToMessage.set(toolCallId, messageId)\n\n // Update UIMessage\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: chunk.toolCallId,\n name: toolName,\n arguments: '',\n state: initialState,\n ...(chunkMetadata !== undefined && { metadata: chunkMetadata }),\n })\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onToolCallStateChange?.(\n messageId,\n chunk.toolCallId,\n initialState,\n '',\n )\n }\n }\n\n /**\n * Handle TOOL_CALL_ARGS event.\n *\n * Appends the delta to the tool call's accumulated arguments string.\n * Transitions state from awaiting-input → input-streaming on first non-empty delta.\n * Attempts partial JSON parse on each update for UI preview.\n *\n * If toolCallId is not found in the Map (no preceding TOOL_CALL_START),\n * this event is silently dropped.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — Step-by-step tool call processing\n */\n private handleToolCallArgsEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_ARGS' }>,\n ): void {\n const toolCallId = chunk.toolCallId\n const messageId = this.toolCallToMessage.get(toolCallId)\n if (!messageId) return\n\n const state = this.getMessageState(messageId)\n if (!state) return\n\n const existingToolCall = state.toolCalls.get(toolCallId)\n if (!existingToolCall) return\n\n const wasAwaitingInput = existingToolCall.state === 'awaiting-input'\n\n // Accumulate arguments from delta\n existingToolCall.arguments += chunk.delta || ''\n\n // Update state\n if (wasAwaitingInput && chunk.delta) {\n existingToolCall.state = 'input-streaming'\n }\n\n // Try to parse the updated arguments\n existingToolCall.parsedArguments = this.jsonParser.parse(\n existingToolCall.arguments,\n )\n\n // Update UIMessage\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: existingToolCall.id,\n name: existingToolCall.name,\n arguments: existingToolCall.arguments,\n state: existingToolCall.state,\n })\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onToolCallStateChange?.(\n messageId,\n existingToolCall.id,\n existingToolCall.state,\n existingToolCall.arguments,\n )\n }\n\n /**\n * Handle TOOL_CALL_END event — arguments are finalized (input-complete).\n * Tool output arrives on TOOL_CALL_RESULT, not on this event.\n *\n * If TOOL_CALL_END carries parsed `input`, use it as the canonical arguments:\n * back-fill the accumulated string when no TOOL_CALL_ARGS deltas were seen\n * (adapters that deliver the whole input on END — e.g. Anthropic\n * server_tool_use / web_search — issue #839) and override the rendered part's\n * `input` with the canonical value.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — End-to-end flow\n */\n private handleToolCallEndEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_END' }>,\n ): void {\n const messageId = this.toolCallToMessage.get(chunk.toolCallId)\n if (!messageId) return\n\n const msgState = this.getMessageState(messageId)\n if (!msgState) return\n\n // The parsed input can ride on the spec `input` field or, for adapters\n // that only stamp it into TanStack metadata, on `metadata.tanstack.input`.\n const input =\n chunk.input !== undefined\n ? chunk.input\n : (tanstackMetadata(chunk)?.input as unknown)\n\n // Transition the tool call to input-complete (the authoritative completion signal)\n const existingToolCall = msgState.toolCalls.get(chunk.toolCallId)\n if (existingToolCall && existingToolCall.state !== 'input-complete') {\n // Back-fill the arguments string from the parsed input when no\n // TOOL_CALL_ARGS deltas were received, so completeToolCall's strict parse\n // surfaces the correct value on the ToolCallPart.\n if (input !== undefined && !existingToolCall.arguments) {\n try {\n existingToolCall.arguments = JSON.stringify(input)\n } catch {\n // circular refs, BigInt, etc. — leave arguments empty rather than\n // aborting stream processing\n }\n }\n\n const index = msgState.toolCallOrder.indexOf(chunk.toolCallId)\n this.completeToolCall(messageId, index, existingToolCall)\n\n // Canonicalize on the parsed input: overrides the accumulated-args parse\n // that completeToolCall wrote (adapters may coerce values differently\n // between streamed args and the final structured input).\n if (input !== undefined) {\n existingToolCall.parsedArguments = input\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: existingToolCall.id,\n name: existingToolCall.name,\n arguments: existingToolCall.arguments,\n state: 'input-complete',\n input,\n ...(existingToolCall.metadata !== undefined && {\n metadata: existingToolCall.metadata,\n }),\n })\n this.emitMessagesChange()\n }\n }\n }\n\n private extractToolResultError(output: unknown): string {\n if (\n output &&\n typeof output === 'object' &&\n 'error' in output &&\n typeof output.error === 'string'\n ) {\n return output.error\n }\n return typeof output === 'string' ? output : 'Tool execution failed'\n }\n\n /**\n * Handle TOOL_CALL_RESULT event (AG-UI spec).\n *\n * Creates a tool-result part and updates the tool-call output field,\n * mirroring the logic from TOOL_CALL_END when it carries a result.\n * This is the spec-compliant path for delivering tool results to the client.\n */\n private handleToolCallResultEvent(\n chunk: Extract<StreamChunk, { type: 'TOOL_CALL_RESULT' }>,\n ): void {\n // A resume stream delivers TOOL_CALL_RESULT for a tool call that was\n // started in a PRIOR run. A preceding MESSAGES_SNAPSHOT resets stream state\n // (clearing `toolCallToMessage`), so fall back to locating the message that\n // owns the tool-call part — matching `addToolResult`/`handleInterrupts`.\n // Without this the result is dropped, the follow-up request omits the tool\n // message, and the still-\"pending\" tool call re-interrupts (issue #532).\n const messageId =\n this.toolCallToMessage.get(chunk.toolCallId) ??\n this.messages.find((m) =>\n m.parts.some(\n (p): p is ToolCallPart =>\n p.type === 'tool-call' && p.id === chunk.toolCallId,\n ),\n )?.id\n if (!messageId) return\n\n const extra = chunk as AdapterYieldChunk\n const isOutputError =\n extra.state === 'output-error' ||\n tanstackMetadata(chunk)?.state === 'output-error'\n\n // Step 1: Update the tool-call part's output field\n let output: unknown\n try {\n output = JSON.parse(chunk.content)\n } catch {\n output = chunk.content\n }\n this.messages = updateToolCallWithOutput(\n this.messages,\n chunk.toolCallId,\n output,\n isOutputError ? 'error' : undefined,\n )\n\n // Step 2: Create/update the tool-result part\n const resultState: ToolResultState = isOutputError ? 'error' : 'complete'\n this.messages = updateToolResultPart(\n this.messages,\n messageId,\n chunk.toolCallId,\n chunk.content,\n resultState,\n resultState === 'error' ? this.extractToolResultError(output) : undefined,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Handle RUN_STARTED event.\n *\n * Registers the run so that RUN_FINISHED can determine whether other\n * runs are still active before finalizing.\n */\n private handleRunStartedEvent(\n chunk: Extract<StreamChunk, { type: 'RUN_STARTED' }>,\n ): void {\n this.activeRuns.add(chunk.runId)\n }\n\n /**\n * Handle RUN_FINISHED event.\n *\n * Records the finishReason and removes the run from activeRuns.\n * Only finalizes when no more runs are active, so that concurrent\n * runs don't interfere with each other.\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — finishReason semantics\n * @see docs/chat-architecture.md#adapter-contract — Why RUN_FINISHED is mandatory\n */\n private handleRunFinishedEvent(\n chunk: Extract<StreamChunk, { type: 'RUN_FINISHED' }>,\n ): void {\n const extra = chunk as AdapterYieldChunk\n this.finishReason =\n extra.finishReason !== undefined\n ? extra.finishReason\n : (tanstackMetadata(chunk)?.finishReason ?? null)\n this.activeRuns.delete(chunk.runId)\n\n if (chunk.outcome?.type === 'interrupt') {\n this.handleInterrupts(chunk.outcome.interrupts)\n }\n\n if (this.activeRuns.size === 0) {\n this.completeAllToolCalls()\n const isIntermediateToolTurn =\n this.finishReason === 'tool_calls' &&\n chunk.outcome?.type !== 'interrupt'\n if (isIntermediateToolTurn) {\n return\n }\n this.isDone = true\n this.finalizeStream()\n }\n }\n\n private handleInterrupts(interrupts: Array<Interrupt>): void {\n const hasGeneric = interruptBatchHasGeneric(interrupts)\n for (const interrupt of interrupts) {\n const metadata =\n interrupt.metadata && typeof interrupt.metadata === 'object'\n ? interrupt.metadata\n : {}\n const kind = typeof metadata.kind === 'string' ? metadata.kind : undefined\n const toolCallId = interrupt.toolCallId\n if (!toolCallId) continue\n\n const toolName =\n typeof metadata.toolName === 'string'\n ? metadata.toolName\n : this.findToolCallName(toolCallId)\n const input = Object.hasOwn(metadata, 'input') ? metadata.input : {}\n\n if (kind === 'approval' || interrupt.reason === 'approval_required') {\n // An interrupt terminal arrives after MESSAGES_SNAPSHOT, which may have\n // rebuilt the message list without the live active-message/tool-call\n // maps. Fall back to locating the assistant message that actually owns\n // the tool-call part so the approval UI (part.state) still updates.\n const resolvedMessageId =\n this.getActiveAssistantMessageId() ??\n this.toolCallToMessage.get(toolCallId) ??\n this.messages.find(\n (m) =>\n m.role === 'assistant' &&\n m.parts.some(\n (p) => p.type === 'tool-call' && p.id === toolCallId,\n ),\n )?.id\n if (resolvedMessageId) {\n this.messages = updateToolCallApproval(\n this.messages,\n resolvedMessageId,\n toolCallId,\n interrupt.id,\n )\n this.emitMessagesChange()\n }\n\n this.events.onApprovalRequest?.({\n toolCallId,\n toolName,\n input,\n approvalId: interrupt.id,\n })\n continue\n }\n\n if (kind === 'client_tool' || interrupt.reason === 'client_tool_input') {\n // Generic interrupts in the same batch decide `toolResume`. Do not\n // run client tools until that policy is `continue`.\n if (hasGeneric) continue\n this.events.onToolCall?.({\n toolCallId,\n toolName,\n input,\n })\n }\n }\n }\n\n private findToolCallName(toolCallId: string): string {\n for (const state of this.messageStates.values()) {\n const toolCall = state.toolCalls.get(toolCallId)\n if (toolCall) return toolCall.name\n }\n return ''\n }\n\n /**\n * Handle RUN_ERROR event\n */\n private handleRunErrorEvent(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n ): void {\n this.hasError = true\n const runId = getChunkRunId(chunk)\n if (runId) {\n this.activeRuns.delete(runId)\n } else {\n this.activeRuns.clear()\n }\n const { messageId } = this.ensureAssistantMessage()\n // Prefer spec field `message`; fall back to deprecated `error.message`.\n // If neither is set, the chunk still carries debug context (provider\n // error codes, request ids, etc.) — log it so the failure isn't silent.\n const errorMessage = chunk.message || 'An error occurred'\n if (!chunk.message) {\n console.error(\n '[StreamProcessor] RUN_ERROR with no message; original chunk:',\n chunk,\n )\n }\n\n if (this.structuredMessageIds.has(messageId)) {\n this.flushStructuredOutputUpdate(messageId)\n this.messages = errorStructuredOutputPart(\n this.messages,\n messageId,\n errorMessage,\n )\n this.structuredMessageIds.delete(messageId)\n this.emitStructuredOutputChange(messageId, 'error')\n this.emitMessagesChange()\n }\n\n // Attach the provider's structured error body (`rawEvent`) and `code` to\n // the surfaced Error so consumers can recover the upstream detail that the\n // RUN_ERROR's `message` alone discards. Both are optional and added only\n // when present, keeping the Error backward compatible.\n this.events.onError?.(runErrorEventToError(chunk))\n }\n\n /**\n * Handle STEP_STARTED event (for thinking/reasoning content).\n *\n * Records the stepId so later REASONING_MESSAGE_CONTENT deltas accumulate\n * into their own ThinkingPart. Does not create a message — the message\n * is lazily created when the first REASONING_MESSAGE_CONTENT arrives.\n */\n private handleStepStartedEvent(\n chunk: Extract<StreamChunk, { type: 'STEP_STARTED' }>,\n ): void {\n const stepId = chunk.stepName || generateMessageId()\n const activeId = this.getActiveAssistantMessageId()\n if (activeId) {\n const state = this.getMessageState(activeId)\n if (state) {\n state.currentThinkingStepId = stepId\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, '')\n state.thinkingStepOrder.push(stepId)\n }\n // Clear any pending stepId from a prior STEP_STARTED that fired\n // before the assistant message existed. Now that we're tracking\n // the step directly on message state, the pending value is stale\n // and must not leak into the next REASONING_MESSAGE_CONTENT.\n this.pendingThinkingStepId = null\n return\n }\n }\n\n // No active message yet — defer until ensureAssistantMessage in\n // REASONING_MESSAGE_CONTENT\n this.pendingThinkingStepId = stepId\n }\n\n /**\n * Handle STEP_FINISHED event.\n *\n * Thinking *content* comes from REASONING_MESSAGE_CONTENT, not STEP_FINISHED.\n * But some adapters (e.g. BytePlus thinking-summary) carry the provider\n * signature blob ONLY on the STEP_FINISHED event, so still extract that here\n * and attach it to the thinking step the reasoning events already built.\n */\n private handleStepFinishedEvent(\n chunk: Extract<StreamChunk, { type: 'STEP_FINISHED' }>,\n ): void {\n const extra = chunk as AdapterYieldChunk\n const signature = extra.signature\n if (!signature) return\n\n const { messageId, state } = this.ensureAssistantMessage(\n this.getActiveAssistantMessageId() ?? undefined,\n )\n const stepId = state.currentThinkingStepId ?? extra.stepId\n if (!stepId) return\n const thinking = state.thinkingSteps.get(stepId)\n if (thinking === undefined) return\n\n state.thinkingStepSignatures.set(stepId, signature)\n this.messages = updateThinkingPart(\n this.messages,\n messageId,\n stepId,\n thinking,\n signature,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Handle REASONING_MESSAGE_CONTENT event (AG-UI reasoning protocol).\n *\n * Accumulates reasoning delta into thinking content and updates the\n * corresponding ThinkingPart in the UIMessage.\n */\n private handleReasoningMessageContentEvent(\n chunk: Extract<StreamChunk, { type: 'REASONING_MESSAGE_CONTENT' }>,\n ): void {\n const { messageId, state } = this.ensureAssistantMessage(\n this.getActiveAssistantMessageId() ?? undefined,\n )\n\n state.hasSeenReasoningEvents = true\n const delta = chunk.delta || ''\n\n this.consumePendingThinkingStep(state)\n\n const stepId = state.currentThinkingStepId ?? chunk.messageId\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, '')\n state.thinkingStepOrder.push(stepId)\n state.currentThinkingStepId = stepId\n }\n\n const nextThinking = (state.thinkingSteps.get(stepId) ?? '') + delta\n state.thinkingSteps.set(stepId, nextThinking)\n\n this.messages = updateThinkingPart(\n this.messages,\n messageId,\n stepId,\n nextThinking,\n state.thinkingStepSignatures.get(stepId),\n )\n this.emitMessagesChange()\n\n this.events.onThinkingUpdate?.(messageId, stepId, nextThinking)\n }\n\n /**\n * Attach a provider signature blob from REASONING_ENCRYPTED_VALUE.\n * `subtype: 'message'` updates ThinkingPart.signature.\n * `subtype: 'tool-call'` stores Gemini thoughtSignature on the tool-call part.\n */\n private handleReasoningEncryptedValueEvent(\n chunk: Extract<StreamChunk, { type: 'REASONING_ENCRYPTED_VALUE' }>,\n ): void {\n const encryptedValue = chunk.encryptedValue\n if (typeof encryptedValue !== 'string' || encryptedValue === '') return\n\n if (chunk.subtype === 'tool-call') {\n this.attachToolCallSignature(chunk.entityId, encryptedValue)\n return\n }\n\n const { messageId, state } = this.ensureAssistantMessage(\n this.getActiveAssistantMessageId() ?? undefined,\n )\n const stepId = state.currentThinkingStepId ?? chunk.entityId\n state.thinkingStepSignatures.set(stepId, encryptedValue)\n const content = state.thinkingSteps.get(stepId) ?? ''\n if (!state.thinkingSteps.has(stepId)) {\n state.thinkingSteps.set(stepId, content)\n state.thinkingStepOrder.push(stepId)\n }\n this.messages = updateThinkingPart(\n this.messages,\n messageId,\n stepId,\n content,\n encryptedValue,\n )\n this.emitMessagesChange()\n }\n\n private attachToolCallSignature(\n toolCallId: string,\n thoughtSignature: string,\n ): void {\n this.messages = this.messages.map((msg) => {\n let changed = false\n const parts = msg.parts.map((part) => {\n if (part.type !== 'tool-call' || part.id !== toolCallId) return part\n changed = true\n return {\n ...part,\n metadata: {\n ...(part.metadata != null && typeof part.metadata === 'object'\n ? part.metadata\n : {}),\n thoughtSignature,\n },\n }\n })\n return changed ? { ...msg, parts } : msg\n })\n for (const state of this.messageStates.values()) {\n const call = state.toolCalls.get(toolCallId)\n if (!call) continue\n call.metadata = {\n ...(call.metadata ?? {}),\n thoughtSignature,\n }\n }\n this.emitMessagesChange()\n }\n\n /**\n * Handle CUSTOM event.\n *\n * Handles custom events consumed by the processor:\n * - 'tool-input-available': Legacy/replay-compatible input for client tool\n * execution. Fires onToolCall.\n * - 'approval-requested': Legacy/replay-compatible input for tool approval.\n * Updates tool-call part state and fires onApprovalRequest.\n *\n * Current core streams represent user-actionable waits through\n * RUN_FINISHED.outcome.type === 'interrupt'; these custom events are not the\n * source of truth for new emissions.\n *\n * @see docs/chat-architecture.md#client-tools-and-approval-flows — Full flow details\n */\n private handleCustomEvent(\n chunk: Extract<StreamChunk, { type: 'CUSTOM' }>,\n ): void {\n const messageId = this.getActiveAssistantMessageId()\n\n if (chunk.name === 'structured-output.start' && chunk.value) {\n const v = chunk.value as { messageId?: string }\n const { messageId: targetId } = this.ensureAssistantMessage(\n v.messageId ?? messageId ?? undefined,\n )\n if (targetId) {\n this.structuredMessageIds.add(targetId)\n this.structuredOutputUpdateBatches.delete(targetId)\n this.events.onStructuredOutputChange?.({\n phase: 'start',\n messageId: targetId,\n status: 'streaming',\n raw: '',\n })\n }\n return\n }\n\n if (chunk.name === 'structured-output.complete' && chunk.value) {\n const v = chunk.value as {\n object: unknown\n raw?: string\n reasoning?: string\n messageId?: string\n }\n const { messageId: targetId } = this.ensureAssistantMessage(\n v.messageId ?? messageId ?? undefined,\n )\n if (targetId) {\n this.flushStructuredOutputUpdate(targetId)\n this.messages = completeStructuredOutputPart(\n this.messages,\n targetId,\n v.object,\n v.raw ?? '',\n v.reasoning,\n )\n this.structuredMessageIds.delete(targetId)\n this.emitStructuredOutputChange(targetId, 'complete')\n this.emitMessagesChange()\n }\n // Fall through so user `onCustomEvent` callbacks still observe the event.\n }\n\n // Handle client tool input availability - trigger client-side execution\n if (chunk.name === 'tool-input-available' && chunk.value) {\n const { toolCallId, toolName, input } = chunk.value as {\n toolCallId: string\n toolName: string\n input: any\n }\n\n // Emit onToolCall event for the client to execute the tool\n this.events.onToolCall?.({\n toolCallId,\n toolName,\n input,\n })\n return\n }\n\n // Handle approval requests\n if (chunk.name === 'approval-requested' && chunk.value) {\n const { toolCallId, toolName, input, approval } = chunk.value as {\n toolCallId: string\n toolName: string\n input: any\n approval: { id: string; needsApproval: boolean }\n }\n\n // Resolve the message containing this tool call. After RUN_FINISHED,\n // activeMessageIds is cleared, so fall back to the toolCallToMessage map\n // which is populated during TOOL_CALL_START and preserved across finalize.\n const resolvedMessageId =\n messageId ?? this.toolCallToMessage.get(toolCallId)\n if (resolvedMessageId) {\n this.messages = updateToolCallApproval(\n this.messages,\n resolvedMessageId,\n toolCallId,\n approval.id,\n )\n this.emitMessagesChange()\n }\n\n // Emit approval request event\n this.events.onApprovalRequest?.({\n toolCallId,\n toolName,\n input,\n approvalId: approval.id,\n })\n return\n }\n\n // Handle MCP Apps ui-resource events — materialize a UIResourcePart on the\n // active assistant message. Never falls through to onCustomEvent because\n // ui-resource is a system event, not a user-defined custom event.\n if (chunk.name === 'ui-resource' && chunk.value) {\n const v: UIResourceEvent['value'] = chunk.value\n // Resolve the target assistant message. When a toolCallId is present, the\n // tool call's OWNER message is authoritative, so prefer it first; fall\n // back to the active assistant id only if the tool call isn't mapped.\n // This avoids misattaching the widget to a different active message in a\n // multi-message session.\n const resolvedMessageId =\n this.toolCallToMessage.get(v.toolCallId) ?? messageId\n if (resolvedMessageId) {\n const part: UIResourcePart = {\n type: 'ui-resource',\n resource: v.resource,\n toolCallId: v.toolCallId,\n toolName: v.toolName,\n ...(v.serverId !== undefined && { serverId: v.serverId }),\n ...(v.meta !== undefined && { meta: v.meta }),\n }\n this.messages = this.messages.map((msg) =>\n msg.id === resolvedMessageId\n ? { ...msg, parts: [...msg.parts, part] }\n : msg,\n )\n this.emitMessagesChange()\n } else {\n // No owner message and no active assistant id — the server read and\n // streamed a widget that has nowhere to attach (e.g. a toolCallId never\n // registered, or the event arrived after the run cleared its active\n // ids). Drop fail-soft, but warn: a vanished widget is otherwise\n // undebuggable from the client.\n console.warn(\n `[mcp-apps] dropped ui-resource: no target message for toolCallId \"${v.toolCallId}\" (toolName \"${v.toolName}\")`,\n )\n }\n return\n }\n\n // Forward non-system custom events to onCustomEvent callback\n if (this.events.onCustomEvent) {\n const toolCallId =\n chunk.value && typeof chunk.value === 'object'\n ? chunk.value.toolCallId\n : undefined\n this.events.onCustomEvent(chunk.name, chunk.value, { toolCallId })\n }\n }\n\n // ============================================\n // Internal Helpers\n // ============================================\n\n /**\n * Detect if an incoming content chunk represents a NEW text segment\n */\n private isNewTextSegment(\n _chunk: Extract<StreamChunk, { type: 'TEXT_MESSAGE_CONTENT' }>,\n _previous: string,\n ): boolean {\n return true\n }\n\n /**\n * Complete all tool calls across all active messages — safety net for stream termination.\n *\n * Called by RUN_FINISHED and finalizeStream(). Force-transitions any tool call\n * not yet in input-complete state. Handles cases where TOOL_CALL_END was\n * missed (adapter bug, network error, aborted stream).\n *\n * @see docs/chat-architecture.md#single-shot-tool-call-response — Safety net behavior\n */\n private completeAllToolCalls(): void {\n for (const messageId of this.activeMessageIds) {\n this.completeAllToolCallsForMessage(messageId)\n }\n }\n\n /**\n * Complete all tool calls for a specific message\n */\n private completeAllToolCallsForMessage(messageId: string): void {\n const state = this.getMessageState(messageId)\n if (!state) return\n\n state.toolCalls.forEach((toolCall, id) => {\n if (toolCall.state !== 'input-complete') {\n const index = state.toolCallOrder.indexOf(id)\n this.completeToolCall(messageId, index, toolCall)\n }\n })\n }\n\n /**\n * Mark a tool call as complete and emit event\n */\n private completeToolCall(\n messageId: string,\n _index: number,\n toolCall: InternalToolCallState,\n ): void {\n // Finalize the internal bookkeeping: the call's input arguments ARE\n // complete regardless of whether execution later failed, so the call still\n // counts as a completed tool call in getCompletedToolCalls()/getState().\n toolCall.state = 'input-complete'\n\n // Only surface `input` from a strict parse. The streaming partial-JSON\n // parser closes unterminated strings, so truncated arguments would become\n // a plausible but wrong object (GitHub issue #1017). If parse fails,\n // `input` stays unset and consumers use the raw `arguments` string.\n let strictParseSucceeded = false\n try {\n toolCall.parsedArguments = JSON.parse(toolCall.arguments)\n strictParseSucceeded = true\n } catch {\n toolCall.parsedArguments = undefined\n }\n\n // Don't downgrade the rendered part of a call that already reached the\n // terminal 'error' state (e.g. an output-error TOOL_CALL_RESULT arrived\n // without a preceding TOOL_CALL_END). The RUN_FINISHED / finalizeStream\n // safety net must not clobber a failed call back to 'input-complete'.\n if (this.isToolCallPartErrored(toolCall.id)) {\n return\n }\n\n // RUN_FINISHED interrupt handling can mark the rendered part as waiting on\n // user action before the completion safety net runs. Keep the parsed\n // argument bookkeeping above, but do not downgrade that visible state.\n if (this.isToolCallPartAwaitingUserAction(toolCall.id)) {\n return\n }\n\n // Update UIMessage. The arguments are complete now, so surface the parsed\n // input on the part from the accumulated TOOL_CALL_ARGS deltas.\n this.messages = updateToolCallPart(this.messages, messageId, {\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: 'input-complete',\n ...(strictParseSucceeded && { input: toolCall.parsedArguments }),\n ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),\n })\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onToolCallStateChange?.(\n messageId,\n toolCall.id,\n 'input-complete',\n toolCall.arguments,\n )\n }\n\n private isToolCallPartAwaitingUserAction(toolCallId: string): boolean {\n return this.messages.some((msg) =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `parts` is typed as required, but seeded ModelMessage-shaped messages can lack it at runtime.\n msg.parts?.some(\n (part) =>\n part.type === 'tool-call' &&\n part.id === toolCallId &&\n (part.state === 'approval-requested' ||\n part.state === 'approval-responded'),\n ),\n )\n }\n\n /**\n * Whether the rendered tool-call part for the given id has reached the\n * terminal 'error' state. Used to prevent the completion safety net from\n * downgrading a failed call back to 'input-complete'.\n */\n private isToolCallPartErrored(toolCallId: string): boolean {\n // `initialMessages` may be ModelMessage-shaped (no `parts`) — e.g. the\n // common pattern of seeding a processor with the same messages passed to\n // `chat()`. Guard the access so iterating them never throws.\n return this.messages.some((msg) =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `parts` is typed as required, but seeded ModelMessage-shaped messages can lack it at runtime.\n msg.parts?.some(\n (part) =>\n part.type === 'tool-call' &&\n part.id === toolCallId &&\n part.state === 'error',\n ),\n )\n }\n\n /**\n * Emit pending text update for a specific message.\n *\n * Calls updateTextPart() which has critical append-vs-replace logic:\n * - If last UIMessage part is TextPart → replaces its content (same segment).\n * - If last part is anything else → pushes new TextPart (new segment after tools).\n *\n * @see docs/chat-architecture.md#uimessage-part-ordering-invariants — Replace vs. push logic\n */\n private emitTextUpdateForMessage(messageId: string): void {\n const state = this.getMessageState(messageId)\n if (!state) return\n\n state.lastEmittedText = state.currentSegmentText\n\n // Update UIMessage\n this.messages = updateTextPart(\n this.messages,\n messageId,\n state.currentSegmentText,\n )\n this.emitMessagesChange()\n\n // Emit granular event\n this.events.onTextUpdate?.(messageId, state.currentSegmentText)\n }\n\n private queueStructuredOutputUpdate(messageId: string, delta: string): void {\n const existing = this.structuredOutputUpdateBatches.get(messageId)\n const next = {\n delta: `${existing?.delta ?? ''}${delta}`,\n chunkCount: (existing?.chunkCount ?? 0) + 1,\n }\n\n this.structuredOutputUpdateBatches.set(messageId, next)\n\n if (next.chunkCount >= STRUCTURED_OUTPUT_UPDATE_BATCH_SIZE) {\n this.flushStructuredOutputUpdate(messageId)\n }\n }\n\n private flushStructuredOutputUpdate(messageId: string): void {\n const batch = this.structuredOutputUpdateBatches.get(messageId)\n if (!batch || batch.chunkCount === 0) return\n\n this.structuredOutputUpdateBatches.delete(messageId)\n this.emitStructuredOutputChange(messageId, 'update', batch.delta)\n }\n\n private emitStructuredOutputChange(\n messageId: string,\n phase: 'update' | 'complete' | 'error',\n delta?: string,\n ): void {\n const part = this.messages\n .find((message) => message.id === messageId)\n ?.parts.find(\n (\n messagePart,\n ): messagePart is Extract<MessagePart, { type: 'structured-output' }> =>\n messagePart.type === 'structured-output',\n )\n if (!part) return\n\n this.events.onStructuredOutputChange?.({\n phase,\n messageId,\n status: part.status,\n raw: part.raw,\n ...(part.partial !== undefined ? { partial: part.partial } : {}),\n ...(part.data !== undefined ? { data: part.data } : {}),\n ...(part.reasoning !== undefined ? { reasoning: part.reasoning } : {}),\n ...(part.errorMessage !== undefined\n ? { errorMessage: part.errorMessage }\n : {}),\n ...(delta !== undefined ? { delta } : {}),\n })\n }\n\n /**\n * Emit messages change event\n */\n private emitMessagesChange(): void {\n this.events.onMessagesChange?.([...this.messages])\n }\n\n /**\n * Finalize the stream — complete all pending operations.\n *\n * Called when the async iterable ends (stream closed). Acts as the final\n * safety net: completes any remaining tool calls, flushes un-emitted text,\n * and fires onStreamEnd.\n *\n * @see docs/chat-architecture.md#single-shot-text-response — Finalization step\n */\n finalizeStream(): void {\n this.isDone = true\n let lastAssistantMessage: UIMessage | undefined\n\n // Finalize ALL active messages\n for (const messageId of this.activeMessageIds) {\n const state = this.getMessageState(messageId)\n if (!state) continue\n\n // Complete any remaining tool calls\n this.completeAllToolCallsForMessage(messageId)\n\n // Emit any pending text if not already emitted\n if (state.currentSegmentText !== state.lastEmittedText) {\n this.emitTextUpdateForMessage(messageId)\n }\n\n state.isComplete = true\n\n const msg = this.messages.find((m) => m.id === messageId)\n if (msg && msg.role === 'assistant') {\n lastAssistantMessage = msg\n }\n }\n\n // The stream closed but one or more structured-output runs never sent\n // their terminal `structured-output.complete`. Snap each lingering\n // streaming part to error so the UI doesn't appear to stream forever,\n // and drop the routing entries so a subsequent run on the same\n // processor instance (long-lived `subscribe()` mode) doesn't reuse\n // the stale ids.\n //\n // The iteration is unconditional w.r.t. `this.hasError` — RUN_ERROR\n // already removed its target messageId from `structuredMessageIds`\n // before reaching finalize, so anything still in the set is by\n // definition a non-errored, never-completed run (the multi-run case:\n // run-A errors, run-B is still streaming when finalize fires).\n for (const messageId of this.structuredMessageIds) {\n this.flushStructuredOutputUpdate(messageId)\n this.messages = errorStructuredOutputPart(\n this.messages,\n messageId,\n 'Stream ended without structured-output.complete',\n )\n this.emitStructuredOutputChange(messageId, 'error')\n }\n this.structuredMessageIds.clear()\n this.structuredOutputUpdateBatches.clear()\n\n this.activeMessageIds.clear()\n\n // Remove whitespace-only assistant messages (handles models like Gemini\n // that sometimes return just \"\\n\" during auto-continuation).\n // Preserve the message on errors so the UI can show error state.\n if (lastAssistantMessage && !this.hasError) {\n if (this.isWhitespaceOnlyMessage(lastAssistantMessage)) {\n this.messages = this.messages.filter(\n (m) => m.id !== lastAssistantMessage.id,\n )\n this.emitMessagesChange()\n return\n }\n }\n\n // Emit stream end for the last assistant message\n if (lastAssistantMessage && !this.streamEndEmitted) {\n this.streamEndEmitted = true\n this.events.onStreamEnd?.(lastAssistantMessage)\n }\n }\n\n /**\n * Get completed tool calls in API format (aggregated across all messages)\n */\n private getCompletedToolCalls(): Array<ToolCall> {\n const result: Array<ToolCall> = []\n for (const state of this.messageStates.values()) {\n for (const tc of state.toolCalls.values()) {\n if (tc.state === 'input-complete') {\n result.push({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.name,\n arguments: tc.arguments,\n },\n // Preserve provider metadata (e.g. Gemini thoughtSignature) on\n // ProcessorResult.toolCalls so callers using process()/getResult()\n // get the same round-trip support as the streaming UI path.\n ...(tc.metadata !== undefined && { metadata: tc.metadata }),\n })\n }\n }\n }\n return result\n }\n\n /**\n * Get current result (aggregated across all messages)\n */\n private getResult(): ProcessorResult {\n const toolCalls = this.getCompletedToolCalls()\n let content = ''\n let thinking = ''\n\n for (const state of this.messageStates.values()) {\n content += state.totalTextContent\n for (const stepId of state.thinkingStepOrder) {\n thinking += state.thinkingSteps.get(stepId) ?? ''\n }\n }\n\n return {\n content,\n thinking: thinking || undefined,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n finishReason: this.finishReason,\n }\n }\n\n /**\n * Get current processor state (aggregated across all messages)\n */\n getState(): ProcessorState {\n let content = ''\n let thinking = ''\n const toolCalls = new Map<string, InternalToolCallState>()\n const toolCallOrder: Array<string> = []\n\n for (const state of this.messageStates.values()) {\n content += state.totalTextContent\n for (const stepId of state.thinkingStepOrder) {\n thinking += state.thinkingSteps.get(stepId) ?? ''\n }\n for (const [id, tc] of state.toolCalls) {\n toolCalls.set(id, tc)\n }\n toolCallOrder.push(...state.toolCallOrder)\n }\n\n return {\n content,\n thinking,\n toolCalls,\n toolCallOrder,\n finishReason: this.finishReason,\n done: this.isDone,\n }\n }\n\n /**\n * Start recording chunks\n */\n startRecording(): void {\n this.recordingEnabled = true\n this.recordingStartTime = Date.now()\n this.recording = {\n version: '1.0',\n timestamp: this.recordingStartTime,\n chunks: [],\n }\n }\n\n /**\n * Get the current recording\n */\n getRecording(): ChunkRecording | null {\n return this.recording\n }\n\n /**\n * Reset stream state (but keep messages)\n */\n private resetStreamState(): void {\n this.messageStates.clear()\n this.activeMessageIds.clear()\n this.activeRuns.clear()\n this.toolCallToMessage.clear()\n this.structuredMessageIds.clear()\n this.structuredOutputUpdateBatches.clear()\n this.pendingManualMessageId = null\n this.pendingThinkingStepId = null\n this.finishReason = null\n this.hasError = false\n this.isDone = false\n this.streamEndEmitted = false\n this.chunkStrategy.reset?.()\n }\n\n /**\n * Full reset (including messages)\n */\n reset(): void {\n this.resetStreamState()\n this.messages = []\n }\n\n /**\n * Check if a message contains only whitespace text and no other meaningful parts\n * (no tool calls, tool results, thinking, etc.)\n */\n private isWhitespaceOnlyMessage(message: UIMessage): boolean {\n if (message.parts.length === 0) return false\n return message.parts.every(\n (part) => part.type === 'text' && part.content.trim() === '',\n )\n }\n\n /**\n * Replay a recording through the processor\n */\n static async replay(\n recording: ChunkRecording,\n options?: StreamProcessorOptions,\n ): Promise<ProcessorResult> {\n const processor = new StreamProcessor(options)\n return processor.process(createReplayStream(recording))\n }\n}\n\n/**\n * Create an async iterable from a recording\n */\nexport function createReplayStream(\n recording: ChunkRecording,\n): AsyncIterable<StreamChunk> {\n return {\n // eslint-disable-next-line @typescript-eslint/require-await -- async generator required by AsyncIterable contract; body has no await\n async *[Symbol.asyncIterator]() {\n for (const { chunk } of recording.chunks) {\n yield chunk\n }\n },\n }\n}\n"],"mappings":"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"}
|