@tanstack/ai-client 0.16.3 → 0.17.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.
@@ -861,7 +861,7 @@ class ChatClient {
861
861
  this.processor.addToolResult(
862
862
  result.toolCallId,
863
863
  result.output,
864
- result.errorText
864
+ result.state === "output-error" ? result.errorText || "Tool execution failed" : void 0
865
865
  );
866
866
  if (this.isLoading) {
867
867
  this.queuePostStreamAction(() => this.checkForContinuation());
@@ -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 }\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 // Add result via processor\n this.processor.addToolResult(\n result.toolCallId,\n result.output,\n result.errorText,\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 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 }\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 // Add result via processor. `result.state` is the authoritative error\n // signal; `addToolResult` infers error-ness from the error message being\n // truthy. Pass an error message ONLY for output-error results (falling back\n // to a default so an empty message like `throw new Error()` still reaches\n // the terminal 'error' state), and `undefined` otherwise — so error\n // signalling derives solely from `result.state`, never from a stray\n // `result.errorText` on a successful result.\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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"}
@@ -58,7 +58,7 @@ export type ChatTransport = {
58
58
  /**
59
59
  * Tool call states - track the lifecycle of a tool call
60
60
  */
61
- export type ToolCallState = 'awaiting-input' | 'input-streaming' | 'input-complete' | 'approval-requested' | 'approval-responded' | 'complete';
61
+ export type ToolCallState = 'awaiting-input' | 'input-streaming' | 'input-complete' | 'approval-requested' | 'approval-responded' | 'complete' | 'error';
62
62
  /**
63
63
  * Tool result states - track the lifecycle of a tool result
64
64
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import type {\n AnyClientTool,\n AudioPart,\n ChunkStrategy,\n ContentPart,\n DocumentPart,\n ImagePart,\n InferToolInput,\n InferToolOutput,\n ModelMessage,\n StreamChunk,\n StructuredOutputPart,\n VideoPart,\n} from '@tanstack/ai/client'\nimport type { ConnectionAdapter } from './connection-adapters'\nimport type { AIDevtoolsClientMetadata } from './devtools'\nimport type { ChatDevtoolsBridgeFactory } from './devtools-noop'\n\nexport type { StructuredOutputPart } from '@tanstack/ai/client'\n\n/**\n * `messages` is the full UIMessage history (not a delta). `data` is the\n * merged body — `ChatClientOptions.body` plus any per-call data passed to\n * `sendMessage(...)`. `threadId` / `runId` are the AG-UI correlation ids\n * the chat client uses to track this turn — forward them to your server\n * if it needs to correlate requests.\n */\nexport interface ChatFetcherInput {\n messages: Array<UIMessage>\n data?: Record<string, unknown>\n threadId: string\n runId: string\n}\n\nexport interface ChatFetcherOptions {\n /** Fires when `stop()` is called or the request is superseded. */\n signal: AbortSignal\n}\n\n/**\n * Direct function that performs a chat request. Mirrors\n * `GenerationFetcher`. Returns either a `Response` (SSE body parsed by the\n * chat client) or an `AsyncIterable<StreamChunk>` (yielded directly). May\n * return the value synchronously, as a `Promise`, or as an async generator\n * (`async function*`) — the chat client awaits whichever shape is returned.\n *\n * @example\n * ```ts\n * useChat({\n * fetcher: ({ messages }, { signal }) =>\n * chatFn({ data: { messages }, signal }),\n * })\n * ```\n */\nexport type ChatFetcher = (\n input: ChatFetcherInput,\n options: ChatFetcherOptions,\n) =>\n | Response\n | AsyncIterable<StreamChunk>\n | Promise<Response | AsyncIterable<StreamChunk>>\n\n/**\n * Distributive `Omit` — applies `Omit<O, K>` per branch of a union so\n * discriminated unions survive omission. Plain `Omit` collapses unions\n * into a single object shape, which would erase the `ChatTransport` XOR\n * when framework hooks omit React-managed callbacks from\n * `ChatClientOptions`.\n */\nexport type DistributedOmit<\n TObject,\n TKeys extends keyof any,\n> = TObject extends unknown ? Omit<TObject, TKeys> : never\n\n/**\n * Discriminated union enforcing that exactly one of `connection` or\n * `fetcher` is provided. Mirrors `GenerationTransport`.\n */\nexport type ChatTransport =\n | { connection: ConnectionAdapter; fetcher?: never }\n | { fetcher: ChatFetcher; connection?: never }\n\n/**\n * Tool call states - track the lifecycle of a tool call\n */\nexport type ToolCallState =\n | 'awaiting-input' // Received start but no arguments yet\n | 'input-streaming' // Partial arguments received\n | 'input-complete' // All arguments received\n | 'approval-requested' // Waiting for user approval\n | 'approval-responded' // User has approved/denied\n | 'complete' // Result is complete\n\n/**\n * Tool result states - track the lifecycle of a tool result\n */\nexport type ToolResultState =\n | 'streaming' // Placeholder for future streamed output\n | 'complete' // Result is complete\n | 'error' // Error occurred\n\n/**\n * ChatClient state - track the lifecycle of a chat\n */\nexport type ChatClientState = 'ready' | 'submitted' | 'streaming' | 'error'\n\n/**\n * Connection lifecycle state for the subscription loop.\n */\nexport type ConnectionStatus =\n | 'disconnected'\n | 'connecting'\n | 'connected'\n | 'error'\n\n/**\n * Multimodal content input for sending messages with rich media.\n * Allows sending text, images, audio, video, and documents to the LLM.\n *\n * @example\n * ```ts\n * // Send an image with a question\n * 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 * id: 'custom-message-id' // optional\n * })\n * ```\n */\nexport interface MultimodalContent {\n /**\n * The content of the message.\n * Can be a simple string or an array of content parts for multimodal messages.\n */\n content: string | Array<ContentPart>\n /**\n * Optional custom ID for the message.\n * If not provided, a unique ID will be generated.\n */\n id?: string\n}\n\n/**\n * Message parts - building blocks of UIMessage\n */\nexport interface TextPart {\n type: 'text'\n content: string\n}\n\n/**\n * Helper type that creates a tool-call part for a specific tool.\n * This is a conditional type to enable proper distribution over union types,\n * creating a discriminated union where `name` is the discriminant.\n */\ntype ToolCallPartForTool<T> = T extends AnyClientTool\n ? {\n type: 'tool-call'\n id: string\n name: T['name']\n arguments: string // JSON string (may be incomplete)\n /** Parsed tool input (typed from inputSchema) */\n input?: InferToolInput<T>\n state: ToolCallState\n /** Approval metadata if tool requires user approval */\n approval?: {\n id: string // Unique approval ID\n needsApproval: boolean // Always true if present\n approved?: boolean // User's decision (undefined until responded)\n }\n /** Tool execution output (for client tools or after approval) */\n output?: InferToolOutput<T>\n }\n : never\n\n/**\n * Fallback tool-call part type when tools are not typed\n */\ntype UntypedToolCallPart = {\n type: 'tool-call'\n id: string\n name: string\n arguments: string\n input?: any\n state: ToolCallState\n approval?: {\n id: string\n needsApproval: boolean\n approved?: boolean\n }\n output?: any\n}\n\n/**\n * Tool call part that creates a proper discriminated union.\n * When TTools is typed, checking `part.name === 'toolName'` will narrow\n * `part.output` to the correct type for that tool.\n *\n * The discriminant is `name`, so code like:\n * ```ts\n * if (part.name === 'recommendGuitar') {\n * // part.output is now typed to the recommendGuitar tool's output\n * }\n * ```\n */\nexport type ToolCallPart<TTools extends ReadonlyArray<AnyClientTool> = any> =\n // Check if we have a concrete tools array (not 'any' or 'never')\n [TTools] extends [never]\n ? UntypedToolCallPart\n : unknown extends TTools\n ? UntypedToolCallPart\n : TTools extends ReadonlyArray<infer Tool>\n ? Tool extends AnyClientTool\n ? ToolCallPartForTool<Tool>\n : UntypedToolCallPart\n : UntypedToolCallPart\n\nexport interface ToolResultPart {\n type: 'tool-result'\n toolCallId: string\n content: string | Array<ContentPart>\n state: ToolResultState\n error?: string // Error message if state is \"error\"\n}\n\nexport interface ThinkingPart {\n type: 'thinking'\n content: string\n}\n\nexport type MessagePart<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TData = unknown,\n> =\n | TextPart\n | ImagePart\n | AudioPart\n | VideoPart\n | DocumentPart\n | ToolCallPart<TTools>\n | ToolResultPart\n | ThinkingPart\n | StructuredOutputPart<TData>\n\n/**\n * UIMessage - Domain-specific message format optimized for building chat UIs\n * Contains parts that can be text, tool calls, or tool results.\n *\n * `TTools` narrows the tool-call/result part types based on the registered\n * tools. `TData` is the schema-inferred type for any `structured-output` part\n * on the message — defaulted to `unknown` so untyped consumers (the core\n * stream processor, the wire converter) don't need to thread a schema generic\n * everywhere; the hook layer (`useChat({ outputSchema })`) substitutes it on\n * the public return so `m.parts.find(p => p.type === 'structured-output').data`\n * is typed without manual casts.\n */\nexport interface UIMessage<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TData = unknown,\n> {\n id: string\n role: 'system' | 'user' | 'assistant'\n parts: Array<MessagePart<TTools, TData>>\n createdAt?: Date\n}\n\nexport interface ChatClientPersistence<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n> {\n getItem: (\n id: string,\n ) =>\n | Array<UIMessage<TTools>>\n | null\n | undefined\n | Promise<Array<UIMessage<TTools>> | null | undefined>\n setItem: (\n id: string,\n messages: Array<UIMessage<TTools>>,\n ) => void | Promise<void>\n removeItem: (id: string) => void | Promise<void>\n}\n\ntype IsUnknown<T> = unknown extends T\n ? [T] extends [unknown]\n ? true\n : false\n : false\n\ntype KnownContext<T> = IsUnknown<T> extends true ? never : T\n\ntype MergeContext<TLeft, TRight> = [TLeft] extends [never]\n ? TRight\n : [TRight] extends [never]\n ? TLeft\n : TLeft & TRight\n\ntype UnionToIntersection<T> = [T] extends [never]\n ? never\n : (T extends unknown ? (value: T) => void : never) extends (\n value: infer TIntersection,\n ) => void\n ? TIntersection\n : never\n\ntype DefinedContext<T> = Exclude<T, undefined>\n\ntype ContextFromExecute<T> = T extends (...args: any) => any\n ? NonNullable<Parameters<T>[1]> extends { context: infer TContext }\n ? KnownContext<TContext>\n : never\n : never\n\ntype ContextFromClientTool<T> = T extends AnyClientTool\n ? T extends { execute?: infer TExecute }\n ? ContextFromExecute<TExecute>\n : never\n : never\n\ntype RequiredContextFromClientToolUnion<T> = T extends unknown\n ? undefined extends ContextFromClientTool<T>\n ? never\n : ContextFromClientTool<T>\n : never\n\ntype ContextFromClientToolUnion<T> = [\n UnionToIntersection<DefinedContext<ContextFromClientTool<T>>>,\n] extends [never]\n ? never\n : [RequiredContextFromClientToolUnion<T>] extends [never]\n ? UnionToIntersection<DefinedContext<ContextFromClientTool<T>>> | undefined\n : UnionToIntersection<DefinedContext<ContextFromClientTool<T>>>\n\ntype ContextFromClientTools<TTools> =\n IsUnknown<TTools> extends true\n ? never\n : TTools extends readonly [infer THead, ...infer TTail]\n ? MergeContext<\n ContextFromClientTool<THead>,\n ContextFromClientTools<TTail>\n >\n : TTools extends ReadonlyArray<infer TItem>\n ? ContextFromClientToolUnion<TItem>\n : never\n\nexport type InferredClientContext<TTools> = [\n ContextFromClientTools<TTools>,\n] extends [never]\n ? unknown\n : ContextFromClientTools<TTools>\n\nexport type ClientContextOptionFromTools<TTools, TContext> = [\n ContextFromClientTools<TTools>,\n] extends [never]\n ? { context?: TContext }\n : undefined extends ContextFromClientTools<TTools>\n ? { context?: TContext & ContextFromClientTools<TTools> }\n : { context: TContext & ContextFromClientTools<TTools> }\n\n/**\n * Base options for `ChatClient`, excluding the transport (`connection` or\n * `fetcher`) which is supplied separately via `ChatTransport` so the XOR\n * is preserved when composing the final `ChatClientOptions` type.\n */\nexport interface ChatClientBaseOptions<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = unknown,\n> {\n /**\n * Initial messages to populate the chat\n */\n initialMessages?: Array<UIMessage<TTools>>\n\n /**\n * Optional persistence adapter for chat messages.\n */\n persistence?: ChatClientPersistence<TTools>\n\n /**\n * Unique identifier for this chat instance\n * Used for managing multiple chats\n */\n id?: string\n\n /**\n * Thread ID to use for this chat session. Persists across sends within\n * the session. If omitted, a unique thread ID is generated.\n */\n threadId?: string\n\n /**\n * Arbitrary client-controlled JSON forwarded to the server in the\n * AG-UI `RunAgentInput.forwardedProps` field. Use this for per-session\n * options like provider/model selection or feature flags that the\n * server endpoint should read.\n *\n * Replaces the legacy `body` option. If both are provided,\n * `forwardedProps` wins on key collision.\n */\n forwardedProps?: Record<string, any>\n\n /**\n * @deprecated Use `forwardedProps` instead. `body` continues to work\n * unchanged — its values are merged into the AG-UI\n * `RunAgentInput.forwardedProps` field on the wire and are also\n * mirrored under the legacy `data` field for servers that have not\n * migrated yet. Will be removed in a future major release.\n */\n body?: Record<string, any>\n\n /**\n * Client-local runtime context passed to client tool implementations.\n *\n * This value is not serialized to the server. Use `forwardedProps` for\n * explicit client-to-server handoff of serializable values.\n */\n context?: TContext\n\n /**\n * Callback when a response is received\n */\n onResponse?: (response?: Response) => void | Promise<void>\n\n /**\n * Callback when a stream chunk is received\n */\n onChunk?: (chunk: StreamChunk) => void\n\n /**\n * Callback when the response is finished\n */\n onFinish?: (message: UIMessage<TTools>) => void\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void\n\n /**\n * Callback when messages change\n */\n onMessagesChange?: (messages: Array<UIMessage<TTools>>) => void\n\n /**\n * Callback when loading state changes\n */\n onLoadingChange?: (isLoading: boolean) => void\n\n /**\n * Callback when error state changes\n */\n onErrorChange?: (error: Error | undefined) => void\n\n /**\n * Callback when chat status changes\n */\n onStatusChange?: (status: ChatClientState) => void\n\n /**\n * Callback when subscription lifecycle changes.\n * This is independent from request lifecycle (`isLoading`, `status`).\n */\n onSubscriptionChange?: (isSubscribed: boolean) => void\n\n /**\n * Callback when connection lifecycle changes.\n */\n onConnectionStatusChange?: (status: ConnectionStatus) => void\n\n /**\n * Callback when session generation activity changes.\n * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).\n * Unlike `onLoadingChange` (request-local), this reflects shared generation\n * activity visible to all subscribers (e.g. across tabs/devices).\n */\n onSessionGeneratingChange?: (isGenerating: boolean) => void\n\n /**\n * Callback when a custom event is received from a server-side tool.\n * Custom events are emitted by tools using `context.emitCustomEvent()` during execution.\n *\n * @param eventType - The name of the custom event\n * @param data - The event payload data\n * @param context - Additional context including the toolCallId that emitted the event\n */\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n\n /**\n * Client-side tools with execution logic\n * When provided, tools with execute functions will be called automatically\n */\n tools?: TTools\n\n /**\n * Devtools hook metadata for this client instance.\n */\n devtools?: Partial<AIDevtoolsClientMetadata>\n\n /**\n * Factory that constructs the devtools bridge. Default is a no-op\n * factory, which keeps `@tanstack/ai-client/devtools` (the heavy\n * bridge implementation) out of the main entry's bundle. Frameworks\n * that need live devtools should pass the real factory from\n * `@tanstack/ai-client/devtools`.\n */\n devtoolsBridgeFactory?: ChatDevtoolsBridgeFactory\n\n /**\n * Stream processing options (optional)\n * Configure chunking strategy\n */\n streamProcessor?: {\n /**\n * Strategy for when to emit text updates\n * Defaults to ImmediateStrategy (every chunk)\n */\n chunkStrategy?: ChunkStrategy\n }\n}\n\n/**\n * Options for `ChatClient`. Exactly one of `connection` or `fetcher` must be\n * provided — the type-level XOR is enforced via `ChatTransport`.\n */\nexport type ChatClientOptions<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = InferredClientContext<TTools>,\n> = DistributedOmit<ChatClientBaseOptions<TTools, TContext>, 'context'> &\n ClientContextOptionFromTools<TTools, TContext> &\n ChatTransport\n\nexport interface ChatRequestBody {\n messages: Array<ModelMessage>\n data?: Record<string, any>\n}\n\n/**\n * Create a typed array of client tools with proper type inference.\n * This eliminates the need for `as const` when defining tool arrays.\n *\n * @example\n * ```ts\n * const tools = clientTools(\n * myTool1.client(() => result1),\n * myTool2.client(() => result2),\n * )\n *\n * // tools is now properly typed as a tuple with literal tool names\n * // This enables type narrowing when checking part.name === 'toolName'\n * ```\n */\nexport function clientTools<const T extends Array<AnyClientTool>>(\n ...tools: T\n): T {\n return tools\n}\n\n/**\n * Helper to create typed chat client options\n * Use this to get proper type inference for messages\n *\n * @example\n * ```ts\n * const tools = clientTools(myTool1, myTool2)\n *\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools,\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * ```\n */\nexport function createChatClientOptions<\n const TTools extends ReadonlyArray<AnyClientTool>,\n TContext = InferredClientContext<TTools>,\n>(\n options: ChatClientOptions<TTools, TContext>,\n): ChatClientOptions<TTools, TContext> {\n return options\n}\n\n/**\n * Extract the message type from chat options\n *\n * @example\n * ```ts\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools: [myTool1, myTool2],\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * // MyMessages is now Array<UIMessage<[typeof myTool1, typeof myTool2]>>\n * ```\n */\nexport type InferChatMessages<T> =\n T extends ChatClientOptions<infer TTools, any>\n ? Array<UIMessage<TTools>>\n : never\n"],"names":[],"mappings":"AA6iBO,SAAS,eACX,OACA;AACH,SAAO;AACT;AAkBO,SAAS,wBAId,SACqC;AACrC,SAAO;AACT;"}
1
+ {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import type {\n AnyClientTool,\n AudioPart,\n ChunkStrategy,\n ContentPart,\n DocumentPart,\n ImagePart,\n InferToolInput,\n InferToolOutput,\n ModelMessage,\n StreamChunk,\n StructuredOutputPart,\n VideoPart,\n} from '@tanstack/ai/client'\nimport type { ConnectionAdapter } from './connection-adapters'\nimport type { AIDevtoolsClientMetadata } from './devtools'\nimport type { ChatDevtoolsBridgeFactory } from './devtools-noop'\n\nexport type { StructuredOutputPart } from '@tanstack/ai/client'\n\n/**\n * `messages` is the full UIMessage history (not a delta). `data` is the\n * merged body — `ChatClientOptions.body` plus any per-call data passed to\n * `sendMessage(...)`. `threadId` / `runId` are the AG-UI correlation ids\n * the chat client uses to track this turn — forward them to your server\n * if it needs to correlate requests.\n */\nexport interface ChatFetcherInput {\n messages: Array<UIMessage>\n data?: Record<string, unknown>\n threadId: string\n runId: string\n}\n\nexport interface ChatFetcherOptions {\n /** Fires when `stop()` is called or the request is superseded. */\n signal: AbortSignal\n}\n\n/**\n * Direct function that performs a chat request. Mirrors\n * `GenerationFetcher`. Returns either a `Response` (SSE body parsed by the\n * chat client) or an `AsyncIterable<StreamChunk>` (yielded directly). May\n * return the value synchronously, as a `Promise`, or as an async generator\n * (`async function*`) — the chat client awaits whichever shape is returned.\n *\n * @example\n * ```ts\n * useChat({\n * fetcher: ({ messages }, { signal }) =>\n * chatFn({ data: { messages }, signal }),\n * })\n * ```\n */\nexport type ChatFetcher = (\n input: ChatFetcherInput,\n options: ChatFetcherOptions,\n) =>\n | Response\n | AsyncIterable<StreamChunk>\n | Promise<Response | AsyncIterable<StreamChunk>>\n\n/**\n * Distributive `Omit` — applies `Omit<O, K>` per branch of a union so\n * discriminated unions survive omission. Plain `Omit` collapses unions\n * into a single object shape, which would erase the `ChatTransport` XOR\n * when framework hooks omit React-managed callbacks from\n * `ChatClientOptions`.\n */\nexport type DistributedOmit<\n TObject,\n TKeys extends keyof any,\n> = TObject extends unknown ? Omit<TObject, TKeys> : never\n\n/**\n * Discriminated union enforcing that exactly one of `connection` or\n * `fetcher` is provided. Mirrors `GenerationTransport`.\n */\nexport type ChatTransport =\n | { connection: ConnectionAdapter; fetcher?: never }\n | { fetcher: ChatFetcher; connection?: never }\n\n/**\n * Tool call states - track the lifecycle of a tool call\n */\nexport type ToolCallState =\n | 'awaiting-input' // Received start but no arguments yet\n | 'input-streaming' // Partial arguments received\n | 'input-complete' // All arguments received\n | 'approval-requested' // Waiting for user approval\n | 'approval-responded' // User has approved/denied\n | 'complete' // Result is complete\n | 'error' // Tool execution failed (terminal)\n\n/**\n * Tool result states - track the lifecycle of a tool result\n */\nexport type ToolResultState =\n | 'streaming' // Placeholder for future streamed output\n | 'complete' // Result is complete\n | 'error' // Error occurred\n\n/**\n * ChatClient state - track the lifecycle of a chat\n */\nexport type ChatClientState = 'ready' | 'submitted' | 'streaming' | 'error'\n\n/**\n * Connection lifecycle state for the subscription loop.\n */\nexport type ConnectionStatus =\n | 'disconnected'\n | 'connecting'\n | 'connected'\n | 'error'\n\n/**\n * Multimodal content input for sending messages with rich media.\n * Allows sending text, images, audio, video, and documents to the LLM.\n *\n * @example\n * ```ts\n * // Send an image with a question\n * 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 * id: 'custom-message-id' // optional\n * })\n * ```\n */\nexport interface MultimodalContent {\n /**\n * The content of the message.\n * Can be a simple string or an array of content parts for multimodal messages.\n */\n content: string | Array<ContentPart>\n /**\n * Optional custom ID for the message.\n * If not provided, a unique ID will be generated.\n */\n id?: string\n}\n\n/**\n * Message parts - building blocks of UIMessage\n */\nexport interface TextPart {\n type: 'text'\n content: string\n}\n\n/**\n * Helper type that creates a tool-call part for a specific tool.\n * This is a conditional type to enable proper distribution over union types,\n * creating a discriminated union where `name` is the discriminant.\n */\ntype ToolCallPartForTool<T> = T extends AnyClientTool\n ? {\n type: 'tool-call'\n id: string\n name: T['name']\n arguments: string // JSON string (may be incomplete)\n /** Parsed tool input (typed from inputSchema) */\n input?: InferToolInput<T>\n state: ToolCallState\n /** Approval metadata if tool requires user approval */\n approval?: {\n id: string // Unique approval ID\n needsApproval: boolean // Always true if present\n approved?: boolean // User's decision (undefined until responded)\n }\n /** Tool execution output (for client tools or after approval) */\n output?: InferToolOutput<T>\n }\n : never\n\n/**\n * Fallback tool-call part type when tools are not typed\n */\ntype UntypedToolCallPart = {\n type: 'tool-call'\n id: string\n name: string\n arguments: string\n input?: any\n state: ToolCallState\n approval?: {\n id: string\n needsApproval: boolean\n approved?: boolean\n }\n output?: any\n}\n\n/**\n * Tool call part that creates a proper discriminated union.\n * When TTools is typed, checking `part.name === 'toolName'` will narrow\n * `part.output` to the correct type for that tool.\n *\n * The discriminant is `name`, so code like:\n * ```ts\n * if (part.name === 'recommendGuitar') {\n * // part.output is now typed to the recommendGuitar tool's output\n * }\n * ```\n */\nexport type ToolCallPart<TTools extends ReadonlyArray<AnyClientTool> = any> =\n // Check if we have a concrete tools array (not 'any' or 'never')\n [TTools] extends [never]\n ? UntypedToolCallPart\n : unknown extends TTools\n ? UntypedToolCallPart\n : TTools extends ReadonlyArray<infer Tool>\n ? Tool extends AnyClientTool\n ? ToolCallPartForTool<Tool>\n : UntypedToolCallPart\n : UntypedToolCallPart\n\nexport interface ToolResultPart {\n type: 'tool-result'\n toolCallId: string\n content: string | Array<ContentPart>\n state: ToolResultState\n error?: string // Error message if state is \"error\"\n}\n\nexport interface ThinkingPart {\n type: 'thinking'\n content: string\n}\n\nexport type MessagePart<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TData = unknown,\n> =\n | TextPart\n | ImagePart\n | AudioPart\n | VideoPart\n | DocumentPart\n | ToolCallPart<TTools>\n | ToolResultPart\n | ThinkingPart\n | StructuredOutputPart<TData>\n\n/**\n * UIMessage - Domain-specific message format optimized for building chat UIs\n * Contains parts that can be text, tool calls, or tool results.\n *\n * `TTools` narrows the tool-call/result part types based on the registered\n * tools. `TData` is the schema-inferred type for any `structured-output` part\n * on the message — defaulted to `unknown` so untyped consumers (the core\n * stream processor, the wire converter) don't need to thread a schema generic\n * everywhere; the hook layer (`useChat({ outputSchema })`) substitutes it on\n * the public return so `m.parts.find(p => p.type === 'structured-output').data`\n * is typed without manual casts.\n */\nexport interface UIMessage<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TData = unknown,\n> {\n id: string\n role: 'system' | 'user' | 'assistant'\n parts: Array<MessagePart<TTools, TData>>\n createdAt?: Date\n}\n\nexport interface ChatClientPersistence<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n> {\n getItem: (\n id: string,\n ) =>\n | Array<UIMessage<TTools>>\n | null\n | undefined\n | Promise<Array<UIMessage<TTools>> | null | undefined>\n setItem: (\n id: string,\n messages: Array<UIMessage<TTools>>,\n ) => void | Promise<void>\n removeItem: (id: string) => void | Promise<void>\n}\n\ntype IsUnknown<T> = unknown extends T\n ? [T] extends [unknown]\n ? true\n : false\n : false\n\ntype KnownContext<T> = IsUnknown<T> extends true ? never : T\n\ntype MergeContext<TLeft, TRight> = [TLeft] extends [never]\n ? TRight\n : [TRight] extends [never]\n ? TLeft\n : TLeft & TRight\n\ntype UnionToIntersection<T> = [T] extends [never]\n ? never\n : (T extends unknown ? (value: T) => void : never) extends (\n value: infer TIntersection,\n ) => void\n ? TIntersection\n : never\n\ntype DefinedContext<T> = Exclude<T, undefined>\n\ntype ContextFromExecute<T> = T extends (...args: any) => any\n ? NonNullable<Parameters<T>[1]> extends { context: infer TContext }\n ? KnownContext<TContext>\n : never\n : never\n\ntype ContextFromClientTool<T> = T extends AnyClientTool\n ? T extends { execute?: infer TExecute }\n ? ContextFromExecute<TExecute>\n : never\n : never\n\ntype RequiredContextFromClientToolUnion<T> = T extends unknown\n ? undefined extends ContextFromClientTool<T>\n ? never\n : ContextFromClientTool<T>\n : never\n\ntype ContextFromClientToolUnion<T> = [\n UnionToIntersection<DefinedContext<ContextFromClientTool<T>>>,\n] extends [never]\n ? never\n : [RequiredContextFromClientToolUnion<T>] extends [never]\n ? UnionToIntersection<DefinedContext<ContextFromClientTool<T>>> | undefined\n : UnionToIntersection<DefinedContext<ContextFromClientTool<T>>>\n\ntype ContextFromClientTools<TTools> =\n IsUnknown<TTools> extends true\n ? never\n : TTools extends readonly [infer THead, ...infer TTail]\n ? MergeContext<\n ContextFromClientTool<THead>,\n ContextFromClientTools<TTail>\n >\n : TTools extends ReadonlyArray<infer TItem>\n ? ContextFromClientToolUnion<TItem>\n : never\n\nexport type InferredClientContext<TTools> = [\n ContextFromClientTools<TTools>,\n] extends [never]\n ? unknown\n : ContextFromClientTools<TTools>\n\nexport type ClientContextOptionFromTools<TTools, TContext> = [\n ContextFromClientTools<TTools>,\n] extends [never]\n ? { context?: TContext }\n : undefined extends ContextFromClientTools<TTools>\n ? { context?: TContext & ContextFromClientTools<TTools> }\n : { context: TContext & ContextFromClientTools<TTools> }\n\n/**\n * Base options for `ChatClient`, excluding the transport (`connection` or\n * `fetcher`) which is supplied separately via `ChatTransport` so the XOR\n * is preserved when composing the final `ChatClientOptions` type.\n */\nexport interface ChatClientBaseOptions<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = unknown,\n> {\n /**\n * Initial messages to populate the chat\n */\n initialMessages?: Array<UIMessage<TTools>>\n\n /**\n * Optional persistence adapter for chat messages.\n */\n persistence?: ChatClientPersistence<TTools>\n\n /**\n * Unique identifier for this chat instance\n * Used for managing multiple chats\n */\n id?: string\n\n /**\n * Thread ID to use for this chat session. Persists across sends within\n * the session. If omitted, a unique thread ID is generated.\n */\n threadId?: string\n\n /**\n * Arbitrary client-controlled JSON forwarded to the server in the\n * AG-UI `RunAgentInput.forwardedProps` field. Use this for per-session\n * options like provider/model selection or feature flags that the\n * server endpoint should read.\n *\n * Replaces the legacy `body` option. If both are provided,\n * `forwardedProps` wins on key collision.\n */\n forwardedProps?: Record<string, any>\n\n /**\n * @deprecated Use `forwardedProps` instead. `body` continues to work\n * unchanged — its values are merged into the AG-UI\n * `RunAgentInput.forwardedProps` field on the wire and are also\n * mirrored under the legacy `data` field for servers that have not\n * migrated yet. Will be removed in a future major release.\n */\n body?: Record<string, any>\n\n /**\n * Client-local runtime context passed to client tool implementations.\n *\n * This value is not serialized to the server. Use `forwardedProps` for\n * explicit client-to-server handoff of serializable values.\n */\n context?: TContext\n\n /**\n * Callback when a response is received\n */\n onResponse?: (response?: Response) => void | Promise<void>\n\n /**\n * Callback when a stream chunk is received\n */\n onChunk?: (chunk: StreamChunk) => void\n\n /**\n * Callback when the response is finished\n */\n onFinish?: (message: UIMessage<TTools>) => void\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void\n\n /**\n * Callback when messages change\n */\n onMessagesChange?: (messages: Array<UIMessage<TTools>>) => void\n\n /**\n * Callback when loading state changes\n */\n onLoadingChange?: (isLoading: boolean) => void\n\n /**\n * Callback when error state changes\n */\n onErrorChange?: (error: Error | undefined) => void\n\n /**\n * Callback when chat status changes\n */\n onStatusChange?: (status: ChatClientState) => void\n\n /**\n * Callback when subscription lifecycle changes.\n * This is independent from request lifecycle (`isLoading`, `status`).\n */\n onSubscriptionChange?: (isSubscribed: boolean) => void\n\n /**\n * Callback when connection lifecycle changes.\n */\n onConnectionStatusChange?: (status: ConnectionStatus) => void\n\n /**\n * Callback when session generation activity changes.\n * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).\n * Unlike `onLoadingChange` (request-local), this reflects shared generation\n * activity visible to all subscribers (e.g. across tabs/devices).\n */\n onSessionGeneratingChange?: (isGenerating: boolean) => void\n\n /**\n * Callback when a custom event is received from a server-side tool.\n * Custom events are emitted by tools using `context.emitCustomEvent()` during execution.\n *\n * @param eventType - The name of the custom event\n * @param data - The event payload data\n * @param context - Additional context including the toolCallId that emitted the event\n */\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n\n /**\n * Client-side tools with execution logic\n * When provided, tools with execute functions will be called automatically\n */\n tools?: TTools\n\n /**\n * Devtools hook metadata for this client instance.\n */\n devtools?: Partial<AIDevtoolsClientMetadata>\n\n /**\n * Factory that constructs the devtools bridge. Default is a no-op\n * factory, which keeps `@tanstack/ai-client/devtools` (the heavy\n * bridge implementation) out of the main entry's bundle. Frameworks\n * that need live devtools should pass the real factory from\n * `@tanstack/ai-client/devtools`.\n */\n devtoolsBridgeFactory?: ChatDevtoolsBridgeFactory\n\n /**\n * Stream processing options (optional)\n * Configure chunking strategy\n */\n streamProcessor?: {\n /**\n * Strategy for when to emit text updates\n * Defaults to ImmediateStrategy (every chunk)\n */\n chunkStrategy?: ChunkStrategy\n }\n}\n\n/**\n * Options for `ChatClient`. Exactly one of `connection` or `fetcher` must be\n * provided — the type-level XOR is enforced via `ChatTransport`.\n */\nexport type ChatClientOptions<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = InferredClientContext<TTools>,\n> = DistributedOmit<ChatClientBaseOptions<TTools, TContext>, 'context'> &\n ClientContextOptionFromTools<TTools, TContext> &\n ChatTransport\n\nexport interface ChatRequestBody {\n messages: Array<ModelMessage>\n data?: Record<string, any>\n}\n\n/**\n * Create a typed array of client tools with proper type inference.\n * This eliminates the need for `as const` when defining tool arrays.\n *\n * @example\n * ```ts\n * const tools = clientTools(\n * myTool1.client(() => result1),\n * myTool2.client(() => result2),\n * )\n *\n * // tools is now properly typed as a tuple with literal tool names\n * // This enables type narrowing when checking part.name === 'toolName'\n * ```\n */\nexport function clientTools<const T extends Array<AnyClientTool>>(\n ...tools: T\n): T {\n return tools\n}\n\n/**\n * Helper to create typed chat client options\n * Use this to get proper type inference for messages\n *\n * @example\n * ```ts\n * const tools = clientTools(myTool1, myTool2)\n *\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools,\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * ```\n */\nexport function createChatClientOptions<\n const TTools extends ReadonlyArray<AnyClientTool>,\n TContext = InferredClientContext<TTools>,\n>(\n options: ChatClientOptions<TTools, TContext>,\n): ChatClientOptions<TTools, TContext> {\n return options\n}\n\n/**\n * Extract the message type from chat options\n *\n * @example\n * ```ts\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools: [myTool1, myTool2],\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * // MyMessages is now Array<UIMessage<[typeof myTool1, typeof myTool2]>>\n * ```\n */\nexport type InferChatMessages<T> =\n T extends ChatClientOptions<infer TTools, any>\n ? Array<UIMessage<TTools>>\n : never\n"],"names":[],"mappings":"AA8iBO,SAAS,eACX,OACA;AACH,SAAO;AACT;AAkBO,SAAS,wBAId,SACqC;AACrC,SAAO;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-client",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "description": "Framework-agnostic headless client for TanStack AI chat, realtime sessions, streaming transports, and media generations.",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -41,8 +41,8 @@
41
41
  "src"
42
42
  ],
43
43
  "dependencies": {
44
- "@tanstack/ai": "0.28.0",
45
- "@tanstack/ai-event-client": "0.5.4"
44
+ "@tanstack/ai": "0.29.0",
45
+ "@tanstack/ai-event-client": "0.6.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@standard-schema/spec": "^1.1.0",
@@ -1218,11 +1218,19 @@ export class ChatClient<
1218
1218
  context,
1219
1219
  )
1220
1220
 
1221
- // Add result via processor
1221
+ // Add result via processor. `result.state` is the authoritative error
1222
+ // signal; `addToolResult` infers error-ness from the error message being
1223
+ // truthy. Pass an error message ONLY for output-error results (falling back
1224
+ // to a default so an empty message like `throw new Error()` still reaches
1225
+ // the terminal 'error' state), and `undefined` otherwise — so error
1226
+ // signalling derives solely from `result.state`, never from a stray
1227
+ // `result.errorText` on a successful result.
1222
1228
  this.processor.addToolResult(
1223
1229
  result.toolCallId,
1224
1230
  result.output,
1225
- result.errorText,
1231
+ result.state === 'output-error'
1232
+ ? result.errorText || 'Tool execution failed'
1233
+ : undefined,
1226
1234
  )
1227
1235
 
1228
1236
  // If stream is in progress, queue continuation check for after it ends
package/src/types.ts CHANGED
@@ -90,6 +90,7 @@ export type ToolCallState =
90
90
  | 'approval-requested' // Waiting for user approval
91
91
  | 'approval-responded' // User has approved/denied
92
92
  | 'complete' // Result is complete
93
+ | 'error' // Tool execution failed (terminal)
93
94
 
94
95
  /**
95
96
  * Tool result states - track the lifecycle of a tool result