@ulearning/ai-sdk-adapter 0.0.0-stage → 1.0.0-next.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.
Files changed (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -2
  3. package/dist/adapters/aiSDKFormatAdapter.d.ts +6 -0
  4. package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
  5. package/dist/adapters/aiSDKFormatAdapter.js +17 -0
  6. package/dist/adapters/aiSDKFormatAdapter.js.map +1 -0
  7. package/dist/adapters/vercelAttachmentAdapter.d.ts +3 -0
  8. package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -0
  9. package/dist/adapters/vercelAttachmentAdapter.js +36 -0
  10. package/dist/adapters/vercelAttachmentAdapter.js.map +1 -0
  11. package/dist/aiSDKExtras.d.ts +13 -0
  12. package/dist/aiSDKExtras.d.ts.map +1 -0
  13. package/dist/aiSDKExtras.js +6 -0
  14. package/dist/aiSDKExtras.js.map +1 -0
  15. package/dist/converters/convertMessage.d.ts +44 -0
  16. package/dist/converters/convertMessage.d.ts.map +1 -0
  17. package/dist/converters/convertMessage.js +465 -0
  18. package/dist/converters/convertMessage.js.map +1 -0
  19. package/dist/converters/toCreateMessage.d.ts +4 -0
  20. package/dist/converters/toCreateMessage.d.ts.map +1 -0
  21. package/dist/converters/toCreateMessage.js +65 -0
  22. package/dist/converters/toCreateMessage.js.map +1 -0
  23. package/dist/converters/toolApprovalAnswers.d.ts +3 -0
  24. package/dist/converters/toolApprovalAnswers.d.ts.map +1 -0
  25. package/dist/converters/toolApprovalAnswers.js +16 -0
  26. package/dist/converters/toolApprovalAnswers.js.map +1 -0
  27. package/dist/hooks.d.ts +17 -0
  28. package/dist/hooks.d.ts.map +1 -0
  29. package/dist/hooks.js +19 -0
  30. package/dist/hooks.js.map +1 -0
  31. package/dist/index.d.ts +17 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +13 -0
  34. package/dist/model-context/injectInteractableContext.d.ts +37 -0
  35. package/dist/model-context/injectInteractableContext.d.ts.map +1 -0
  36. package/dist/model-context/injectInteractableContext.js +54 -0
  37. package/dist/model-context/injectInteractableContext.js.map +1 -0
  38. package/dist/model-context/injectQuoteContext.d.ts +25 -0
  39. package/dist/model-context/injectQuoteContext.d.ts.map +1 -0
  40. package/dist/model-context/injectQuoteContext.js +51 -0
  41. package/dist/model-context/injectQuoteContext.js.map +1 -0
  42. package/dist/runtime/AISDKChat.d.ts +13 -0
  43. package/dist/runtime/AISDKChat.d.ts.map +1 -0
  44. package/dist/runtime/AISDKChat.js +34 -0
  45. package/dist/runtime/AISDKChat.js.map +1 -0
  46. package/dist/runtime/AISDKThreads.d.ts +24 -0
  47. package/dist/runtime/AISDKThreads.d.ts.map +1 -0
  48. package/dist/runtime/AISDKThreads.js +92 -0
  49. package/dist/runtime/AISDKThreads.js.map +1 -0
  50. package/dist/runtime/DynamicChatTransport.d.ts +50 -0
  51. package/dist/runtime/DynamicChatTransport.d.ts.map +1 -0
  52. package/dist/runtime/DynamicChatTransport.js +153 -0
  53. package/dist/runtime/DynamicChatTransport.js.map +1 -0
  54. package/dist/runtime/getResumableAdapter.d.ts +5 -0
  55. package/dist/runtime/getResumableAdapter.d.ts.map +1 -0
  56. package/dist/runtime/getResumableAdapter.js +11 -0
  57. package/dist/runtime/getResumableAdapter.js.map +1 -0
  58. package/dist/runtime/toolHistoryCodec.d.ts +20 -0
  59. package/dist/runtime/toolHistoryCodec.d.ts.map +1 -0
  60. package/dist/runtime/toolHistoryCodec.js +122 -0
  61. package/dist/runtime/toolHistoryCodec.js.map +1 -0
  62. package/dist/runtime/useAISDKRuntime.d.ts +95 -0
  63. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -0
  64. package/dist/runtime/useAISDKRuntime.js +754 -0
  65. package/dist/runtime/useAISDKRuntime.js.map +1 -0
  66. package/dist/runtime/useChatRuntime.d.ts +17 -0
  67. package/dist/runtime/useChatRuntime.d.ts.map +1 -0
  68. package/dist/runtime/useChatRuntime.js +50 -0
  69. package/dist/runtime/useChatRuntime.js.map +1 -0
  70. package/dist/runtime/useChatSnapshot.d.ts +6 -0
  71. package/dist/runtime/useChatSnapshot.d.ts.map +1 -0
  72. package/dist/runtime/useChatSnapshot.js +62 -0
  73. package/dist/runtime/useChatSnapshot.js.map +1 -0
  74. package/dist/runtime/useChatThread.d.ts +121 -0
  75. package/dist/runtime/useChatThread.d.ts.map +1 -0
  76. package/dist/runtime/useChatThread.js +255 -0
  77. package/dist/runtime/useChatThread.js.map +1 -0
  78. package/dist/runtime/useDynamicChatTransport.d.ts +4 -0
  79. package/dist/runtime/useDynamicChatTransport.d.ts.map +1 -0
  80. package/dist/runtime/useDynamicChatTransport.js +26 -0
  81. package/dist/runtime/useDynamicChatTransport.js.map +1 -0
  82. package/dist/runtime/useExternalHistory.d.ts +12 -0
  83. package/dist/runtime/useExternalHistory.d.ts.map +1 -0
  84. package/dist/runtime/useExternalHistory.js +556 -0
  85. package/dist/runtime/useExternalHistory.js.map +1 -0
  86. package/dist/runtime/useMessageQueue.d.ts +14 -0
  87. package/dist/runtime/useMessageQueue.d.ts.map +1 -0
  88. package/dist/runtime/useMessageQueue.js +142 -0
  89. package/dist/runtime/useMessageQueue.js.map +1 -0
  90. package/dist/runtime/useResourceCleanup.d.ts +2 -0
  91. package/dist/runtime/useResourceCleanup.d.ts.map +1 -0
  92. package/dist/runtime/useResourceCleanup.js +47 -0
  93. package/dist/runtime/useResourceCleanup.js.map +1 -0
  94. package/dist/runtime/useStreamingTiming.d.ts +12 -0
  95. package/dist/runtime/useStreamingTiming.d.ts.map +1 -0
  96. package/dist/runtime/useStreamingTiming.js +36 -0
  97. package/dist/runtime/useStreamingTiming.js.map +1 -0
  98. package/dist/tools/generativeTools.d.ts +71 -0
  99. package/dist/tools/generativeTools.d.ts.map +1 -0
  100. package/dist/tools/generativeTools.js +258 -0
  101. package/dist/tools/generativeTools.js.map +1 -0
  102. package/dist/tools/mcp-stdio.node.d.ts +2 -0
  103. package/dist/tools/mcp-stdio.node.d.ts.map +1 -0
  104. package/dist/tools/mcp-stdio.node.js +1 -0
  105. package/dist/tools/mcp-stdio.unsupported.d.ts +3 -0
  106. package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -0
  107. package/dist/tools/mcp-stdio.unsupported.js +10 -0
  108. package/dist/tools/mcp-stdio.unsupported.js.map +1 -0
  109. package/dist/transport/AssistantChatTransport.d.ts +20 -0
  110. package/dist/transport/AssistantChatTransport.d.ts.map +1 -0
  111. package/dist/transport/AssistantChatTransport.js +151 -0
  112. package/dist/transport/AssistantChatTransport.js.map +1 -0
  113. package/dist/transport/resumable.d.ts +36 -0
  114. package/dist/transport/resumable.d.ts.map +1 -0
  115. package/dist/transport/resumable.js +112 -0
  116. package/dist/transport/resumable.js.map +1 -0
  117. package/dist/usage.d.ts +24 -0
  118. package/dist/usage.d.ts.map +1 -0
  119. package/dist/usage.js +121 -0
  120. package/dist/usage.js.map +1 -0
  121. package/dist/utils/getVercelAIMessages.d.ts +4 -0
  122. package/dist/utils/getVercelAIMessages.d.ts.map +1 -0
  123. package/dist/utils/getVercelAIMessages.js +8 -0
  124. package/dist/utils/getVercelAIMessages.js.map +1 -0
  125. package/dist/utils/sliceMessagesUntil.d.ts +3 -0
  126. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -0
  127. package/dist/utils/sliceMessagesUntil.js +10 -0
  128. package/dist/utils/sliceMessagesUntil.js.map +1 -0
  129. package/package.json +96 -3
  130. package/src/__tests__/standalone.e2e.ts +118 -0
  131. package/src/adapters/aiSDKFormatAdapter.test.ts +36 -0
  132. package/src/adapters/aiSDKFormatAdapter.ts +18 -0
  133. package/src/adapters/vercelAttachmentAdapter.test.ts +52 -0
  134. package/src/adapters/vercelAttachmentAdapter.ts +36 -0
  135. package/src/aiSDKExtras.ts +31 -0
  136. package/src/converters/convertMessage.metadata-key.test.tsx +182 -0
  137. package/src/converters/convertMessage.test.ts +2428 -0
  138. package/src/converters/convertMessage.tool-args-status.test.tsx +185 -0
  139. package/src/converters/convertMessage.ts +919 -0
  140. package/src/converters/toCreateMessage.test.ts +716 -0
  141. package/src/converters/toCreateMessage.ts +110 -0
  142. package/src/converters/toolApprovalAnswers.ts +27 -0
  143. package/src/hooks.test.ts +168 -0
  144. package/src/hooks.ts +26 -0
  145. package/src/index.ts +29 -0
  146. package/src/model-context/injectInteractableContext.test.ts +167 -0
  147. package/src/model-context/injectInteractableContext.ts +64 -0
  148. package/src/model-context/injectQuoteContext.test.ts +33 -0
  149. package/src/model-context/injectQuoteContext.ts +63 -0
  150. package/src/runtime/AISDKChat.artifacts.test.ts +48 -0
  151. package/src/runtime/AISDKChat.integration.test.tsx +148 -0
  152. package/src/runtime/AISDKChat.test.ts +241 -0
  153. package/src/runtime/AISDKChat.ts +43 -0
  154. package/src/runtime/AISDKThreads.test.ts +811 -0
  155. package/src/runtime/AISDKThreads.ts +194 -0
  156. package/src/runtime/DynamicChatTransport.test.ts +228 -0
  157. package/src/runtime/DynamicChatTransport.ts +281 -0
  158. package/src/runtime/__tests__/controlled-transport.ts +70 -0
  159. package/src/runtime/__tests__/tap.ts +80 -0
  160. package/src/runtime/__tests__/vue.ts +46 -0
  161. package/src/runtime/getResumableAdapter.ts +16 -0
  162. package/src/runtime/toolHistoryCodec.test.ts +187 -0
  163. package/src/runtime/toolHistoryCodec.ts +245 -0
  164. package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +1956 -0
  165. package/src/runtime/useAISDKRuntime.approval.test.tsx +580 -0
  166. package/src/runtime/useAISDKRuntime.denied-tool.test.tsx +122 -0
  167. package/src/runtime/useAISDKRuntime.fast-refresh.test.tsx +302 -0
  168. package/src/runtime/useAISDKRuntime.metadata-cache.test.tsx +157 -0
  169. package/src/runtime/useAISDKRuntime.queue.test.tsx +707 -0
  170. package/src/runtime/useAISDKRuntime.reload.test.tsx +217 -0
  171. package/src/runtime/useAISDKRuntime.test.ts +2345 -0
  172. package/src/runtime/useAISDKRuntime.tool-output.test.tsx +113 -0
  173. package/src/runtime/useAISDKRuntime.ts +1503 -0
  174. package/src/runtime/useAISDKRuntime.voice.test.tsx +319 -0
  175. package/src/runtime/useChatOnTap.test.ts +60 -0
  176. package/src/runtime/useChatRuntime.fast-refresh.test.tsx +83 -0
  177. package/src/runtime/useChatRuntime.integration.test.tsx +661 -0
  178. package/src/runtime/useChatRuntime.local-storage.test.tsx +114 -0
  179. package/src/runtime/useChatRuntime.native.test.ts +147 -0
  180. package/src/runtime/useChatRuntime.run-telemetry.test.tsx +226 -0
  181. package/src/runtime/useChatRuntime.test.ts +619 -0
  182. package/src/runtime/useChatRuntime.ts +80 -0
  183. package/src/runtime/useChatSnapshot.test.ts +60 -0
  184. package/src/runtime/useChatSnapshot.ts +86 -0
  185. package/src/runtime/useChatThread.binding.test.tsx +140 -0
  186. package/src/runtime/useChatThread.test.ts +993 -0
  187. package/src/runtime/useChatThread.transport.test.tsx +149 -0
  188. package/src/runtime/useChatThread.ts +533 -0
  189. package/src/runtime/useDynamicChatTransport.ts +26 -0
  190. package/src/runtime/useExternalHistory.test.ts +4187 -0
  191. package/src/runtime/useExternalHistory.ts +902 -0
  192. package/src/runtime/useMessageQueue.effects.test.tsx +88 -0
  193. package/src/runtime/useMessageQueue.test.tsx +256 -0
  194. package/src/runtime/useMessageQueue.ts +189 -0
  195. package/src/runtime/useResourceCleanup.ts +58 -0
  196. package/src/runtime/useStreamingTiming.test.tsx +96 -0
  197. package/src/runtime/useStreamingTiming.ts +69 -0
  198. package/src/tools/generativeTools.test.ts +1058 -0
  199. package/src/tools/generativeTools.ts +491 -0
  200. package/src/tools/mcp-stdio.node.ts +1 -0
  201. package/src/tools/mcp-stdio.unsupported.ts +12 -0
  202. package/src/transport/AssistantChatTransport.test.ts +631 -0
  203. package/src/transport/AssistantChatTransport.ts +236 -0
  204. package/src/transport/resumable.test.ts +262 -0
  205. package/src/transport/resumable.ts +162 -0
  206. package/src/usage.test.ts +204 -0
  207. package/src/usage.ts +179 -0
  208. package/src/utils/getVercelAIMessages.ts +11 -0
  209. package/src/utils/sliceMessagesUntil.test.ts +51 -0
  210. package/src/utils/sliceMessagesUntil.ts +16 -0
@@ -0,0 +1,754 @@
1
+ "use client";
2
+ import { aiSDKV6FormatAdapter } from "../adapters/aiSDKFormatAdapter.js";
3
+ import { vercelAttachmentAdapter } from "../adapters/vercelAttachmentAdapter.js";
4
+ import { aiSDKExtras } from "../aiSDKExtras.js";
5
+ import { AISDKMessageConverter, convertAISDKMessage, createAISDKMetadataKey } from "../converters/convertMessage.js";
6
+ import { toCreateMessage } from "../converters/toCreateMessage.js";
7
+ import { sliceMessagesUntil } from "../utils/sliceMessagesUntil.js";
8
+ import { getVercelAIMessages } from "../utils/getVercelAIMessages.js";
9
+ import { toExportedMessageRepository, useExternalHistory } from "./useExternalHistory.js";
10
+ import { aiSdkStreamingTimingAccessors } from "./useStreamingTiming.js";
11
+ import { useMessageQueue } from "./useMessageQueue.js";
12
+ import { MessageRepository, appendToolInteraction, consumeSuggestionResult } from "@ulearning/ai-core/internal";
13
+ import { generateId, getToolName, isToolUIPart } from "ai";
14
+ import { useExternalStoreRuntime } from "@ulearning/ai-core/vue";
15
+ import { getExternalStoreMessages, pickExternalStoreSharedOptions } from "@ulearning/ai-core";
16
+ import { wrapModelContentEnvelope } from "@ulearning/ai-stream/internal";
17
+ import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "@ulearning/ai-tap";
18
+ import { useReplaySafeEffect } from "@ulearning/ai-store/internal";
19
+ import { useExternalMessageConverterValue, useRuntimeAdaptersValue, useRuntimeResource, useStreamingTimingValue } from "@ulearning/ai-core/vue/internal";
20
+ import { isResourceExecution } from "@ulearning/ai-tap/internal";
21
+ import { toValue } from "vue";
22
+ // src/runtime/useAISDKRuntime.ts
23
+ const toUIMessage = (createMessage, fallbackRole) => ({
24
+ ...createMessage,
25
+ id: createMessage.id ?? generateId(),
26
+ role: createMessage.role ?? fallbackRole
27
+ });
28
+ const toVoiceTranscriptUIMessage = (message) => ({
29
+ id: message.id,
30
+ role: message.role,
31
+ parts: message.content.filter((part) => part.type === "text").map((part) => ({
32
+ type: "text",
33
+ text: part.text
34
+ })),
35
+ metadata: {
36
+ ...message.metadata.modality && { modality: message.metadata.modality },
37
+ ...Object.keys(message.metadata.custom).length > 0 && { custom: message.metadata.custom }
38
+ }
39
+ });
40
+ const EMPTY_SUGGESTIONS = [];
41
+ const useGeneratedSuggestions = (suggestionAdapter, messages, isRunning) => {
42
+ const [suggestions, setSuggestions] = useState(EMPTY_SUGGESTIONS);
43
+ const controllerRef = useRef(null);
44
+ const wasRunningRef = useRef(false);
45
+ const messagesRef = useRef(messages);
46
+ useInsertionEffect(() => {
47
+ messagesRef.current = messages;
48
+ }, [messages]);
49
+ const adapterKey = suggestionAdapter?.key;
50
+ const adapterRef = useRef(suggestionAdapter);
51
+ const lastAdapterKeyRef = useRef(adapterKey);
52
+ const adapterReplacedRef = useRef(false);
53
+ useInsertionEffect(() => {
54
+ const previousAdapter = adapterRef.current;
55
+ adapterRef.current = suggestionAdapter;
56
+ const adapterReplaced = previousAdapter !== void 0 && suggestionAdapter !== void 0 && lastAdapterKeyRef.current !== adapterKey;
57
+ adapterReplacedRef.current ||= adapterReplaced;
58
+ if (suggestionAdapter === void 0 || adapterReplaced) controllerRef.current?.abort();
59
+ lastAdapterKeyRef.current = adapterKey;
60
+ }, [adapterKey, suggestionAdapter]);
61
+ const hasAdapter = suggestionAdapter !== void 0;
62
+ const adapterPresentRef = useRef(hasAdapter);
63
+ useEffect(() => {
64
+ const clearSuggestions = () => {
65
+ controllerRef.current?.abort();
66
+ controllerRef.current = null;
67
+ setSuggestions((prev) => prev.length === 0 ? prev : EMPTY_SUGGESTIONS);
68
+ };
69
+ const adapter = adapterRef.current;
70
+ const adapterRestored = hasAdapter && !adapterPresentRef.current;
71
+ adapterPresentRef.current = hasAdapter;
72
+ const adapterReplaced = adapterReplacedRef.current;
73
+ adapterReplacedRef.current = false;
74
+ if (!adapter) {
75
+ clearSuggestions();
76
+ wasRunningRef.current = isRunning;
77
+ return;
78
+ }
79
+ if (adapterReplaced) clearSuggestions();
80
+ if (isRunning) {
81
+ if (!wasRunningRef.current) clearSuggestions();
82
+ wasRunningRef.current = true;
83
+ return;
84
+ }
85
+ if (!wasRunningRef.current && !adapterReplaced && !adapterRestored) return;
86
+ wasRunningRef.current = false;
87
+ const currentMessages = messagesRef.current;
88
+ const last = currentMessages.at(-1);
89
+ if (last?.role !== "assistant") return;
90
+ if (last.status?.type === "requires-action") return;
91
+ const controller = new AbortController();
92
+ controllerRef.current = controller;
93
+ const { signal } = controller;
94
+ (async () => {
95
+ try {
96
+ const promiseOrGenerator = adapter.generate({
97
+ messages: currentMessages,
98
+ signal
99
+ });
100
+ await consumeSuggestionResult(promiseOrGenerator, {
101
+ signal,
102
+ onUpdate: (nextSuggestions) => {
103
+ if (controllerRef.current !== controller) return;
104
+ setSuggestions(nextSuggestions);
105
+ }
106
+ });
107
+ } catch {}
108
+ })();
109
+ }, [
110
+ adapterKey,
111
+ hasAdapter,
112
+ isRunning
113
+ ]);
114
+ useReplaySafeEffect(() => {
115
+ return () => {
116
+ controllerRef.current?.abort();
117
+ };
118
+ }, []);
119
+ return suggestions;
120
+ };
121
+ const NO_CANCELLED_MESSAGE_IDS = /* @__PURE__ */ new Set();
122
+ const NO_SUPERSEDED_APPROVAL_PROJECTION = Object.freeze({
123
+ approvalIds: Object.freeze(/* @__PURE__ */ new Set()),
124
+ statusMessageIds: Object.freeze(/* @__PURE__ */ new Set())
125
+ });
126
+ const NO_TOOL_APPROVAL_RESPONSES = /* @__PURE__ */ new Map();
127
+ const chatRuntimeStates = /* @__PURE__ */ new WeakMap();
128
+ const getChatRuntimeState = (owner) => {
129
+ if (owner === void 0) return void 0;
130
+ let state = chatRuntimeStates.get(owner);
131
+ if (state === void 0) {
132
+ state = {
133
+ approvals: /* @__PURE__ */ new Map(),
134
+ cancelledMessageIds: NO_CANCELLED_MESSAGE_IDS,
135
+ cancelledToolCallIds: /* @__PURE__ */ new Set()
136
+ };
137
+ chatRuntimeStates.set(owner, state);
138
+ }
139
+ return state;
140
+ };
141
+ const toApprovalResponses = (owned) => owned && owned.size > 0 ? new Map([...owned].map(([id, entry]) => [id, entry.response])) : NO_TOOL_APPROVAL_RESPONSES;
142
+ /**
143
+ * The answers live on the owner, but each mounted runtime renders them from
144
+ * its own state, so a write has to be announced: the runtime that performed it
145
+ * may already be unmounted (a rollback resolving after a remount), and another
146
+ * runtime may be mounted over the same owner.
147
+ */
148
+ const chatRuntimeListeners = /* @__PURE__ */ new WeakMap();
149
+ const subscribeToChatRuntimeState = (owner, listener) => {
150
+ const listeners = chatRuntimeListeners.get(owner) ?? /* @__PURE__ */ new Set();
151
+ chatRuntimeListeners.set(owner, listeners);
152
+ listeners.add(listener);
153
+ return () => {
154
+ listeners.delete(listener);
155
+ };
156
+ };
157
+ const notifyChatRuntimeState = (owner) => {
158
+ for (const listener of [...chatRuntimeListeners.get(owner) ?? []]) listener();
159
+ };
160
+ const getSupersededApprovalProjection = (messages, hostApprovalIds, joinStrategy, isRunning) => {
161
+ const approvalIds = /* @__PURE__ */ new Set();
162
+ const statusMessageIds = /* @__PURE__ */ new Set();
163
+ const lastIndex = messages.length - 1;
164
+ let lastAssistant;
165
+ let hasSupersededApproval = false;
166
+ let previousWasVoice = false;
167
+ const flush = () => {
168
+ const hasOpenToolPart = lastAssistant === messages[lastIndex] && lastAssistant?.parts?.some((part) => {
169
+ if (typeof part?.type !== "string" || !isToolUIPart(part) || part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return false;
170
+ const approval = part.approval;
171
+ const resolution = approval?.resolution ?? (approval?.descriptor)?.resolution;
172
+ return resolution !== "cancelled" && resolution !== "expired";
173
+ });
174
+ if (lastAssistant && hasSupersededApproval && !hasOpenToolPart && !(isRunning && lastAssistant === messages[lastIndex])) statusMessageIds.add(lastAssistant.id);
175
+ lastAssistant = void 0;
176
+ hasSupersededApproval = false;
177
+ previousWasVoice = false;
178
+ };
179
+ for (let index = 0; index < messages.length; index++) {
180
+ const message = messages[index];
181
+ if (message.role !== "assistant") flush();
182
+ else {
183
+ const isVoice = message.metadata?.modality === "voice";
184
+ if (isVoice || previousWasVoice || joinStrategy === "none") flush();
185
+ lastAssistant = message;
186
+ previousWasVoice = isVoice;
187
+ }
188
+ for (const part of message.parts ?? []) {
189
+ if (typeof part?.type !== "string" || !isToolUIPart(part) || part.state !== "approval-requested") continue;
190
+ const approval = part.approval;
191
+ if (!approval) continue;
192
+ const approvalId = approval.id;
193
+ if (hostApprovalIds.has(approvalId)) continue;
194
+ const resolution = approval.resolution ?? approval.descriptor?.resolution;
195
+ if (resolution === "cancelled" || resolution === "expired") continue;
196
+ if (index !== lastIndex) {
197
+ approvalIds.add(approvalId);
198
+ if (message.role === "assistant") hasSupersededApproval = true;
199
+ }
200
+ }
201
+ }
202
+ flush();
203
+ if (approvalIds.size === 0 && statusMessageIds.size === 0) return NO_SUPERSEDED_APPROVAL_PROJECTION;
204
+ return {
205
+ approvalIds,
206
+ statusMessageIds
207
+ };
208
+ };
209
+ const findRawToolMessageIndex = (messages, messageId, toolCallId, joinStrategy) => {
210
+ const containsToolCall = (message) => message.parts?.some((part) => typeof part?.type === "string" && isToolUIPart(part) && part.toolCallId === toolCallId) === true;
211
+ const messageIndex = messages.findIndex((message) => message.id === messageId);
212
+ if (messageIndex === -1) return -1;
213
+ if (containsToolCall(messages[messageIndex])) return messageIndex;
214
+ if (joinStrategy === "none" || messages[messageIndex]?.role !== "assistant") return -1;
215
+ let start = messageIndex;
216
+ while (start > 0 && messages[start - 1]?.role === "assistant") start--;
217
+ let end = messageIndex;
218
+ while (end + 1 < messages.length && messages[end + 1]?.role === "assistant") end++;
219
+ for (let index = start; index <= end; index++) if (containsToolCall(messages[index])) return index;
220
+ return -1;
221
+ };
222
+ const toChatError = (error) => {
223
+ const { code, statusCode } = error;
224
+ return {
225
+ code: typeof code === "string" ? code : error.name !== "Error" ? error.name : "unknown",
226
+ message: error.message,
227
+ ...typeof statusCode === "number" && { statusCode }
228
+ };
229
+ };
230
+ const useAISDKRuntimeResource = (chatHelpers, adapter = {}) => {
231
+ const { adapters, toCreateMessage: customToCreateMessage, cancelPendingToolCallsOnSend = true, onResume, canResume, onResumeToolCall, onRespondToToolApproval: customOnRespondToToolApproval, joinStrategy, messageRepository, unstable_onBranchChange, unstable_enableMessageQueue } = adapter;
232
+ const suggestionAdapter = adapters?.suggestion;
233
+ const contextAdapters = useRuntimeAdaptersValue();
234
+ const [queueError, setQueueError] = useState();
235
+ const runtimeError = unstable_enableMessageQueue ? queueError ?? chatHelpers.error : chatHelpers.error;
236
+ const [toolStatuses, setToolStatuses] = useState({});
237
+ const [cancelledMessages, setCancelledMessages] = useState(null);
238
+ const approvalOwner = adapter.unstable_hostApprovalOwner;
239
+ const chatRuntimeState = getChatRuntimeState(approvalOwner);
240
+ const ownedApprovals = chatRuntimeState?.approvals;
241
+ const localCancelledToolCallIdsRef = useRef(/* @__PURE__ */ new Set());
242
+ const cancelledToolCallIds = chatRuntimeState?.cancelledToolCallIds ?? localCancelledToolCallIdsRef.current;
243
+ const ownedCancelledMessageIds = useSyncExternalStore(useCallback((listener) => approvalOwner ? subscribeToChatRuntimeState(approvalOwner, listener) : () => {}, [approvalOwner]), useCallback(() => chatRuntimeState?.cancelledMessageIds ?? NO_CANCELLED_MESSAGE_IDS, [chatRuntimeState]), () => NO_CANCELLED_MESSAGE_IDS);
244
+ const [toolApprovalResponses, setToolApprovalResponses] = useState(() => toApprovalResponses(ownedApprovals));
245
+ const [toolArtifacts, setToolArtifacts] = useState(() => /* @__PURE__ */ new Map());
246
+ const [toolInteractions, setToolInteractions] = useState(() => /* @__PURE__ */ new Map());
247
+ const toolApprovalResponsesRef = useRef(new Map(toApprovalResponses(ownedApprovals)));
248
+ const ownedApprovalIdsRef = useRef(new Set(ownedApprovals?.keys()));
249
+ const hostApprovalIdsRef = useRef(new Set(ownedApprovals?.keys()));
250
+ useEffect(() => {
251
+ if (!approvalOwner || !ownedApprovals) return void 0;
252
+ const sync = () => {
253
+ for (const id of ownedApprovalIdsRef.current) toolApprovalResponsesRef.current.delete(id);
254
+ for (const [id, entry] of ownedApprovals) toolApprovalResponsesRef.current.set(id, entry.response);
255
+ ownedApprovalIdsRef.current = new Set(ownedApprovals.keys());
256
+ hostApprovalIdsRef.current = new Set(toolApprovalResponsesRef.current.keys());
257
+ setToolApprovalResponses((prev) => {
258
+ const next = new Map(toolApprovalResponsesRef.current);
259
+ return prev.size === next.size && [...next].every(([id, response]) => prev.get(id) === response) ? prev : next;
260
+ });
261
+ };
262
+ const unsubscribe = subscribeToChatRuntimeState(approvalOwner, sync);
263
+ sync();
264
+ return unsubscribe;
265
+ }, [approvalOwner, ownedApprovals]);
266
+ const lastApprovalOwnerRef = useRef(approvalOwner);
267
+ if (lastApprovalOwnerRef.current !== approvalOwner) {
268
+ lastApprovalOwnerRef.current = approvalOwner;
269
+ hostApprovalIdsRef.current = new Set(ownedApprovals?.keys());
270
+ toolApprovalResponsesRef.current = new Map(toApprovalResponses(ownedApprovals));
271
+ ownedApprovalIdsRef.current = new Set(ownedApprovals?.keys());
272
+ setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
273
+ }
274
+ const toolArgsKeyOrderCacheRef = useRef(/* @__PURE__ */ new Map());
275
+ const [createdAtCaches] = useState(() => /* @__PURE__ */ new Map());
276
+ let createdAtCache = createdAtCaches.get(chatHelpers.id);
277
+ if (!createdAtCache) {
278
+ createdAtCache = /* @__PURE__ */ new Map();
279
+ createdAtCaches.set(chatHelpers.id, createdAtCache);
280
+ }
281
+ const toolLastInputCacheRef = useRef(/* @__PURE__ */ new Map());
282
+ const toolArgsTextCacheRef = useRef(/* @__PURE__ */ new WeakMap());
283
+ const mcpAppMetadataCacheRef = useRef(/* @__PURE__ */ new Map());
284
+ const toolArtifactsRef = useRef(/* @__PURE__ */ new Map());
285
+ const toolInteractionsRef = useRef(/* @__PURE__ */ new Map());
286
+ const lastRunConfigRef = useRef(void 0);
287
+ const restoreToolArtifacts = useCallback((next) => {
288
+ toolArtifactsRef.current = next;
289
+ setToolArtifacts(next);
290
+ }, []);
291
+ const restoreToolInteractions = useCallback((next) => {
292
+ toolInteractionsRef.current = next;
293
+ setToolInteractions(next);
294
+ }, []);
295
+ const getToolArtifacts = useCallback(() => toolArtifactsRef.current, []);
296
+ const getToolInteractions = useCallback(() => toolInteractionsRef.current, []);
297
+ const hasExecutingTools = Object.values(toolStatuses).some((s) => s?.type === "executing");
298
+ const providerIsRunning = chatHelpers.status === "submitted" || chatHelpers.status === "streaming";
299
+ const isRunning = providerIsRunning || hasExecutingTools;
300
+ const wasProviderRunningRef = useRef(providerIsRunning);
301
+ const messageTiming = useStreamingTimingValue(chatHelpers.messages, isRunning, aiSdkStreamingTimingAccessors);
302
+ const lastMessage = chatHelpers.messages.at(-1);
303
+ const optimisticMessageId = isRunning && lastMessage?.role === "assistant" ? lastMessage.id : void 0;
304
+ const cancelledMessageIds = chatRuntimeState ? ownedCancelledMessageIds : cancelledMessages?.chatId === chatHelpers.id ? cancelledMessages.ids : NO_CANCELLED_MESSAGE_IDS;
305
+ const supportsRichToolApprovalResponses = customOnRespondToToolApproval != null;
306
+ const supersededApprovalProjectionRef = useRef(NO_SUPERSEDED_APPROVAL_PROJECTION);
307
+ const supersededApprovalProjection = useMemo(() => {
308
+ const projection = getSupersededApprovalProjection(chatHelpers.messages, hostApprovalIdsRef.current, joinStrategy, isRunning);
309
+ const previous = supersededApprovalProjectionRef.current;
310
+ if (projection.approvalIds.size === previous.approvalIds.size && projection.statusMessageIds.size === previous.statusMessageIds.size && [...projection.approvalIds].every((id) => previous.approvalIds.has(id)) && [...projection.statusMessageIds].every((id) => previous.statusMessageIds.has(id))) return previous;
311
+ supersededApprovalProjectionRef.current = projection;
312
+ return projection;
313
+ }, [
314
+ chatHelpers.messages,
315
+ joinStrategy,
316
+ isRunning,
317
+ toolApprovalResponses
318
+ ]);
319
+ const toThreadMessages = useCallback((sourceMessages) => {
320
+ const metadata = {
321
+ createdAtCache,
322
+ supportsRichToolApprovalResponses,
323
+ toolArtifacts: toolArtifactsRef.current,
324
+ toolInteractions: toolInteractionsRef.current,
325
+ toolApprovalResponses: toolApprovalResponsesRef.current
326
+ };
327
+ return AISDKMessageConverter.toThreadMessages(sourceMessages, false, metadata);
328
+ }, [createdAtCache, supportsRichToolApprovalResponses]);
329
+ const retractCancellation = useCallback((chatId, messageId) => {
330
+ if (chatRuntimeState !== void 0 && approvalOwner !== void 0) {
331
+ if (!chatRuntimeState.cancelledMessageIds.has(messageId)) return;
332
+ const ids = new Set(chatRuntimeState.cancelledMessageIds);
333
+ ids.delete(messageId);
334
+ chatRuntimeState.cancelledMessageIds = ids;
335
+ notifyChatRuntimeState(approvalOwner);
336
+ return;
337
+ }
338
+ setCancelledMessages((prev) => {
339
+ if (prev?.chatId !== chatId || !prev.ids.has(messageId)) return prev;
340
+ const ids = new Set(prev.ids);
341
+ ids.delete(messageId);
342
+ return {
343
+ chatId,
344
+ ids
345
+ };
346
+ });
347
+ }, [approvalOwner, chatRuntimeState]);
348
+ const resumedMessageId = providerIsRunning && lastMessage?.role === "assistant" ? lastMessage.id : void 0;
349
+ useEffect(() => {
350
+ const wasProviderRunning = wasProviderRunningRef.current;
351
+ wasProviderRunningRef.current = providerIsRunning;
352
+ if (wasProviderRunning || !resumedMessageId) return;
353
+ retractCancellation(chatHelpers.id, resumedMessageId);
354
+ }, [
355
+ providerIsRunning,
356
+ resumedMessageId,
357
+ chatHelpers.id,
358
+ retractCancellation
359
+ ]);
360
+ const [getConverterMetadataKey] = useState(createAISDKMetadataKey);
361
+ const messages = useExternalMessageConverterValue({
362
+ callback: convertAISDKMessage,
363
+ isRunning,
364
+ messages: chatHelpers.messages,
365
+ joinStrategy,
366
+ metadata: useMemo(() => ({
367
+ createdAtCache,
368
+ toolStatuses,
369
+ messageTiming,
370
+ toolArgsKeyOrderCache: toolArgsKeyOrderCacheRef.current,
371
+ toolArgsTextCache: toolArgsTextCacheRef.current,
372
+ toolLastInputCache: toolLastInputCacheRef.current,
373
+ mcpAppMetadataCache: mcpAppMetadataCacheRef.current,
374
+ toolArtifacts,
375
+ toolInteractions,
376
+ supportsRichToolApprovalResponses,
377
+ cancelledToolApprovalIds: supersededApprovalProjection.approvalIds,
378
+ cancelledStatusMessageIds: supersededApprovalProjection.statusMessageIds,
379
+ ...optimisticMessageId && { optimisticMessageId },
380
+ ...chatHelpers.error && { error: toChatError(chatHelpers.error) },
381
+ ...cancelledMessageIds.size > 0 && { cancelledMessageIds },
382
+ ...toolApprovalResponses.size > 0 && { toolApprovalResponses }
383
+ }), [
384
+ createdAtCache,
385
+ toolStatuses,
386
+ messageTiming,
387
+ optimisticMessageId,
388
+ chatHelpers.error,
389
+ cancelledMessageIds,
390
+ toolApprovalResponses,
391
+ supportsRichToolApprovalResponses,
392
+ supersededApprovalProjection,
393
+ toolArtifacts,
394
+ toolInteractions
395
+ ]),
396
+ getMetadataKey: getConverterMetadataKey
397
+ });
398
+ const exportedMessageRepository = useMemo(() => {
399
+ if (!messageRepository) return void 0;
400
+ const converted = toExportedMessageRepository(toThreadMessages, messageRepository);
401
+ return converted.messages.length > 0 ? converted : void 0;
402
+ }, [messageRepository, toThreadMessages]);
403
+ const generatedSuggestions = useGeneratedSuggestions(suggestionAdapter, messages, isRunning);
404
+ const [runtimeRef] = useState(() => ({ get current() {
405
+ return runtime;
406
+ } }));
407
+ const { isLoading, deleteMessage: deleteHistoryMessage, markRunCancelled, persistToolInteractions, persistToolApprovalResponses } = useExternalHistory(runtimeRef, adapters?.history ?? contextAdapters?.history, toThreadMessages, aiSDKV6FormatAdapter, (messages) => {
408
+ chatHelpers.setMessages(messages);
409
+ }, getToolArtifacts, restoreToolArtifacts, getToolInteractions, restoreToolInteractions, toolApprovalResponsesRef.current, () => {
410
+ for (const [id, entry] of ownedApprovals ?? []) toolApprovalResponsesRef.current.set(id, entry.response);
411
+ hostApprovalIdsRef.current = new Set(toolApprovalResponsesRef.current.keys());
412
+ setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
413
+ });
414
+ const { id: chatId, messages: chatMessages, status: chatStatus } = chatHelpers;
415
+ const extras = useMemo(() => aiSDKExtras.provide({
416
+ chat: chatHelpers,
417
+ error: runtimeError
418
+ }), [
419
+ chatId,
420
+ chatMessages,
421
+ chatStatus,
422
+ runtimeError
423
+ ]);
424
+ const completePendingToolCalls = async () => {
425
+ if (!cancelPendingToolCallsOnSend) return;
426
+ chatHelpers.setMessages((messages) => {
427
+ let hasChanges = false;
428
+ const next = messages.map((message) => {
429
+ if (message.role !== "assistant") return message;
430
+ let messageChanged = false;
431
+ const parts = message.parts?.map((part) => {
432
+ if (typeof part?.type !== "string" || !isToolUIPart(part)) return part;
433
+ if (part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return part;
434
+ messageChanged = true;
435
+ const { approval: _approval, ...rest } = part;
436
+ return {
437
+ ...rest,
438
+ state: "output-error",
439
+ errorText: "User cancelled tool call by sending a new message."
440
+ };
441
+ });
442
+ if (!messageChanged) return message;
443
+ hasChanges = true;
444
+ return {
445
+ ...message,
446
+ parts
447
+ };
448
+ });
449
+ if (!hasChanges) return messages;
450
+ return next;
451
+ });
452
+ };
453
+ const respondViaAISDK = ({ approvalId, approved, reason }) => Promise.resolve(chatHelpers.addToolApprovalResponse({
454
+ id: approvalId,
455
+ approved,
456
+ ...reason != null && { reason },
457
+ options: { metadata: lastRunConfigRef.current }
458
+ }));
459
+ const respondViaHost = async (onRespond, response) => {
460
+ const { approvalId } = response;
461
+ const requested = chatHelpers.messages.flatMap((message) => message.parts.flatMap((part) => typeof part?.type === "string" && isToolUIPart(part) ? [{
462
+ messageId: message.id,
463
+ part
464
+ }] : [])).find(({ part }) => part.state === "approval-requested" && part.approval?.id === approvalId);
465
+ if (!requested || hostApprovalIdsRef.current.has(approvalId)) throw new Error(`Tool approval ${approvalId} is not waiting for a response.`);
466
+ const startedWith = ownedApprovals;
467
+ const startedOwner = approvalOwner;
468
+ const startedResponses = toolApprovalResponsesRef.current;
469
+ let isApplied = false;
470
+ const applyResponse = (applied) => {
471
+ isApplied = applied;
472
+ if (applied) startedWith?.set(approvalId, { response });
473
+ else startedWith?.delete(approvalId);
474
+ if (startedOwner) {
475
+ notifyChatRuntimeState(startedOwner);
476
+ return;
477
+ }
478
+ if (lastApprovalOwnerRef.current !== startedOwner) return;
479
+ if (applied) hostApprovalIdsRef.current.add(approvalId);
480
+ else hostApprovalIdsRef.current.delete(approvalId);
481
+ if (applied) toolApprovalResponsesRef.current.set(approvalId, response);
482
+ else toolApprovalResponsesRef.current.delete(approvalId);
483
+ setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
484
+ };
485
+ applyResponse(true);
486
+ try {
487
+ await onRespond(response, {
488
+ toolCallId: requested.part.toolCallId,
489
+ toolName: getToolName(requested.part),
490
+ respondViaAISDK: async () => {
491
+ try {
492
+ await respondViaAISDK(response);
493
+ } finally {
494
+ applyResponse(false);
495
+ }
496
+ }
497
+ });
498
+ } catch (error) {
499
+ if (isApplied) applyResponse(false);
500
+ throw error;
501
+ }
502
+ const remainsApplied = startedWith?.has(approvalId) ?? startedResponses.has(approvalId);
503
+ if (isApplied && remainsApplied) await persistToolApprovalResponses(requested.messageId);
504
+ };
505
+ const lastAISDKMessageId = (threadMessageId) => {
506
+ if (threadMessageId == null) return null;
507
+ const threadMessage = runtimeRef.current.thread.getState().messages.find((message) => message.id === threadMessageId);
508
+ return (threadMessage && getVercelAIMessages(threadMessage).at(-1)?.id) ?? threadMessageId;
509
+ };
510
+ const cancelRun = async () => {
511
+ markRunCancelled();
512
+ for (const toolCallId of Object.keys(toolStatuses)) cancelledToolCallIds.add(toolCallId);
513
+ const message = chatHelpers.messages.at(-1);
514
+ const cancelledId = isRunning && message?.role === "assistant" ? message.id : void 0;
515
+ if (cancelledId) {
516
+ const liveIds = new Set(chatHelpers.messages.map((m) => m.id));
517
+ if (chatRuntimeState !== void 0 && approvalOwner !== void 0) {
518
+ chatRuntimeState.cancelledMessageIds = /* @__PURE__ */ new Set([...[...chatRuntimeState.cancelledMessageIds].filter((id) => liveIds.has(id)), cancelledId]);
519
+ notifyChatRuntimeState(approvalOwner);
520
+ } else setCancelledMessages((prev) => {
521
+ const kept = prev?.chatId === chatHelpers.id ? [...prev.ids].filter((id) => liveIds.has(id)) : [];
522
+ return {
523
+ chatId: chatHelpers.id,
524
+ ids: /* @__PURE__ */ new Set([...kept, cancelledId])
525
+ };
526
+ });
527
+ }
528
+ try {
529
+ await chatHelpers.stop();
530
+ } catch (error) {
531
+ if (!(error instanceof Error && error.name === "AbortError")) {
532
+ if (cancelledId) retractCancellation(chatHelpers.id, cancelledId);
533
+ throw error;
534
+ }
535
+ }
536
+ };
537
+ const sendNew = async (message) => {
538
+ setQueueError(void 0);
539
+ const createMessage = (customToCreateMessage ?? toCreateMessage)(message);
540
+ if (!(message.startRun ?? message.role === "user")) {
541
+ chatHelpers.setMessages((current) => [...current, toUIMessage(createMessage, message.role)]);
542
+ return;
543
+ }
544
+ lastRunConfigRef.current = message.runConfig;
545
+ await completePendingToolCalls();
546
+ await chatHelpers.sendMessage(createMessage, { metadata: message.runConfig });
547
+ };
548
+ const messageQueue = useMessageQueue({
549
+ onError: (error) => {
550
+ if (error?.name === "AbortError") setQueueError(void 0);
551
+ else setQueueError(error);
552
+ },
553
+ enabled: unstable_enableMessageQueue === true,
554
+ isRunning,
555
+ isSendDisabled: adapter.isSendDisabled === true,
556
+ send: sendNew,
557
+ cancel: cancelRun
558
+ });
559
+ const hasSeededRepositoryRef = useRef(false);
560
+ const shouldFeedRepository = exportedMessageRepository != null && !hasSeededRepositoryRef.current && messages.length === 0;
561
+ const runtime = useExternalStoreRuntime({
562
+ unstable_persistsHistory: true,
563
+ isRunning: providerIsRunning,
564
+ ...shouldFeedRepository ? { messageRepository: exportedMessageRepository } : { messages },
565
+ unstable_enableToolInvocations: true,
566
+ setToolStatuses,
567
+ setMessages: (messages) => chatHelpers.setMessages(messages.map(getVercelAIMessages).filter(Boolean).flat()),
568
+ onImport: (messages) => chatHelpers.setMessages(messages.map(getVercelAIMessages).filter(Boolean).flat()),
569
+ onVoiceTranscript: (message) => chatHelpers.setMessages((current) => [...current, toVoiceTranscriptUIMessage(message)]),
570
+ onExportExternalState: () => {
571
+ const exported = runtimeRef.current.thread.export();
572
+ const expandedMessages = [];
573
+ const lastInnerIdMap = /* @__PURE__ */ new Map();
574
+ for (const item of exported.messages) {
575
+ const innerMessages = getExternalStoreMessages(item.message);
576
+ let parentId = item.parentId != null ? lastInnerIdMap.get(item.parentId) ?? item.parentId : null;
577
+ for (const innerMessage of innerMessages) {
578
+ expandedMessages.push({
579
+ parentId,
580
+ message: innerMessage
581
+ });
582
+ parentId = aiSDKV6FormatAdapter.getId(innerMessage);
583
+ }
584
+ if (innerMessages.length > 0) lastInnerIdMap.set(item.message.id, aiSDKV6FormatAdapter.getId(innerMessages[innerMessages.length - 1]));
585
+ }
586
+ const result = { messages: expandedMessages };
587
+ if (exported.headId != null) result.headId = lastInnerIdMap.get(exported.headId) ?? exported.headId;
588
+ return result;
589
+ },
590
+ onLoadExternalState: (repo) => {
591
+ const exportedRepo = toExportedMessageRepository(toThreadMessages, repo);
592
+ runtimeRef.current.thread.import(exportedRepo);
593
+ },
594
+ onCancel: messageQueue.cancel,
595
+ onNew: sendNew,
596
+ ...messageQueue.adapter && { queue: messageQueue.adapter },
597
+ onEdit: async (message) => {
598
+ messageQueue.clear();
599
+ const createMessage = (customToCreateMessage ?? toCreateMessage)(message);
600
+ const parentId = lastAISDKMessageId(message.parentId);
601
+ if (!(message.startRun ?? message.role === "user")) {
602
+ chatHelpers.setMessages((current) => [...sliceMessagesUntil(current, parentId), toUIMessage(createMessage, message.role)]);
603
+ return;
604
+ }
605
+ lastRunConfigRef.current = message.runConfig;
606
+ chatHelpers.setMessages((current) => sliceMessagesUntil(current, parentId));
607
+ await chatHelpers.sendMessage(createMessage, { metadata: message.runConfig });
608
+ },
609
+ onDelete: async (messageId) => {
610
+ const threadMessages = runtimeRef.current.thread.getState().messages;
611
+ const messageIndex = threadMessages.findIndex((message) => message.id === messageId);
612
+ if (messageIndex === -1) return;
613
+ await deleteHistoryMessage(messageId);
614
+ let removedToolArtifact = false;
615
+ let removedToolInteractions = false;
616
+ let removedToolApprovalResponse = false;
617
+ let removedHostApprovalId = false;
618
+ const nextToolArtifacts = new Map(toolArtifactsRef.current);
619
+ const nextToolInteractions = new Map(toolInteractionsRef.current);
620
+ for (const part of threadMessages[messageIndex].content) if (part.type === "tool-call") {
621
+ removedToolArtifact = nextToolArtifacts.delete(part.toolCallId) || removedToolArtifact;
622
+ removedToolInteractions = nextToolInteractions.delete(part.toolCallId) || removedToolInteractions;
623
+ if (part.approval) {
624
+ ownedApprovals?.delete(part.approval.id);
625
+ removedToolApprovalResponse = toolApprovalResponsesRef.current.delete(part.approval.id) || removedToolApprovalResponse;
626
+ removedHostApprovalId = hostApprovalIdsRef.current.delete(part.approval.id) || removedHostApprovalId;
627
+ }
628
+ }
629
+ if (removedToolArtifact) {
630
+ toolArtifactsRef.current = nextToolArtifacts;
631
+ setToolArtifacts(nextToolArtifacts);
632
+ }
633
+ if (removedToolInteractions) {
634
+ toolInteractionsRef.current = nextToolInteractions;
635
+ setToolInteractions(nextToolInteractions);
636
+ }
637
+ if (removedToolApprovalResponse || removedHostApprovalId) {
638
+ if (approvalOwner) notifyChatRuntimeState(approvalOwner);
639
+ setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
640
+ }
641
+ const deleteIds = new Set(getExternalStoreMessages(threadMessages[messageIndex]).map((message) => message.id));
642
+ chatHelpers.setMessages((current) => current.filter((message) => !deleteIds.has(message.id)));
643
+ },
644
+ onReload: async (parentId, config) => {
645
+ messageQueue.clear();
646
+ lastRunConfigRef.current = config.runConfig;
647
+ const newMessages = sliceMessagesUntil(chatHelpers.messages, lastAISDKMessageId(parentId));
648
+ const reloaded = chatHelpers.messages[newMessages.length];
649
+ if (reloaded?.role === "assistant") newMessages.push(reloaded);
650
+ chatHelpers.setMessages(newMessages);
651
+ await chatHelpers.regenerate({ metadata: config.runConfig });
652
+ },
653
+ onAddToolResult: ({ messageId, toolCallId, toolName, result, isError, artifact, modelContent }) => {
654
+ const wasCancelled = cancelledToolCallIds.delete(toolCallId);
655
+ if (artifact !== void 0) {
656
+ const next = new Map(toolArtifactsRef.current);
657
+ next.set(toolCallId, artifact);
658
+ toolArtifactsRef.current = next;
659
+ setToolArtifacts(next);
660
+ }
661
+ const targetIndex = findRawToolMessageIndex(chatHelpers.messages, messageId, toolCallId, joinStrategy);
662
+ const errorText = typeof result === "string" ? result : JSON.stringify(result);
663
+ const output = !isError && modelContent !== void 0 ? wrapModelContentEnvelope(result, modelContent) : result;
664
+ if (targetIndex >= 0 && (targetIndex !== chatHelpers.messages.length - 1 || wasCancelled)) {
665
+ const target = chatHelpers.messages[targetIndex];
666
+ const targetPart = target.parts.find((part) => typeof part?.type === "string" && isToolUIPart(part) && part.toolCallId === toolCallId);
667
+ if (targetPart?.state === "output-error" || targetPart?.state === "output-denied" || targetPart?.state === "output-available" && !targetPart.preliminary) return Promise.resolve();
668
+ const targetMessageId = target.id;
669
+ chatHelpers.setMessages((current) => current.map((message) => {
670
+ if (message.id !== targetMessageId) return message;
671
+ return {
672
+ ...message,
673
+ parts: message.parts.map((part) => {
674
+ if (typeof part?.type !== "string" || !isToolUIPart(part) || part.toolCallId !== toolCallId) return part;
675
+ const { preliminary: _preliminary, ...finalPart } = part;
676
+ return {
677
+ ...finalPart,
678
+ state: isError ? "output-error" : "output-available",
679
+ output: isError ? void 0 : output,
680
+ errorText: isError ? errorText : void 0
681
+ };
682
+ })
683
+ };
684
+ }));
685
+ return Promise.resolve();
686
+ }
687
+ if (wasCancelled) return Promise.resolve();
688
+ const options = { metadata: lastRunConfigRef.current };
689
+ if (isError) return Promise.resolve(chatHelpers.addToolOutput({
690
+ state: "output-error",
691
+ tool: toolName ?? toolCallId,
692
+ toolCallId,
693
+ errorText,
694
+ options
695
+ }));
696
+ return Promise.resolve(chatHelpers.addToolOutput({
697
+ tool: toolName,
698
+ toolCallId,
699
+ output,
700
+ options
701
+ }));
702
+ },
703
+ onRespondToToolApproval: (response) => {
704
+ if (supersededApprovalProjection.approvalIds.has(response.approvalId)) return Promise.reject(/* @__PURE__ */ new Error(`Tool approval ${response.approvalId} is not waiting for a response.`));
705
+ return customOnRespondToToolApproval ? respondViaHost(customOnRespondToToolApproval, response) : respondViaAISDK(response);
706
+ },
707
+ unstable_onRecordToolInteraction: ({ messageId, toolCallId, interaction }) => {
708
+ const next = new Map(toolInteractionsRef.current);
709
+ next.set(toolCallId, appendToolInteraction(next.get(toolCallId), interaction));
710
+ toolInteractionsRef.current = next;
711
+ setToolInteractions(next);
712
+ return persistToolInteractions(messageId);
713
+ },
714
+ ...pickExternalStoreSharedOptions(adapter),
715
+ ...adapter.unstable_messageRepositoryInstance && { unstable_messageRepositoryInstance: adapter.unstable_messageRepositoryInstance },
716
+ ...suggestionAdapter ? { suggestions: generatedSuggestions } : {},
717
+ ...onResume && { onResume },
718
+ ...canResume !== void 0 && { canResume },
719
+ ...onResumeToolCall && { onResumeToolCall },
720
+ ...unstable_onBranchChange && { unstable_onBranchChange },
721
+ adapters: {
722
+ attachments: vercelAttachmentAdapter,
723
+ ...contextAdapters,
724
+ ...adapters
725
+ },
726
+ extras,
727
+ isLoading
728
+ });
729
+ const setMessagesRef = useRef(chatHelpers.setMessages);
730
+ useInsertionEffect(() => {
731
+ setMessagesRef.current = chatHelpers.setMessages;
732
+ }, [chatHelpers.setMessages]);
733
+ useEffect(() => {
734
+ if (hasSeededRepositoryRef.current) return;
735
+ if (!exportedMessageRepository) return;
736
+ if (chatHelpers.messages.length > 0) {
737
+ hasSeededRepositoryRef.current = true;
738
+ return;
739
+ }
740
+ const tempRepo = new MessageRepository();
741
+ tempRepo.import(exportedMessageRepository);
742
+ setMessagesRef.current(tempRepo.getMessages().flatMap(getExternalStoreMessages));
743
+ hasSeededRepositoryRef.current = true;
744
+ }, [exportedMessageRepository, chatHelpers.messages.length]);
745
+ return runtime;
746
+ };
747
+ /** Vue setup owns the resource; reactive helper/adapter getters stay live. */
748
+ const useAISDKRuntime = (helpers, adapter = {}) => {
749
+ if (isResourceExecution()) return useAISDKRuntimeResource(toValue(helpers), toValue(adapter));
750
+ return useRuntimeResource(() => useAISDKRuntimeResource(toValue(helpers), toValue(adapter)));
751
+ };
752
+ export { useAISDKRuntime };
753
+
754
+ //# sourceMappingURL=useAISDKRuntime.js.map