@tanstack/ai-client 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,39 @@
1
1
  import { AnyClientTool, ModelMessage, StreamChunk } from '@tanstack/ai/client';
2
2
  import { ConnectionAdapter } from './connection-adapters.js';
3
3
  import { ChatClientOptions, ChatClientState, ChatFetcher, ConnectionStatus, MultimodalContent, UIMessage } from './types.js';
4
- export declare class ChatClient {
4
+ type ChatClientUpdateOptionsWithoutContext<TTools extends ReadonlyArray<AnyClientTool>> = {
5
+ connection?: ConnectionAdapter;
6
+ fetcher?: ChatFetcher;
7
+ /** @deprecated Use `forwardedProps` instead. */
8
+ body?: Record<string, any>;
9
+ forwardedProps?: Record<string, any>;
10
+ tools?: TTools;
11
+ onResponse?: (response?: Response) => void | Promise<void>;
12
+ onChunk?: (chunk: StreamChunk) => void;
13
+ onFinish?: (message: UIMessage) => void;
14
+ onError?: (error: Error) => void;
15
+ onSubscriptionChange?: (isSubscribed: boolean) => void;
16
+ onConnectionStatusChange?: (status: ConnectionStatus) => void;
17
+ onSessionGeneratingChange?: (isGenerating: boolean) => void;
18
+ onCustomEvent?: (eventType: string, data: unknown, context: {
19
+ toolCallId?: string;
20
+ }) => void;
21
+ };
22
+ type ClientToolResult = {
23
+ toolCallId: string;
24
+ tool: string;
25
+ output: any;
26
+ state?: 'output-available' | 'output-error';
27
+ errorText?: string;
28
+ };
29
+ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = any, TContext = unknown> {
5
30
  private readonly processor;
6
31
  private connection;
7
32
  private readonly uniqueId;
8
33
  private readonly threadId;
9
34
  private bodyOption;
10
35
  private forwardedPropsOption;
36
+ private context;
11
37
  private pendingMessageBody;
12
38
  private isLoading;
13
39
  private isSubscribed;
@@ -28,6 +54,8 @@ export declare class ChatClient {
28
54
  private currentMessageId;
29
55
  private readonly postStreamActions;
30
56
  private readonly pendingToolExecutions;
57
+ private activeClientTools;
58
+ private activeContext;
31
59
  private continuationPending;
32
60
  private subscriptionAbortController;
33
61
  private processingResolve;
@@ -39,7 +67,7 @@ export declare class ChatClient {
39
67
  private readonly activeRunIds;
40
68
  private devtoolsMounted;
41
69
  private readonly callbacksRef;
42
- constructor(options: ChatClientOptions);
70
+ constructor(options: ChatClientOptions<TTools, TContext>);
43
71
  mountDevtools(): void;
44
72
  private generateUniqueId;
45
73
  private setIsLoading;
@@ -154,14 +182,9 @@ export declare class ChatClient {
154
182
  /**
155
183
  * Add the result of a client-side tool execution
156
184
  */
157
- addToolResult(result: {
158
- toolCallId: string;
159
- tool: string;
160
- output: any;
161
- state?: 'output-available' | 'output-error';
162
- errorText?: string;
163
- }): Promise<void>;
164
- private addToolResultInternal;
185
+ addToolResult(result: ClientToolResult): Promise<void>;
186
+ private addToolResultForClientTool;
187
+ private validateClientToolOutput;
165
188
  /**
166
189
  * Respond to a tool approval request
167
190
  */
@@ -190,7 +213,7 @@ export declare class ChatClient {
190
213
  /**
191
214
  * Get current messages
192
215
  */
193
- getMessages(): Array<UIMessage>;
216
+ getMessages(): Array<UIMessage<TTools>>;
194
217
  /**
195
218
  * Get loading state
196
219
  */
@@ -221,27 +244,12 @@ export declare class ChatClient {
221
244
  /**
222
245
  * Manually set messages
223
246
  */
224
- setMessagesManually(messages: Array<UIMessage>): void;
247
+ setMessagesManually(messages: Array<UIMessage<TTools>>): void;
225
248
  /**
226
249
  * Update options refs (for use in React hooks to avoid recreating client)
227
250
  */
228
- updateOptions(options: {
229
- connection?: ConnectionAdapter;
230
- fetcher?: ChatFetcher;
231
- /** @deprecated Use `forwardedProps` instead. */
232
- body?: Record<string, any>;
233
- forwardedProps?: Record<string, any>;
234
- tools?: ReadonlyArray<AnyClientTool>;
235
- onResponse?: (response?: Response) => void | Promise<void>;
236
- onChunk?: (chunk: StreamChunk) => void;
237
- onFinish?: (message: UIMessage) => void;
238
- onError?: (error: Error) => void;
239
- onSubscriptionChange?: (isSubscribed: boolean) => void;
240
- onConnectionStatusChange?: (status: ConnectionStatus) => void;
241
- onSessionGeneratingChange?: (isGenerating: boolean) => void;
242
- onCustomEvent?: (eventType: string, data: unknown, context: {
243
- toolCallId?: string;
244
- }) => void;
245
- }): void;
251
+ updateOptions(options: ChatClientUpdateOptionsWithoutContext<TTools>): void;
252
+ updateOptions(options: ChatClientUpdateOptionsWithoutContext<TTools> & Pick<ChatClientOptions<TTools, TContext>, 'context'>): void;
246
253
  dispose(): void;
247
254
  }
255
+ export {};
@@ -1,4 +1,4 @@
1
- import { StreamProcessor, normalizeToUIMessage, generateMessageId, convertSchemaToJsonSchema } from "@tanstack/ai/client";
1
+ import { StreamProcessor, normalizeToUIMessage, generateMessageId, convertSchemaToJsonSchema, isStandardSchema, parseWithStandardSchema } from "@tanstack/ai/client";
2
2
  import { createNoOpChatDevtoolsBridge } from "./devtools-noop.js";
3
3
  import { normalizeConnectionAdapter, fetcherToConnectionAdapter } from "./connection-adapters.js";
4
4
  function resolveTransport(transport) {
@@ -23,6 +23,7 @@ class ChatClient {
23
23
  // merged on every send, with `forwardedProps` winning on key collision.
24
24
  bodyOption = {};
25
25
  forwardedPropsOption = {};
26
+ context = void 0;
26
27
  pendingMessageBody = void 0;
27
28
  isLoading = false;
28
29
  isSubscribed = false;
@@ -44,6 +45,8 @@ class ChatClient {
44
45
  postStreamActions = [];
45
46
  // Track pending client tool executions to await them before stream finalization
46
47
  pendingToolExecutions = /* @__PURE__ */ new Map();
48
+ activeClientTools = null;
49
+ activeContext = void 0;
47
50
  // Flag to deduplicate continuation checks during action draining
48
51
  continuationPending = false;
49
52
  subscriptionAbortController = null;
@@ -63,6 +66,7 @@ class ChatClient {
63
66
  this.threadId = options.threadId || this.generateUniqueId("thread");
64
67
  this.bodyOption = options.body || {};
65
68
  this.forwardedPropsOption = options.forwardedProps || {};
69
+ this.context = options.context;
66
70
  this.connection = normalizeConnectionAdapter(resolveTransport(options));
67
71
  this.clientToolsRef = { current: /* @__PURE__ */ new Map() };
68
72
  if (options.tools) {
@@ -186,24 +190,32 @@ class ChatClient {
186
190
  }
187
191
  },
188
192
  onToolCall: (args) => {
189
- const clientTool = this.clientToolsRef.current.get(args.toolName);
193
+ const clientTools = this.activeClientTools ?? this.clientToolsRef.current;
194
+ const clientTool = clientTools.get(args.toolName);
190
195
  const executeFunc = clientTool?.execute;
191
196
  if (executeFunc) {
192
197
  const runEventContext = this.devtoolsBridge.getCurrentRunEventContext();
193
198
  const executionPromise = (async () => {
194
199
  try {
195
- const output = await executeFunc(args.input);
196
- await this.addToolResultInternal(
200
+ const context = this.activeClientTools === null ? this.context : this.activeContext;
201
+ const output = await executeFunc(args.input, {
202
+ toolCallId: args.toolCallId,
203
+ context,
204
+ emitCustomEvent: () => {
205
+ }
206
+ });
207
+ await this.addToolResultForClientTool(
197
208
  {
198
209
  toolCallId: args.toolCallId,
199
210
  tool: args.toolName,
200
211
  output,
201
212
  state: "output-available"
202
213
  },
214
+ clientTool,
203
215
  runEventContext
204
216
  );
205
217
  } catch (error) {
206
- await this.addToolResultInternal(
218
+ await this.addToolResultForClientTool(
207
219
  {
208
220
  toolCallId: args.toolCallId,
209
221
  tool: args.toolName,
@@ -211,6 +223,7 @@ class ChatClient {
211
223
  state: "output-error",
212
224
  errorText: error.message
213
225
  },
226
+ clientTool,
214
227
  runEventContext
215
228
  );
216
229
  } finally {
@@ -545,6 +558,8 @@ class ChatClient {
545
558
  let runTerminalEventEmitted = false;
546
559
  try {
547
560
  const messages = this.processor.getMessages();
561
+ const clientTools = new Map(this.clientToolsRef.current);
562
+ const runtimeContext = this.context;
548
563
  await this.callbacksRef.current.onResponse();
549
564
  if (signal.aborted) {
550
565
  return false;
@@ -558,19 +573,19 @@ class ChatClient {
558
573
  this.currentStreamId = this.generateUniqueId("stream");
559
574
  this.devtoolsBridge.setCurrentStreamId(this.currentStreamId);
560
575
  this.currentMessageId = null;
576
+ this.activeClientTools = clientTools;
577
+ this.activeContext = runtimeContext;
561
578
  this.processor.prepareAssistantMessage();
562
579
  this.ensureSubscription();
563
580
  const processingComplete = this.waitForProcessing();
564
581
  const runContext = {
565
582
  threadId: this.threadId,
566
583
  runId: `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
567
- clientTools: Array.from(this.clientToolsRef.current.values()).map(
568
- (t) => ({
569
- name: t.name,
570
- description: t.description,
571
- parameters: t.inputSchema ? convertSchemaToJsonSchema(t.inputSchema) : { type: "object" }
572
- })
573
- ),
584
+ clientTools: Array.from(clientTools.values()).map((t) => ({
585
+ name: t.name,
586
+ description: t.description,
587
+ parameters: t.inputSchema ? convertSchemaToJsonSchema(t.inputSchema) : { type: "object" }
588
+ })),
574
589
  forwardedProps: { ...mergedBody }
575
590
  };
576
591
  this.devtoolsBridge.beginRun(runContext.runId, this.threadId);
@@ -639,6 +654,8 @@ class ChatClient {
639
654
  this.currentStreamId = null;
640
655
  this.devtoolsBridge.setCurrentStreamId(null);
641
656
  this.currentMessageId = null;
657
+ this.activeClientTools = null;
658
+ this.activeContext = void 0;
642
659
  this.abortController = null;
643
660
  this.setIsLoading(false);
644
661
  this.pendingMessageBody = void 0;
@@ -740,9 +757,25 @@ class ChatClient {
740
757
  * Add the result of a client-side tool execution
741
758
  */
742
759
  async addToolResult(result) {
743
- await this.addToolResultInternal(result);
760
+ const clientTool = this.clientToolsRef.current.get(result.tool);
761
+ await this.addToolResultForClientTool(result, clientTool);
744
762
  }
745
- async addToolResultInternal(result, context) {
763
+ async addToolResultForClientTool(result, clientTool, context) {
764
+ if (clientTool && result.state !== "output-error") {
765
+ try {
766
+ result = {
767
+ ...result,
768
+ output: this.validateClientToolOutput(clientTool, result.output)
769
+ };
770
+ } catch (error) {
771
+ result = {
772
+ ...result,
773
+ output: null,
774
+ state: "output-error",
775
+ errorText: error.message
776
+ };
777
+ }
778
+ }
746
779
  this.events.toolResultAdded(
747
780
  result.toolCallId,
748
781
  result.tool,
@@ -761,6 +794,12 @@ class ChatClient {
761
794
  }
762
795
  await this.checkForContinuation();
763
796
  }
797
+ validateClientToolOutput(clientTool, output) {
798
+ if (clientTool.outputSchema && isStandardSchema(clientTool.outputSchema)) {
799
+ return parseWithStandardSchema(clientTool.outputSchema, output);
800
+ }
801
+ return output;
802
+ }
764
803
  /**
765
804
  * Respond to a tool approval request
766
805
  */
@@ -904,9 +943,6 @@ class ChatClient {
904
943
  this.processor.setMessages(messages);
905
944
  this.devtoolsBridge.emitSnapshot();
906
945
  }
907
- /**
908
- * Update options refs (for use in React hooks to avoid recreating client)
909
- */
910
946
  updateOptions(options) {
911
947
  if (options.connection !== void 0 || options.fetcher !== void 0) {
912
948
  const wasSubscribed = this.isSubscribed;
@@ -937,6 +973,9 @@ class ChatClient {
937
973
  if (options.forwardedProps !== void 0) {
938
974
  this.forwardedPropsOption = options.forwardedProps;
939
975
  }
976
+ if ("context" in options) {
977
+ this.context = options.context;
978
+ }
940
979
  if (options.tools !== void 0) {
941
980
  this.clientToolsRef.current = /* @__PURE__ */ new Map();
942
981
  for (const tool of options.tools) {
@@ -1 +1 @@
1
- {"version":3,"file":"chat-client.js","sources":["../../src/chat-client.ts"],"sourcesContent":["import {\n StreamProcessor,\n convertSchemaToJsonSchema,\n generateMessageId,\n normalizeToUIMessage,\n} from '@tanstack/ai/client'\nimport { createNoOpChatDevtoolsBridge } from './devtools-noop'\nimport {\n fetcherToConnectionAdapter,\n normalizeConnectionAdapter,\n} from './connection-adapters'\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\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 private readonly processor: StreamProcessor\n private connection: SubscribeConnectionAdapter\n private readonly uniqueId: string\n private readonly threadId: string\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 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 // 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) {\n this.uniqueId = options.id || this.generateUniqueId('chat')\n this.threadId = options.threadId || this.generateUniqueId('thread')\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.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 this.processor = new StreamProcessor({\n ...(options.streamProcessor?.chunkStrategy\n ? { chunkStrategy: options.streamProcessor.chunkStrategy }\n : {}),\n ...(options.initialMessages\n ? { initialMessages: options.initialMessages }\n : {}),\n events: {\n onMessagesChange: (messages: Array<UIMessage>) => {\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 clientTool = this.clientToolsRef.current.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 output = await executeFunc(args.input)\n await this.addToolResultInternal(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output,\n state: 'output-available',\n },\n runEventContext,\n )\n } catch (error: any) {\n await this.addToolResultInternal(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output: null,\n state: 'output-error',\n errorText: error.message,\n },\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\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 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.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 this.callbacksRef.current.onChunk(chunk)\n if (chunk.type === 'RUN_STARTED') {\n this.activeRunIds.add(chunk.runId)\n this.setSessionGenerating(true)\n }\n this.devtoolsBridge.observeChunk(chunk)\n this.processor.processChunk(chunk)\n // RUN_FINISHED / RUN_ERROR signal run completion — resolve processing\n // (redundant if onStreamEnd already resolved it, harmless)\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n // RUN_FINISHED has runId in its schema; RUN_ERROR carries it via the\n // AG-UI passthrough so adapters can correlate per-run errors. Extract\n // both so a RUN_ERROR with a runId only clears that run, not every\n // active run in the session.\n const runId =\n 'runId' in chunk && typeof chunk.runId === 'string'\n ? chunk.runId\n : undefined\n if (runId) {\n this.activeRunIds.delete(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 }\n this.setSessionGenerating(this.activeRunIds.size > 0)\n this.resolveProcessing()\n }\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\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\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\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: `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n clientTools: Array.from(this.clientToolsRef.current.values()).map(\n (t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema\n ? convertSchemaToJsonSchema(t.inputSchema)\n : { type: 'object' },\n }),\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.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 this.cancelInFlightStream({ setReadyStatus: true })\n this.events.stopped()\n }\n\n /**\n * Clear all messages\n */\n clear(): void {\n this.processor.clearMessages()\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: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n }): Promise<void> {\n await this.addToolResultInternal(result)\n }\n\n private async addToolResultInternal(\n result: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n },\n context?: ChatClientRunEventContext,\n ): Promise<void> {\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 /**\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> {\n return this.processor.getMessages()\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>): 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: {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n /** @deprecated Use `forwardedProps` instead. */\n body?: Record<string, any>\n forwardedProps?: Record<string, any>\n tools?: ReadonlyArray<AnyClientTool>\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 }): 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 slot independently so callers can update one without\n // wiping the other. (Passing `undefined` for either field is a \"leave\n // unchanged\" signal - to clear a slot, pass an empty object `{}`.)\n if (options.body !== undefined) {\n this.bodyOption = options.body\n }\n if (options.forwardedProps !== undefined) {\n this.forwardedPropsOption = options.forwardedProps\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 normalizeConnectionAdapter,\n} from './connection-adapters'\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 // 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 // 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 this.processor = new StreamProcessor({\n ...(options.streamProcessor?.chunkStrategy\n ? { chunkStrategy: options.streamProcessor.chunkStrategy }\n : {}),\n ...(options.initialMessages\n ? { initialMessages: options.initialMessages }\n : {}),\n events: {\n onMessagesChange: (messages: Array<UIMessage>) => {\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\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 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.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 this.callbacksRef.current.onChunk(chunk)\n if (chunk.type === 'RUN_STARTED') {\n this.activeRunIds.add(chunk.runId)\n this.setSessionGenerating(true)\n }\n this.devtoolsBridge.observeChunk(chunk)\n this.processor.processChunk(chunk)\n // RUN_FINISHED / RUN_ERROR signal run completion — resolve processing\n // (redundant if onStreamEnd already resolved it, harmless)\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n // RUN_FINISHED has runId in its schema; RUN_ERROR carries it via the\n // AG-UI passthrough so adapters can correlate per-run errors. Extract\n // both so a RUN_ERROR with a runId only clears that run, not every\n // active run in the session.\n const runId =\n 'runId' in chunk && typeof chunk.runId === 'string'\n ? chunk.runId\n : undefined\n if (runId) {\n this.activeRunIds.delete(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 }\n this.setSessionGenerating(this.activeRunIds.size > 0)\n this.resolveProcessing()\n }\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\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: `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\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.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 this.cancelInFlightStream({ setReadyStatus: true })\n this.events.stopped()\n }\n\n /**\n * Clear all messages\n */\n clear(): void {\n this.processor.clearMessages()\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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"}
@@ -758,7 +758,7 @@ function hydrateToolCallOutputs(parts, fixtureOutput) {
758
758
  (candidate) => candidate.type === "tool-call" && candidate.id === part.toolCallId && candidate.output === void 0
759
759
  );
760
760
  if (toolCall) {
761
- toolCall.output = parseFixtureResultContent(part.content);
761
+ toolCall.output = Array.isArray(part.content) ? part.content : parseFixtureResultContent(part.content);
762
762
  }
763
763
  }
764
764
  if (fixtureOutput.mappedToolCallId && fixtureOutput.output !== void 0) {