@assistant-ui/ai-sdk 0.0.11 → 0.0.12

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.
Files changed (119) hide show
  1. package/dist/converters/convertMessage.d.ts +8 -6
  2. package/dist/converters/convertMessage.d.ts.map +1 -1
  3. package/dist/converters/convertMessage.js +48 -22
  4. package/dist/converters/convertMessage.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.native.d.ts +1 -1
  9. package/dist/index.native.d.ts.map +1 -1
  10. package/dist/index.native.js +1 -1
  11. package/dist/model-context/injectInteractableContext.d.ts +3 -0
  12. package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
  13. package/dist/model-context/injectInteractableContext.js +3 -0
  14. package/dist/model-context/injectInteractableContext.js.map +1 -1
  15. package/dist/model-context/injectQuoteContext.d.ts +1 -0
  16. package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
  17. package/dist/model-context/injectQuoteContext.js +1 -0
  18. package/dist/model-context/injectQuoteContext.js.map +1 -1
  19. package/dist/runtime/AISDKChat.js +1 -1
  20. package/dist/runtime/AISDKThreads.d.ts +5 -0
  21. package/dist/runtime/AISDKThreads.d.ts.map +1 -1
  22. package/dist/runtime/AISDKThreads.js +37 -4
  23. package/dist/runtime/AISDKThreads.js.map +1 -1
  24. package/dist/runtime/DynamicChatTransport.d.ts +49 -0
  25. package/dist/runtime/DynamicChatTransport.d.ts.map +1 -0
  26. package/dist/runtime/DynamicChatTransport.js +147 -0
  27. package/dist/runtime/DynamicChatTransport.js.map +1 -0
  28. package/dist/runtime/getResumableAdapter.d.ts +5 -0
  29. package/dist/runtime/getResumableAdapter.d.ts.map +1 -0
  30. package/dist/runtime/getResumableAdapter.js +12 -0
  31. package/dist/runtime/getResumableAdapter.js.map +1 -0
  32. package/dist/runtime/sdkIdentity.js +1 -1
  33. package/dist/runtime/toolHistoryCodec.d.ts +20 -0
  34. package/dist/runtime/toolHistoryCodec.d.ts.map +1 -0
  35. package/dist/runtime/toolHistoryCodec.js +107 -0
  36. package/dist/runtime/toolHistoryCodec.js.map +1 -0
  37. package/dist/runtime/useAISDKRuntime.d.ts +4 -1
  38. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
  39. package/dist/runtime/useAISDKRuntime.js +154 -155
  40. package/dist/runtime/useAISDKRuntime.js.map +1 -1
  41. package/dist/runtime/useChatRuntime.d.ts +14 -2
  42. package/dist/runtime/useChatRuntime.d.ts.map +1 -1
  43. package/dist/runtime/useChatRuntime.js +12 -3
  44. package/dist/runtime/useChatRuntime.js.map +1 -1
  45. package/dist/runtime/useChatThread.d.ts +3 -2
  46. package/dist/runtime/useChatThread.d.ts.map +1 -1
  47. package/dist/runtime/useChatThread.js +56 -36
  48. package/dist/runtime/useChatThread.js.map +1 -1
  49. package/dist/runtime/useDynamicChatTransport.d.ts +4 -0
  50. package/dist/runtime/useDynamicChatTransport.d.ts.map +1 -0
  51. package/dist/runtime/useDynamicChatTransport.js +64 -0
  52. package/dist/runtime/useDynamicChatTransport.js.map +1 -0
  53. package/dist/runtime/useExternalHistory.d.ts.map +1 -1
  54. package/dist/runtime/useExternalHistory.js +11 -109
  55. package/dist/runtime/useExternalHistory.js.map +1 -1
  56. package/dist/runtime/useResourceCleanup.js +1 -1
  57. package/dist/runtime/useStreamingTiming.js +2 -2
  58. package/dist/runtime/useStreamingTiming.js.map +1 -1
  59. package/dist/tools/generativeTools.d.ts +2 -1
  60. package/dist/tools/generativeTools.d.ts.map +1 -1
  61. package/dist/tools/generativeTools.js +5 -5
  62. package/dist/tools/generativeTools.js.map +1 -1
  63. package/dist/usage.js +1 -1
  64. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
  65. package/dist/utils/sliceMessagesUntil.js +1 -2
  66. package/dist/utils/sliceMessagesUntil.js.map +1 -1
  67. package/package.json +11 -10
  68. package/src/converters/convertMessage.test.ts +120 -2
  69. package/src/converters/convertMessage.tool-args-status.test.tsx +185 -0
  70. package/src/converters/convertMessage.ts +60 -19
  71. package/src/converters/toCreateMessage.test.ts +27 -0
  72. package/src/index.native.ts +1 -1
  73. package/src/index.ts +1 -1
  74. package/src/model-context/injectInteractableContext.ts +3 -0
  75. package/src/model-context/injectQuoteContext.ts +1 -0
  76. package/src/runtime/AISDKChat.integration.test.tsx +57 -2
  77. package/src/runtime/AISDKThreads.cloud.test.ts +3 -0
  78. package/src/runtime/AISDKThreads.test.ts +181 -0
  79. package/src/runtime/AISDKThreads.ts +31 -4
  80. package/src/runtime/DynamicChatTransport.test.ts +203 -0
  81. package/src/runtime/DynamicChatTransport.ts +273 -0
  82. package/src/runtime/__tests__/controlled-transport.ts +3 -0
  83. package/src/runtime/getResumableAdapter.ts +16 -0
  84. package/src/runtime/toolHistoryCodec.test.ts +161 -0
  85. package/src/runtime/toolHistoryCodec.ts +207 -0
  86. package/src/runtime/useAISDKRuntime.approval.test.tsx +12 -0
  87. package/src/runtime/useAISDKRuntime.reload.test.tsx +219 -0
  88. package/src/runtime/useAISDKRuntime.test.ts +538 -5
  89. package/src/runtime/useAISDKRuntime.ts +161 -51
  90. package/src/runtime/useChatRuntime.integration.test.tsx +319 -4
  91. package/src/runtime/useChatRuntime.local-storage.test.tsx +123 -0
  92. package/src/runtime/useChatRuntime.test.ts +107 -1
  93. package/src/runtime/useChatRuntime.ts +26 -4
  94. package/src/runtime/useChatThread.binding.test.tsx +143 -0
  95. package/src/runtime/useChatThread.ts +98 -81
  96. package/src/runtime/useDynamicChatTransport.ts +26 -0
  97. package/src/runtime/useExternalHistory.test.ts +111 -0
  98. package/src/runtime/useExternalHistory.ts +14 -210
  99. package/src/runtime/useStreamingTiming.ts +2 -2
  100. package/src/tools/generativeTools.test.ts +1 -1
  101. package/src/tools/generativeTools.ts +5 -6
  102. package/src/utils/sliceMessagesUntil.test.ts +2 -6
  103. package/src/utils/sliceMessagesUntil.ts +1 -5
  104. package/dist/converters/modelContentEnvelope.d.ts +0 -14
  105. package/dist/converters/modelContentEnvelope.d.ts.map +0 -1
  106. package/dist/converters/modelContentEnvelope.js +0 -22
  107. package/dist/converters/modelContentEnvelope.js.map +0 -1
  108. package/dist/converters/toolOutputConversion.d.ts +0 -26
  109. package/dist/converters/toolOutputConversion.d.ts.map +0 -1
  110. package/dist/converters/toolOutputConversion.js +0 -31
  111. package/dist/converters/toolOutputConversion.js.map +0 -1
  112. package/dist/tools/frontendTools.d.ts +0 -30
  113. package/dist/tools/frontendTools.d.ts.map +0 -1
  114. package/dist/tools/frontendTools.js +0 -33
  115. package/dist/tools/frontendTools.js.map +0 -1
  116. package/src/converters/modelContentEnvelope.ts +0 -41
  117. package/src/converters/toolOutputConversion.ts +0 -26
  118. package/src/tools/frontendTools.test.ts +0 -205
  119. package/src/tools/frontendTools.ts +0 -83
@@ -2,37 +2,14 @@
2
2
  import { useAISDKRuntime } from "./useAISDKRuntime.js";
3
3
  import { AssistantChatTransport } from "../transport/AssistantChatTransport.js";
4
4
  import { useResourceCleanup } from "./useResourceCleanup.js";
5
+ import { getResumableAdapter } from "./getResumableAdapter.js";
6
+ import { DynamicChatTransport, getResumedStreamIds } from "./DynamicChatTransport.js";
7
+ import { useDynamicChatTransport } from "./useDynamicChatTransport.js";
5
8
  import { pickExternalStoreSharedOptions } from "@assistant-ui/core";
6
- import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "@assistant-ui/tap/react-shim";
9
+ import { useCallback, useEffect, useEffectEvent, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "@assistant-ui/tap/react-shim";
7
10
  import { Chat, useChat } from "@ai-sdk/react";
8
11
  //#region src/runtime/useChatThread.ts
9
- const useDynamicChatTransport = (transport) => {
10
- const transportRef = useRef(transport);
11
- useEffect(() => {
12
- transportRef.current = transport;
13
- });
14
- return useMemo(() => new Proxy(transportRef.current, { get(_, prop) {
15
- const res = transportRef.current[prop];
16
- return typeof res === "function" ? res.bind(transportRef.current) : res;
17
- } }), []);
18
- };
19
- const getResumableAdapter = (transport) => {
20
- if (transport instanceof AssistantChatTransport) return transport.getResumableAdapter();
21
- const candidate = transport.getResumableAdapter;
22
- if (typeof candidate !== "function") return void 0;
23
- return candidate.call(transport);
24
- };
25
12
  const getNoPendingStreamId = () => null;
26
- const resumedStreamIdsByStorage = /* @__PURE__ */ new WeakMap();
27
- const getResumedStreamIds = (storage) => {
28
- if (!storage) return /* @__PURE__ */ new Set();
29
- let resumedStreamIds = resumedStreamIdsByStorage.get(storage);
30
- if (!resumedStreamIds) {
31
- resumedStreamIds = /* @__PURE__ */ new Set();
32
- resumedStreamIdsByStorage.set(storage, resumedStreamIds);
33
- }
34
- return resumedStreamIds;
35
- };
36
13
  /**
37
14
  * Splits the combined options into the assistant-ui side and the `ChatInit`
38
15
  * remainder the AI SDK consumes, so external `Chat` construction forwards the
@@ -86,8 +63,27 @@ const createChat = (init, callbacksRef) => {
86
63
  const useChatThread = (options, env) => {
87
64
  const { adapters, transport: transportOptions, throttle, toCreateMessage, onResume, onResumeToolCall, onRespondToToolApproval, onResumeError, joinStrategy, messageRepository, unstable_onBranchChange, chatInit: chatOptions } = splitChatThreadOptions(options);
88
65
  const { id, isMainThread, getThreadListItem, stopOnClientDestroy = true, hostDestroySignal, chat: externalChat, messageRepositoryInstance } = env;
89
- const sourceTransport = useMemo(() => transportOptions === void 0 ? new AssistantChatTransport() : externalChat === void 0 && transportOptions instanceof AssistantChatTransport ? transportOptions.__internal_clone() : transportOptions, [transportOptions, externalChat]);
90
- const transport = useDynamicChatTransport(sourceTransport);
66
+ const defaultTransport = useMemo(() => new AssistantChatTransport(), []);
67
+ const configuredTransport = transportOptions ?? defaultTransport;
68
+ const transport = useDynamicChatTransport(configuredTransport, externalChat === void 0);
69
+ const transportContextOwner = useMemo(() => ({}), []);
70
+ const getThreadListItemRef = useRef(getThreadListItem);
71
+ useInsertionEffect(() => {
72
+ getThreadListItemRef.current = getThreadListItem;
73
+ }, [getThreadListItem]);
74
+ const getCurrentThreadListItem = useCallback(() => getThreadListItemRef.current(), []);
75
+ const resumableStorage = useMemo(() => transport instanceof DynamicChatTransport ? transport.getCurrentResumableStorage() : getResumableAdapter(transport)?.storage, [transport]);
76
+ const transportBindingRef = useRef(null);
77
+ let initialTransportBinding = null;
78
+ const chatTransport = useMemo(() => transport instanceof DynamicChatTransport ? transport.createThreadProxy(transportContextOwner, () => {
79
+ const binding = transportBindingRef.current ?? initialTransportBinding;
80
+ if (!binding) throw new Error("Chat transport used before runtime setup");
81
+ return binding;
82
+ }) : transport, [
83
+ initialTransportBinding,
84
+ transport,
85
+ transportContextOwner
86
+ ]);
91
87
  const latestChatOptionsRef = useRef(chatOptions);
92
88
  useEffect(() => {
93
89
  latestChatOptionsRef.current = chatOptions;
@@ -95,7 +91,7 @@ const useChatThread = (options, env) => {
95
91
  const [ownedChat] = useState(() => externalChat ?? createChat({
96
92
  ...chatOptions,
97
93
  id,
98
- transport
94
+ transport: chatTransport
99
95
  }, latestChatOptionsRef));
100
96
  const chat = useChat({
101
97
  chat: externalChat ?? ownedChat,
@@ -123,14 +119,36 @@ const useChatThread = (options, env) => {
123
119
  unstable_hostApprovalOwner: externalChat ?? ownedChat,
124
120
  ...unstable_onBranchChange && { unstable_onBranchChange }
125
121
  });
126
- if (sourceTransport instanceof AssistantChatTransport) {
127
- sourceTransport.setRuntime(runtime);
128
- sourceTransport.__internal_setGetThreadListItem(getThreadListItem);
122
+ initialTransportBinding = {
123
+ runtime,
124
+ getThreadListItem: getCurrentThreadListItem
125
+ };
126
+ useInsertionEffect(() => {
127
+ transportBindingRef.current = {
128
+ runtime,
129
+ getThreadListItem: getCurrentThreadListItem
130
+ };
131
+ }, [runtime, getCurrentThreadListItem]);
132
+ const registerTransportContext = useEffectEvent((dynamicTransport, chatId) => {
133
+ dynamicTransport.setThreadContext(chatId, transportContextOwner, runtime, getCurrentThreadListItem);
134
+ return () => dynamicTransport.unregisterThread(chatId, transportContextOwner);
135
+ });
136
+ useInsertionEffect(() => {
137
+ if (!(transport instanceof DynamicChatTransport)) return void 0;
138
+ return registerTransportContext(transport, id);
139
+ }, [
140
+ id,
141
+ runtime,
142
+ transport,
143
+ transportContextOwner
144
+ ]);
145
+ if (!(transport instanceof DynamicChatTransport) && configuredTransport instanceof AssistantChatTransport) {
146
+ configuredTransport.setRuntime(runtime);
147
+ configuredTransport.__internal_setGetThreadListItem(getCurrentThreadListItem);
129
148
  }
130
149
  const subscribeToRuntime = useCallback((callback) => runtime.thread.subscribe(callback), [runtime]);
131
150
  const getHistoryLoadingSnapshot = useCallback(() => runtime.thread.getState().isLoading, [runtime]);
132
151
  const isLoadingHistory = useSyncExternalStore(subscribeToRuntime, getHistoryLoadingSnapshot, getHistoryLoadingSnapshot);
133
- const resumableStorage = useMemo(() => getResumableAdapter(sourceTransport)?.storage, [sourceTransport]);
134
152
  const subscribeToResumableStorage = useCallback((callback_0) => isMainThread ? resumableStorage?.subscribe?.(callback_0, id) ?? (() => {}) : () => {}, [
135
153
  id,
136
154
  isMainThread,
@@ -143,12 +161,13 @@ const useChatThread = (options, env) => {
143
161
  ]);
144
162
  const pendingStreamId = useSyncExternalStore(subscribeToResumableStorage, getPendingStreamId, getNoPendingStreamId);
145
163
  const isChatRunning = chat.status === "submitted" || chat.status === "streaming";
146
- const resumedStreamIds = useMemo(() => getResumedStreamIds(resumableStorage), [resumableStorage]);
164
+ const staticResumedStreamIds = useMemo(() => getResumedStreamIds(resumableStorage), [resumableStorage]);
147
165
  const onResumeErrorRef = useRef(onResumeError);
148
166
  useEffect(() => {
149
167
  onResumeErrorRef.current = onResumeError;
150
168
  });
151
169
  useEffect(() => {
170
+ const resumedStreamIds = transport instanceof DynamicChatTransport ? transport.getResumedStreamIds() : staticResumedStreamIds;
152
171
  if (!pendingStreamId || resumedStreamIds.has(pendingStreamId)) return;
153
172
  if (isChatRunning) {
154
173
  resumedStreamIds.add(pendingStreamId);
@@ -181,7 +200,8 @@ const useChatThread = (options, env) => {
181
200
  pendingStreamId,
182
201
  ownedChat,
183
202
  resumableStorage,
184
- resumedStreamIds
203
+ staticResumedStreamIds,
204
+ transport
185
205
  ]);
186
206
  return runtime;
187
207
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useChatThread.js","names":["Chat","useChat","UIMessage","MessageRepository","pickExternalStoreSharedOptions","AssistantRuntime","ExternalStoreSharedOptions","useAISDKRuntime","AISDKRuntimeAdapter","CustomToCreateMessageFunction","ChatInit","ChatTransport","AssistantChatTransport","InitializableThreadListItem","AssistantChatResumableOptions","ResumableClientStorage","useCallback","useEffect","useMemo","useRef","useState","useSyncExternalStore","useResourceCleanup","ChatThreadOptions","UI_MESSAGE","throttle","adapters","toCreateMessage","onResume","onResumeToolCall","onRespondToToolApproval","onResumeError","error","joinStrategy","messageRepository","unstable_onBranchChange","ChatThreadEnvironment","id","isMainThread","getThreadListItem","stopOnClientDestroy","hostDestroySignal","AbortSignal","chat","messageRepositoryInstance","useDynamicChatTransport","transport","transportRef","current","dynamicTransport","Proxy","get","_","prop","res","bind","getResumableAdapter","candidate","undefined","call","getNoPendingStreamId","resumedStreamIdsByStorage","WeakMap","Set","getResumedStreamIds","storage","resumedStreamIds","set","splitChatThreadOptions","options","isDisabled","_isDisabled","isSendDisabled","_isSendDisabled","unstable_capabilities","_unstable_capabilities","suggestions","_suggestions","chatInit","ChatCallbacks","Pick","requestsByChat","createChat","init","callbacksRef","sendMessages","Symbol","reconnectToStream","onToolCall","arg","onData","onFinish","onError","sendAutomaticallyWhen","useChatThread","env","transportOptions","chatOptions","externalChat","sourceTransport","__internal_clone","latestChatOptionsRef","ownedChat","stop","catch","runtime","threadList","threadId","unstable_messageRepositoryInstance","unstable_hostApprovalOwner","setRuntime","__internal_setGetThreadListItem","subscribeToRuntime","callback","thread","subscribe","getHistoryLoadingSnapshot","getState","isLoading","isLoadingHistory","resumableStorage","subscribeToResumableStorage","getPendingStreamId","getStreamId","pendingStreamId","isChatRunning","status","onResumeErrorRef","has","add","activeChat","clearError","pending","resumeStream","request","then","err","console","warn","callbackError","clear"],"sources":["../../src/runtime/useChatThread.ts"],"sourcesContent":["\"use client\";\n\nimport { Chat, useChat, type UIMessage } from \"@ai-sdk/react\";\nimport type { MessageRepository } from \"@assistant-ui/core/internal\";\nimport {\n pickExternalStoreSharedOptions,\n type AssistantRuntime,\n type ExternalStoreSharedOptions,\n} from \"@assistant-ui/core\";\nimport {\n useAISDKRuntime,\n type AISDKRuntimeAdapter,\n type CustomToCreateMessageFunction,\n} from \"./useAISDKRuntime\";\nimport type { ChatInit, ChatTransport } from \"ai\";\nimport {\n AssistantChatTransport,\n type InitializableThreadListItem,\n} from \"../transport/AssistantChatTransport\";\nimport type {\n AssistantChatResumableOptions,\n ResumableClientStorage,\n} from \"../transport/resumable\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { useResourceCleanup } from \"./useResourceCleanup\";\n\nexport type ChatThreadOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> &\n ExternalStoreSharedOptions & {\n throttle?: number | undefined;\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n toCreateMessage?: CustomToCreateMessageFunction;\n onResume?: AISDKRuntimeAdapter[\"onResume\"];\n onResumeToolCall?: AISDKRuntimeAdapter[\"onResumeToolCall\"];\n onRespondToToolApproval?: AISDKRuntimeAdapter[\"onRespondToToolApproval\"];\n /**\n * Called when an automatic resumable stream reconnect fails. Use this to\n * surface a toast, report telemetry, or mark the thread as needing a\n * retry. The failed stream id is cleared after the callback unless a\n * newer id has replaced it.\n */\n onResumeError?: ((error: unknown) => void) | undefined;\n joinStrategy?: AISDKRuntimeAdapter[\"joinStrategy\"];\n messageRepository?: AISDKRuntimeAdapter<UI_MESSAGE>[\"messageRepository\"];\n unstable_onBranchChange?: AISDKRuntimeAdapter[\"unstable_onBranchChange\"];\n };\n\nexport type ChatThreadEnvironment<UI_MESSAGE extends UIMessage = UIMessage> = {\n id: string;\n isMainThread: boolean;\n getThreadListItem: () => InitializableThreadListItem | undefined;\n stopOnClientDestroy?: boolean;\n /**\n * Aborts when the React component hosting the runtime is deleted. A nested\n * runtime resolves the destroy signal of the provider above it, which\n * outlives the nested component, so this stops the chat on its own unmount.\n */\n hostDestroySignal?: AbortSignal | undefined;\n /**\n * An externally owned chat instance. State lives on the instance, so it\n * survives the hosting resource unmounting; construction options are read\n * from the instance.\n */\n chat?: Chat<UI_MESSAGE> | undefined;\n /**\n * An externally owned per-thread message repository. Hosts that route\n * multiple threads through one mounting pass a distinct instance per\n * thread so histories and branches stay isolated.\n */\n messageRepositoryInstance?: MessageRepository | undefined;\n};\n\nconst useDynamicChatTransport = <UI_MESSAGE extends UIMessage = UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): ChatTransport<UI_MESSAGE> => {\n const transportRef = useRef<ChatTransport<UI_MESSAGE>>(transport);\n useEffect(() => {\n transportRef.current = transport;\n });\n const dynamicTransport = useMemo(\n () =>\n new Proxy(transportRef.current, {\n get(_, prop) {\n const res =\n transportRef.current[prop as keyof ChatTransport<UI_MESSAGE>];\n return typeof res === \"function\"\n ? res.bind(transportRef.current)\n : res;\n },\n }),\n [],\n );\n return dynamicTransport;\n};\n\nconst getResumableAdapter = <UI_MESSAGE extends UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): AssistantChatResumableOptions | undefined => {\n if (transport instanceof AssistantChatTransport) {\n return transport.getResumableAdapter();\n }\n const candidate = (transport as { getResumableAdapter?: () => unknown })\n .getResumableAdapter;\n if (typeof candidate !== \"function\") return undefined;\n return candidate.call(transport) as AssistantChatResumableOptions | undefined;\n};\n\nconst getNoPendingStreamId = () => null;\n\nconst resumedStreamIdsByStorage = new WeakMap<\n ResumableClientStorage,\n Set<string>\n>();\n\nconst getResumedStreamIds = (storage: ResumableClientStorage | undefined) => {\n if (!storage) return new Set<string>();\n let resumedStreamIds = resumedStreamIdsByStorage.get(storage);\n if (!resumedStreamIds) {\n resumedStreamIds = new Set();\n resumedStreamIdsByStorage.set(storage, resumedStreamIds);\n }\n return resumedStreamIds;\n};\n\n/**\n * Splits the combined options into the assistant-ui side and the `ChatInit`\n * remainder the AI SDK consumes, so external `Chat` construction forwards the\n * same fields `useChat` would.\n */\nexport const splitChatThreadOptions = <UI_MESSAGE extends UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n) => {\n const {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n isDisabled: _isDisabled,\n isSendDisabled: _isSendDisabled,\n unstable_capabilities: _unstable_capabilities,\n suggestions: _suggestions,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n ...chatInit\n } = options ?? {};\n // peel guard: any shared key left in `chatInit` collapses this to `never`\n true satisfies keyof typeof chatInit &\n keyof ExternalStoreSharedOptions extends never\n ? true\n : never;\n return {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit,\n };\n};\n\ntype ChatCallbacks<UI_MESSAGE extends UIMessage> = Pick<\n ChatInit<UI_MESSAGE>,\n \"onToolCall\" | \"onData\" | \"onFinish\" | \"onError\" | \"sendAutomaticallyWhen\"\n>;\n\nconst requestsByChat = new WeakMap<object, symbol>();\n\n/**\n * Constructs a `Chat` whose callbacks read the latest options through\n * `callbacksRef`, the forwarding `useChat` applies only to a chat it\n * constructs itself.\n */\nexport const createChat = <UI_MESSAGE extends UIMessage>(\n init: ChatInit<UI_MESSAGE>,\n callbacksRef: { readonly current: ChatCallbacks<UI_MESSAGE> | undefined },\n): Chat<UI_MESSAGE> => {\n const transport = init.transport;\n const chat = new Chat<UI_MESSAGE>({\n ...init,\n ...(transport && {\n transport: {\n sendMessages: (options) => {\n requestsByChat.set(chat, Symbol());\n return transport.sendMessages(options);\n },\n reconnectToStream: (options) => {\n requestsByChat.set(chat, Symbol());\n return transport.reconnectToStream(options);\n },\n },\n }),\n onToolCall: (arg) => callbacksRef.current?.onToolCall?.(arg),\n onData: (arg) => callbacksRef.current?.onData?.(arg),\n onFinish: (arg) => callbacksRef.current?.onFinish?.(arg),\n onError: (arg) => callbacksRef.current?.onError?.(arg),\n sendAutomaticallyWhen: (arg) =>\n callbacksRef.current?.sendAutomaticallyWhen?.(arg) ?? false,\n });\n return chat;\n};\n\nexport const useChatThread = <UI_MESSAGE extends UIMessage = UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n env: ChatThreadEnvironment<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit: chatOptions,\n } = splitChatThreadOptions(options);\n\n const {\n id,\n isMainThread,\n getThreadListItem,\n stopOnClientDestroy = true,\n hostDestroySignal,\n chat: externalChat,\n messageRepositoryInstance,\n } = env;\n\n // Wiring below is per thread and mutated on the instance, so a transport\n // shared across simultaneously mounted threads is last-writer-wins. A\n // caller-owned chat is already bound to its own clone, so cloning again\n // here would wire a copy the chat never sends through.\n const sourceTransport = useMemo(\n () =>\n transportOptions === undefined\n ? new AssistantChatTransport()\n : externalChat === undefined &&\n transportOptions instanceof AssistantChatTransport\n ? transportOptions.__internal_clone()\n : transportOptions,\n [transportOptions, externalChat],\n );\n const transport = useDynamicChatTransport(sourceTransport);\n\n const latestChatOptionsRef = useRef(chatOptions);\n useEffect(() => {\n latestChatOptionsRef.current = chatOptions;\n });\n // `useChat` stops a chat it constructs whenever it unmounts, and a\n // resource's soft unmount runs that cleanup, so the thread owns its chat.\n const [ownedChat] = useState(\n () =>\n externalChat ??\n createChat({ ...chatOptions, id, transport }, latestChatOptionsRef),\n );\n\n const chat = useChat({\n chat: externalChat ?? ownedChat,\n ...(throttle !== undefined && { throttle }),\n });\n\n useResourceCleanup(\n stopOnClientDestroy,\n () => {\n void chat.stop().catch(() => {});\n },\n hostDestroySignal,\n );\n\n const runtime = useAISDKRuntime(chat, {\n adapters: {\n ...adapters,\n threadList: { threadId: id, ...adapters?.threadList },\n },\n ...pickExternalStoreSharedOptions(options ?? {}),\n ...(toCreateMessage && { toCreateMessage }),\n ...(onResume && { onResume }),\n ...(onResumeToolCall && { onResumeToolCall }),\n ...(onRespondToToolApproval && { onRespondToToolApproval }),\n ...(joinStrategy && { joinStrategy }),\n ...(messageRepository && { messageRepository }),\n ...(messageRepositoryInstance && {\n unstable_messageRepositoryInstance: messageRepositoryInstance,\n }),\n // The chat outlives this runtime when a host mounts only the visible\n // thread, so a host approval answer is kept with it. This is the Chat\n // instance, not the useChat helpers, which are re-minted every render and\n // would be a dead WeakMap key by the next one.\n unstable_hostApprovalOwner: externalChat ?? ownedChat,\n ...(unstable_onBranchChange && { unstable_onBranchChange }),\n });\n\n // Wire in render, not an effect: a send from a descendant's mount effect\n // runs before this hook's effect would (effects fire child-first), and must\n // see a wired transport. The clone is per thread, so a discarded render's\n // wiring is discarded with it and the committed render re-wires the same\n // instance.\n if (sourceTransport instanceof AssistantChatTransport) {\n sourceTransport.setRuntime(runtime);\n sourceTransport.__internal_setGetThreadListItem(getThreadListItem);\n }\n\n const subscribeToRuntime = useCallback(\n (callback: () => void) => runtime.thread.subscribe(callback),\n [runtime],\n );\n const getHistoryLoadingSnapshot = useCallback(\n () => runtime.thread.getState().isLoading,\n [runtime],\n );\n const isLoadingHistory = useSyncExternalStore(\n subscribeToRuntime,\n getHistoryLoadingSnapshot,\n getHistoryLoadingSnapshot,\n );\n\n const resumableStorage = useMemo(\n () => getResumableAdapter(sourceTransport)?.storage,\n [sourceTransport],\n );\n const subscribeToResumableStorage = useCallback(\n (callback: () => void) =>\n isMainThread\n ? (resumableStorage?.subscribe?.(callback, id) ?? (() => {}))\n : () => {},\n [id, isMainThread, resumableStorage],\n );\n const getPendingStreamId = useCallback(\n () => (isMainThread ? (resumableStorage?.getStreamId(id) ?? null) : null),\n [id, isMainThread, resumableStorage],\n );\n const pendingStreamId = useSyncExternalStore(\n subscribeToResumableStorage,\n getPendingStreamId,\n getNoPendingStreamId,\n );\n const isChatRunning =\n chat.status === \"submitted\" || chat.status === \"streaming\";\n\n const resumedStreamIds = useMemo(\n () => getResumedStreamIds(resumableStorage),\n [resumableStorage],\n );\n const onResumeErrorRef = useRef(onResumeError);\n useEffect(() => {\n onResumeErrorRef.current = onResumeError;\n });\n useEffect(() => {\n if (!pendingStreamId || resumedStreamIds.has(pendingStreamId)) {\n return;\n }\n if (isChatRunning) {\n resumedStreamIds.add(pendingStreamId);\n return;\n }\n if (isLoadingHistory) return;\n resumedStreamIds.add(pendingStreamId);\n const activeChat = externalChat ?? ownedChat;\n activeChat.clearError();\n const pending = chat.resumeStream();\n const request = requestsByChat.get(activeChat);\n pending\n .then(() => {\n // Chat.error is shared with sends and resumes that can start before\n // this promise settles, including inside the caller's onFinish.\n if (requestsByChat.get(activeChat) === request && activeChat.error) {\n throw activeChat.error;\n }\n })\n .catch((err: unknown) => {\n console.warn(\"[assistant-ui] resumable: resume failed\", err);\n try {\n onResumeErrorRef.current?.(err);\n } catch (callbackError) {\n console.error(\n \"[assistant-ui] resumable: onResumeError callback failed\",\n callbackError,\n );\n } finally {\n if (resumableStorage?.getStreamId(id) === pendingStreamId) {\n resumableStorage.clear(id);\n }\n }\n });\n }, [\n chat,\n externalChat,\n id,\n isChatRunning,\n isLoadingHistory,\n pendingStreamId,\n ownedChat,\n resumableStorage,\n resumedStreamIds,\n ]);\n\n return runtime;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useChatThread.js","names":["Chat","useChat","UIMessage","MessageRepository","pickExternalStoreSharedOptions","AssistantRuntime","ExternalStoreSharedOptions","useAISDKRuntime","AISDKRuntimeAdapter","CustomToCreateMessageFunction","ChatInit","AssistantChatTransport","InitializableThreadListItem","useCallback","useEffect","useEffectEvent","useInsertionEffect","useMemo","useRef","useState","useSyncExternalStore","useResourceCleanup","DynamicChatTransport","getResumedStreamIds","getResumableAdapter","useDynamicChatTransport","ChatThreadOptions","UI_MESSAGE","throttle","adapters","toCreateMessage","onResume","onResumeToolCall","onRespondToToolApproval","onResumeError","error","joinStrategy","messageRepository","unstable_onBranchChange","ChatThreadEnvironment","id","isMainThread","getThreadListItem","stopOnClientDestroy","hostDestroySignal","AbortSignal","chat","messageRepositoryInstance","ChatThreadTransportBinding","runtime","getNoPendingStreamId","splitChatThreadOptions","options","transport","isDisabled","_isDisabled","isSendDisabled","_isSendDisabled","unstable_capabilities","_unstable_capabilities","suggestions","_suggestions","chatInit","ChatCallbacks","Pick","requestsByChat","WeakMap","createChat","init","callbacksRef","current","sendMessages","set","Symbol","reconnectToStream","onToolCall","arg","onData","onFinish","onError","sendAutomaticallyWhen","useChatThread","env","transportOptions","chatOptions","externalChat","defaultTransport","configuredTransport","undefined","transportContextOwner","getThreadListItemRef","getCurrentThreadListItem","resumableStorage","getCurrentResumableStorage","storage","transportBindingRef","initialTransportBinding","chatTransport","createThreadProxy","binding","Error","latestChatOptionsRef","ownedChat","stop","catch","threadList","threadId","unstable_messageRepositoryInstance","unstable_hostApprovalOwner","registerTransportContext","dynamicTransport","chatId","setThreadContext","unregisterThread","setRuntime","__internal_setGetThreadListItem","subscribeToRuntime","callback","thread","subscribe","getHistoryLoadingSnapshot","getState","isLoading","isLoadingHistory","subscribeToResumableStorage","getPendingStreamId","getStreamId","pendingStreamId","isChatRunning","status","staticResumedStreamIds","onResumeErrorRef","resumedStreamIds","has","add","activeChat","clearError","pending","resumeStream","request","get","then","err","console","warn","callbackError","clear"],"sources":["../../src/runtime/useChatThread.ts"],"sourcesContent":["\"use client\";\n\nimport { Chat, useChat, type UIMessage } from \"@ai-sdk/react\";\nimport type { MessageRepository } from \"@assistant-ui/core/internal\";\nimport {\n pickExternalStoreSharedOptions,\n type AssistantRuntime,\n type ExternalStoreSharedOptions,\n} from \"@assistant-ui/core\";\nimport {\n useAISDKRuntime,\n type AISDKRuntimeAdapter,\n type CustomToCreateMessageFunction,\n} from \"./useAISDKRuntime\";\nimport type { ChatInit } from \"ai\";\nimport {\n AssistantChatTransport,\n type InitializableThreadListItem,\n} from \"../transport/AssistantChatTransport\";\nimport {\n useCallback,\n useEffect,\n useEffectEvent,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { useResourceCleanup } from \"./useResourceCleanup\";\nimport {\n DynamicChatTransport,\n getResumedStreamIds,\n} from \"./DynamicChatTransport\";\nimport { getResumableAdapter } from \"./getResumableAdapter\";\nimport { useDynamicChatTransport } from \"./useDynamicChatTransport\";\n\nexport type ChatThreadOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> &\n ExternalStoreSharedOptions & {\n throttle?: number | undefined;\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n toCreateMessage?: CustomToCreateMessageFunction;\n onResume?: AISDKRuntimeAdapter[\"onResume\"];\n onResumeToolCall?: AISDKRuntimeAdapter[\"onResumeToolCall\"];\n onRespondToToolApproval?: AISDKRuntimeAdapter[\"onRespondToToolApproval\"];\n /**\n * Called when an automatic resumable stream reconnect fails. Use this to\n * surface a toast, report telemetry, or mark the thread as needing a\n * retry. The failed stream id is cleared after the callback unless a\n * newer id has replaced it.\n */\n onResumeError?: ((error: unknown) => void) | undefined;\n joinStrategy?: AISDKRuntimeAdapter[\"joinStrategy\"];\n messageRepository?: AISDKRuntimeAdapter<UI_MESSAGE>[\"messageRepository\"];\n /** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\n unstable_onBranchChange?: AISDKRuntimeAdapter[\"unstable_onBranchChange\"];\n };\n\nexport type ChatThreadEnvironment<UI_MESSAGE extends UIMessage = UIMessage> = {\n id: string;\n isMainThread: boolean;\n getThreadListItem: () => InitializableThreadListItem | undefined;\n stopOnClientDestroy?: boolean;\n /**\n * Aborts when the React component hosting the runtime is deleted. A nested\n * runtime resolves the destroy signal of the provider above it, which\n * outlives the nested component, so this stops the chat on its own unmount.\n */\n hostDestroySignal?: AbortSignal | undefined;\n /**\n * An externally owned chat instance. State lives on the instance, so it\n * survives the hosting resource unmounting; construction options are read\n * from the instance.\n */\n chat?: Chat<UI_MESSAGE> | undefined;\n /**\n * An externally owned per-thread message repository. Hosts that route\n * multiple threads through one mounting pass a distinct instance per\n * thread so histories and branches stay isolated.\n */\n messageRepositoryInstance?: MessageRepository | undefined;\n};\n\ntype ChatThreadTransportBinding = {\n runtime: AssistantRuntime;\n getThreadListItem: () => InitializableThreadListItem | undefined;\n};\n\nconst getNoPendingStreamId = () => null;\n\n/**\n * Splits the combined options into the assistant-ui side and the `ChatInit`\n * remainder the AI SDK consumes, so external `Chat` construction forwards the\n * same fields `useChat` would.\n */\nexport const splitChatThreadOptions = <UI_MESSAGE extends UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n) => {\n const {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n isDisabled: _isDisabled,\n isSendDisabled: _isSendDisabled,\n unstable_capabilities: _unstable_capabilities,\n suggestions: _suggestions,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n ...chatInit\n } = options ?? {};\n // peel guard: any shared key left in `chatInit` collapses this to `never`\n true satisfies keyof typeof chatInit &\n keyof ExternalStoreSharedOptions extends never\n ? true\n : never;\n return {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit,\n };\n};\n\ntype ChatCallbacks<UI_MESSAGE extends UIMessage> = Pick<\n ChatInit<UI_MESSAGE>,\n \"onToolCall\" | \"onData\" | \"onFinish\" | \"onError\" | \"sendAutomaticallyWhen\"\n>;\n\nconst requestsByChat = new WeakMap<object, symbol>();\n\n/**\n * Constructs a `Chat` whose callbacks read the latest options through\n * `callbacksRef`, the forwarding `useChat` applies only to a chat it\n * constructs itself.\n */\nexport const createChat = <UI_MESSAGE extends UIMessage>(\n init: ChatInit<UI_MESSAGE>,\n callbacksRef: { readonly current: ChatCallbacks<UI_MESSAGE> | undefined },\n): Chat<UI_MESSAGE> => {\n const transport = init.transport;\n const chat = new Chat<UI_MESSAGE>({\n ...init,\n ...(transport && {\n transport: {\n sendMessages: (options) => {\n requestsByChat.set(chat, Symbol());\n return transport.sendMessages(options);\n },\n reconnectToStream: (options) => {\n requestsByChat.set(chat, Symbol());\n return transport.reconnectToStream(options);\n },\n },\n }),\n onToolCall: (arg) => callbacksRef.current?.onToolCall?.(arg),\n onData: (arg) => callbacksRef.current?.onData?.(arg),\n onFinish: (arg) => callbacksRef.current?.onFinish?.(arg),\n onError: (arg) => callbacksRef.current?.onError?.(arg),\n sendAutomaticallyWhen: (arg) =>\n callbacksRef.current?.sendAutomaticallyWhen?.(arg) ?? false,\n });\n return chat;\n};\n\nexport const useChatThread = <UI_MESSAGE extends UIMessage = UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n env: ChatThreadEnvironment<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit: chatOptions,\n } = splitChatThreadOptions(options);\n\n const {\n id,\n isMainThread,\n getThreadListItem,\n stopOnClientDestroy = true,\n hostDestroySignal,\n chat: externalChat,\n messageRepositoryInstance,\n } = env;\n\n const defaultTransport = useMemo(() => new AssistantChatTransport(), []);\n const configuredTransport = transportOptions ?? defaultTransport;\n const transport = useDynamicChatTransport(\n configuredTransport,\n externalChat === undefined,\n );\n const transportContextOwner = useMemo(() => ({}), []);\n const getThreadListItemRef = useRef(getThreadListItem);\n useInsertionEffect(() => {\n getThreadListItemRef.current = getThreadListItem;\n }, [getThreadListItem]);\n const getCurrentThreadListItem = useCallback(\n () => getThreadListItemRef.current(),\n [],\n );\n const resumableStorage = useMemo(\n () =>\n transport instanceof DynamicChatTransport\n ? transport.getCurrentResumableStorage()\n : getResumableAdapter(transport)?.storage,\n [transport],\n );\n\n const transportBindingRef = useRef<ChatThreadTransportBinding | null>(null);\n // This is null when useMemo runs, so the proxy pins its mount render's binding. The fallback\n // serves only pre-commit sends; after insertion effects, transportBindingRef is authoritative.\n let initialTransportBinding: ChatThreadTransportBinding | null = null;\n const chatTransport = useMemo(\n () =>\n transport instanceof DynamicChatTransport\n ? transport.createThreadProxy(transportContextOwner, () => {\n const binding =\n transportBindingRef.current ?? initialTransportBinding;\n if (!binding) {\n throw new Error(\"Chat transport used before runtime setup\");\n }\n return binding;\n })\n : transport,\n [initialTransportBinding, transport, transportContextOwner],\n );\n\n const latestChatOptionsRef = useRef(chatOptions);\n useEffect(() => {\n latestChatOptionsRef.current = chatOptions;\n });\n // `useChat` stops a chat it constructs whenever it unmounts, and a\n // resource's soft unmount runs that cleanup, so the thread owns its chat.\n const [ownedChat] = useState(\n () =>\n externalChat ??\n createChat(\n { ...chatOptions, id, transport: chatTransport },\n latestChatOptionsRef,\n ),\n );\n\n const chat = useChat({\n chat: externalChat ?? ownedChat,\n ...(throttle !== undefined && { throttle }),\n });\n\n useResourceCleanup(\n stopOnClientDestroy,\n () => {\n void chat.stop().catch(() => {});\n },\n hostDestroySignal,\n );\n\n const runtime = useAISDKRuntime(chat, {\n adapters: {\n ...adapters,\n threadList: { threadId: id, ...adapters?.threadList },\n },\n ...pickExternalStoreSharedOptions(options ?? {}),\n ...(toCreateMessage && { toCreateMessage }),\n ...(onResume && { onResume }),\n ...(onResumeToolCall && { onResumeToolCall }),\n ...(onRespondToToolApproval && { onRespondToToolApproval }),\n ...(joinStrategy && { joinStrategy }),\n ...(messageRepository && { messageRepository }),\n ...(messageRepositoryInstance && {\n unstable_messageRepositoryInstance: messageRepositoryInstance,\n }),\n // The chat outlives this runtime when a host mounts only the visible\n // thread, so a host approval answer is kept with it. This is the Chat\n // instance, not the useChat helpers, which are re-minted every render and\n // would be a dead WeakMap key by the next one.\n unstable_hostApprovalOwner: externalChat ?? ownedChat,\n ...(unstable_onBranchChange && { unstable_onBranchChange }),\n });\n initialTransportBinding = {\n runtime,\n getThreadListItem: getCurrentThreadListItem,\n };\n useInsertionEffect(() => {\n transportBindingRef.current = {\n runtime,\n getThreadListItem: getCurrentThreadListItem,\n };\n }, [runtime, getCurrentThreadListItem]);\n\n const registerTransportContext = useEffectEvent(\n (dynamicTransport: DynamicChatTransport<UI_MESSAGE>, chatId: string) => {\n dynamicTransport.setThreadContext(\n chatId,\n transportContextOwner,\n runtime,\n getCurrentThreadListItem,\n );\n return () =>\n dynamicTransport.unregisterThread(chatId, transportContextOwner);\n },\n );\n useInsertionEffect(() => {\n if (!(transport instanceof DynamicChatTransport)) return undefined;\n return registerTransportContext(transport, id);\n }, [id, runtime, transport, transportContextOwner]);\n\n if (\n !(transport instanceof DynamicChatTransport) &&\n configuredTransport instanceof AssistantChatTransport\n ) {\n configuredTransport.setRuntime(runtime);\n configuredTransport.__internal_setGetThreadListItem(\n getCurrentThreadListItem,\n );\n }\n\n const subscribeToRuntime = useCallback(\n (callback: () => void) => runtime.thread.subscribe(callback),\n [runtime],\n );\n const getHistoryLoadingSnapshot = useCallback(\n () => runtime.thread.getState().isLoading,\n [runtime],\n );\n const isLoadingHistory = useSyncExternalStore(\n subscribeToRuntime,\n getHistoryLoadingSnapshot,\n getHistoryLoadingSnapshot,\n );\n\n const subscribeToResumableStorage = useCallback(\n (callback: () => void) =>\n isMainThread\n ? (resumableStorage?.subscribe?.(callback, id) ?? (() => {}))\n : () => {},\n [id, isMainThread, resumableStorage],\n );\n const getPendingStreamId = useCallback(\n () => (isMainThread ? (resumableStorage?.getStreamId(id) ?? null) : null),\n [id, isMainThread, resumableStorage],\n );\n const pendingStreamId = useSyncExternalStore(\n subscribeToResumableStorage,\n getPendingStreamId,\n getNoPendingStreamId,\n );\n const isChatRunning =\n chat.status === \"submitted\" || chat.status === \"streaming\";\n\n const staticResumedStreamIds = useMemo(\n () => getResumedStreamIds(resumableStorage),\n [resumableStorage],\n );\n const onResumeErrorRef = useRef(onResumeError);\n useEffect(() => {\n onResumeErrorRef.current = onResumeError;\n });\n useEffect(() => {\n const resumedStreamIds =\n transport instanceof DynamicChatTransport\n ? transport.getResumedStreamIds()\n : staticResumedStreamIds;\n if (!pendingStreamId || resumedStreamIds.has(pendingStreamId)) {\n return;\n }\n if (isChatRunning) {\n resumedStreamIds.add(pendingStreamId);\n return;\n }\n if (isLoadingHistory) return;\n resumedStreamIds.add(pendingStreamId);\n const activeChat = externalChat ?? ownedChat;\n activeChat.clearError();\n const pending = chat.resumeStream();\n const request = requestsByChat.get(activeChat);\n pending\n .then(() => {\n // Chat.error is shared with sends and resumes that can start before\n // this promise settles, including inside the caller's onFinish.\n if (requestsByChat.get(activeChat) === request && activeChat.error) {\n throw activeChat.error;\n }\n })\n .catch((err: unknown) => {\n console.warn(\"[assistant-ui] resumable: resume failed\", err);\n try {\n onResumeErrorRef.current?.(err);\n } catch (callbackError) {\n console.error(\n \"[assistant-ui] resumable: onResumeError callback failed\",\n callbackError,\n );\n } finally {\n if (resumableStorage?.getStreamId(id) === pendingStreamId) {\n resumableStorage.clear(id);\n }\n }\n });\n }, [\n chat,\n externalChat,\n id,\n isChatRunning,\n isLoadingHistory,\n pendingStreamId,\n ownedChat,\n resumableStorage,\n staticResumedStreamIds,\n transport,\n ]);\n\n return runtime;\n};\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { UIMessage } from "@ai-sdk/react";
2
+ import type { ChatTransport } from "ai";
3
+ export declare const useDynamicChatTransport: <UI_MESSAGE extends UIMessage>(transport: ChatTransport<UI_MESSAGE>, enabled?: boolean) => ChatTransport<UI_MESSAGE>;
4
+ //# sourceMappingURL=useDynamicChatTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDynamicChatTransport.d.ts","sourceRoot":"","sources":["../../src/runtime/useDynamicChatTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAIxC,eAAO,MAAM,uBAAuB,GAAI,UAAU,SAAS,SAAS,EAClE,WAAW,aAAa,CAAC,UAAU,CAAC,EACpC,iBAAc,KACb,aAAa,CAAC,UAAU,CAiB1B,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { DynamicChatTransport } from "./DynamicChatTransport.js";
2
+ import { useEffect, useInsertionEffect, useState } from "@assistant-ui/tap/react-shim";
3
+ import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
4
+ //#region src/runtime/useDynamicChatTransport.ts
5
+ const useDynamicChatTransport = (transport, t0) => {
6
+ const $ = c(12);
7
+ const enabled = t0 === void 0 ? true : t0;
8
+ let t1;
9
+ if ($[0] !== transport) {
10
+ t1 = () => transport instanceof DynamicChatTransport ? transport : new DynamicChatTransport(transport);
11
+ $[0] = transport;
12
+ $[1] = t1;
13
+ } else t1 = $[1];
14
+ const [dynamicTransport] = useState(t1);
15
+ let t2;
16
+ let t3;
17
+ if ($[2] !== dynamicTransport || $[3] !== enabled || $[4] !== transport) {
18
+ t2 = () => {
19
+ if (!enabled || dynamicTransport === transport) return;
20
+ dynamicTransport.setTransport(transport);
21
+ };
22
+ t3 = [
23
+ dynamicTransport,
24
+ enabled,
25
+ transport
26
+ ];
27
+ $[2] = dynamicTransport;
28
+ $[3] = enabled;
29
+ $[4] = transport;
30
+ $[5] = t2;
31
+ $[6] = t3;
32
+ } else {
33
+ t2 = $[5];
34
+ t3 = $[6];
35
+ }
36
+ useInsertionEffect(t2, t3);
37
+ let t4;
38
+ let t5;
39
+ if ($[7] !== dynamicTransport || $[8] !== enabled || $[9] !== transport) {
40
+ t4 = () => {
41
+ if (!enabled || dynamicTransport === transport) return;
42
+ dynamicTransport.flushTransportChange();
43
+ };
44
+ t5 = [
45
+ dynamicTransport,
46
+ enabled,
47
+ transport
48
+ ];
49
+ $[7] = dynamicTransport;
50
+ $[8] = enabled;
51
+ $[9] = transport;
52
+ $[10] = t4;
53
+ $[11] = t5;
54
+ } else {
55
+ t4 = $[10];
56
+ t5 = $[11];
57
+ }
58
+ useEffect(t4, t5);
59
+ return enabled ? dynamicTransport : transport;
60
+ };
61
+ //#endregion
62
+ export { useDynamicChatTransport };
63
+
64
+ //# sourceMappingURL=useDynamicChatTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDynamicChatTransport.js","names":["UIMessage","ChatTransport","useEffect","useInsertionEffect","useState","DynamicChatTransport","useDynamicChatTransport","transport","t0","$","_c","enabled","undefined","t1","dynamicTransport","t2","t3","setTransport","t4","t5","flushTransportChange","_c"],"sources":["../../src/runtime/useDynamicChatTransport.ts"],"sourcesContent":["import type { UIMessage } from \"@ai-sdk/react\";\nimport type { ChatTransport } from \"ai\";\nimport { useEffect, useInsertionEffect, useState } from \"react\";\nimport { DynamicChatTransport } from \"./DynamicChatTransport\";\n\nexport const useDynamicChatTransport = <UI_MESSAGE extends UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n enabled = true,\n): ChatTransport<UI_MESSAGE> => {\n const [dynamicTransport] = useState(() =>\n transport instanceof DynamicChatTransport\n ? transport\n : new DynamicChatTransport(transport),\n );\n\n useInsertionEffect(() => {\n if (!enabled || dynamicTransport === transport) return;\n dynamicTransport.setTransport(transport);\n }, [dynamicTransport, enabled, transport]);\n useEffect(() => {\n if (!enabled || dynamicTransport === transport) return;\n dynamicTransport.flushTransportChange();\n }, [dynamicTransport, enabled, transport]);\n\n return enabled ? dynamicTransport : transport;\n};\n"],"mappings":";;;;AAKA,MAAaM,2BAA0BC,WAAAC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAErC,MAAAC,UAAAH,OAAAI,KAAAA,IAAA,OAAAJ;CAAc,IAAAK;CAAA,IAAAJ,EAAA,OAAAF,WAAA;EAEsBM,WAClCN,qBAAqBF,uBAArBE,YAAA,IAEQF,qBAAqBE,SAAS;EAACE,EAAA,KAAAF;EAAAE,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAHzC,MAAA,CAAAK,oBAA2BV,SAASS,EAIpC;CAAE,IAAAE;CAAA,IAAAC;CAAA,IAAAP,EAAA,OAAAK,oBAAAL,EAAA,OAAAE,WAAAF,EAAA,OAAAF,WAAA;EAEiBQ,WAAA;GACjB,IAAI,CAACJ,WAAWG,qBAAqBP,WAAS;GAC9CO,iBAAgBG,aAAcV,SAAS;EAAC;EACvCS,KAAA;GAACF;GAAkBH;GAASJ;EAAS;EAACE,EAAA,KAAAK;EAAAL,EAAA,KAAAE;EAAAF,EAAA,KAAAF;EAAAE,EAAA,KAAAM;EAAAN,EAAA,KAAAO;CAAA,OAAA;EAAAD,KAAAN,EAAA;EAAAO,KAAAP,EAAA;CAAA;CAHzCN,mBAAmBY,IAGhBC,EAAsC;CAAC,IAAAE;CAAA,IAAAC;CAAA,IAAAV,EAAA,OAAAK,oBAAAL,EAAA,OAAAE,WAAAF,EAAA,OAAAF,WAAA;EAChCW,WAAA;GACR,IAAI,CAACP,WAAWG,qBAAqBP,WAAS;GAC9CO,iBAAgBM,qBAAsB;EAAC;EACtCD,KAAA;GAACL;GAAkBH;GAASJ;EAAS;EAACE,EAAA,KAAAK;EAAAL,EAAA,KAAAE;EAAAF,EAAA,KAAAF;EAAAE,EAAA,MAAAS;EAAAT,EAAA,MAAAU;CAAA,OAAA;EAAAD,KAAAT,EAAA;EAAAU,KAAAV,EAAA;CAAA;CAHzCP,UAAUgB,IAGPC,EAAsC;CAAC,OAEnCR,UAAAG,mBAAAP;AAAsC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useExternalHistory.d.ts","sourceRoot":"","sources":["../../src/runtime/useExternalHistory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EAEpB,uBAAuB,EACvB,yBAAyB,EACzB,4BAA4B,EAC5B,2BAA2B,EAC5B,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAIL,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAGf,eAAO,MAAM,2BAA2B,GAAI,QAAQ,EAClD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,UAAU,uBAAuB,CAAC,QAAQ,CAAC,KAC1C,yBAoBF,CAAC;AAsOF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,EACzC,YAAY,SAAS,CAAC,gBAAgB,CAAC,EACvC,gBAAgB,oBAAoB,GAAG,SAAS,EAChD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,sBAAsB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,EACzD,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,EAC7C,gBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,0BAA0B,MAAM,IAAI,EACpC,mBAAmB,GAAG,CAAC,MAAM,EAAE,2BAA2B,CAAC,EAC3D,6BAA6B,MAAM,IAAI,EACvC,wBAAwB,GAAG,CAAC,MAAM,EAAE,4BAA4B,CAAC,EACjE,kCAAkC,MAAM,IAAI;;+BA+dxB,MAAM;yCA3UZ,MAAM;8CAAN,MAAM;CAyXrB,CAAC"}
1
+ {"version":3,"file":"useExternalHistory.d.ts","sourceRoot":"","sources":["../../src/runtime/useExternalHistory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EAEpB,uBAAuB,EACvB,yBAAyB,EACzB,4BAA4B,EAC5B,2BAA2B,EAC5B,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAIL,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAUf,eAAO,MAAM,2BAA2B,GAAI,QAAQ,EAClD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,UAAU,uBAAuB,CAAC,QAAQ,CAAC,KAC1C,yBAoBF,CAAC;AA2BF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,EACzC,YAAY,SAAS,CAAC,gBAAgB,CAAC,EACvC,gBAAgB,oBAAoB,GAAG,SAAS,EAChD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,sBAAsB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,EACzD,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,EAC7C,gBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,0BAA0B,MAAM,IAAI,EACpC,mBAAmB,GAAG,CAAC,MAAM,EAAE,2BAA2B,CAAC,EAC3D,6BAA6B,MAAM,IAAI,EACvC,wBAAwB,GAAG,CAAC,MAAM,EAAE,4BAA4B,CAAC,EACjE,kCAAkC,MAAM,IAAI;;+BAkexB,MAAM;yCA3UZ,MAAM;8CAAN,MAAM;CAyXrB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { normalizeToolApprovalAnswers } from "../converters/toolApprovalAnswers.js";
3
- import { MessageRepository, readToolInteractionLog } from "@assistant-ui/core/internal";
2
+ import { addToolData, collectToolApprovalResponses, collectToolArtifacts, collectToolInteractions, restoreToolData } from "./toolHistoryCodec.js";
3
+ import { MessageRepository } from "@assistant-ui/core/internal";
4
4
  import { getExternalStoreMessages } from "@assistant-ui/core";
5
5
  import { useCallback, useEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
6
6
  import { useAui } from "@assistant-ui/store";
@@ -28,107 +28,6 @@ const toExportedMessageRepository = (toThreadMessages, messages) => {
28
28
  const hasUnansweredApproval = (message) => message.content.some((part) => part.type === "tool-call" && part.approval != null && part.approval.approved === void 0 && part.approval.resolution === void 0);
29
29
  const isAwaitingToolApproval = (message) => message.status?.type === "requires-action" && (message.status.reason === "tool-calls" || message.status.reason === "interrupt" && hasUnansweredApproval(message));
30
30
  const isTerminalMessage = (message) => message.status === void 0 || message.status.type === "complete" || message.status.type === "incomplete";
31
- const TOOL_ARTIFACTS_METADATA_KEY = "__aui_toolArtifacts";
32
- const TOOL_INTERACTIONS_METADATA_KEY = "__aui_toolInteractions";
33
- const TOOL_APPROVAL_RESPONSES_METADATA_KEY = "__aui_toolApprovalResponses";
34
- const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
35
- const collectToolArtifacts = (message, toolArtifacts) => {
36
- if (!toolArtifacts) return void 0;
37
- const entries = message.content.flatMap((part) => {
38
- if (part.type !== "tool-call") return [];
39
- const artifact = toolArtifacts.get(part.toolCallId);
40
- return artifact === void 0 ? [] : [[part.toolCallId, artifact]];
41
- });
42
- return entries.length > 0 ? Object.fromEntries(entries) : void 0;
43
- };
44
- const collectToolInteractions = (message, toolInteractions) => {
45
- if (!toolInteractions) return void 0;
46
- const entries = message.content.flatMap((part) => {
47
- if (part.type !== "tool-call") return [];
48
- const interactions = toolInteractions.get(part.toolCallId);
49
- return interactions === void 0 ? [] : [[part.toolCallId, interactions]];
50
- });
51
- return entries.length > 0 ? Object.fromEntries(entries) : void 0;
52
- };
53
- const collectToolApprovalResponses = (message, toolApprovalResponses) => {
54
- if (!toolApprovalResponses) return void 0;
55
- const entries = message.content.flatMap((part) => {
56
- if (part.type !== "tool-call" || !part.approval) return [];
57
- const response = toolApprovalResponses.get(part.approval.id);
58
- if (!response) return [];
59
- return [[part.approval.id, {
60
- approved: response.approved,
61
- ...response.optionId != null && { optionId: response.optionId },
62
- ...response.text != null && { text: response.text },
63
- ...response.answers != null && { answers: response.answers },
64
- ...response.reason != null && { reason: response.reason }
65
- }]];
66
- });
67
- return entries.length > 0 ? Object.fromEntries(entries) : void 0;
68
- };
69
- const addToolData = (message, toolArtifacts, toolInteractions, toolApprovalResponses) => {
70
- if (!toolArtifacts && !toolInteractions && !toolApprovalResponses || !isRecord(message) || !Array.isArray(message.parts)) return message;
71
- const toolCallIds = message.parts.flatMap((part) => {
72
- if (!isRecord(part) || typeof part.toolCallId !== "string") return [];
73
- return [part.toolCallId];
74
- });
75
- const artifacts = toolArtifacts ? Object.fromEntries(toolCallIds.flatMap((toolCallId) => Object.hasOwn(toolArtifacts, toolCallId) ? [[toolCallId, toolArtifacts[toolCallId]]] : [])) : void 0;
76
- const interactions = toolInteractions ? Object.fromEntries(toolCallIds.flatMap((toolCallId) => Object.hasOwn(toolInteractions, toolCallId) ? [[toolCallId, toolInteractions[toolCallId]]] : [])) : void 0;
77
- const approvalIds = message.parts.flatMap((part) => {
78
- if (!isRecord(part) || !isRecord(part.approval)) return [];
79
- const approvalId = part.approval.id;
80
- return typeof approvalId === "string" ? [approvalId] : [];
81
- });
82
- const approvalResponses = toolApprovalResponses ? Object.fromEntries(approvalIds.flatMap((approvalId) => Object.hasOwn(toolApprovalResponses, approvalId) ? [[approvalId, toolApprovalResponses[approvalId]]] : [])) : void 0;
83
- const hasArtifacts = !!artifacts && Object.keys(artifacts).length > 0;
84
- const hasInteractions = !!interactions && Object.keys(interactions).length > 0;
85
- const hasApprovalResponses = !!approvalResponses && Object.keys(approvalResponses).length > 0;
86
- if (!hasArtifacts && !hasInteractions && !hasApprovalResponses) return message;
87
- const metadata = isRecord(message.metadata) ? message.metadata : {};
88
- return {
89
- ...message,
90
- metadata: {
91
- ...metadata,
92
- ...hasArtifacts && { [TOOL_ARTIFACTS_METADATA_KEY]: artifacts },
93
- ...hasInteractions && { [TOOL_INTERACTIONS_METADATA_KEY]: interactions },
94
- ...hasApprovalResponses && { [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: approvalResponses }
95
- }
96
- };
97
- };
98
- const restoreToolData = (message, toolArtifacts, toolInteractions, toolApprovalResponses) => {
99
- if (!isRecord(message) || !isRecord(message.metadata)) return message;
100
- const metadata = message.metadata;
101
- const hasArtifacts = Object.hasOwn(metadata, TOOL_ARTIFACTS_METADATA_KEY);
102
- const hasInteractions = Object.hasOwn(metadata, TOOL_INTERACTIONS_METADATA_KEY);
103
- const hasApprovalResponses = Object.hasOwn(metadata, TOOL_APPROVAL_RESPONSES_METADATA_KEY);
104
- if (!hasArtifacts && !hasInteractions && !hasApprovalResponses) return message;
105
- const artifacts = metadata[TOOL_ARTIFACTS_METADATA_KEY];
106
- if (toolArtifacts && isRecord(artifacts)) for (const [toolCallId, artifact] of Object.entries(artifacts)) toolArtifacts.set(toolCallId, artifact);
107
- const interactions = metadata[TOOL_INTERACTIONS_METADATA_KEY];
108
- if (toolInteractions && isRecord(interactions)) for (const [toolCallId, value] of Object.entries(interactions)) {
109
- const log = readToolInteractionLog(value);
110
- if (log) toolInteractions.set(toolCallId, log);
111
- }
112
- const approvalResponses = metadata[TOOL_APPROVAL_RESPONSES_METADATA_KEY];
113
- if (toolApprovalResponses && isRecord(approvalResponses)) for (const [approvalId, value] of Object.entries(approvalResponses)) {
114
- if (!isRecord(value) || typeof value.approved !== "boolean") continue;
115
- const answers = normalizeToolApprovalAnswers(value.answers);
116
- toolApprovalResponses.set(approvalId, {
117
- approvalId,
118
- approved: value.approved,
119
- ...typeof value.optionId === "string" && { optionId: value.optionId },
120
- ...typeof value.text === "string" && { text: value.text },
121
- ...answers && Object.keys(answers).length > 0 && { answers },
122
- ...typeof value.reason === "string" && { reason: value.reason }
123
- });
124
- }
125
- const { [TOOL_ARTIFACTS_METADATA_KEY]: _, [TOOL_INTERACTIONS_METADATA_KEY]: __, [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: ___, ...restMetadata } = metadata;
126
- const { metadata: _metadata, ...restMessage } = message;
127
- return Object.keys(restMetadata).length === 0 ? restMessage : {
128
- ...restMessage,
129
- metadata: restMetadata
130
- };
131
- };
132
31
  const encodeContent = (storageFormatAdapter, item) => JSON.stringify(storageFormatAdapter.encode(item));
133
32
  const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storageFormatAdapter, onSetMessages, toolArtifacts, onToolArtifactsRestored, toolInteractions, onToolInteractionsRestored, toolApprovalResponses, onToolApprovalResponsesRestored) => {
134
33
  const loadedRef = useRef(false);
@@ -154,6 +53,8 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
154
53
  const loadHistory = async () => {
155
54
  try {
156
55
  const repo = await formatAdapter.load();
56
+ const threadState = runtimeRef.current.thread.getState();
57
+ if (threadState.isRunning || threadState.messages.length > 0) return;
157
58
  toolArtifacts?.clear();
158
59
  toolInteractions?.clear();
159
60
  toolApprovalResponses?.clear();
@@ -198,13 +99,14 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
198
99
  if (optionalThreadListItem()?.getState().remoteId) setItemEpoch((n) => n + 1);
199
100
  });
200
101
  }
201
- const threadState = runtimeRef.current.thread.getState();
202
- if (threadState.isRunning || threadState.messages.length > 0) {
102
+ const threadState_0 = runtimeRef.current.thread.getState();
103
+ if (threadState_0.isRunning || threadState_0.messages.length > 0) {
203
104
  loadedRef.current = true;
204
105
  setHasLoaded(true);
205
106
  return;
206
107
  }
207
108
  loadedRef.current = true;
109
+ setHasLoaded(false);
208
110
  loadHistory();
209
111
  }, [
210
112
  formatAdapter,
@@ -277,12 +179,12 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
277
179
  const idleScheduledInnerIds = /* @__PURE__ */ new Set();
278
180
  const unpersistedInnerIds = (message_0) => isTerminalMessage(message_0) ? getExternalStoreMessages(message_0).map((innerMessage_1) => storageFormatAdapter.getId(innerMessage_1)).filter((innerId_0) => !persistedInnerMessages.current.has(innerId_0)) : [];
279
181
  const unsubscribe = runtimeRef.current.thread.subscribe(() => {
280
- const threadState_0 = runtimeRef.current.thread.getState();
281
- const { isRunning } = threadState_0;
182
+ const threadState_1 = runtimeRef.current.thread.getState();
183
+ const { isRunning } = threadState_1;
282
184
  const wasRunning = wasRunningRef.current;
283
185
  wasRunningRef.current = isRunning;
284
186
  if (runStartRef.current != null) {
285
- const lastMsg = threadState_0.messages.at(-1);
187
+ const lastMsg = threadState_1.messages.at(-1);
286
188
  if (lastMsg?.role === "assistant") {
287
189
  const currentToolCallCount = lastMsg.content.filter((p) => p.type === "tool-call").length;
288
190
  while (toolCallCountRef.current < currentToolCallCount) {
@@ -307,7 +209,7 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
307
209
  if (wasRunning) {
308
210
  if (runStartRef.current != null) stepBoundariesRef.current.push(Date.now() - runStartRef.current);
309
211
  } else {
310
- const pending = threadState_0.messages.flatMap(unpersistedInnerIds);
212
+ const pending = threadState_1.messages.flatMap(unpersistedInnerIds);
311
213
  if (pending.every((innerId_1) => idleScheduledInnerIds.has(innerId_1))) return;
312
214
  for (const innerId_2 of pending) idleScheduledInnerIds.add(innerId_2);
313
215
  }