@tanstack/ai-client 0.21.0 → 0.22.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.
@@ -1 +1 @@
1
- {"version":3,"file":"chat-client.js","sources":["../../src/chat-client.ts"],"sourcesContent":["import {\n StreamProcessor,\n convertSchemaToJsonSchema,\n generateMessageId,\n isStandardSchema,\n normalizeToUIMessage,\n parseWithStandardSchema,\n} from '@tanstack/ai/client'\nimport { createNoOpChatDevtoolsBridge } from './devtools-noop'\nimport {\n fetcherToConnectionAdapter,\n getChunkRunId,\n normalizeConnectionAdapter,\n} from './connection-adapters'\nimport { ChatPersistor } from './client-persistor'\nimport type {\n AnyClientTool,\n ContentPart,\n ModelMessage,\n StreamChunk,\n} from '@tanstack/ai/client'\nimport type {\n ConnectionAdapter,\n SubscribeConnectionAdapter,\n} from './connection-adapters'\nimport type {\n ChatClientEventEmitter,\n ChatClientRunEventContext,\n} from './events'\nimport type {\n AIDevtoolsChatSnapshot,\n ChatDevtoolsBridge,\n ChatDevtoolsBridgeOptions,\n} from './devtools'\nimport type {\n ChatClientOptions,\n ChatClientState,\n ChatFetcher,\n ConnectionStatus,\n MessagePart,\n MultimodalContent,\n ToolCallPart,\n UIMessage,\n} from './types'\n\ntype ChatClientUpdateOptionsWithoutContext<\n TTools extends ReadonlyArray<AnyClientTool>,\n> = {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n /** @deprecated Use `forwardedProps` instead. */\n body?: Record<string, any>\n forwardedProps?: Record<string, any>\n tools?: TTools\n onResponse?: (response?: Response) => void | Promise<void>\n onChunk?: (chunk: StreamChunk) => void\n onFinish?: (message: UIMessage) => void\n onError?: (error: Error) => void\n onSubscriptionChange?: (isSubscribed: boolean) => void\n onConnectionStatusChange?: (status: ConnectionStatus) => void\n onSessionGeneratingChange?: (isGenerating: boolean) => void\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n}\n\ntype ClientToolResult = {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n}\n\nfunction resolveTransport(transport: {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n}): ConnectionAdapter {\n const { connection, fetcher } = transport\n if (connection && fetcher) {\n throw new Error(\n 'ChatClient: pass either `connection` or `fetcher`, not both.',\n )\n }\n if (connection) return connection\n if (fetcher) return fetcherToConnectionAdapter(fetcher)\n throw new Error('ChatClient: either `connection` or `fetcher` is required.')\n}\n\nexport class ChatClient<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = unknown,\n> {\n private readonly processor: StreamProcessor\n private connection: SubscribeConnectionAdapter\n private readonly uniqueId: string\n private readonly threadId: string\n // All persistence concerns (hydrate / save / clear, plus suppression of late\n // chunks after a mid-stream clear) live in ChatPersistor so this class stays\n // focused on streaming. Undefined when no `persistence` adapter is configured.\n private readonly persistor?: ChatPersistor\n private currentRunId: string | null = null\n // Track the legacy `body` option and the canonical `forwardedProps`\n // option as separate slots so that `updateOptions({ forwardedProps })`\n // doesn't wipe a previously-set `body` (and vice versa). They are\n // merged on every send, with `forwardedProps` winning on key collision.\n private bodyOption: Record<string, any> = {}\n private forwardedPropsOption: Record<string, any> = {}\n private context: TContext | undefined = undefined\n private pendingMessageBody: Record<string, any> | undefined = undefined\n private isLoading = false\n private isSubscribed = false\n private error: Error | undefined = undefined\n private status: ChatClientState = 'ready'\n private connectionStatus: ConnectionStatus = 'disconnected'\n private abortController: AbortController | null = null\n private readonly clientToolsRef: { current: Map<string, AnyClientTool> }\n private readonly devtoolsBridge: ChatDevtoolsBridge\n /**\n * Alias for `this.events`. The bridge installs an\n * emitter that auto-attaches run/thread context and auto-emits a\n * snapshot after every event, so chat-client only ever calls\n * `this.events.X(...)` exactly like it did before devtools landed.\n */\n private readonly events: ChatClientEventEmitter\n private currentStreamId: string | null = null\n private currentMessageId: string | null = null\n private readonly postStreamActions: Array<() => Promise<void>> = []\n // Track pending client tool executions to await them before stream finalization\n private readonly pendingToolExecutions: Map<string, Promise<void>> = new Map()\n private activeClientTools: Map<string, AnyClientTool> | null = null\n private activeContext: TContext | undefined = undefined\n // Flag to deduplicate continuation checks during action draining\n private continuationPending = false\n private subscriptionAbortController: AbortController | null = null\n private processingResolve: (() => void) | null = null\n private errorReportedGeneration: number | null = null\n private streamGeneration = 0\n // Tracks whether a queued checkForContinuation was skipped because\n // continuationPending was true (chained approval scenario)\n private continuationSkipped = false\n private draining = false\n private sessionGenerating = false\n private readonly activeRunIds = new Set<string>()\n private devtoolsMounted = false\n\n private readonly callbacksRef: {\n current: {\n onResponse: (response?: Response) => void | Promise<void>\n onChunk: (chunk: StreamChunk) => void\n onFinish: (message: UIMessage) => void\n onError: (error: Error) => void\n onMessagesChange: (messages: Array<UIMessage>) => void\n onLoadingChange: (isLoading: boolean) => void\n onErrorChange: (error: Error | undefined) => void\n onStatusChange: (status: ChatClientState) => void\n onSubscriptionChange: (isSubscribed: boolean) => void\n onConnectionStatusChange: (status: ConnectionStatus) => void\n onSessionGeneratingChange: (isGenerating: boolean) => void\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n }\n }\n\n constructor(options: ChatClientOptions<TTools, TContext>) {\n this.uniqueId = options.id || this.generateUniqueId('chat')\n this.threadId = options.threadId || this.generateUniqueId('thread')\n if (options.persistence) {\n this.persistor = new ChatPersistor(\n options.persistence,\n this.uniqueId,\n (messages) => this.processor.setMessages(messages),\n )\n }\n // Both `body` (deprecated) and `forwardedProps` populate the AG-UI\n // `RunAgentInput.forwardedProps` wire field. They are stored\n // separately so `updateOptions` can replace one without touching the\n // other; the merge happens at send time, with `forwardedProps`\n // winning on key collision.\n this.bodyOption = options.body || {}\n this.forwardedPropsOption = options.forwardedProps || {}\n this.context = options.context\n this.connection = normalizeConnectionAdapter(resolveTransport(options))\n\n // Build client tools map\n this.clientToolsRef = { current: new Map() }\n if (options.tools) {\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n }\n\n this.devtoolsBridge = (\n options.devtoolsBridgeFactory ?? createNoOpChatDevtoolsBridge\n )(this.buildDevtoolsBridgeOptions(options.devtools))\n this.events = this.devtoolsBridge.events\n\n this.callbacksRef = {\n current: {\n onResponse: options.onResponse || (() => {}),\n onChunk: options.onChunk || (() => {}),\n onFinish: options.onFinish || (() => {}),\n onError: options.onError || (() => {}),\n onMessagesChange: options.onMessagesChange || (() => {}),\n onLoadingChange: options.onLoadingChange || (() => {}),\n onErrorChange: options.onErrorChange || (() => {}),\n onStatusChange: options.onStatusChange || (() => {}),\n onSubscriptionChange: options.onSubscriptionChange || (() => {}),\n onConnectionStatusChange:\n options.onConnectionStatusChange || (() => {}),\n onSessionGeneratingChange:\n options.onSessionGeneratingChange || (() => {}),\n onCustomEvent: options.onCustomEvent || (() => {}),\n },\n }\n\n // Create StreamProcessor with event handlers.\n // Use conditional spreads so we don't pass `undefined` into\n // `StreamProcessorOptions` fields under `exactOptionalPropertyTypes`.\n const persistedMessages = this.persistor?.readInitial()\n const initialMessages = Array.isArray(persistedMessages)\n ? persistedMessages\n : options.initialMessages\n\n this.processor = new StreamProcessor({\n ...(options.streamProcessor?.chunkStrategy\n ? { chunkStrategy: options.streamProcessor.chunkStrategy }\n : {}),\n ...(initialMessages ? { initialMessages } : {}),\n events: {\n onMessagesChange: (messages: Array<UIMessage>) => {\n this.persistor?.notifyMessagesChanged(messages)\n this.callbacksRef.current.onMessagesChange(messages)\n },\n onStreamStart: () => {\n this.setStatus('streaming')\n const assistantMessageId =\n this.processor.getCurrentAssistantMessageId()\n if (!assistantMessageId) {\n return\n }\n const messages = this.processor.getMessages()\n const assistantMessage = messages.find(\n (m: UIMessage) => m.id === assistantMessageId,\n )\n if (assistantMessage) {\n this.currentMessageId = assistantMessage.id\n this.events.messageAppended(\n assistantMessage,\n this.currentStreamId || undefined,\n )\n }\n },\n onStreamEnd: (message: UIMessage) => {\n this.callbacksRef.current.onFinish(message)\n this.setStatus('ready')\n // Resolve the processing-complete promise so streamResponse can continue\n this.resolveProcessing()\n },\n onError: (error: Error) => {\n this.reportStreamError(error)\n },\n onTextUpdate: (messageId: string, content: string) => {\n // Emit text update to devtools\n if (this.currentStreamId) {\n this.events.textUpdated(this.currentStreamId, messageId, content)\n }\n },\n onThinkingUpdate: (messageId: string, content: string) => {\n // Emit thinking update to devtools\n if (this.currentStreamId) {\n this.events.thinkingUpdated(\n this.currentStreamId,\n messageId,\n content,\n undefined,\n )\n }\n },\n onStructuredOutputChange: (args) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const eventName =\n args.phase === 'start'\n ? 'structured-output:started'\n : args.phase === 'complete'\n ? 'structured-output:completed'\n : args.phase === 'error'\n ? 'structured-output:errored'\n : 'structured-output:updated'\n\n this.currentMessageId = args.messageId\n this.events.structuredOutputChanged(\n eventName,\n streamId,\n args.messageId,\n {\n status: args.status,\n raw: args.raw,\n ...(args.partial !== undefined ? { partial: args.partial } : {}),\n ...(args.data !== undefined ? { data: args.data } : {}),\n ...(args.reasoning !== undefined\n ? { reasoning: args.reasoning }\n : {}),\n ...(args.errorMessage !== undefined\n ? { errorMessage: args.errorMessage }\n : {}),\n ...(args.delta !== undefined ? { delta: args.delta } : {}),\n },\n )\n },\n onToolCallStateChange: (\n messageId: string,\n toolCallId: string,\n state: string,\n args: string,\n ) => {\n // Get the tool name from the messages\n const messages = this.processor.getMessages()\n const message = messages.find((m: UIMessage) => m.id === messageId)\n const toolCallPart = message?.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.id === toolCallId,\n )\n const toolName = toolCallPart?.name || 'unknown'\n\n // Emit tool call state change to devtools\n if (this.currentStreamId) {\n this.events.toolCallStateChanged(\n this.currentStreamId,\n messageId,\n toolCallId,\n toolName,\n state,\n args,\n )\n }\n },\n onToolCall: (args: {\n toolCallId: string\n toolName: string\n input: any\n }) => {\n // Handle client-side tool execution automatically\n const clientTools =\n this.activeClientTools ?? this.clientToolsRef.current\n const clientTool = clientTools.get(args.toolName)\n const executeFunc = clientTool?.execute\n if (executeFunc) {\n // Capture the run context at execution-start so a tool whose\n // result lands AFTER the originating run finishes still reports\n // back against the originating run, not whatever run is\n // current when the result emits.\n const runEventContext =\n this.devtoolsBridge.getCurrentRunEventContext()\n // Create and track the execution promise\n const executionPromise = (async () => {\n try {\n const context =\n this.activeClientTools === null\n ? this.context\n : this.activeContext\n const output = await executeFunc(args.input, {\n toolCallId: args.toolCallId,\n context: context as TContext,\n emitCustomEvent: () => {},\n })\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output,\n state: 'output-available',\n },\n clientTool,\n runEventContext,\n )\n } catch (error: any) {\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output: null,\n state: 'output-error',\n errorText: error.message,\n },\n clientTool,\n runEventContext,\n )\n } finally {\n // Remove from pending when complete\n this.pendingToolExecutions.delete(args.toolCallId)\n }\n })()\n\n // Track the pending execution\n this.pendingToolExecutions.set(args.toolCallId, executionPromise)\n }\n },\n onApprovalRequest: (args: {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n }) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const messageIdForApproval =\n this.findMessageIdForToolCall(args.toolCallId) ??\n this.currentMessageId ??\n ''\n\n this.events.approvalRequested(\n streamId,\n messageIdForApproval,\n args.toolCallId,\n args.toolName,\n args.input,\n args.approvalId,\n )\n },\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => {\n this.callbacksRef.current.onCustomEvent(eventType, data, context)\n },\n },\n })\n\n this.persistor?.hydrateAsync(persistedMessages)\n }\n\n mountDevtools(): void {\n if (this.devtoolsMounted) {\n return\n }\n\n this.devtoolsMounted = true\n this.devtoolsBridge.mountWithTools(this.processor.getMessages().length)\n }\n\n /**\n * Drain a runId-less RUN_ERROR that belongs to a cleared run the client is\n * still tracking. The persistor owns the cleared-run bookkeeping; the client\n * owns the active-run / session / processing state.\n */\n private drainIgnoredRunlessChunk(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_ERROR') return\n const runId = this.persistor?.takeRunlessRunId()\n if (!runId) return\n this.activeRunIds.delete(runId)\n this.setSessionGenerating(this.activeRunIds.size > 0)\n this.resolveProcessing()\n }\n\n private updateRunLifecycle(\n chunk: StreamChunk,\n options?: { resolveProcessing?: boolean },\n ): void {\n if (chunk.type === 'RUN_STARTED') {\n const chunkRunId = getChunkRunId(chunk) ?? chunk.runId\n this.activeRunIds.add(chunkRunId)\n this.persistor?.onRunStarted(chunkRunId)\n this.setSessionGenerating(true)\n return\n }\n\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') {\n return\n }\n\n const runId = getChunkRunId(chunk)\n if (runId) {\n this.activeRunIds.delete(runId)\n this.persistor?.onRunSettled(runId)\n } else if (chunk.type === 'RUN_ERROR') {\n // RUN_ERROR without runId is a session-level error; clear all runs.\n this.activeRunIds.clear()\n this.persistor?.onSessionRunError()\n }\n this.setSessionGenerating(this.activeRunIds.size > 0)\n if (options?.resolveProcessing !== false) {\n this.resolveProcessing()\n }\n }\n\n private generateUniqueId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n\n private setIsLoading(isLoading: boolean): void {\n this.isLoading = isLoading\n this.callbacksRef.current.onLoadingChange(isLoading)\n this.events.loadingChanged(isLoading)\n }\n\n private setStatus(status: ChatClientState): void {\n this.status = status\n this.callbacksRef.current.onStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setIsSubscribed(isSubscribed: boolean): void {\n this.isSubscribed = isSubscribed\n this.callbacksRef.current.onSubscriptionChange(isSubscribed)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setConnectionStatus(status: ConnectionStatus): void {\n this.connectionStatus = status\n this.callbacksRef.current.onConnectionStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setSessionGenerating(isGenerating: boolean): void {\n if (this.sessionGenerating === isGenerating) return\n this.sessionGenerating = isGenerating\n this.callbacksRef.current.onSessionGeneratingChange(isGenerating)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private resetSessionGenerating(): void {\n this.activeRunIds.clear()\n this.persistor?.resetIgnored()\n this.setSessionGenerating(false)\n }\n\n private setError(error: Error | undefined): void {\n this.error = error\n this.callbacksRef.current.onErrorChange(error)\n this.events.errorChanged(error?.message || null)\n }\n\n private buildDevtoolsBridgeOptions(\n devtools: ChatClientOptions['devtools'],\n ): ChatDevtoolsBridgeOptions {\n return {\n hookId: this.uniqueId,\n clientId: this.uniqueId,\n threadId: this.threadId,\n metadata: {\n hookName: devtools?.hookName ?? 'useChat',\n outputKind: devtools?.outputKind ?? 'chat',\n ...(devtools?.framework ? { framework: devtools.framework } : {}),\n ...(devtools?.name ? { name: devtools.name } : {}),\n },\n getSnapshot: () => this.getDevtoolsSnapshot(),\n getTools: () => this.clientToolsRef.current.values(),\n getMessages: () => this.processor.getMessages(),\n setMessages: (messages: Array<UIMessage>) => {\n this.processor.setMessages(messages)\n },\n addToolResult: (toolCallId, output, errorText) => {\n this.processor.addToolResult(toolCallId, output, errorText)\n },\n generateId: (prefix) => this.generateUniqueId(prefix),\n }\n }\n\n private getDevtoolsSnapshot(): AIDevtoolsChatSnapshot {\n return {\n messages: this.processor.getMessages(),\n status: this.status,\n isLoading: this.isLoading,\n isSubscribed: this.isSubscribed,\n connectionStatus: this.connectionStatus,\n sessionGenerating: this.sessionGenerating,\n activeRunIds: Array.from(this.activeRunIds),\n ...(this.error ? { error: this.error.message } : {}),\n }\n }\n\n private findMessageIdForToolCall(toolCallId: string): string | undefined {\n const messages = this.processor.getMessages()\n for (const message of messages) {\n const match = message.parts.find(\n (part: MessagePart): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === toolCallId,\n )\n if (match) return message.id\n }\n return undefined\n }\n\n private abortSubscriptionLoop(): void {\n this.subscriptionAbortController?.abort()\n this.subscriptionAbortController = null\n }\n\n private resolveProcessing(): void {\n this.processingResolve?.()\n this.processingResolve = null\n }\n\n private cancelInFlightStream(options?: {\n setReadyStatus?: boolean\n abortSubscription?: boolean\n }): void {\n this.abortController?.abort()\n this.abortController = null\n if (options?.abortSubscription) {\n this.abortSubscriptionLoop()\n }\n this.resolveProcessing()\n this.setIsLoading(false)\n if (options?.setReadyStatus) {\n this.setStatus('ready')\n }\n }\n\n private reportStreamError(error: Error): void {\n const alreadyReported =\n this.errorReportedGeneration === this.streamGeneration\n this.setError(error)\n // Preserve request-level error semantics even if a RUN_ERROR arrives\n // slightly after loading flips false during stream teardown.\n if (\n this.isLoading ||\n this.status === 'submitted' ||\n this.status === 'streaming'\n ) {\n this.setStatus('error')\n }\n if (!alreadyReported) {\n this.errorReportedGeneration = this.streamGeneration\n this.callbacksRef.current.onError(error)\n }\n }\n\n /**\n * Start the background subscription loop.\n */\n private startSubscription(): void {\n this.subscriptionAbortController = new AbortController()\n const signal = this.subscriptionAbortController.signal\n\n this.consumeSubscription(signal)\n .catch((err) => {\n if (err instanceof Error && err.name !== 'AbortError') {\n this.setConnectionStatus('error')\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.reportStreamError(err)\n }\n // Resolve pending processing so streamResponse doesn't hang\n this.resolveProcessing()\n })\n .finally(() => {\n // Ignore stale loops that were superseded by a restart.\n if (this.subscriptionAbortController?.signal !== signal) {\n return\n }\n this.subscriptionAbortController = null\n if (!signal.aborted && this.isSubscribed) {\n this.setIsSubscribed(false)\n if (this.connectionStatus !== 'error') {\n this.setConnectionStatus('disconnected')\n }\n }\n })\n }\n\n /**\n * Consume chunks from the connection subscription.\n */\n private async consumeSubscription(signal: AbortSignal): Promise<void> {\n const stream = this.connection.subscribe(signal)\n for await (const chunk of stream) {\n if (signal.aborted) break\n if (this.connectionStatus === 'connecting') {\n this.setConnectionStatus('connected')\n }\n const shouldIgnore = this.persistor?.shouldIgnoreChunk(chunk) ?? false\n if (shouldIgnore) {\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n if (getChunkRunId(chunk)) {\n this.updateRunLifecycle(chunk, { resolveProcessing: false })\n } else {\n this.drainIgnoredRunlessChunk(chunk)\n }\n }\n continue\n }\n this.callbacksRef.current.onChunk(chunk)\n this.devtoolsBridge.observeChunk(chunk)\n this.processor.processChunk(chunk)\n // Run lifecycle (active-run tracking, session-generating state, and\n // processing resolution for RUN_FINISHED / RUN_ERROR) is handled in a\n // single place so the ignored-chunk path above and this path can't\n // diverge. RUN_ERROR carries its runId via the AG-UI passthrough so a\n // per-run error only clears that run, while a runId-less RUN_ERROR is\n // treated as a session-level error that clears every active run.\n this.updateRunLifecycle(chunk)\n // Yield control back to event loop for UI updates\n await new Promise((resolve) => setTimeout(resolve, 0))\n }\n }\n\n /**\n * Ensure subscription loop is running, starting it if needed.\n */\n private ensureSubscription(): void {\n if (!this.isSubscribed) {\n this.subscribe()\n return\n }\n if (\n !this.subscriptionAbortController ||\n this.subscriptionAbortController.signal.aborted\n ) {\n this.subscribe({ restart: true })\n }\n }\n\n /**\n * Create a promise that resolves when onStreamEnd fires.\n * Used by streamResponse to await processing completion.\n */\n private waitForProcessing(): Promise<void> {\n // Resolve any stale promise (e.g., from a previous aborted request)\n this.resolveProcessing()\n return new Promise<void>((resolve) => {\n this.processingResolve = resolve\n })\n }\n\n /**\n * Send a message and stream the response.\n * Supports both simple string content and multimodal content (images, audio, video, documents).\n *\n * @param content - The message content. Can be:\n * - A simple string for text-only messages\n * - A MultimodalContent object with content array and optional custom ID\n * @param body - Optional body parameters to merge with the client's base body for this request.\n * Uses shallow merge with per-message body taking priority.\n *\n * @example\n * ```ts\n * // Simple text message\n * await client.sendMessage('Hello!')\n *\n * // Text message with custom body params\n * await client.sendMessage('Hello!', { temperature: 0.7 })\n *\n * // Multimodal message with image\n * await client.sendMessage({\n * content: [\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ]\n * })\n *\n * // Multimodal message with custom ID and body params\n * await client.sendMessage(\n * {\n * content: [\n * { type: 'text', content: 'Describe this audio' },\n * { type: 'audio', source: { type: 'data', value: 'base64...' } }\n * ],\n * id: 'custom-message-id'\n * },\n * { model: 'gpt-4-audio' }\n * )\n * ```\n */\n async sendMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n ): Promise<void> {\n this.mountDevtools()\n const emptyMessage = typeof content === 'string' && !content.trim()\n if (emptyMessage || this.isLoading) {\n return\n }\n // Normalize input to extract content, id, and validate\n const normalizedContent = this.normalizeMessageInput(content)\n\n // Store the per-message body for use in streamResponse\n this.pendingMessageBody = body\n\n // Add user message via processor\n const userMessage = this.processor.addUserMessage(\n normalizedContent.content,\n normalizedContent.id,\n )\n this.events.messageSent(userMessage.id, normalizedContent.content)\n\n await this.streamResponse()\n }\n\n /**\n * Normalize the message input to extract content and optional id.\n * Trims string content automatically.\n */\n private normalizeMessageInput(input: string | MultimodalContent): {\n content: string | Array<ContentPart>\n id?: string\n } {\n if (typeof input === 'string') {\n return { content: input.trim() }\n }\n return { content: input.content, id: input.id }\n }\n\n /**\n * Append a message and stream the response\n */\n async append(message: UIMessage | ModelMessage): Promise<void> {\n this.mountDevtools()\n // Normalize the message to ensure it has id and createdAt\n const normalizedMessage = normalizeToUIMessage(message, generateMessageId)\n\n // Skip system messages - they're handled via systemPrompts, not UIMessages\n if (normalizedMessage.role === 'system') {\n return\n }\n\n // Type assertion: after checking for system, we know it's user or assistant\n const uiMessage = normalizedMessage as UIMessage\n\n // Emit message appended event\n this.events.messageAppended(uiMessage)\n\n // Add to messages\n const messages = this.processor.getMessages()\n this.processor.setMessages([...messages, uiMessage])\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue the response for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(async () => {\n await this.streamResponse()\n })\n return\n }\n\n await this.streamResponse()\n }\n\n /**\n * Stream a response from the LLM.\n * Returns true if the stream completed successfully, false on abort or error.\n */\n private async streamResponse(): Promise<boolean> {\n // Guard against concurrent streams - if already loading, skip\n if (this.isLoading) {\n return false\n }\n\n // Track generation so a superseded stream's cleanup doesn't clobber the new one\n const generation = ++this.streamGeneration\n const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n this.currentRunId = runId\n\n this.setIsLoading(true)\n this.setStatus('submitted')\n this.setError(undefined)\n this.errorReportedGeneration = null\n this.abortController = new AbortController()\n // Capture the signal immediately so that a concurrent stop() or\n // sendMessage() that reassigns this.abortController cannot cause\n // connect() to receive a stale or null signal.\n const signal = this.abortController.signal\n // Reset pending tool executions for the new stream\n this.pendingToolExecutions.clear()\n let streamCompletedSuccessfully = false\n let activeDevtoolsRunId: string | null = null\n let runTerminalEventEmitted = false\n\n try {\n // Get UIMessages with parts (preserves approval state and client tool results)\n const messages = this.processor.getMessages()\n const clientTools = new Map(this.clientToolsRef.current)\n const runtimeContext = this.context\n\n // Call onResponse callback\n await this.callbacksRef.current.onResponse()\n\n // If the stream was cancelled during the onResponse await (e.g. stop()\n // from a callback or unmount, or reload() superseding this stream),\n // bail out before allocating waitForProcessing() — otherwise the\n // resolveProcessing() that ran during cancellation is a no-op and the\n // await processingComplete below would deadlock.\n if (signal.aborted) {\n return false\n }\n\n // Merge sources for the wire `forwardedProps` field, in priority\n // order (later spreads win):\n // 1. Legacy `body` option (deprecated).\n // 2. Canonical `forwardedProps` option (wins over `body`).\n // 3. Per-message `body` arg passed to `sendMessage` (highest).\n // The AG-UI standard `threadId` is sent at the wire's top level for\n // run/conversation correlation, so we no longer auto-emit a separate\n // `conversationId` here — `chat({ threadId })` server-side covers the\n // same role for devtools/observability.\n const mergedBody = {\n ...this.bodyOption,\n ...this.forwardedPropsOption,\n ...this.pendingMessageBody,\n }\n\n // Clear the pending message body after use\n this.pendingMessageBody = undefined\n\n // Generate stream ID — assistant message will be created by stream events\n this.currentStreamId = this.generateUniqueId('stream')\n this.devtoolsBridge.setCurrentStreamId(this.currentStreamId)\n this.currentMessageId = null\n this.activeClientTools = clientTools\n this.activeContext = runtimeContext\n\n // Reset processor stream state for new response — prevents stale\n // messageStates entries (from a previous stream) from blocking\n // creation of a new assistant message (e.g. after reload).\n this.processor.prepareAssistantMessage()\n\n // Ensure subscription loop is running\n this.ensureSubscription()\n\n // Set up promise that resolves when onStreamEnd fires\n const processingComplete = this.waitForProcessing()\n\n // Build per-send run context for AG-UI compliance\n // Note: mergedBody already contains the merged this.body + pendingMessageBody\n // (pendingMessageBody was cleared above, so we use mergedBody as forwardedProps)\n // Convert each client tool's `inputSchema` (a Standard Schema:\n // Zod, ArkType, Valibot, etc.) to JSON Schema for the wire. Foreign\n // AG-UI servers consuming `RunAgentInput.tools[].parameters` expect\n // JSON Schema; sending a Standard Schema instance directly would\n // serialize to an unusable shape.\n const runContext = {\n threadId: this.threadId,\n runId,\n clientTools: Array.from(clientTools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema\n ? convertSchemaToJsonSchema(t.inputSchema)\n : { type: 'object' },\n })),\n forwardedProps: { ...mergedBody },\n }\n this.devtoolsBridge.beginRun(runContext.runId, this.threadId)\n activeDevtoolsRunId = runContext.runId\n this.devtoolsBridge.emitRunLifecycle(\n 'run:created',\n runContext.runId,\n 'created',\n )\n this.devtoolsBridge.emitRunLifecycle(\n 'run:started',\n runContext.runId,\n 'started',\n )\n this.devtoolsBridge.emitSnapshot()\n\n // Send through normalized connection (pushes chunks to subscription queue)\n await this.connection.send(messages, mergedBody, signal, runContext)\n\n // Wait for subscription loop to finish processing all chunks\n await processingComplete\n\n // If this stream was superseded (e.g. by reload()), bail out —\n // the new stream owns the processor and processingResolve now.\n if (generation !== this.streamGeneration) {\n return false\n }\n\n // A RUN_ERROR from the stream transitions status to error.\n // Do not treat this stream as a successful completion.\n if (this.status === 'error') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n this.error ? { error: this.error.message } : {},\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n\n // Wait for pending client tool executions\n if (this.pendingToolExecutions.size > 0) {\n await Promise.all(this.pendingToolExecutions.values())\n }\n\n // Finalize (idempotent — may already be done by RUN_FINISHED handler)\n this.processor.finalizeStream()\n streamCompletedSuccessfully = true\n } catch (err) {\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n if (generation === this.streamGeneration) {\n this.reportStreamError(err)\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n { error: err.message },\n )\n runTerminalEventEmitted = true\n }\n }\n }\n } finally {\n // Only clean up if this is still the active stream.\n // A superseded stream (e.g. reload() started a new one) must not\n // clobber the new stream's abortController or isLoading state.\n if (generation === this.streamGeneration) {\n this.currentStreamId = null\n this.devtoolsBridge.setCurrentStreamId(null)\n this.currentMessageId = null\n this.currentRunId = null\n this.activeClientTools = null\n this.activeContext = undefined\n this.abortController = null\n this.setIsLoading(false)\n this.pendingMessageBody = undefined // Ensure it's cleared even on error\n\n if (activeDevtoolsRunId && !runTerminalEventEmitted) {\n if (streamCompletedSuccessfully) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:completed',\n activeDevtoolsRunId,\n 'completed',\n )\n } else if (signal.aborted) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n }\n }\n\n // Drain any actions that were queued while the stream was in progress\n await this.drainPostStreamActions()\n\n // Continue conversation if the stream ended with a tool result (server tool completed)\n // but ONLY if the model indicated it wants to continue (finishReason !== 'stop').\n // When finishReason is 'stop', the model is done — don't re-send.\n if (streamCompletedSuccessfully) {\n const messages = this.processor.getMessages()\n const lastPart = messages.at(-1)?.parts.at(-1)\n const { finishReason } = this.processor.getState()\n\n if (\n lastPart?.type === 'tool-result' &&\n finishReason !== 'stop' &&\n this.shouldAutoSend()\n ) {\n try {\n await this.checkForContinuation()\n } catch (error) {\n console.error('Failed to continue flow after tool result:', error)\n }\n } else if (this.status !== 'ready') {\n // Terminal run, but onStreamEnd never fired: the processor had no\n // assistant message to emit it for (e.g. a bare RUN_FINISHED{stop},\n // #421). The normal path already set 'ready', so this is a no-op.\n this.setStatus('ready')\n }\n }\n }\n }\n\n return streamCompletedSuccessfully\n }\n\n /**\n * Start the client subscription loop.\n * This controls the connection lifecycle independently from request lifecycle.\n */\n subscribe(options?: { restart?: boolean }): void {\n const restart = options?.restart === true\n if (this.isSubscribed && !restart) {\n return\n }\n\n if (this.isSubscribed && restart) {\n this.abortSubscriptionLoop()\n }\n\n this.setIsSubscribed(true)\n this.setConnectionStatus('connecting')\n this.startSubscription()\n }\n\n /**\n * Unsubscribe and fully tear down live behavior.\n * This aborts an in-flight request and the subscription loop.\n */\n unsubscribe(): void {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n }\n\n /**\n * Reload the last assistant message\n */\n async reload(): Promise<void> {\n const messages = this.processor.getMessages()\n if (messages.length === 0) return\n\n // Find the last user message\n const lastUserMessageIndex = messages.findLastIndex(\n (m) => m.role === 'user',\n )\n\n if (lastUserMessageIndex === -1) return\n\n // Cancel any active stream before reloading\n if (this.isLoading) {\n this.cancelInFlightStream()\n }\n\n this.events.reloaded(lastUserMessageIndex)\n\n // Remove all messages after the last user message\n this.processor.removeMessagesAfter(lastUserMessageIndex)\n this.devtoolsBridge.emitSnapshot()\n\n // Resend\n await this.streamResponse()\n }\n\n /**\n * Stop the current stream\n */\n stop(): void {\n const hadLocalStream = this.abortController !== null\n this.cancelInFlightStream({ setReadyStatus: true })\n if (hadLocalStream) {\n this.resetSessionGenerating()\n }\n this.events.stopped()\n }\n\n /**\n * Clear all messages\n */\n clear(): void {\n if (this.persistor) {\n this.persistor.snapshotClear({\n messages: this.processor.getMessages(),\n activeRunIds: this.activeRunIds,\n currentRunId: this.currentRunId,\n })\n if (this.isLoading) {\n this.cancelInFlightStream({ setReadyStatus: true })\n this.resetSessionGenerating()\n } else if (this.activeRunIds.size > 0) {\n this.resetSessionGenerating()\n }\n // Suppress persisting the empty snapshot that clearMessages emits, then\n // remove the stored conversation outright.\n this.persistor.beginClear()\n }\n this.processor.clearMessages()\n this.persistor?.remove()\n this.setError(undefined)\n this.events.messagesCleared()\n }\n\n /**\n * Add the result of a client-side tool execution\n */\n async addToolResult(result: ClientToolResult): Promise<void> {\n const clientTool = this.clientToolsRef.current.get(result.tool)\n await this.addToolResultForClientTool(result, clientTool)\n }\n\n private async addToolResultForClientTool(\n result: ClientToolResult,\n clientTool: AnyClientTool | undefined,\n context?: ChatClientRunEventContext,\n ): Promise<void> {\n if (clientTool && result.state !== 'output-error') {\n try {\n result = {\n ...result,\n output: this.validateClientToolOutput(clientTool, result.output),\n }\n } catch (error: any) {\n result = {\n ...result,\n output: null,\n state: 'output-error',\n errorText: error.message,\n }\n }\n }\n\n this.events.toolResultAdded(\n result.toolCallId,\n result.tool,\n result.output,\n result.state || 'output-available',\n context,\n )\n\n // Forward an error message only for output-error results (with a fallback so\n // a message-less `throw new Error()` still reaches the terminal 'error'\n // state); a stray errorText on a successful result must not signal an error.\n this.processor.addToolResult(\n result.toolCallId,\n result.output,\n result.state === 'output-error'\n ? result.errorText || 'Tool execution failed'\n : undefined,\n )\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() => this.checkForContinuation())\n return\n }\n\n await this.checkForContinuation()\n }\n\n private validateClientToolOutput(\n clientTool: AnyClientTool,\n output: any,\n ): any {\n if (clientTool.outputSchema && isStandardSchema(clientTool.outputSchema)) {\n return parseWithStandardSchema(clientTool.outputSchema, output)\n }\n\n return output\n }\n\n /**\n * Respond to a tool approval request\n */\n async addToolApprovalResponse(response: {\n id: string // approval.id, not toolCallId\n approved: boolean\n }): Promise<void> {\n // Find the tool call ID from the approval ID\n const messages = this.processor.getMessages()\n let foundToolCallId: string | undefined\n\n for (const msg of messages) {\n const toolCallPart = msg.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === response.id,\n )\n if (toolCallPart) {\n foundToolCallId = toolCallPart.id\n break\n }\n }\n\n if (foundToolCallId) {\n this.events.toolApprovalResponded(\n response.id,\n foundToolCallId,\n response.approved,\n )\n }\n\n // Add response via processor\n this.processor.addToolApprovalResponse(response.id, response.approved)\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() => this.checkForContinuation())\n return\n }\n\n await this.checkForContinuation()\n }\n\n /**\n * Queue an action to be executed after the current stream ends\n */\n private queuePostStreamAction(action: () => Promise<void>): void {\n this.postStreamActions.push(action)\n }\n\n /**\n * Drain and execute all queued post-stream actions\n */\n private async drainPostStreamActions(): Promise<void> {\n if (this.draining) return\n this.draining = true\n try {\n let action: (() => Promise<void>) | undefined\n while ((action = this.postStreamActions.shift()) !== undefined) {\n await action()\n }\n } finally {\n this.draining = false\n }\n }\n\n /**\n * Check if we should continue the flow and do so if needed\n */\n private async checkForContinuation(): Promise<void> {\n // Prevent duplicate continuation attempts\n if (this.continuationPending || this.isLoading) {\n this.continuationSkipped = true\n return\n }\n\n if (this.shouldAutoSend()) {\n this.continuationPending = true\n this.continuationSkipped = false\n let succeeded = false\n try {\n succeeded = await this.streamResponse()\n } finally {\n this.continuationPending = false\n }\n // If a queued check was skipped while continuationPending was true\n // (e.g. a chained approval responded to during the stream), re-evaluate\n // now that the flag is cleared. Only replay after a successful stream —\n // aborted or errored streams should not trigger further continuation.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated asynchronously during await\n if (this.continuationSkipped && succeeded) {\n this.continuationSkipped = false\n await this.checkForContinuation()\n }\n }\n }\n\n /**\n * Check if all tool calls are complete and we should auto-send.\n * Requires that there is at least one tool call in the last assistant message;\n * a text-only response has nothing to auto-send.\n */\n private shouldAutoSend(): boolean {\n const messages = this.processor.getMessages()\n const lastAssistant = messages.findLast(\n (m: UIMessage) => m.role === 'assistant',\n )\n if (!lastAssistant) return false\n const hasToolCalls = lastAssistant.parts.some(\n (p: MessagePart) => p.type === 'tool-call',\n )\n if (!hasToolCalls) return false\n return this.processor.areAllToolsComplete()\n }\n\n /**\n * Get current messages\n */\n getMessages(): Array<UIMessage<TTools>> {\n return this.processor.getMessages() as Array<UIMessage<TTools>>\n }\n\n /**\n * Get loading state\n */\n getIsLoading(): boolean {\n return this.isLoading\n }\n\n /**\n * Get current status\n */\n getStatus(): ChatClientState {\n return this.status\n }\n\n /**\n * Get whether the subscription loop is active\n */\n getIsSubscribed(): boolean {\n return this.isSubscribed\n }\n\n /**\n * Get current connection lifecycle status\n */\n getConnectionStatus(): ConnectionStatus {\n return this.connectionStatus\n }\n\n /**\n * Whether the shared session is actively generating.\n * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).\n * Unlike `isLoading` (request-local), this reflects shared generation\n * activity visible to all subscribers (e.g. across tabs/devices).\n */\n getSessionGenerating(): boolean {\n return this.sessionGenerating\n }\n\n /**\n * Get current error\n */\n getError(): Error | undefined {\n return this.error\n }\n\n /**\n * Manually set messages\n */\n setMessagesManually(messages: Array<UIMessage<TTools>>): void {\n this.processor.setMessages(messages)\n this.devtoolsBridge.emitSnapshot()\n }\n\n /**\n * Update options refs (for use in React hooks to avoid recreating client)\n */\n updateOptions(options: ChatClientUpdateOptionsWithoutContext<TTools>): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> &\n Pick<ChatClientOptions<TTools, TContext>, 'context'>,\n ): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> & {\n context?: TContext | undefined\n },\n ): void {\n if (options.connection !== undefined || options.fetcher !== undefined) {\n const wasSubscribed = this.isSubscribed\n\n if (this.isLoading) {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n } else if (wasSubscribed) {\n this.abortSubscriptionLoop()\n }\n\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n this.connection = normalizeConnectionAdapter(\n resolveTransport({\n connection: options.connection,\n fetcher: options.fetcher,\n }),\n )\n\n if (wasSubscribed) {\n this.subscribe()\n }\n }\n // Replace each wire-payload slot independently so callers can update one\n // without wiping the other. Passing `undefined` for `body` or\n // `forwardedProps` leaves that slot unchanged; context is cleared when the\n // key is present with an `undefined` value.\n if (options.body !== undefined) {\n this.bodyOption = options.body\n }\n if (options.forwardedProps !== undefined) {\n this.forwardedPropsOption = options.forwardedProps\n }\n if ('context' in options) {\n this.context = options.context\n }\n if (options.tools !== undefined) {\n this.clientToolsRef.current = new Map()\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n this.devtoolsBridge.notifyToolsChanged()\n }\n if (options.onResponse !== undefined) {\n this.callbacksRef.current.onResponse = options.onResponse\n }\n if (options.onChunk !== undefined) {\n this.callbacksRef.current.onChunk = options.onChunk\n }\n if (options.onFinish !== undefined) {\n this.callbacksRef.current.onFinish = options.onFinish\n }\n if (options.onError !== undefined) {\n this.callbacksRef.current.onError = options.onError\n }\n if (options.onSubscriptionChange !== undefined) {\n this.callbacksRef.current.onSubscriptionChange =\n options.onSubscriptionChange\n }\n if (options.onConnectionStatusChange !== undefined) {\n this.callbacksRef.current.onConnectionStatusChange =\n options.onConnectionStatusChange\n }\n if (options.onSessionGeneratingChange !== undefined) {\n this.callbacksRef.current.onSessionGeneratingChange =\n options.onSessionGeneratingChange\n }\n if (options.onCustomEvent !== undefined) {\n this.callbacksRef.current.onCustomEvent = options.onCustomEvent\n }\n }\n\n dispose(): void {\n this.unsubscribe()\n this.devtoolsBridge.dispose()\n this.devtoolsMounted = false\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-client.js","sources":["../../src/chat-client.ts"],"sourcesContent":["import {\n StreamProcessor,\n convertSchemaToJsonSchema,\n generateMessageId,\n isStandardSchema,\n normalizeToUIMessage,\n parseWithStandardSchema,\n} from '@tanstack/ai/client'\nimport { createNoOpChatDevtoolsBridge } from './devtools-noop'\nimport {\n fetcherToConnectionAdapter,\n getChunkRunId,\n normalizeConnectionAdapter,\n} from './connection-adapters'\nimport { ChatPersistor } from './client-persistor'\nimport type {\n AnyClientTool,\n ContentPart,\n ModelMessage,\n StreamChunk,\n} from '@tanstack/ai/client'\nimport type {\n ConnectionAdapter,\n SubscribeConnectionAdapter,\n} from './connection-adapters'\nimport type {\n ChatClientEventEmitter,\n ChatClientRunEventContext,\n} from './events'\nimport type {\n AIDevtoolsChatSnapshot,\n ChatDevtoolsBridge,\n ChatDevtoolsBridgeOptions,\n} from './devtools'\nimport type {\n ChatClientOptions,\n ChatClientState,\n ChatFetcher,\n ConnectionStatus,\n MessagePart,\n MultimodalContent,\n QueueBusyReason,\n QueueOption,\n QueueStrategy,\n QueuedMessage,\n SendMessageOptions,\n ToolCallPart,\n UIMessage,\n WhenBusy,\n} from './types'\n\n/** Internal queue entry — public {@link QueuedMessage} plus optional per-send body. */\ninterface InternalQueuedMessage extends QueuedMessage {\n body?: Record<string, any>\n}\n\ntype ChatClientUpdateOptionsWithoutContext<\n TTools extends ReadonlyArray<AnyClientTool>,\n> = {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n /** @deprecated Use `forwardedProps` instead. */\n body?: Record<string, any>\n forwardedProps?: Record<string, any>\n tools?: TTools\n queue?: QueueOption\n onResponse?: (response?: Response) => void | Promise<void>\n onChunk?: (chunk: StreamChunk) => void\n onFinish?: (message: UIMessage) => void\n onError?: (error: Error) => void\n onSubscriptionChange?: (isSubscribed: boolean) => void\n onConnectionStatusChange?: (status: ConnectionStatus) => void\n onSessionGeneratingChange?: (isGenerating: boolean) => void\n onQueueChange?: (queue: Array<QueuedMessage>) => void\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n}\n\ntype ClientToolResult = {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n}\n\nfunction resolveTransport(transport: {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n}): ConnectionAdapter {\n const { connection, fetcher } = transport\n if (connection && fetcher) {\n throw new Error(\n 'ChatClient: pass either `connection` or `fetcher`, not both.',\n )\n }\n if (connection) return connection\n if (fetcher) return fetcherToConnectionAdapter(fetcher)\n throw new Error('ChatClient: either `connection` or `fetcher` is required.')\n}\n\nexport interface NormalizedQueueConfig {\n whenBusy: WhenBusy\n drain: 'fifo' | 'batch'\n onOverflow: 'reject' | 'drop-oldest'\n maxSize?: number\n strategy?: QueueStrategy\n}\n\nexport function normalizeQueueOption(\n option: QueueOption | undefined,\n): NormalizedQueueConfig {\n const base: NormalizedQueueConfig = {\n whenBusy: 'queue',\n drain: 'fifo',\n onOverflow: 'reject',\n }\n if (!option) return base\n if (typeof option === 'string') return { ...base, whenBusy: option }\n if (typeof option === 'function') return { ...base, strategy: option }\n\n const maxSize = option.maxSize\n if (maxSize !== undefined) {\n if (!Number.isInteger(maxSize) || maxSize < 0) {\n throw new Error(\n 'ChatClient: queue.maxSize must be a non-negative integer',\n )\n }\n }\n\n return {\n whenBusy: option.whenBusy ?? 'queue',\n drain: option.drain ?? 'fifo',\n onOverflow: option.onOverflow ?? 'reject',\n ...(maxSize !== undefined ? { maxSize } : {}),\n }\n}\n\n/**\n * Merge a run of queued messages into a single send for `drain: 'batch'`.\n * All-string content is joined with newlines; mixed/multimodal content is\n * flattened into a single `ContentPart` array. The last item's `body` wins.\n */\nfunction mergeQueuedMessages(items: Array<InternalQueuedMessage>): {\n content: string | MultimodalContent\n body?: Record<string, any>\n} {\n const body = items.at(-1)?.body\n const stringContents: Array<string> = []\n for (const item of items) {\n if (typeof item.content !== 'string') {\n break\n }\n stringContents.push(item.content)\n }\n if (stringContents.length === items.length) {\n return {\n content: stringContents.join('\\n'),\n ...(body !== undefined ? { body } : {}),\n }\n }\n const parts: Array<ContentPart> = []\n for (const item of items) {\n if (typeof item.content === 'string') {\n parts.push({ type: 'text', content: item.content })\n } else if (typeof item.content.content === 'string') {\n parts.push({ type: 'text', content: item.content.content })\n } else {\n parts.push(...item.content.content)\n }\n }\n return {\n content: { content: parts },\n ...(body !== undefined ? { body } : {}),\n }\n}\n\nexport class ChatClient<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = unknown,\n> {\n private readonly processor: StreamProcessor\n private connection: SubscribeConnectionAdapter\n private readonly uniqueId: string\n private readonly threadId: string\n // All persistence concerns (hydrate / save / clear, plus suppression of late\n // chunks after a mid-stream clear) live in ChatPersistor so this class stays\n // focused on streaming. Undefined when no `persistence` adapter is configured.\n private readonly persistor?: ChatPersistor\n private currentRunId: string | null = null\n // Track the legacy `body` option and the canonical `forwardedProps`\n // option as separate slots so that `updateOptions({ forwardedProps })`\n // doesn't wipe a previously-set `body` (and vice versa). They are\n // merged on every send, with `forwardedProps` winning on key collision.\n private bodyOption: Record<string, any> = {}\n private forwardedPropsOption: Record<string, any> = {}\n private context: TContext | undefined = undefined\n private pendingMessageBody: Record<string, any> | undefined = undefined\n private queueConfig: NormalizedQueueConfig\n private messageQueue: Array<InternalQueuedMessage> = []\n /**\n * True from the moment `sendMessage` claims the client until its\n * `streamResponse` settles. Closes the race where concurrent callers both\n * see `isLoading === false`, both append a user message, and only one stream\n * actually runs (leaving stranded user messages with no reply).\n */\n private sendInFlight = false\n /**\n * True while `drainQueue` is delivering queued messages. Concurrent\n * `sendMessage` calls during a drain are treated as busy and follow\n * `whenBusy` (default: queue).\n */\n private messageQueueDraining = false\n /**\n * Set by `whenBusy: 'interrupt'` so an in-progress FIFO drain loop stops\n * before starting the next queued item (the interrupting send owns the client).\n */\n private stopMessageQueueDrain = false\n /**\n * Sync claim held for the duration of `deliverMessage` so concurrent\n * deliverers cannot both append a user message before only one stream runs.\n */\n private deliverClaim = false\n private isLoading = false\n private isSubscribed = false\n private error: Error | undefined = undefined\n private status: ChatClientState = 'ready'\n private connectionStatus: ConnectionStatus = 'disconnected'\n private abortController: AbortController | null = null\n private readonly clientToolsRef: { current: Map<string, AnyClientTool> }\n private readonly devtoolsBridge: ChatDevtoolsBridge\n /**\n * Alias for `this.events`. The bridge installs an\n * emitter that auto-attaches run/thread context and auto-emits a\n * snapshot after every event, so chat-client only ever calls\n * `this.events.X(...)` exactly like it did before devtools landed.\n */\n private readonly events: ChatClientEventEmitter\n private currentStreamId: string | null = null\n private currentMessageId: string | null = null\n private readonly postStreamActions: Array<() => Promise<void>> = []\n // Track pending client tool executions to await them before stream finalization\n private readonly pendingToolExecutions: Map<string, Promise<void>> = new Map()\n private activeClientTools: Map<string, AnyClientTool> | null = null\n private activeContext: TContext | undefined = undefined\n // Flag to deduplicate continuation checks during action draining\n private continuationPending = false\n private subscriptionAbortController: AbortController | null = null\n private processingResolve: (() => void) | null = null\n private errorReportedGeneration: number | null = null\n private streamGeneration = 0\n // Tracks whether a queued checkForContinuation was skipped because\n // continuationPending was true (chained approval scenario)\n private continuationSkipped = false\n private draining = false\n private sessionGenerating = false\n private readonly activeRunIds = new Set<string>()\n private devtoolsMounted = false\n\n private readonly callbacksRef: {\n current: {\n onResponse: (response?: Response) => void | Promise<void>\n onChunk: (chunk: StreamChunk) => void\n onFinish: (message: UIMessage) => void\n onError: (error: Error) => void\n onMessagesChange: (messages: Array<UIMessage>) => void\n onLoadingChange: (isLoading: boolean) => void\n onErrorChange: (error: Error | undefined) => void\n onStatusChange: (status: ChatClientState) => void\n onSubscriptionChange: (isSubscribed: boolean) => void\n onConnectionStatusChange: (status: ConnectionStatus) => void\n onSessionGeneratingChange: (isGenerating: boolean) => void\n onQueueChange: (queue: Array<QueuedMessage>) => void\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n }\n }\n\n constructor(options: ChatClientOptions<TTools, TContext>) {\n this.uniqueId = options.id || this.generateUniqueId('chat')\n this.threadId = options.threadId || this.generateUniqueId('thread')\n if (options.persistence) {\n this.persistor = new ChatPersistor(\n options.persistence,\n this.uniqueId,\n (messages) => this.processor.setMessages(messages),\n )\n }\n // Both `body` (deprecated) and `forwardedProps` populate the AG-UI\n // `RunAgentInput.forwardedProps` wire field. They are stored\n // separately so `updateOptions` can replace one without touching the\n // other; the merge happens at send time, with `forwardedProps`\n // winning on key collision.\n this.bodyOption = options.body || {}\n this.forwardedPropsOption = options.forwardedProps || {}\n this.context = options.context\n this.queueConfig = normalizeQueueOption(options.queue)\n this.connection = normalizeConnectionAdapter(resolveTransport(options))\n\n // Build client tools map\n this.clientToolsRef = { current: new Map() }\n if (options.tools) {\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n }\n\n this.devtoolsBridge = (\n options.devtoolsBridgeFactory ?? createNoOpChatDevtoolsBridge\n )(this.buildDevtoolsBridgeOptions(options.devtools))\n this.events = this.devtoolsBridge.events\n\n this.callbacksRef = {\n current: {\n onResponse: options.onResponse || (() => {}),\n onChunk: options.onChunk || (() => {}),\n onFinish: options.onFinish || (() => {}),\n onError: options.onError || (() => {}),\n onMessagesChange: options.onMessagesChange || (() => {}),\n onLoadingChange: options.onLoadingChange || (() => {}),\n onErrorChange: options.onErrorChange || (() => {}),\n onStatusChange: options.onStatusChange || (() => {}),\n onSubscriptionChange: options.onSubscriptionChange || (() => {}),\n onConnectionStatusChange:\n options.onConnectionStatusChange || (() => {}),\n onSessionGeneratingChange:\n options.onSessionGeneratingChange || (() => {}),\n onQueueChange: options.onQueueChange || (() => {}),\n onCustomEvent: options.onCustomEvent || (() => {}),\n },\n }\n\n // Create StreamProcessor with event handlers.\n // Use conditional spreads so we don't pass `undefined` into\n // `StreamProcessorOptions` fields under `exactOptionalPropertyTypes`.\n const persistedMessages = this.persistor?.readInitial()\n const initialMessages = Array.isArray(persistedMessages)\n ? persistedMessages\n : options.initialMessages\n\n this.processor = new StreamProcessor({\n ...(options.streamProcessor?.chunkStrategy\n ? { chunkStrategy: options.streamProcessor.chunkStrategy }\n : {}),\n ...(initialMessages ? { initialMessages } : {}),\n events: {\n onMessagesChange: (messages: Array<UIMessage>) => {\n this.persistor?.notifyMessagesChanged(messages)\n this.callbacksRef.current.onMessagesChange(messages)\n },\n onStreamStart: () => {\n this.setStatus('streaming')\n const assistantMessageId =\n this.processor.getCurrentAssistantMessageId()\n if (!assistantMessageId) {\n return\n }\n const messages = this.processor.getMessages()\n const assistantMessage = messages.find(\n (m: UIMessage) => m.id === assistantMessageId,\n )\n if (assistantMessage) {\n this.currentMessageId = assistantMessage.id\n this.events.messageAppended(\n assistantMessage,\n this.currentStreamId || undefined,\n )\n }\n },\n onStreamEnd: (message: UIMessage) => {\n this.callbacksRef.current.onFinish(message)\n this.setStatus('ready')\n // Resolve the processing-complete promise so streamResponse can continue\n this.resolveProcessing()\n },\n onError: (error: Error) => {\n this.reportStreamError(error)\n },\n onTextUpdate: (messageId: string, content: string) => {\n // Emit text update to devtools\n if (this.currentStreamId) {\n this.events.textUpdated(this.currentStreamId, messageId, content)\n }\n },\n onThinkingUpdate: (messageId: string, content: string) => {\n // Emit thinking update to devtools\n if (this.currentStreamId) {\n this.events.thinkingUpdated(\n this.currentStreamId,\n messageId,\n content,\n undefined,\n )\n }\n },\n onStructuredOutputChange: (args) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const eventName =\n args.phase === 'start'\n ? 'structured-output:started'\n : args.phase === 'complete'\n ? 'structured-output:completed'\n : args.phase === 'error'\n ? 'structured-output:errored'\n : 'structured-output:updated'\n\n this.currentMessageId = args.messageId\n this.events.structuredOutputChanged(\n eventName,\n streamId,\n args.messageId,\n {\n status: args.status,\n raw: args.raw,\n ...(args.partial !== undefined ? { partial: args.partial } : {}),\n ...(args.data !== undefined ? { data: args.data } : {}),\n ...(args.reasoning !== undefined\n ? { reasoning: args.reasoning }\n : {}),\n ...(args.errorMessage !== undefined\n ? { errorMessage: args.errorMessage }\n : {}),\n ...(args.delta !== undefined ? { delta: args.delta } : {}),\n },\n )\n },\n onToolCallStateChange: (\n messageId: string,\n toolCallId: string,\n state: string,\n args: string,\n ) => {\n // Get the tool name from the messages\n const messages = this.processor.getMessages()\n const message = messages.find((m: UIMessage) => m.id === messageId)\n const toolCallPart = message?.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.id === toolCallId,\n )\n const toolName = toolCallPart?.name || 'unknown'\n\n // Emit tool call state change to devtools\n if (this.currentStreamId) {\n this.events.toolCallStateChanged(\n this.currentStreamId,\n messageId,\n toolCallId,\n toolName,\n state,\n args,\n )\n }\n },\n onToolCall: (args: {\n toolCallId: string\n toolName: string\n input: any\n }) => {\n // Handle client-side tool execution automatically\n const clientTools =\n this.activeClientTools ?? this.clientToolsRef.current\n const clientTool = clientTools.get(args.toolName)\n const executeFunc = clientTool?.execute\n if (executeFunc) {\n // Capture the run context at execution-start so a tool whose\n // result lands AFTER the originating run finishes still reports\n // back against the originating run, not whatever run is\n // current when the result emits.\n const runEventContext =\n this.devtoolsBridge.getCurrentRunEventContext()\n // Create and track the execution promise\n const executionPromise = (async () => {\n try {\n const context =\n this.activeClientTools === null\n ? this.context\n : this.activeContext\n const output = await executeFunc(args.input, {\n toolCallId: args.toolCallId,\n context: context as TContext,\n emitCustomEvent: () => {},\n })\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output,\n state: 'output-available',\n },\n clientTool,\n runEventContext,\n )\n } catch (error: any) {\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output: null,\n state: 'output-error',\n errorText: error.message,\n },\n clientTool,\n runEventContext,\n )\n } finally {\n // Remove from pending when complete\n this.pendingToolExecutions.delete(args.toolCallId)\n }\n })()\n\n // Track the pending execution\n this.pendingToolExecutions.set(args.toolCallId, executionPromise)\n }\n },\n onApprovalRequest: (args: {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n }) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const messageIdForApproval =\n this.findMessageIdForToolCall(args.toolCallId) ??\n this.currentMessageId ??\n ''\n\n this.events.approvalRequested(\n streamId,\n messageIdForApproval,\n args.toolCallId,\n args.toolName,\n args.input,\n args.approvalId,\n )\n },\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => {\n this.callbacksRef.current.onCustomEvent(eventType, data, context)\n },\n },\n })\n\n this.persistor?.hydrateAsync(persistedMessages)\n }\n\n mountDevtools(): void {\n if (this.devtoolsMounted) {\n return\n }\n\n this.devtoolsMounted = true\n this.devtoolsBridge.mountWithTools(this.processor.getMessages().length)\n }\n\n /**\n * Drain a runId-less RUN_ERROR that belongs to a cleared run the client is\n * still tracking. The persistor owns the cleared-run bookkeeping; the client\n * owns the active-run / session / processing state.\n */\n private drainIgnoredRunlessChunk(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_ERROR') return\n const runId = this.persistor?.takeRunlessRunId()\n if (!runId) return\n this.activeRunIds.delete(runId)\n this.setSessionGenerating(this.activeRunIds.size > 0)\n this.resolveProcessing()\n }\n\n private updateRunLifecycle(\n chunk: StreamChunk,\n options?: { resolveProcessing?: boolean },\n ): void {\n if (chunk.type === 'RUN_STARTED') {\n const chunkRunId = getChunkRunId(chunk) ?? chunk.runId\n this.activeRunIds.add(chunkRunId)\n this.persistor?.onRunStarted(chunkRunId)\n this.setSessionGenerating(true)\n return\n }\n\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') {\n return\n }\n\n const runId = getChunkRunId(chunk)\n if (runId) {\n this.activeRunIds.delete(runId)\n this.persistor?.onRunSettled(runId)\n } else if (chunk.type === 'RUN_ERROR') {\n // RUN_ERROR without runId is a session-level error; clear all runs.\n this.activeRunIds.clear()\n this.persistor?.onSessionRunError()\n }\n this.setSessionGenerating(this.activeRunIds.size > 0)\n if (options?.resolveProcessing !== false) {\n this.resolveProcessing()\n }\n }\n\n private generateUniqueId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n\n private setIsLoading(isLoading: boolean): void {\n this.isLoading = isLoading\n this.callbacksRef.current.onLoadingChange(isLoading)\n this.events.loadingChanged(isLoading)\n }\n\n private setStatus(status: ChatClientState): void {\n this.status = status\n this.callbacksRef.current.onStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setIsSubscribed(isSubscribed: boolean): void {\n this.isSubscribed = isSubscribed\n this.callbacksRef.current.onSubscriptionChange(isSubscribed)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setConnectionStatus(status: ConnectionStatus): void {\n this.connectionStatus = status\n this.callbacksRef.current.onConnectionStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setSessionGenerating(isGenerating: boolean): void {\n if (this.sessionGenerating === isGenerating) return\n this.sessionGenerating = isGenerating\n this.callbacksRef.current.onSessionGeneratingChange(isGenerating)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private resetSessionGenerating(): void {\n this.activeRunIds.clear()\n this.persistor?.resetIgnored()\n this.setSessionGenerating(false)\n }\n\n private setError(error: Error | undefined): void {\n this.error = error\n this.callbacksRef.current.onErrorChange(error)\n this.events.errorChanged(error?.message || null)\n }\n\n private buildDevtoolsBridgeOptions(\n devtools: ChatClientOptions['devtools'],\n ): ChatDevtoolsBridgeOptions {\n return {\n hookId: this.uniqueId,\n clientId: this.uniqueId,\n threadId: this.threadId,\n metadata: {\n hookName: devtools?.hookName ?? 'useChat',\n outputKind: devtools?.outputKind ?? 'chat',\n ...(devtools?.framework ? { framework: devtools.framework } : {}),\n ...(devtools?.name ? { name: devtools.name } : {}),\n },\n getSnapshot: () => this.getDevtoolsSnapshot(),\n getTools: () => this.clientToolsRef.current.values(),\n getMessages: () => this.processor.getMessages(),\n setMessages: (messages: Array<UIMessage>) => {\n this.processor.setMessages(messages)\n },\n addToolResult: (toolCallId, output, errorText) => {\n this.processor.addToolResult(toolCallId, output, errorText)\n },\n generateId: (prefix) => this.generateUniqueId(prefix),\n }\n }\n\n private getDevtoolsSnapshot(): AIDevtoolsChatSnapshot {\n return {\n messages: this.processor.getMessages(),\n status: this.status,\n isLoading: this.isLoading,\n isSubscribed: this.isSubscribed,\n connectionStatus: this.connectionStatus,\n sessionGenerating: this.sessionGenerating,\n activeRunIds: Array.from(this.activeRunIds),\n queue: this.getQueue(),\n ...(this.error ? { error: this.error.message } : {}),\n }\n }\n\n private findMessageIdForToolCall(toolCallId: string): string | undefined {\n const messages = this.processor.getMessages()\n for (const message of messages) {\n const match = message.parts.find(\n (part: MessagePart): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === toolCallId,\n )\n if (match) return message.id\n }\n return undefined\n }\n\n private abortSubscriptionLoop(): void {\n this.subscriptionAbortController?.abort()\n this.subscriptionAbortController = null\n }\n\n private resolveProcessing(): void {\n this.processingResolve?.()\n this.processingResolve = null\n }\n\n private cancelInFlightStream(options?: {\n setReadyStatus?: boolean\n abortSubscription?: boolean\n }): void {\n this.abortController?.abort()\n this.abortController = null\n if (options?.abortSubscription) {\n this.abortSubscriptionLoop()\n }\n this.resolveProcessing()\n this.setIsLoading(false)\n // Release deliver claim so an interrupting `deliverMessage` can append\n // after abort (the superseded deliver's finally also clears the claim).\n this.deliverClaim = false\n if (options?.setReadyStatus) {\n this.setStatus('ready')\n }\n }\n\n private reportStreamError(error: Error): void {\n const alreadyReported =\n this.errorReportedGeneration === this.streamGeneration\n this.setError(error)\n // Preserve request-level error semantics even if a RUN_ERROR arrives\n // slightly after loading flips false during stream teardown.\n if (\n this.isLoading ||\n this.status === 'submitted' ||\n this.status === 'streaming'\n ) {\n this.setStatus('error')\n }\n if (!alreadyReported) {\n this.errorReportedGeneration = this.streamGeneration\n this.callbacksRef.current.onError(error)\n }\n }\n\n /**\n * Start the background subscription loop.\n */\n private startSubscription(): void {\n this.subscriptionAbortController = new AbortController()\n const signal = this.subscriptionAbortController.signal\n\n this.consumeSubscription(signal)\n .catch((err) => {\n if (err instanceof Error && err.name !== 'AbortError') {\n this.setConnectionStatus('error')\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.reportStreamError(err)\n }\n // Resolve pending processing so streamResponse doesn't hang\n this.resolveProcessing()\n })\n .finally(() => {\n // Ignore stale loops that were superseded by a restart.\n if (this.subscriptionAbortController?.signal !== signal) {\n return\n }\n this.subscriptionAbortController = null\n if (!signal.aborted && this.isSubscribed) {\n this.setIsSubscribed(false)\n if (this.connectionStatus !== 'error') {\n this.setConnectionStatus('disconnected')\n }\n }\n })\n }\n\n /**\n * Consume chunks from the connection subscription.\n */\n private async consumeSubscription(signal: AbortSignal): Promise<void> {\n const stream = this.connection.subscribe(signal)\n for await (const chunk of stream) {\n if (signal.aborted) break\n if (this.connectionStatus === 'connecting') {\n this.setConnectionStatus('connected')\n }\n const shouldIgnore = this.persistor?.shouldIgnoreChunk(chunk) ?? false\n if (shouldIgnore) {\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n if (getChunkRunId(chunk)) {\n this.updateRunLifecycle(chunk, { resolveProcessing: false })\n } else {\n this.drainIgnoredRunlessChunk(chunk)\n }\n }\n continue\n }\n this.callbacksRef.current.onChunk(chunk)\n this.devtoolsBridge.observeChunk(chunk)\n this.processor.processChunk(chunk)\n // Run lifecycle (active-run tracking, session-generating state, and\n // processing resolution for RUN_FINISHED / RUN_ERROR) is handled in a\n // single place so the ignored-chunk path above and this path can't\n // diverge. RUN_ERROR carries its runId via the AG-UI passthrough so a\n // per-run error only clears that run, while a runId-less RUN_ERROR is\n // treated as a session-level error that clears every active run.\n this.updateRunLifecycle(chunk)\n // Yield control back to event loop for UI updates\n await new Promise((resolve) => setTimeout(resolve, 0))\n }\n }\n\n /**\n * Ensure subscription loop is running, starting it if needed.\n */\n private ensureSubscription(): void {\n if (!this.isSubscribed) {\n this.subscribe()\n return\n }\n if (\n !this.subscriptionAbortController ||\n this.subscriptionAbortController.signal.aborted\n ) {\n this.subscribe({ restart: true })\n }\n }\n\n /**\n * Create a promise that resolves when onStreamEnd fires.\n * Used by streamResponse to await processing completion.\n */\n private waitForProcessing(): Promise<void> {\n // Resolve any stale promise (e.g., from a previous aborted request)\n this.resolveProcessing()\n return new Promise<void>((resolve) => {\n this.processingResolve = resolve\n })\n }\n\n /**\n * Send a message and stream the response.\n * Supports both simple string content and multimodal content (images, audio, video, documents).\n *\n * @param content - The message content. Can be:\n * - A simple string for text-only messages\n * - A MultimodalContent object with content array and optional custom ID\n * @param body - Optional body parameters to merge with the client's base body for this request.\n * Uses shallow merge with per-message body taking priority.\n * @param sendOptions - Per-call overrides, e.g. `{ whenBusy: 'interrupt' }` to\n * override the configured queue policy for this one send.\n *\n * @example\n * ```ts\n * // Simple text message\n * await client.sendMessage('Hello!')\n *\n * // Text message with custom body params\n * await client.sendMessage('Hello!', { temperature: 0.7 })\n *\n * // Per-call whenBusy override (body must still be the 2nd arg on ChatClient)\n * await client.sendMessage('Urgent', undefined, { whenBusy: 'interrupt' })\n *\n * // Multimodal message with image\n * await client.sendMessage({\n * content: [\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ]\n * })\n *\n * // Multimodal message with custom ID and body params\n * await client.sendMessage(\n * {\n * content: [\n * { type: 'text', content: 'Describe this audio' },\n * { type: 'audio', source: { type: 'data', value: 'base64...' } }\n * ],\n * id: 'custom-message-id'\n * },\n * { model: 'gpt-4-audio' }\n * )\n * ```\n */\n async sendMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n sendOptions?: SendMessageOptions,\n ): Promise<void> {\n this.mountDevtools()\n const emptyMessage = typeof content === 'string' && !content.trim()\n if (emptyMessage) {\n return\n }\n\n if (this.isSendBusy()) {\n const { action, id } = this.decideWhenBusy(content, sendOptions)\n if (action === 'drop') {\n return\n }\n if (action === 'queue') {\n this.enqueueMessage(content, body, id)\n return\n }\n // 'interrupt': abort the current stream, then send now.\n // Unlike stop(), does not flush already-queued messages — they drain\n // after this interrupting send settles successfully.\n // Claim sendInFlight *before* cancelling so a concurrent send cannot\n // slip in between cancel and the deliver below.\n this.stopMessageQueueDrain = true\n this.sendInFlight = true\n this.cancelInFlightStream({ setReadyStatus: true })\n this.resetSessionGenerating()\n } else {\n this.sendInFlight = true\n }\n\n try {\n await this.deliverMessage(content, body)\n } finally {\n this.sendInFlight = false\n }\n }\n\n /** True while a stream is active, a send is claiming the client, or the queue is draining. */\n private isSendBusy(): boolean {\n return this.isLoading || this.sendInFlight || this.messageQueueDraining\n }\n\n private resolveBusyReason(): QueueBusyReason {\n if (this.isLoading) return 'streaming'\n if (this.messageQueueDraining) return 'draining'\n return 'sendInFlight'\n }\n\n /**\n * Append a user message and run the stream. Used by both direct sends and\n * queue drains — callers are responsible for busy/queue policy.\n *\n * Claims delivery synchronously before appending so concurrent callers\n * cannot both add a user message when only one stream can run.\n */\n private async deliverMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n ): Promise<boolean> {\n if (this.isLoading || this.deliverClaim) {\n return false\n }\n this.deliverClaim = true\n try {\n const normalizedContent = this.normalizeMessageInput(content)\n this.pendingMessageBody = body\n const userMessage = this.processor.addUserMessage(\n normalizedContent.content,\n normalizedContent.id,\n )\n this.events.messageSent(userMessage.id, normalizedContent.content)\n return await this.streamResponse()\n } finally {\n this.deliverClaim = false\n }\n }\n\n /**\n * Resolve the effective action for a send that arrives while busy.\n * The returned `id` is the id that will be stored if the action is `queue`.\n */\n private decideWhenBusy(\n content: string | MultimodalContent,\n sendOptions?: SendMessageOptions,\n ): { action: WhenBusy; id: string } {\n const id = this.generateUniqueId('queued')\n if (sendOptions?.whenBusy) {\n return { action: sendOptions.whenBusy, id }\n }\n const { strategy, whenBusy } = this.queueConfig\n if (strategy) {\n const { action } = strategy({\n pending: {\n id,\n content,\n createdAt: Date.now(),\n },\n busyReason: this.resolveBusyReason(),\n queued: this.getQueue(),\n })\n return { action, id }\n }\n return { action: whenBusy, id }\n }\n\n private enqueueMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n id?: string,\n ): void {\n const { maxSize, onOverflow } = this.queueConfig\n if (maxSize !== undefined && this.messageQueue.length >= maxSize) {\n // maxSize 0 is a hard cap (never queue). drop-oldest cannot make room.\n if (onOverflow === 'reject' || maxSize === 0) {\n return\n }\n this.messageQueue.shift() // drop-oldest\n }\n this.messageQueue.push({\n id: id ?? this.generateUniqueId('queued'),\n content,\n createdAt: Date.now(),\n ...(body !== undefined ? { body } : {}),\n })\n this.emitQueueChange()\n }\n\n /**\n * Normalize the message input to extract content and optional id.\n * Trims string content automatically.\n */\n private normalizeMessageInput(input: string | MultimodalContent): {\n content: string | Array<ContentPart>\n id?: string\n } {\n if (typeof input === 'string') {\n return { content: input.trim() }\n }\n return { content: input.content, id: input.id }\n }\n\n /**\n * Append a message and stream the response\n */\n async append(message: UIMessage | ModelMessage): Promise<void> {\n this.mountDevtools()\n // Normalize the message to ensure it has id and createdAt\n const normalizedMessage = normalizeToUIMessage(message, generateMessageId)\n\n // Skip system messages - they're handled via systemPrompts, not UIMessages\n if (normalizedMessage.role === 'system') {\n return\n }\n\n // Type assertion: after checking for system, we know it's user or assistant\n const uiMessage = normalizedMessage as UIMessage\n\n // Emit message appended event\n this.events.messageAppended(uiMessage)\n\n // Add to messages\n const messages = this.processor.getMessages()\n this.processor.setMessages([...messages, uiMessage])\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue the response for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(async () => {\n await this.streamResponse()\n })\n return\n }\n\n await this.streamResponse()\n }\n\n /**\n * Stream a response from the LLM.\n * Returns true if the stream completed successfully, false on abort or error.\n */\n private async streamResponse(): Promise<boolean> {\n // Guard against concurrent streams - if already loading, skip\n if (this.isLoading) {\n return false\n }\n\n // Track generation so a superseded stream's cleanup doesn't clobber the new one\n const generation = ++this.streamGeneration\n const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n this.currentRunId = runId\n\n this.setIsLoading(true)\n // Hand off from deliverClaim to isLoading so nested drain can call\n // deliverMessage after this stream settles (while the outer deliver\n // is still on the stack).\n this.deliverClaim = false\n this.setStatus('submitted')\n this.setError(undefined)\n this.errorReportedGeneration = null\n this.abortController = new AbortController()\n // Capture the signal immediately so that a concurrent stop() or\n // sendMessage() that reassigns this.abortController cannot cause\n // connect() to receive a stale or null signal.\n const signal = this.abortController.signal\n // Reset pending tool executions for the new stream\n this.pendingToolExecutions.clear()\n let streamCompletedSuccessfully = false\n let activeDevtoolsRunId: string | null = null\n let runTerminalEventEmitted = false\n\n try {\n // Get UIMessages with parts (preserves approval state and client tool results)\n const messages = this.processor.getMessages()\n const clientTools = new Map(this.clientToolsRef.current)\n const runtimeContext = this.context\n\n // Call onResponse callback\n await this.callbacksRef.current.onResponse()\n\n // If the stream was cancelled during the onResponse await (e.g. stop()\n // from a callback or unmount, or reload() superseding this stream),\n // bail out before allocating waitForProcessing() — otherwise the\n // resolveProcessing() that ran during cancellation is a no-op and the\n // await processingComplete below would deadlock.\n if (signal.aborted) {\n return false\n }\n\n // Merge sources for the wire `forwardedProps` field, in priority\n // order (later spreads win):\n // 1. Legacy `body` option (deprecated).\n // 2. Canonical `forwardedProps` option (wins over `body`).\n // 3. Per-message `body` arg passed to `sendMessage` (highest).\n // The AG-UI standard `threadId` is sent at the wire's top level for\n // run/conversation correlation, so we no longer auto-emit a separate\n // `conversationId` here — `chat({ threadId })` server-side covers the\n // same role for devtools/observability.\n const mergedBody = {\n ...this.bodyOption,\n ...this.forwardedPropsOption,\n ...this.pendingMessageBody,\n }\n\n // Clear the pending message body after use\n this.pendingMessageBody = undefined\n\n // Generate stream ID — assistant message will be created by stream events\n this.currentStreamId = this.generateUniqueId('stream')\n this.devtoolsBridge.setCurrentStreamId(this.currentStreamId)\n this.currentMessageId = null\n this.activeClientTools = clientTools\n this.activeContext = runtimeContext\n\n // Reset processor stream state for new response — prevents stale\n // messageStates entries (from a previous stream) from blocking\n // creation of a new assistant message (e.g. after reload).\n this.processor.prepareAssistantMessage()\n\n // Ensure subscription loop is running\n this.ensureSubscription()\n\n // Set up promise that resolves when onStreamEnd fires\n const processingComplete = this.waitForProcessing()\n\n // Build per-send run context for AG-UI compliance\n // Note: mergedBody already contains the merged this.body + pendingMessageBody\n // (pendingMessageBody was cleared above, so we use mergedBody as forwardedProps)\n // Convert each client tool's `inputSchema` (a Standard Schema:\n // Zod, ArkType, Valibot, etc.) to JSON Schema for the wire. Foreign\n // AG-UI servers consuming `RunAgentInput.tools[].parameters` expect\n // JSON Schema; sending a Standard Schema instance directly would\n // serialize to an unusable shape.\n const runContext = {\n threadId: this.threadId,\n runId,\n clientTools: Array.from(clientTools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema\n ? convertSchemaToJsonSchema(t.inputSchema)\n : { type: 'object' },\n })),\n forwardedProps: { ...mergedBody },\n }\n this.devtoolsBridge.beginRun(runContext.runId, this.threadId)\n activeDevtoolsRunId = runContext.runId\n this.devtoolsBridge.emitRunLifecycle(\n 'run:created',\n runContext.runId,\n 'created',\n )\n this.devtoolsBridge.emitRunLifecycle(\n 'run:started',\n runContext.runId,\n 'started',\n )\n this.devtoolsBridge.emitSnapshot()\n\n // Send through normalized connection (pushes chunks to subscription queue)\n await this.connection.send(messages, mergedBody, signal, runContext)\n\n // Wait for subscription loop to finish processing all chunks\n await processingComplete\n\n // If this stream was superseded (e.g. by reload()), bail out —\n // the new stream owns the processor and processingResolve now.\n if (generation !== this.streamGeneration) {\n return false\n }\n\n // A RUN_ERROR from the stream transitions status to error.\n // Do not treat this stream as a successful completion.\n if (this.status === 'error') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n this.error ? { error: this.error.message } : {},\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n\n // Wait for pending client tool executions\n if (this.pendingToolExecutions.size > 0) {\n await Promise.all(this.pendingToolExecutions.values())\n }\n\n // Finalize (idempotent — may already be done by RUN_FINISHED handler)\n this.processor.finalizeStream()\n streamCompletedSuccessfully = true\n } catch (err) {\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n if (generation === this.streamGeneration) {\n this.reportStreamError(err)\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n { error: err.message },\n )\n runTerminalEventEmitted = true\n }\n }\n }\n } finally {\n // Only clean up if this is still the active stream.\n // A superseded stream (e.g. reload() started a new one) must not\n // clobber the new stream's abortController or isLoading state.\n if (generation === this.streamGeneration) {\n this.currentStreamId = null\n this.devtoolsBridge.setCurrentStreamId(null)\n this.currentMessageId = null\n this.currentRunId = null\n this.activeClientTools = null\n this.activeContext = undefined\n this.abortController = null\n this.setIsLoading(false)\n this.pendingMessageBody = undefined // Ensure it's cleared even on error\n\n if (activeDevtoolsRunId && !runTerminalEventEmitted) {\n if (streamCompletedSuccessfully) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:completed',\n activeDevtoolsRunId,\n 'completed',\n )\n } else if (signal.aborted) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n }\n }\n\n // Drain any actions that were queued while the stream was in progress\n await this.drainPostStreamActions()\n\n // Continue conversation if the stream ended with a tool result (server tool completed)\n // but ONLY if the model indicated it wants to continue (finishReason !== 'stop').\n // When finishReason is 'stop', the model is done — don't re-send.\n if (streamCompletedSuccessfully) {\n const messages = this.processor.getMessages()\n const lastPart = messages.at(-1)?.parts.at(-1)\n const { finishReason } = this.processor.getState()\n\n if (\n lastPart?.type === 'tool-result' &&\n finishReason !== 'stop' &&\n this.shouldAutoSend()\n ) {\n try {\n await this.checkForContinuation()\n } catch (error) {\n console.error('Failed to continue flow after tool result:', error)\n // Continuation failed without starting a new stream — don't\n // leave queued user messages stranded forever. (isLoading is\n // already false in this finally block.)\n await this.drainQueue()\n }\n } else {\n if (this.status !== 'ready') {\n // Terminal run, but onStreamEnd never fired: the processor had\n // no assistant message to emit it for (e.g. a bare\n // RUN_FINISHED{stop}, #421). The normal path already set\n // 'ready', so this is a no-op.\n this.setStatus('ready')\n }\n // Auto-send queued messages once the run fully settles. When a\n // continuation runs instead (tool-result branch above), that\n // continuation's own finally drains the queue. Skip if a drain\n // loop is already walking the queue (avoids nested re-entry).\n if (!this.messageQueueDraining) {\n await this.drainQueue()\n }\n }\n } else {\n // Error/abort settle for the active generation: don't strand or\n // later mis-order queued messages. A failed turn flushes the queue\n // (consistent with stop()); it must NOT auto-drain into a likely\n // broken endpoint.\n this.flushQueue()\n }\n }\n }\n\n return streamCompletedSuccessfully\n }\n\n /**\n * Start the client subscription loop.\n * This controls the connection lifecycle independently from request lifecycle.\n */\n subscribe(options?: { restart?: boolean }): void {\n const restart = options?.restart === true\n if (this.isSubscribed && !restart) {\n return\n }\n\n if (this.isSubscribed && restart) {\n this.abortSubscriptionLoop()\n }\n\n this.setIsSubscribed(true)\n this.setConnectionStatus('connecting')\n this.startSubscription()\n }\n\n /**\n * Unsubscribe and fully tear down live behavior.\n * This aborts an in-flight request and the subscription loop.\n */\n unsubscribe(): void {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n this.discardPendingSends()\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n }\n\n /**\n * Reload the last assistant message\n */\n async reload(): Promise<void> {\n const messages = this.processor.getMessages()\n if (messages.length === 0) return\n\n // Find the last user message\n const lastUserMessageIndex = messages.findLastIndex(\n (m) => m.role === 'user',\n )\n\n if (lastUserMessageIndex === -1) return\n\n // Cancel any active stream before reloading\n if (this.isLoading) {\n this.cancelInFlightStream()\n }\n // Discard pending follow-ups so \"regenerate last answer\" does not also\n // auto-send messages that were typed during the previous stream.\n this.discardPendingSends()\n\n this.events.reloaded(lastUserMessageIndex)\n\n // Remove all messages after the last user message\n this.processor.removeMessagesAfter(lastUserMessageIndex)\n this.devtoolsBridge.emitSnapshot()\n\n // Resend\n await this.streamResponse()\n }\n\n /**\n * Stop the current stream\n */\n stop(): void {\n const hadLocalStream = this.abortController !== null\n this.cancelInFlightStream({ setReadyStatus: true })\n this.discardPendingSends()\n if (hadLocalStream) {\n this.resetSessionGenerating()\n }\n this.events.stopped()\n }\n\n /**\n * Clear all messages\n */\n clear(): void {\n if (this.persistor) {\n this.persistor.snapshotClear({\n messages: this.processor.getMessages(),\n activeRunIds: this.activeRunIds,\n currentRunId: this.currentRunId,\n })\n if (this.isLoading) {\n this.cancelInFlightStream({ setReadyStatus: true })\n this.resetSessionGenerating()\n } else if (this.activeRunIds.size > 0) {\n this.resetSessionGenerating()\n }\n // Suppress persisting the empty snapshot that clearMessages emits, then\n // remove the stored conversation outright.\n this.persistor.beginClear()\n }\n this.processor.clearMessages()\n this.discardPendingSends()\n this.persistor?.remove()\n this.setError(undefined)\n this.events.messagesCleared()\n }\n\n /**\n * Add the result of a client-side tool execution\n */\n async addToolResult(result: ClientToolResult): Promise<void> {\n const clientTool = this.clientToolsRef.current.get(result.tool)\n await this.addToolResultForClientTool(result, clientTool)\n }\n\n private async addToolResultForClientTool(\n result: ClientToolResult,\n clientTool: AnyClientTool | undefined,\n context?: ChatClientRunEventContext,\n ): Promise<void> {\n if (clientTool && result.state !== 'output-error') {\n try {\n result = {\n ...result,\n output: this.validateClientToolOutput(clientTool, result.output),\n }\n } catch (error: any) {\n result = {\n ...result,\n output: null,\n state: 'output-error',\n errorText: error.message,\n }\n }\n }\n\n this.events.toolResultAdded(\n result.toolCallId,\n result.tool,\n result.output,\n result.state || 'output-available',\n context,\n )\n\n // Forward an error message only for output-error results (with a fallback so\n // a message-less `throw new Error()` still reaches the terminal 'error'\n // state); a stray errorText on a successful result must not signal an error.\n this.processor.addToolResult(\n result.toolCallId,\n result.output,\n result.state === 'output-error'\n ? result.errorText || 'Tool execution failed'\n : undefined,\n )\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() => this.checkForContinuation())\n return\n }\n\n await this.checkForContinuation()\n }\n\n private validateClientToolOutput(\n clientTool: AnyClientTool,\n output: any,\n ): any {\n if (clientTool.outputSchema && isStandardSchema(clientTool.outputSchema)) {\n return parseWithStandardSchema(clientTool.outputSchema, output)\n }\n\n return output\n }\n\n /**\n * Respond to a tool approval request\n */\n async addToolApprovalResponse(response: {\n id: string // approval.id, not toolCallId\n approved: boolean\n }): Promise<void> {\n // Find the tool call ID from the approval ID\n const messages = this.processor.getMessages()\n let foundToolCallId: string | undefined\n\n for (const msg of messages) {\n const toolCallPart = msg.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === response.id,\n )\n if (toolCallPart) {\n foundToolCallId = toolCallPart.id\n break\n }\n }\n\n if (foundToolCallId) {\n this.events.toolApprovalResponded(\n response.id,\n foundToolCallId,\n response.approved,\n )\n }\n\n // Add response via processor\n this.processor.addToolApprovalResponse(response.id, response.approved)\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() => this.checkForContinuation())\n return\n }\n\n await this.checkForContinuation()\n }\n\n /**\n * Queue an action to be executed after the current stream ends\n */\n private queuePostStreamAction(action: () => Promise<void>): void {\n this.postStreamActions.push(action)\n }\n\n /**\n * Drain and execute all queued post-stream actions\n */\n private async drainPostStreamActions(): Promise<void> {\n if (this.draining) return\n this.draining = true\n try {\n let action: (() => Promise<void>) | undefined\n while ((action = this.postStreamActions.shift()) !== undefined) {\n await action()\n }\n } finally {\n this.draining = false\n }\n }\n\n /**\n * Check if we should continue the flow and do so if needed\n */\n private async checkForContinuation(): Promise<void> {\n // Prevent duplicate continuation attempts\n if (this.continuationPending || this.isLoading) {\n this.continuationSkipped = true\n return\n }\n\n if (this.shouldAutoSend()) {\n this.continuationPending = true\n this.continuationSkipped = false\n let succeeded = false\n try {\n succeeded = await this.streamResponse()\n } finally {\n this.continuationPending = false\n }\n // If a queued check was skipped while continuationPending was true\n // (e.g. a chained approval responded to during the stream), re-evaluate\n // now that the flag is cleared. Only replay after a successful stream —\n // aborted or errored streams should not trigger further continuation.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated asynchronously during await\n if (this.continuationSkipped && succeeded) {\n this.continuationSkipped = false\n await this.checkForContinuation()\n }\n }\n }\n\n /**\n * Check if all tool calls are complete and we should auto-send.\n * Requires that there is at least one tool call in the last assistant message;\n * a text-only response has nothing to auto-send.\n */\n private shouldAutoSend(): boolean {\n const messages = this.processor.getMessages()\n const lastAssistant = messages.findLast(\n (m: UIMessage) => m.role === 'assistant',\n )\n if (!lastAssistant) return false\n const hasToolCalls = lastAssistant.parts.some(\n (p: MessagePart) => p.type === 'tool-call',\n )\n if (!hasToolCalls) return false\n return this.processor.areAllToolsComplete()\n }\n\n /**\n * Get current messages\n */\n getMessages(): Array<UIMessage<TTools>> {\n return this.processor.getMessages() as Array<UIMessage<TTools>>\n }\n\n /**\n * True when an interrupt (or another direct send) claimed the client during\n * a drain. Read via a method so cross-await mutations are not constant-folded\n * by control-flow analysis.\n */\n private shouldAbortMessageQueueDrain(): boolean {\n return this.isLoading || this.stopMessageQueueDrain\n }\n\n /**\n * Deliver queued messages after a successful settle.\n * - `batch`: merge everything currently queued into one send, looping so\n * messages enqueued during that batch stream are not stranded.\n * - `fifo`: walk the queue in a loop, one stream at a time, until empty\n * (or until another send claims the client via interrupt).\n *\n * Uses `deliverMessage` directly so drains do not re-enter `sendMessage`'s\n * busy/queue policy (which would re-queue items and strand the rest).\n */\n private async drainQueue(): Promise<void> {\n // Note: do not gate on `sendInFlight`. Normal sends still hold\n // `sendInFlight` while `streamResponse`'s finally invokes drain; blocking\n // on it would permanently strand the queue.\n if (\n this.messageQueueDraining ||\n this.isLoading ||\n this.messageQueue.length === 0\n ) {\n return\n }\n\n this.messageQueueDraining = true\n this.stopMessageQueueDrain = false\n try {\n if (this.queueConfig.drain === 'batch') {\n while (this.messageQueue.length > 0) {\n if (this.shouldAbortMessageQueueDrain()) {\n return\n }\n const items = this.messageQueue.splice(0)\n this.emitQueueChange()\n const merged = mergeQueuedMessages(items)\n const completed = await this.deliverMessage(\n merged.content,\n merged.body,\n )\n // Failed/aborted deliver flushes the rest of the queue in streamResponse.\n if (!completed || this.shouldAbortMessageQueueDrain()) {\n return\n }\n }\n return\n }\n\n while (this.messageQueue.length > 0) {\n // Interrupt (or a new direct send) claimed the client — stop draining;\n // remaining items stay queued and will drain after that send settles.\n if (this.shouldAbortMessageQueueDrain()) {\n return\n }\n const next = this.messageQueue.shift()\n if (next === undefined) {\n return\n }\n this.emitQueueChange()\n const completed = await this.deliverMessage(next.content, next.body)\n // Failed/aborted deliver flushes the rest of the queue in streamResponse.\n if (!completed || this.shouldAbortMessageQueueDrain()) {\n return\n }\n }\n } finally {\n this.messageQueueDraining = false\n this.stopMessageQueueDrain = false\n }\n }\n\n /**\n * Drop any in-flight send claim and discard pending queued messages\n * (stop / error / clear / unsubscribe / reload).\n */\n private discardPendingSends(): void {\n this.sendInFlight = false\n this.flushQueue()\n }\n\n /**\n * Get the current send queue (messages held while a stream was in flight).\n */\n getQueue(): Array<QueuedMessage> {\n return this.messageQueue.map(({ id, content, createdAt }) => ({\n id,\n content,\n createdAt,\n }))\n }\n\n private emitQueueChange(): void {\n this.callbacksRef.current.onQueueChange(this.getQueue())\n this.devtoolsBridge.emitSnapshot()\n }\n\n /**\n * Remove a queued message by id before it drains.\n */\n cancelQueued(id: string): void {\n const index = this.messageQueue.findIndex((m) => m.id === id)\n if (index === -1) return\n this.messageQueue.splice(index, 1)\n this.emitQueueChange()\n }\n\n /**\n * Discard all pending queued messages (stop / error / clear / unsubscribe /\n * reload). Does not send them. Emits `onQueueChange([])` when anything was\n * removed.\n */\n private flushQueue(): void {\n if (this.messageQueue.length === 0) return\n this.messageQueue = []\n this.emitQueueChange()\n }\n\n /**\n * Get loading state\n */\n getIsLoading(): boolean {\n return this.isLoading\n }\n\n /**\n * Get current status\n */\n getStatus(): ChatClientState {\n return this.status\n }\n\n /**\n * Get whether the subscription loop is active\n */\n getIsSubscribed(): boolean {\n return this.isSubscribed\n }\n\n /**\n * Get current connection lifecycle status\n */\n getConnectionStatus(): ConnectionStatus {\n return this.connectionStatus\n }\n\n /**\n * Whether the shared session is actively generating.\n * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).\n * Unlike `isLoading` (request-local), this reflects shared generation\n * activity visible to all subscribers (e.g. across tabs/devices).\n */\n getSessionGenerating(): boolean {\n return this.sessionGenerating\n }\n\n /**\n * Get current error\n */\n getError(): Error | undefined {\n return this.error\n }\n\n /**\n * Manually set messages\n */\n setMessagesManually(messages: Array<UIMessage<TTools>>): void {\n this.processor.setMessages(messages)\n this.devtoolsBridge.emitSnapshot()\n }\n\n /**\n * Update options refs (for use in React hooks to avoid recreating client)\n */\n updateOptions(options: ChatClientUpdateOptionsWithoutContext<TTools>): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> &\n Pick<ChatClientOptions<TTools, TContext>, 'context'>,\n ): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> & {\n context?: TContext | undefined\n },\n ): void {\n if (options.connection !== undefined || options.fetcher !== undefined) {\n const wasSubscribed = this.isSubscribed\n\n if (this.isLoading) {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n } else if (wasSubscribed) {\n this.abortSubscriptionLoop()\n }\n\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n this.connection = normalizeConnectionAdapter(\n resolveTransport({\n connection: options.connection,\n fetcher: options.fetcher,\n }),\n )\n\n if (wasSubscribed) {\n this.subscribe()\n }\n }\n // Replace each wire-payload slot independently so callers can update one\n // without wiping the other. Passing `undefined` for `body` or\n // `forwardedProps` leaves that slot unchanged; context is cleared when the\n // key is present with an `undefined` value.\n if (options.body !== undefined) {\n this.bodyOption = options.body\n }\n if (options.forwardedProps !== undefined) {\n this.forwardedPropsOption = options.forwardedProps\n }\n if ('context' in options) {\n this.context = options.context\n }\n if (options.tools !== undefined) {\n this.clientToolsRef.current = new Map()\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n this.devtoolsBridge.notifyToolsChanged()\n }\n if (options.queue !== undefined) {\n this.queueConfig = normalizeQueueOption(options.queue)\n }\n if (options.onResponse !== undefined) {\n this.callbacksRef.current.onResponse = options.onResponse\n }\n if (options.onChunk !== undefined) {\n this.callbacksRef.current.onChunk = options.onChunk\n }\n if (options.onFinish !== undefined) {\n this.callbacksRef.current.onFinish = options.onFinish\n }\n if (options.onError !== undefined) {\n this.callbacksRef.current.onError = options.onError\n }\n if (options.onSubscriptionChange !== undefined) {\n this.callbacksRef.current.onSubscriptionChange =\n options.onSubscriptionChange\n }\n if (options.onConnectionStatusChange !== undefined) {\n this.callbacksRef.current.onConnectionStatusChange =\n options.onConnectionStatusChange\n }\n if (options.onSessionGeneratingChange !== undefined) {\n this.callbacksRef.current.onSessionGeneratingChange =\n options.onSessionGeneratingChange\n }\n if (options.onQueueChange !== undefined) {\n this.callbacksRef.current.onQueueChange = options.onQueueChange\n }\n if (options.onCustomEvent !== undefined) {\n this.callbacksRef.current.onCustomEvent = options.onCustomEvent\n }\n }\n\n dispose(): void {\n this.unsubscribe()\n this.devtoolsBridge.dispose()\n this.devtoolsMounted = false\n }\n}\n"],"names":[],"mappings":";;;;AAyFA,SAAS,iBAAiB,WAGJ;AACpB,QAAM,EAAE,YAAY,QAAA,IAAY;AAChC,MAAI,cAAc,SAAS;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,MAAI,WAAY,QAAO;AACvB,MAAI,QAAS,QAAO,2BAA2B,OAAO;AACtD,QAAM,IAAI,MAAM,2DAA2D;AAC7E;AAUO,SAAS,qBACd,QACuB;AACvB,QAAM,OAA8B;AAAA,IAClC,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,EAAA;AAEd,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,WAAW,SAAU,QAAO,EAAE,GAAG,MAAM,UAAU,OAAA;AAC5D,MAAI,OAAO,WAAW,WAAY,QAAO,EAAE,GAAG,MAAM,UAAU,OAAA;AAE9D,QAAM,UAAU,OAAO;AACvB,MAAI,YAAY,QAAW;AACzB,QAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,GAAG;AAC7C,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,OAAO,YAAY;AAAA,IAC7B,OAAO,OAAO,SAAS;AAAA,IACvB,YAAY,OAAO,cAAc;AAAA,IACjC,GAAI,YAAY,SAAY,EAAE,YAAY,CAAA;AAAA,EAAC;AAE/C;AAOA,SAAS,oBAAoB,OAG3B;AACA,QAAM,OAAO,MAAM,GAAG,EAAE,GAAG;AAC3B,QAAM,iBAAgC,CAAA;AACtC,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,KAAK,YAAY,UAAU;AACpC;AAAA,IACF;AACA,mBAAe,KAAK,KAAK,OAAO;AAAA,EAClC;AACA,MAAI,eAAe,WAAW,MAAM,QAAQ;AAC1C,WAAO;AAAA,MACL,SAAS,eAAe,KAAK,IAAI;AAAA,MACjC,GAAI,SAAS,SAAY,EAAE,SAAS,CAAA;AAAA,IAAC;AAAA,EAEzC;AACA,QAAM,QAA4B,CAAA;AAClC,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,KAAK,YAAY,UAAU;AACpC,YAAM,KAAK,EAAE,MAAM,QAAQ,SAAS,KAAK,SAAS;AAAA,IACpD,WAAW,OAAO,KAAK,QAAQ,YAAY,UAAU;AACnD,YAAM,KAAK,EAAE,MAAM,QAAQ,SAAS,KAAK,QAAQ,SAAS;AAAA,IAC5D,OAAO;AACL,YAAM,KAAK,GAAG,KAAK,QAAQ,OAAO;AAAA,IACpC;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS,EAAE,SAAS,MAAA;AAAA,IACpB,GAAI,SAAS,SAAY,EAAE,SAAS,CAAA;AAAA,EAAC;AAEzC;AAEO,MAAM,WAGX;AAAA,EACiB;AAAA,EACT;AAAA,EACS;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,EACT,eAA8B;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9B,aAAkC,CAAA;AAAA,EAClC,uBAA4C,CAAA;AAAA,EAC5C,UAAgC;AAAA,EAChC,qBAAsD;AAAA,EACtD;AAAA,EACA,eAA6C,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7C,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKvB,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxB,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,QAA2B;AAAA,EAC3B,SAA0B;AAAA,EAC1B,mBAAqC;AAAA,EACrC,kBAA0C;AAAA,EACjC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,EACT,kBAAiC;AAAA,EACjC,mBAAkC;AAAA,EACzB,oBAAgD,CAAA;AAAA;AAAA,EAEhD,4CAAwD,IAAA;AAAA,EACjE,oBAAuD;AAAA,EACvD,gBAAsC;AAAA;AAAA,EAEtC,sBAAsB;AAAA,EACtB,8BAAsD;AAAA,EACtD,oBAAyC;AAAA,EACzC,0BAAyC;AAAA,EACzC,mBAAmB;AAAA;AAAA;AAAA,EAGnB,sBAAsB;AAAA,EACtB,WAAW;AAAA,EACX,oBAAoB;AAAA,EACX,mCAAmB,IAAA;AAAA,EAC5B,kBAAkB;AAAA,EAET;AAAA,EAsBjB,YAAY,SAA8C;AACxD,SAAK,WAAW,QAAQ,MAAM,KAAK,iBAAiB,MAAM;AAC1D,SAAK,WAAW,QAAQ,YAAY,KAAK,iBAAiB,QAAQ;AAClE,QAAI,QAAQ,aAAa;AACvB,WAAK,YAAY,IAAI;AAAA,QACnB,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,CAAC,aAAa,KAAK,UAAU,YAAY,QAAQ;AAAA,MAAA;AAAA,IAErD;AAMA,SAAK,aAAa,QAAQ,QAAQ,CAAA;AAClC,SAAK,uBAAuB,QAAQ,kBAAkB,CAAA;AACtD,SAAK,UAAU,QAAQ;AACvB,SAAK,cAAc,qBAAqB,QAAQ,KAAK;AACrD,SAAK,aAAa,2BAA2B,iBAAiB,OAAO,CAAC;AAGtE,SAAK,iBAAiB,EAAE,SAAS,oBAAI,MAAI;AACzC,QAAI,QAAQ,OAAO;AACjB,iBAAW,QAAQ,QAAQ,OAAO;AAChC,aAAK,eAAe,QAAQ,IAAI,KAAK,MAAM,IAAI;AAAA,MACjD;AAAA,IACF;AAEA,SAAK,kBACH,QAAQ,yBAAyB,8BACjC,KAAK,2BAA2B,QAAQ,QAAQ,CAAC;AACnD,SAAK,SAAS,KAAK,eAAe;AAElC,SAAK,eAAe;AAAA,MAClB,SAAS;AAAA,QACP,YAAY,QAAQ,eAAe,MAAM;AAAA,QAAC;AAAA,QAC1C,SAAS,QAAQ,YAAY,MAAM;AAAA,QAAC;AAAA,QACpC,UAAU,QAAQ,aAAa,MAAM;AAAA,QAAC;AAAA,QACtC,SAAS,QAAQ,YAAY,MAAM;AAAA,QAAC;AAAA,QACpC,kBAAkB,QAAQ,qBAAqB,MAAM;AAAA,QAAC;AAAA,QACtD,iBAAiB,QAAQ,oBAAoB,MAAM;AAAA,QAAC;AAAA,QACpD,eAAe,QAAQ,kBAAkB,MAAM;AAAA,QAAC;AAAA,QAChD,gBAAgB,QAAQ,mBAAmB,MAAM;AAAA,QAAC;AAAA,QAClD,sBAAsB,QAAQ,yBAAyB,MAAM;AAAA,QAAC;AAAA,QAC9D,0BACE,QAAQ,6BAA6B,MAAM;AAAA,QAAC;AAAA,QAC9C,2BACE,QAAQ,8BAA8B,MAAM;AAAA,QAAC;AAAA,QAC/C,eAAe,QAAQ,kBAAkB,MAAM;AAAA,QAAC;AAAA,QAChD,eAAe,QAAQ,kBAAkB,MAAM;AAAA,QAAC;AAAA,MAAA;AAAA,IAClD;AAMF,UAAM,oBAAoB,KAAK,WAAW,YAAA;AAC1C,UAAM,kBAAkB,MAAM,QAAQ,iBAAiB,IACnD,oBACA,QAAQ;AAEZ,SAAK,YAAY,IAAI,gBAAgB;AAAA,MACnC,GAAI,QAAQ,iBAAiB,gBACzB,EAAE,eAAe,QAAQ,gBAAgB,cAAA,IACzC,CAAA;AAAA,MACJ,GAAI,kBAAkB,EAAE,gBAAA,IAAoB,CAAA;AAAA,MAC5C,QAAQ;AAAA,QACN,kBAAkB,CAAC,aAA+B;AAChD,eAAK,WAAW,sBAAsB,QAAQ;AAC9C,eAAK,aAAa,QAAQ,iBAAiB,QAAQ;AAAA,QACrD;AAAA,QACA,eAAe,MAAM;AACnB,eAAK,UAAU,WAAW;AAC1B,gBAAM,qBACJ,KAAK,UAAU,6BAAA;AACjB,cAAI,CAAC,oBAAoB;AACvB;AAAA,UACF;AACA,gBAAM,WAAW,KAAK,UAAU,YAAA;AAChC,gBAAM,mBAAmB,SAAS;AAAA,YAChC,CAAC,MAAiB,EAAE,OAAO;AAAA,UAAA;AAE7B,cAAI,kBAAkB;AACpB,iBAAK,mBAAmB,iBAAiB;AACzC,iBAAK,OAAO;AAAA,cACV;AAAA,cACA,KAAK,mBAAmB;AAAA,YAAA;AAAA,UAE5B;AAAA,QACF;AAAA,QACA,aAAa,CAAC,YAAuB;AACnC,eAAK,aAAa,QAAQ,SAAS,OAAO;AAC1C,eAAK,UAAU,OAAO;AAEtB,eAAK,kBAAA;AAAA,QACP;AAAA,QACA,SAAS,CAAC,UAAiB;AACzB,eAAK,kBAAkB,KAAK;AAAA,QAC9B;AAAA,QACA,cAAc,CAAC,WAAmB,YAAoB;AAEpD,cAAI,KAAK,iBAAiB;AACxB,iBAAK,OAAO,YAAY,KAAK,iBAAiB,WAAW,OAAO;AAAA,UAClE;AAAA,QACF;AAAA,QACA,kBAAkB,CAAC,WAAmB,YAAoB;AAExD,cAAI,KAAK,iBAAiB;AACxB,iBAAK,OAAO;AAAA,cACV,KAAK;AAAA,cACL;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UAEJ;AAAA,QACF;AAAA,QACA,0BAA0B,CAAC,SAAS;AAClC,gBAAM,WAAW,KAAK,eAAe,gBAAA;AACrC,gBAAM,YACJ,KAAK,UAAU,UACX,8BACA,KAAK,UAAU,aACb,gCACA,KAAK,UAAU,UACb,8BACA;AAEV,eAAK,mBAAmB,KAAK;AAC7B,eAAK,OAAO;AAAA,YACV;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL;AAAA,cACE,QAAQ,KAAK;AAAA,cACb,KAAK,KAAK;AAAA,cACV,GAAI,KAAK,YAAY,SAAY,EAAE,SAAS,KAAK,QAAA,IAAY,CAAA;AAAA,cAC7D,GAAI,KAAK,SAAS,SAAY,EAAE,MAAM,KAAK,KAAA,IAAS,CAAA;AAAA,cACpD,GAAI,KAAK,cAAc,SACnB,EAAE,WAAW,KAAK,UAAA,IAClB,CAAA;AAAA,cACJ,GAAI,KAAK,iBAAiB,SACtB,EAAE,cAAc,KAAK,aAAA,IACrB,CAAA;AAAA,cACJ,GAAI,KAAK,UAAU,SAAY,EAAE,OAAO,KAAK,UAAU,CAAA;AAAA,YAAC;AAAA,UAC1D;AAAA,QAEJ;AAAA,QACA,uBAAuB,CACrB,WACA,YACA,OACA,SACG;AAEH,gBAAM,WAAW,KAAK,UAAU,YAAA;AAChC,gBAAM,UAAU,SAAS,KAAK,CAAC,MAAiB,EAAE,OAAO,SAAS;AAClE,gBAAM,eAAe,SAAS,MAAM;AAAA,YAClC,CAAC,MACC,EAAE,SAAS,eAAe,EAAE,OAAO;AAAA,UAAA;AAEvC,gBAAM,WAAW,cAAc,QAAQ;AAGvC,cAAI,KAAK,iBAAiB;AACxB,iBAAK,OAAO;AAAA,cACV,KAAK;AAAA,cACL;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UAEJ;AAAA,QACF;AAAA,QACA,YAAY,CAAC,SAIP;AAEJ,gBAAM,cACJ,KAAK,qBAAqB,KAAK,eAAe;AAChD,gBAAM,aAAa,YAAY,IAAI,KAAK,QAAQ;AAChD,gBAAM,cAAc,YAAY;AAChC,cAAI,aAAa;AAKf,kBAAM,kBACJ,KAAK,eAAe,0BAAA;AAEtB,kBAAM,oBAAoB,YAAY;AACpC,kBAAI;AACF,sBAAM,UACJ,KAAK,sBAAsB,OACvB,KAAK,UACL,KAAK;AACX,sBAAM,SAAS,MAAM,YAAY,KAAK,OAAO;AAAA,kBAC3C,YAAY,KAAK;AAAA,kBACjB;AAAA,kBACA,iBAAiB,MAAM;AAAA,kBAAC;AAAA,gBAAA,CACzB;AACD,sBAAM,KAAK;AAAA,kBACT;AAAA,oBACE,YAAY,KAAK;AAAA,oBACjB,MAAM,KAAK;AAAA,oBACX;AAAA,oBACA,OAAO;AAAA,kBAAA;AAAA,kBAET;AAAA,kBACA;AAAA,gBAAA;AAAA,cAEJ,SAAS,OAAY;AACnB,sBAAM,KAAK;AAAA,kBACT;AAAA,oBACE,YAAY,KAAK;AAAA,oBACjB,MAAM,KAAK;AAAA,oBACX,QAAQ;AAAA,oBACR,OAAO;AAAA,oBACP,WAAW,MAAM;AAAA,kBAAA;AAAA,kBAEnB;AAAA,kBACA;AAAA,gBAAA;AAAA,cAEJ,UAAA;AAEE,qBAAK,sBAAsB,OAAO,KAAK,UAAU;AAAA,cACnD;AAAA,YACF,GAAA;AAGA,iBAAK,sBAAsB,IAAI,KAAK,YAAY,gBAAgB;AAAA,UAClE;AAAA,QACF;AAAA,QACA,mBAAmB,CAAC,SAKd;AACJ,gBAAM,WAAW,KAAK,eAAe,gBAAA;AACrC,gBAAM,uBACJ,KAAK,yBAAyB,KAAK,UAAU,KAC7C,KAAK,oBACL;AAEF,eAAK,OAAO;AAAA,YACV;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,UAAA;AAAA,QAET;AAAA,QACA,eAAe,CACb,WACA,MACA,YACG;AACH,eAAK,aAAa,QAAQ,cAAc,WAAW,MAAM,OAAO;AAAA,QAClE;AAAA,MAAA;AAAA,IACF,CACD;AAED,SAAK,WAAW,aAAa,iBAAiB;AAAA,EAChD;AAAA,EAEA,gBAAsB;AACpB,QAAI,KAAK,iBAAiB;AACxB;AAAA,IACF;AAEA,SAAK,kBAAkB;AACvB,SAAK,eAAe,eAAe,KAAK,UAAU,YAAA,EAAc,MAAM;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,yBAAyB,OAA0B;AACzD,QAAI,MAAM,SAAS,YAAa;AAChC,UAAM,QAAQ,KAAK,WAAW,iBAAA;AAC9B,QAAI,CAAC,MAAO;AACZ,SAAK,aAAa,OAAO,KAAK;AAC9B,SAAK,qBAAqB,KAAK,aAAa,OAAO,CAAC;AACpD,SAAK,kBAAA;AAAA,EACP;AAAA,EAEQ,mBACN,OACA,SACM;AACN,QAAI,MAAM,SAAS,eAAe;AAChC,YAAM,aAAa,cAAc,KAAK,KAAK,MAAM;AACjD,WAAK,aAAa,IAAI,UAAU;AAChC,WAAK,WAAW,aAAa,UAAU;AACvC,WAAK,qBAAqB,IAAI;AAC9B;AAAA,IACF;AAEA,QAAI,MAAM,SAAS,kBAAkB,MAAM,SAAS,aAAa;AAC/D;AAAA,IACF;AAEA,UAAM,QAAQ,cAAc,KAAK;AACjC,QAAI,OAAO;AACT,WAAK,aAAa,OAAO,KAAK;AAC9B,WAAK,WAAW,aAAa,KAAK;AAAA,IACpC,WAAW,MAAM,SAAS,aAAa;AAErC,WAAK,aAAa,MAAA;AAClB,WAAK,WAAW,kBAAA;AAAA,IAClB;AACA,SAAK,qBAAqB,KAAK,aAAa,OAAO,CAAC;AACpD,QAAI,SAAS,sBAAsB,OAAO;AACxC,WAAK,kBAAA;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,iBAAiB,QAAwB;AAC/C,WAAO,GAAG,MAAM,IAAI,KAAK,KAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,EAC3E;AAAA,EAEQ,aAAa,WAA0B;AAC7C,SAAK,YAAY;AACjB,SAAK,aAAa,QAAQ,gBAAgB,SAAS;AACnD,SAAK,OAAO,eAAe,SAAS;AAAA,EACtC;AAAA,EAEQ,UAAU,QAA+B;AAC/C,SAAK,SAAS;AACd,SAAK,aAAa,QAAQ,eAAe,MAAM;AAC/C,SAAK,eAAe,aAAA;AAAA,EACtB;AAAA,EAEQ,gBAAgB,cAA6B;AACnD,SAAK,eAAe;AACpB,SAAK,aAAa,QAAQ,qBAAqB,YAAY;AAC3D,SAAK,eAAe,aAAA;AAAA,EACtB;AAAA,EAEQ,oBAAoB,QAAgC;AAC1D,SAAK,mBAAmB;AACxB,SAAK,aAAa,QAAQ,yBAAyB,MAAM;AACzD,SAAK,eAAe,aAAA;AAAA,EACtB;AAAA,EAEQ,qBAAqB,cAA6B;AACxD,QAAI,KAAK,sBAAsB,aAAc;AAC7C,SAAK,oBAAoB;AACzB,SAAK,aAAa,QAAQ,0BAA0B,YAAY;AAChE,SAAK,eAAe,aAAA;AAAA,EACtB;AAAA,EAEQ,yBAA+B;AACrC,SAAK,aAAa,MAAA;AAClB,SAAK,WAAW,aAAA;AAChB,SAAK,qBAAqB,KAAK;AAAA,EACjC;AAAA,EAEQ,SAAS,OAAgC;AAC/C,SAAK,QAAQ;AACb,SAAK,aAAa,QAAQ,cAAc,KAAK;AAC7C,SAAK,OAAO,aAAa,OAAO,WAAW,IAAI;AAAA,EACjD;AAAA,EAEQ,2BACN,UAC2B;AAC3B,WAAO;AAAA,MACL,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,UAAU;AAAA,QACR,UAAU,UAAU,YAAY;AAAA,QAChC,YAAY,UAAU,cAAc;AAAA,QACpC,GAAI,UAAU,YAAY,EAAE,WAAW,SAAS,UAAA,IAAc,CAAA;AAAA,QAC9D,GAAI,UAAU,OAAO,EAAE,MAAM,SAAS,KAAA,IAAS,CAAA;AAAA,MAAC;AAAA,MAElD,aAAa,MAAM,KAAK,oBAAA;AAAA,MACxB,UAAU,MAAM,KAAK,eAAe,QAAQ,OAAA;AAAA,MAC5C,aAAa,MAAM,KAAK,UAAU,YAAA;AAAA,MAClC,aAAa,CAAC,aAA+B;AAC3C,aAAK,UAAU,YAAY,QAAQ;AAAA,MACrC;AAAA,MACA,eAAe,CAAC,YAAY,QAAQ,cAAc;AAChD,aAAK,UAAU,cAAc,YAAY,QAAQ,SAAS;AAAA,MAC5D;AAAA,MACA,YAAY,CAAC,WAAW,KAAK,iBAAiB,MAAM;AAAA,IAAA;AAAA,EAExD;AAAA,EAEQ,sBAA8C;AACpD,WAAO;AAAA,MACL,UAAU,KAAK,UAAU,YAAA;AAAA,MACzB,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,kBAAkB,KAAK;AAAA,MACvB,mBAAmB,KAAK;AAAA,MACxB,cAAc,MAAM,KAAK,KAAK,YAAY;AAAA,MAC1C,OAAO,KAAK,SAAA;AAAA,MACZ,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,YAAY,CAAA;AAAA,IAAC;AAAA,EAEtD;AAAA,EAEQ,yBAAyB,YAAwC;AACvE,UAAM,WAAW,KAAK,UAAU,YAAA;AAChC,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,QAAQ,MAAM;AAAA,QAC1B,CAAC,SACC,KAAK,SAAS,eAAe,KAAK,OAAO;AAAA,MAAA;AAE7C,UAAI,cAAc,QAAQ;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,wBAA8B;AACpC,SAAK,6BAA6B,MAAA;AAClC,SAAK,8BAA8B;AAAA,EACrC;AAAA,EAEQ,oBAA0B;AAChC,SAAK,oBAAA;AACL,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEQ,qBAAqB,SAGpB;AACP,SAAK,iBAAiB,MAAA;AACtB,SAAK,kBAAkB;AACvB,QAAI,SAAS,mBAAmB;AAC9B,WAAK,sBAAA;AAAA,IACP;AACA,SAAK,kBAAA;AACL,SAAK,aAAa,KAAK;AAGvB,SAAK,eAAe;AACpB,QAAI,SAAS,gBAAgB;AAC3B,WAAK,UAAU,OAAO;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,kBAAkB,OAAoB;AAC5C,UAAM,kBACJ,KAAK,4BAA4B,KAAK;AACxC,SAAK,SAAS,KAAK;AAGnB,QACE,KAAK,aACL,KAAK,WAAW,eAChB,KAAK,WAAW,aAChB;AACA,WAAK,UAAU,OAAO;AAAA,IACxB;AACA,QAAI,CAAC,iBAAiB;AACpB,WAAK,0BAA0B,KAAK;AACpC,WAAK,aAAa,QAAQ,QAAQ,KAAK;AAAA,IACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAA0B;AAChC,SAAK,8BAA8B,IAAI,gBAAA;AACvC,UAAM,SAAS,KAAK,4BAA4B;AAEhD,SAAK,oBAAoB,MAAM,EAC5B,MAAM,CAAC,QAAQ;AACd,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,aAAK,oBAAoB,OAAO;AAChC,aAAK,uBAAA;AACL,aAAK,gBAAgB,KAAK;AAC1B,aAAK,kBAAkB,GAAG;AAAA,MAC5B;AAEA,WAAK,kBAAA;AAAA,IACP,CAAC,EACA,QAAQ,MAAM;AAEb,UAAI,KAAK,6BAA6B,WAAW,QAAQ;AACvD;AAAA,MACF;AACA,WAAK,8BAA8B;AACnC,UAAI,CAAC,OAAO,WAAW,KAAK,cAAc;AACxC,aAAK,gBAAgB,KAAK;AAC1B,YAAI,KAAK,qBAAqB,SAAS;AACrC,eAAK,oBAAoB,cAAc;AAAA,QACzC;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,oBAAoB,QAAoC;AACpE,UAAM,SAAS,KAAK,WAAW,UAAU,MAAM;AAC/C,qBAAiB,SAAS,QAAQ;AAChC,UAAI,OAAO,QAAS;AACpB,UAAI,KAAK,qBAAqB,cAAc;AAC1C,aAAK,oBAAoB,WAAW;AAAA,MACtC;AACA,YAAM,eAAe,KAAK,WAAW,kBAAkB,KAAK,KAAK;AACjE,UAAI,cAAc;AAChB,YAAI,MAAM,SAAS,kBAAkB,MAAM,SAAS,aAAa;AAC/D,cAAI,cAAc,KAAK,GAAG;AACxB,iBAAK,mBAAmB,OAAO,EAAE,mBAAmB,OAAO;AAAA,UAC7D,OAAO;AACL,iBAAK,yBAAyB,KAAK;AAAA,UACrC;AAAA,QACF;AACA;AAAA,MACF;AACA,WAAK,aAAa,QAAQ,QAAQ,KAAK;AACvC,WAAK,eAAe,aAAa,KAAK;AACtC,WAAK,UAAU,aAAa,KAAK;AAOjC,WAAK,mBAAmB,KAAK;AAE7B,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,CAAC,CAAC;AAAA,IACvD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA2B;AACjC,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,UAAA;AACL;AAAA,IACF;AACA,QACE,CAAC,KAAK,+BACN,KAAK,4BAA4B,OAAO,SACxC;AACA,WAAK,UAAU,EAAE,SAAS,KAAA,CAAM;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,oBAAmC;AAEzC,SAAK,kBAAA;AACL,WAAO,IAAI,QAAc,CAAC,YAAY;AACpC,WAAK,oBAAoB;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8CA,MAAM,YACJ,SACA,MACA,aACe;AACf,SAAK,cAAA;AACL,UAAM,eAAe,OAAO,YAAY,YAAY,CAAC,QAAQ,KAAA;AAC7D,QAAI,cAAc;AAChB;AAAA,IACF;AAEA,QAAI,KAAK,cAAc;AACrB,YAAM,EAAE,QAAQ,GAAA,IAAO,KAAK,eAAe,SAAS,WAAW;AAC/D,UAAI,WAAW,QAAQ;AACrB;AAAA,MACF;AACA,UAAI,WAAW,SAAS;AACtB,aAAK,eAAe,SAAS,MAAM,EAAE;AACrC;AAAA,MACF;AAMA,WAAK,wBAAwB;AAC7B,WAAK,eAAe;AACpB,WAAK,qBAAqB,EAAE,gBAAgB,KAAA,CAAM;AAClD,WAAK,uBAAA;AAAA,IACP,OAAO;AACL,WAAK,eAAe;AAAA,IACtB;AAEA,QAAI;AACF,YAAM,KAAK,eAAe,SAAS,IAAI;AAAA,IACzC,UAAA;AACE,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA,EAGQ,aAAsB;AAC5B,WAAO,KAAK,aAAa,KAAK,gBAAgB,KAAK;AAAA,EACrD;AAAA,EAEQ,oBAAqC;AAC3C,QAAI,KAAK,UAAW,QAAO;AAC3B,QAAI,KAAK,qBAAsB,QAAO;AACtC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,eACZ,SACA,MACkB;AAClB,QAAI,KAAK,aAAa,KAAK,cAAc;AACvC,aAAO;AAAA,IACT;AACA,SAAK,eAAe;AACpB,QAAI;AACF,YAAM,oBAAoB,KAAK,sBAAsB,OAAO;AAC5D,WAAK,qBAAqB;AAC1B,YAAM,cAAc,KAAK,UAAU;AAAA,QACjC,kBAAkB;AAAA,QAClB,kBAAkB;AAAA,MAAA;AAEpB,WAAK,OAAO,YAAY,YAAY,IAAI,kBAAkB,OAAO;AACjE,aAAO,MAAM,KAAK,eAAA;AAAA,IACpB,UAAA;AACE,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eACN,SACA,aACkC;AAClC,UAAM,KAAK,KAAK,iBAAiB,QAAQ;AACzC,QAAI,aAAa,UAAU;AACzB,aAAO,EAAE,QAAQ,YAAY,UAAU,GAAA;AAAA,IACzC;AACA,UAAM,EAAE,UAAU,SAAA,IAAa,KAAK;AACpC,QAAI,UAAU;AACZ,YAAM,EAAE,OAAA,IAAW,SAAS;AAAA,QAC1B,SAAS;AAAA,UACP;AAAA,UACA;AAAA,UACA,WAAW,KAAK,IAAA;AAAA,QAAI;AAAA,QAEtB,YAAY,KAAK,kBAAA;AAAA,QACjB,QAAQ,KAAK,SAAA;AAAA,MAAS,CACvB;AACD,aAAO,EAAE,QAAQ,GAAA;AAAA,IACnB;AACA,WAAO,EAAE,QAAQ,UAAU,GAAA;AAAA,EAC7B;AAAA,EAEQ,eACN,SACA,MACA,IACM;AACN,UAAM,EAAE,SAAS,WAAA,IAAe,KAAK;AACrC,QAAI,YAAY,UAAa,KAAK,aAAa,UAAU,SAAS;AAEhE,UAAI,eAAe,YAAY,YAAY,GAAG;AAC5C;AAAA,MACF;AACA,WAAK,aAAa,MAAA;AAAA,IACpB;AACA,SAAK,aAAa,KAAK;AAAA,MACrB,IAAI,MAAM,KAAK,iBAAiB,QAAQ;AAAA,MACxC;AAAA,MACA,WAAW,KAAK,IAAA;AAAA,MAChB,GAAI,SAAS,SAAY,EAAE,SAAS,CAAA;AAAA,IAAC,CACtC;AACD,SAAK,gBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAAsB,OAG5B;AACA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,EAAE,SAAS,MAAM,OAAK;AAAA,IAC/B;AACA,WAAO,EAAE,SAAS,MAAM,SAAS,IAAI,MAAM,GAAA;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,SAAkD;AAC7D,SAAK,cAAA;AAEL,UAAM,oBAAoB,qBAAqB,SAAS,iBAAiB;AAGzE,QAAI,kBAAkB,SAAS,UAAU;AACvC;AAAA,IACF;AAGA,UAAM,YAAY;AAGlB,SAAK,OAAO,gBAAgB,SAAS;AAGrC,UAAM,WAAW,KAAK,UAAU,YAAA;AAChC,SAAK,UAAU,YAAY,CAAC,GAAG,UAAU,SAAS,CAAC;AACnD,SAAK,eAAe,aAAA;AAGpB,QAAI,KAAK,WAAW;AAClB,WAAK,sBAAsB,YAAY;AACrC,cAAM,KAAK,eAAA;AAAA,MACb,CAAC;AACD;AAAA,IACF;AAEA,UAAM,KAAK,eAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBAAmC;AAE/C,QAAI,KAAK,WAAW;AAClB,aAAO;AAAA,IACT;AAGA,UAAM,aAAa,EAAE,KAAK;AAC1B,UAAM,QAAQ,OAAO,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AACzE,SAAK,eAAe;AAEpB,SAAK,aAAa,IAAI;AAItB,SAAK,eAAe;AACpB,SAAK,UAAU,WAAW;AAC1B,SAAK,SAAS,MAAS;AACvB,SAAK,0BAA0B;AAC/B,SAAK,kBAAkB,IAAI,gBAAA;AAI3B,UAAM,SAAS,KAAK,gBAAgB;AAEpC,SAAK,sBAAsB,MAAA;AAC3B,QAAI,8BAA8B;AAClC,QAAI,sBAAqC;AACzC,QAAI,0BAA0B;AAE9B,QAAI;AAEF,YAAM,WAAW,KAAK,UAAU,YAAA;AAChC,YAAM,cAAc,IAAI,IAAI,KAAK,eAAe,OAAO;AACvD,YAAM,iBAAiB,KAAK;AAG5B,YAAM,KAAK,aAAa,QAAQ,WAAA;AAOhC,UAAI,OAAO,SAAS;AAClB,eAAO;AAAA,MACT;AAWA,YAAM,aAAa;AAAA,QACjB,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,MAAA;AAIV,WAAK,qBAAqB;AAG1B,WAAK,kBAAkB,KAAK,iBAAiB,QAAQ;AACrD,WAAK,eAAe,mBAAmB,KAAK,eAAe;AAC3D,WAAK,mBAAmB;AACxB,WAAK,oBAAoB;AACzB,WAAK,gBAAgB;AAKrB,WAAK,UAAU,wBAAA;AAGf,WAAK,mBAAA;AAGL,YAAM,qBAAqB,KAAK,kBAAA;AAUhC,YAAM,aAAa;AAAA,QACjB,UAAU,KAAK;AAAA,QACf;AAAA,QACA,aAAa,MAAM,KAAK,YAAY,QAAQ,EAAE,IAAI,CAAC,OAAO;AAAA,UACxD,MAAM,EAAE;AAAA,UACR,aAAa,EAAE;AAAA,UACf,YAAY,EAAE,cACV,0BAA0B,EAAE,WAAW,IACvC,EAAE,MAAM,SAAA;AAAA,QAAS,EACrB;AAAA,QACF,gBAAgB,EAAE,GAAG,WAAA;AAAA,MAAW;AAElC,WAAK,eAAe,SAAS,WAAW,OAAO,KAAK,QAAQ;AAC5D,4BAAsB,WAAW;AACjC,WAAK,eAAe;AAAA,QAClB;AAAA,QACA,WAAW;AAAA,QACX;AAAA,MAAA;AAEF,WAAK,eAAe;AAAA,QAClB;AAAA,QACA,WAAW;AAAA,QACX;AAAA,MAAA;AAEF,WAAK,eAAe,aAAA;AAGpB,YAAM,KAAK,WAAW,KAAK,UAAU,YAAY,QAAQ,UAAU;AAGnE,YAAM;AAIN,UAAI,eAAe,KAAK,kBAAkB;AACxC,eAAO;AAAA,MACT;AAIA,UAAI,KAAK,WAAW,SAAS;AAC3B,YAAI,qBAAqB;AACvB,eAAK,eAAe;AAAA,YAClB;AAAA,YACA;AAAA,YACA;AAAA,YACA,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,YAAY,CAAA;AAAA,UAAC;AAEhD,oCAA0B;AAAA,QAC5B;AACA,eAAO;AAAA,MACT;AAGA,UAAI,KAAK,sBAAsB,OAAO,GAAG;AACvC,cAAM,QAAQ,IAAI,KAAK,sBAAsB,QAAQ;AAAA,MACvD;AAGA,WAAK,UAAU,eAAA;AACf,oCAA8B;AAAA,IAChC,SAAS,KAAK;AACZ,UAAI,eAAe,OAAO;AACxB,YAAI,IAAI,SAAS,cAAc;AAC7B,cAAI,qBAAqB;AACvB,iBAAK,eAAe;AAAA,cAClB;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAEF,sCAA0B;AAAA,UAC5B;AACA,iBAAO;AAAA,QACT;AACA,YAAI,eAAe,KAAK,kBAAkB;AACxC,eAAK,kBAAkB,GAAG;AAC1B,cAAI,qBAAqB;AACvB,iBAAK,eAAe;AAAA,cAClB;AAAA,cACA;AAAA,cACA;AAAA,cACA,EAAE,OAAO,IAAI,QAAA;AAAA,YAAQ;AAEvB,sCAA0B;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF,UAAA;AAIE,UAAI,eAAe,KAAK,kBAAkB;AACxC,aAAK,kBAAkB;AACvB,aAAK,eAAe,mBAAmB,IAAI;AAC3C,aAAK,mBAAmB;AACxB,aAAK,eAAe;AACpB,aAAK,oBAAoB;AACzB,aAAK,gBAAgB;AACrB,aAAK,kBAAkB;AACvB,aAAK,aAAa,KAAK;AACvB,aAAK,qBAAqB;AAE1B,YAAI,uBAAuB,CAAC,yBAAyB;AACnD,cAAI,6BAA6B;AAC/B,iBAAK,eAAe;AAAA,cAClB;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UAEJ,WAAW,OAAO,SAAS;AACzB,iBAAK,eAAe;AAAA,cAClB;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UAEJ;AAAA,QACF;AAGA,cAAM,KAAK,uBAAA;AAKX,YAAI,6BAA6B;AAC/B,gBAAM,WAAW,KAAK,UAAU,YAAA;AAChC,gBAAM,WAAW,SAAS,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE;AAC7C,gBAAM,EAAE,aAAA,IAAiB,KAAK,UAAU,SAAA;AAExC,cACE,UAAU,SAAS,iBACnB,iBAAiB,UACjB,KAAK,kBACL;AACA,gBAAI;AACF,oBAAM,KAAK,qBAAA;AAAA,YACb,SAAS,OAAO;AACd,sBAAQ,MAAM,8CAA8C,KAAK;AAIjE,oBAAM,KAAK,WAAA;AAAA,YACb;AAAA,UACF,OAAO;AACL,gBAAI,KAAK,WAAW,SAAS;AAK3B,mBAAK,UAAU,OAAO;AAAA,YACxB;AAKA,gBAAI,CAAC,KAAK,sBAAsB;AAC9B,oBAAM,KAAK,WAAA;AAAA,YACb;AAAA,UACF;AAAA,QACF,OAAO;AAKL,eAAK,WAAA;AAAA,QACP;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,SAAuC;AAC/C,UAAM,UAAU,SAAS,YAAY;AACrC,QAAI,KAAK,gBAAgB,CAAC,SAAS;AACjC;AAAA,IACF;AAEA,QAAI,KAAK,gBAAgB,SAAS;AAChC,WAAK,sBAAA;AAAA,IACP;AAEA,SAAK,gBAAgB,IAAI;AACzB,SAAK,oBAAoB,YAAY;AACrC,SAAK,kBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAoB;AAClB,SAAK,qBAAqB;AAAA,MACxB,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,IAAA,CACpB;AACD,SAAK,oBAAA;AACL,SAAK,uBAAA;AACL,SAAK,gBAAgB,KAAK;AAC1B,SAAK,oBAAoB,cAAc;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAwB;AAC5B,UAAM,WAAW,KAAK,UAAU,YAAA;AAChC,QAAI,SAAS,WAAW,EAAG;AAG3B,UAAM,uBAAuB,SAAS;AAAA,MACpC,CAAC,MAAM,EAAE,SAAS;AAAA,IAAA;AAGpB,QAAI,yBAAyB,GAAI;AAGjC,QAAI,KAAK,WAAW;AAClB,WAAK,qBAAA;AAAA,IACP;AAGA,SAAK,oBAAA;AAEL,SAAK,OAAO,SAAS,oBAAoB;AAGzC,SAAK,UAAU,oBAAoB,oBAAoB;AACvD,SAAK,eAAe,aAAA;AAGpB,UAAM,KAAK,eAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,UAAM,iBAAiB,KAAK,oBAAoB;AAChD,SAAK,qBAAqB,EAAE,gBAAgB,KAAA,CAAM;AAClD,SAAK,oBAAA;AACL,QAAI,gBAAgB;AAClB,WAAK,uBAAA;AAAA,IACP;AACA,SAAK,OAAO,QAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,QAAI,KAAK,WAAW;AAClB,WAAK,UAAU,cAAc;AAAA,QAC3B,UAAU,KAAK,UAAU,YAAA;AAAA,QACzB,cAAc,KAAK;AAAA,QACnB,cAAc,KAAK;AAAA,MAAA,CACpB;AACD,UAAI,KAAK,WAAW;AAClB,aAAK,qBAAqB,EAAE,gBAAgB,KAAA,CAAM;AAClD,aAAK,uBAAA;AAAA,MACP,WAAW,KAAK,aAAa,OAAO,GAAG;AACrC,aAAK,uBAAA;AAAA,MACP;AAGA,WAAK,UAAU,WAAA;AAAA,IACjB;AACA,SAAK,UAAU,cAAA;AACf,SAAK,oBAAA;AACL,SAAK,WAAW,OAAA;AAChB,SAAK,SAAS,MAAS;AACvB,SAAK,OAAO,gBAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,QAAyC;AAC3D,UAAM,aAAa,KAAK,eAAe,QAAQ,IAAI,OAAO,IAAI;AAC9D,UAAM,KAAK,2BAA2B,QAAQ,UAAU;AAAA,EAC1D;AAAA,EAEA,MAAc,2BACZ,QACA,YACA,SACe;AACf,QAAI,cAAc,OAAO,UAAU,gBAAgB;AACjD,UAAI;AACF,iBAAS;AAAA,UACP,GAAG;AAAA,UACH,QAAQ,KAAK,yBAAyB,YAAY,OAAO,MAAM;AAAA,QAAA;AAAA,MAEnE,SAAS,OAAY;AACnB,iBAAS;AAAA,UACP,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,WAAW,MAAM;AAAA,QAAA;AAAA,MAErB;AAAA,IACF;AAEA,SAAK,OAAO;AAAA,MACV,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO,SAAS;AAAA,MAChB;AAAA,IAAA;AAMF,SAAK,UAAU;AAAA,MACb,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO,UAAU,iBACb,OAAO,aAAa,0BACpB;AAAA,IAAA;AAIN,QAAI,KAAK,WAAW;AAClB,WAAK,sBAAsB,MAAM,KAAK,qBAAA,CAAsB;AAC5D;AAAA,IACF;AAEA,UAAM,KAAK,qBAAA;AAAA,EACb;AAAA,EAEQ,yBACN,YACA,QACK;AACL,QAAI,WAAW,gBAAgB,iBAAiB,WAAW,YAAY,GAAG;AACxE,aAAO,wBAAwB,WAAW,cAAc,MAAM;AAAA,IAChE;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,UAGZ;AAEhB,UAAM,WAAW,KAAK,UAAU,YAAA;AAChC,QAAI;AAEJ,eAAW,OAAO,UAAU;AAC1B,YAAM,eAAe,IAAI,MAAM;AAAA,QAC7B,CAAC,MACC,EAAE,SAAS,eAAe,EAAE,UAAU,OAAO,SAAS;AAAA,MAAA;AAE1D,UAAI,cAAc;AAChB,0BAAkB,aAAa;AAC/B;AAAA,MACF;AAAA,IACF;AAEA,QAAI,iBAAiB;AACnB,WAAK,OAAO;AAAA,QACV,SAAS;AAAA,QACT;AAAA,QACA,SAAS;AAAA,MAAA;AAAA,IAEb;AAGA,SAAK,UAAU,wBAAwB,SAAS,IAAI,SAAS,QAAQ;AACrE,SAAK,eAAe,aAAA;AAGpB,QAAI,KAAK,WAAW;AAClB,WAAK,sBAAsB,MAAM,KAAK,qBAAA,CAAsB;AAC5D;AAAA,IACF;AAEA,UAAM,KAAK,qBAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB,QAAmC;AAC/D,SAAK,kBAAkB,KAAK,MAAM;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,yBAAwC;AACpD,QAAI,KAAK,SAAU;AACnB,SAAK,WAAW;AAChB,QAAI;AACF,UAAI;AACJ,cAAQ,SAAS,KAAK,kBAAkB,MAAA,OAAa,QAAW;AAC9D,cAAM,OAAA;AAAA,MACR;AAAA,IACF,UAAA;AACE,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,uBAAsC;AAElD,QAAI,KAAK,uBAAuB,KAAK,WAAW;AAC9C,WAAK,sBAAsB;AAC3B;AAAA,IACF;AAEA,QAAI,KAAK,kBAAkB;AACzB,WAAK,sBAAsB;AAC3B,WAAK,sBAAsB;AAC3B,UAAI,YAAY;AAChB,UAAI;AACF,oBAAY,MAAM,KAAK,eAAA;AAAA,MACzB,UAAA;AACE,aAAK,sBAAsB;AAAA,MAC7B;AAMA,UAAI,KAAK,uBAAuB,WAAW;AACzC,aAAK,sBAAsB;AAC3B,cAAM,KAAK,qBAAA;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAA0B;AAChC,UAAM,WAAW,KAAK,UAAU,YAAA;AAChC,UAAM,gBAAgB,SAAS;AAAA,MAC7B,CAAC,MAAiB,EAAE,SAAS;AAAA,IAAA;AAE/B,QAAI,CAAC,cAAe,QAAO;AAC3B,UAAM,eAAe,cAAc,MAAM;AAAA,MACvC,CAAC,MAAmB,EAAE,SAAS;AAAA,IAAA;AAEjC,QAAI,CAAC,aAAc,QAAO;AAC1B,WAAO,KAAK,UAAU,oBAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAwC;AACtC,WAAO,KAAK,UAAU,YAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,+BAAwC;AAC9C,WAAO,KAAK,aAAa,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,aAA4B;AAIxC,QACE,KAAK,wBACL,KAAK,aACL,KAAK,aAAa,WAAW,GAC7B;AACA;AAAA,IACF;AAEA,SAAK,uBAAuB;AAC5B,SAAK,wBAAwB;AAC7B,QAAI;AACF,UAAI,KAAK,YAAY,UAAU,SAAS;AACtC,eAAO,KAAK,aAAa,SAAS,GAAG;AACnC,cAAI,KAAK,gCAAgC;AACvC;AAAA,UACF;AACA,gBAAM,QAAQ,KAAK,aAAa,OAAO,CAAC;AACxC,eAAK,gBAAA;AACL,gBAAM,SAAS,oBAAoB,KAAK;AACxC,gBAAM,YAAY,MAAM,KAAK;AAAA,YAC3B,OAAO;AAAA,YACP,OAAO;AAAA,UAAA;AAGT,cAAI,CAAC,aAAa,KAAK,gCAAgC;AACrD;AAAA,UACF;AAAA,QACF;AACA;AAAA,MACF;AAEA,aAAO,KAAK,aAAa,SAAS,GAAG;AAGnC,YAAI,KAAK,gCAAgC;AACvC;AAAA,QACF;AACA,cAAM,OAAO,KAAK,aAAa,MAAA;AAC/B,YAAI,SAAS,QAAW;AACtB;AAAA,QACF;AACA,aAAK,gBAAA;AACL,cAAM,YAAY,MAAM,KAAK,eAAe,KAAK,SAAS,KAAK,IAAI;AAEnE,YAAI,CAAC,aAAa,KAAK,gCAAgC;AACrD;AAAA,QACF;AAAA,MACF;AAAA,IACF,UAAA;AACE,WAAK,uBAAuB;AAC5B,WAAK,wBAAwB;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAA4B;AAClC,SAAK,eAAe;AACpB,SAAK,WAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,WAAiC;AAC/B,WAAO,KAAK,aAAa,IAAI,CAAC,EAAE,IAAI,SAAS,iBAAiB;AAAA,MAC5D;AAAA,MACA;AAAA,MACA;AAAA,IAAA,EACA;AAAA,EACJ;AAAA,EAEQ,kBAAwB;AAC9B,SAAK,aAAa,QAAQ,cAAc,KAAK,UAAU;AACvD,SAAK,eAAe,aAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,IAAkB;AAC7B,UAAM,QAAQ,KAAK,aAAa,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AAC5D,QAAI,UAAU,GAAI;AAClB,SAAK,aAAa,OAAO,OAAO,CAAC;AACjC,SAAK,gBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,aAAmB;AACzB,QAAI,KAAK,aAAa,WAAW,EAAG;AACpC,SAAK,eAAe,CAAA;AACpB,SAAK,gBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,eAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,YAA6B;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,kBAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAwC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,uBAAgC;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAA8B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,UAA0C;AAC5D,SAAK,UAAU,YAAY,QAAQ;AACnC,SAAK,eAAe,aAAA;AAAA,EACtB;AAAA,EAUA,cACE,SAGM;AACN,QAAI,QAAQ,eAAe,UAAa,QAAQ,YAAY,QAAW;AACrE,YAAM,gBAAgB,KAAK;AAE3B,UAAI,KAAK,WAAW;AAClB,aAAK,qBAAqB;AAAA,UACxB,gBAAgB;AAAA,UAChB,mBAAmB;AAAA,QAAA,CACpB;AAAA,MACH,WAAW,eAAe;AACxB,aAAK,sBAAA;AAAA,MACP;AAEA,WAAK,uBAAA;AACL,WAAK,gBAAgB,KAAK;AAC1B,WAAK,oBAAoB,cAAc;AACvC,WAAK,aAAa;AAAA,QAChB,iBAAiB;AAAA,UACf,YAAY,QAAQ;AAAA,UACpB,SAAS,QAAQ;AAAA,QAAA,CAClB;AAAA,MAAA;AAGH,UAAI,eAAe;AACjB,aAAK,UAAA;AAAA,MACP;AAAA,IACF;AAKA,QAAI,QAAQ,SAAS,QAAW;AAC9B,WAAK,aAAa,QAAQ;AAAA,IAC5B;AACA,QAAI,QAAQ,mBAAmB,QAAW;AACxC,WAAK,uBAAuB,QAAQ;AAAA,IACtC;AACA,QAAI,aAAa,SAAS;AACxB,WAAK,UAAU,QAAQ;AAAA,IACzB;AACA,QAAI,QAAQ,UAAU,QAAW;AAC/B,WAAK,eAAe,UAAU,oBAAI,IAAA;AAClC,iBAAW,QAAQ,QAAQ,OAAO;AAChC,aAAK,eAAe,QAAQ,IAAI,KAAK,MAAM,IAAI;AAAA,MACjD;AACA,WAAK,eAAe,mBAAA;AAAA,IACtB;AACA,QAAI,QAAQ,UAAU,QAAW;AAC/B,WAAK,cAAc,qBAAqB,QAAQ,KAAK;AAAA,IACvD;AACA,QAAI,QAAQ,eAAe,QAAW;AACpC,WAAK,aAAa,QAAQ,aAAa,QAAQ;AAAA,IACjD;AACA,QAAI,QAAQ,YAAY,QAAW;AACjC,WAAK,aAAa,QAAQ,UAAU,QAAQ;AAAA,IAC9C;AACA,QAAI,QAAQ,aAAa,QAAW;AAClC,WAAK,aAAa,QAAQ,WAAW,QAAQ;AAAA,IAC/C;AACA,QAAI,QAAQ,YAAY,QAAW;AACjC,WAAK,aAAa,QAAQ,UAAU,QAAQ;AAAA,IAC9C;AACA,QAAI,QAAQ,yBAAyB,QAAW;AAC9C,WAAK,aAAa,QAAQ,uBACxB,QAAQ;AAAA,IACZ;AACA,QAAI,QAAQ,6BAA6B,QAAW;AAClD,WAAK,aAAa,QAAQ,2BACxB,QAAQ;AAAA,IACZ;AACA,QAAI,QAAQ,8BAA8B,QAAW;AACnD,WAAK,aAAa,QAAQ,4BACxB,QAAQ;AAAA,IACZ;AACA,QAAI,QAAQ,kBAAkB,QAAW;AACvC,WAAK,aAAa,QAAQ,gBAAgB,QAAQ;AAAA,IACpD;AACA,QAAI,QAAQ,kBAAkB,QAAW;AACvC,WAAK,aAAa,QAAQ,gBAAgB,QAAQ;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,YAAA;AACL,SAAK,eAAe,QAAA;AACpB,SAAK,kBAAkB;AAAA,EACzB;AACF;"}
@@ -1,6 +1,6 @@
1
1
  import { AnyClientTool, StreamChunk } from '@tanstack/ai/client';
2
2
  import { ChatClientEventContext, ChatClientEventEmitter, ChatClientRunEventContext } from './events.js';
3
- import { ChatClientState, ConnectionStatus, UIMessage } from './types.js';
3
+ import { ChatClientState, ConnectionStatus, QueuedMessage, UIMessage } from './types.js';
4
4
  export interface AIDevtoolsDisplayOptions {
5
5
  name?: string;
6
6
  }
@@ -75,6 +75,7 @@ export interface AIDevtoolsChatSnapshot {
75
75
  connectionStatus: ConnectionStatus;
76
76
  sessionGenerating: boolean;
77
77
  activeRunIds: Array<string>;
78
+ queue?: Array<QueuedMessage>;
78
79
  error?: string;
79
80
  }
80
81
  export declare function createAIDevtoolsGenerationPreview(input: AIDevtoolsGenerationPreviewInput): AIDevtoolsGenerationPreview;