@assistant-ui/ai-sdk 0.0.10 → 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 (124) 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 +106 -28
  4. package/dist/converters/convertMessage.js.map +1 -1
  5. package/dist/converters/toolApprovalAnswers.d.ts +3 -0
  6. package/dist/converters/toolApprovalAnswers.d.ts.map +1 -0
  7. package/dist/converters/toolApprovalAnswers.js +17 -0
  8. package/dist/converters/toolApprovalAnswers.js.map +1 -0
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.native.d.ts +1 -1
  13. package/dist/index.native.d.ts.map +1 -1
  14. package/dist/index.native.js +1 -1
  15. package/dist/model-context/injectInteractableContext.d.ts +3 -0
  16. package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
  17. package/dist/model-context/injectInteractableContext.js +3 -0
  18. package/dist/model-context/injectInteractableContext.js.map +1 -1
  19. package/dist/model-context/injectQuoteContext.d.ts +1 -0
  20. package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
  21. package/dist/model-context/injectQuoteContext.js +1 -0
  22. package/dist/model-context/injectQuoteContext.js.map +1 -1
  23. package/dist/runtime/AISDKChat.js +1 -1
  24. package/dist/runtime/AISDKThreads.d.ts +5 -0
  25. package/dist/runtime/AISDKThreads.d.ts.map +1 -1
  26. package/dist/runtime/AISDKThreads.js +37 -4
  27. package/dist/runtime/AISDKThreads.js.map +1 -1
  28. package/dist/runtime/DynamicChatTransport.d.ts +49 -0
  29. package/dist/runtime/DynamicChatTransport.d.ts.map +1 -0
  30. package/dist/runtime/DynamicChatTransport.js +147 -0
  31. package/dist/runtime/DynamicChatTransport.js.map +1 -0
  32. package/dist/runtime/getResumableAdapter.d.ts +5 -0
  33. package/dist/runtime/getResumableAdapter.d.ts.map +1 -0
  34. package/dist/runtime/getResumableAdapter.js +12 -0
  35. package/dist/runtime/getResumableAdapter.js.map +1 -0
  36. package/dist/runtime/sdkIdentity.js +1 -1
  37. package/dist/runtime/toolHistoryCodec.d.ts +20 -0
  38. package/dist/runtime/toolHistoryCodec.d.ts.map +1 -0
  39. package/dist/runtime/toolHistoryCodec.js +107 -0
  40. package/dist/runtime/toolHistoryCodec.js.map +1 -0
  41. package/dist/runtime/useAISDKRuntime.d.ts +4 -1
  42. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
  43. package/dist/runtime/useAISDKRuntime.js +154 -155
  44. package/dist/runtime/useAISDKRuntime.js.map +1 -1
  45. package/dist/runtime/useChatRuntime.d.ts +14 -2
  46. package/dist/runtime/useChatRuntime.d.ts.map +1 -1
  47. package/dist/runtime/useChatRuntime.js +12 -3
  48. package/dist/runtime/useChatRuntime.js.map +1 -1
  49. package/dist/runtime/useChatThread.d.ts +3 -2
  50. package/dist/runtime/useChatThread.d.ts.map +1 -1
  51. package/dist/runtime/useChatThread.js +56 -36
  52. package/dist/runtime/useChatThread.js.map +1 -1
  53. package/dist/runtime/useDynamicChatTransport.d.ts +4 -0
  54. package/dist/runtime/useDynamicChatTransport.d.ts.map +1 -0
  55. package/dist/runtime/useDynamicChatTransport.js +64 -0
  56. package/dist/runtime/useDynamicChatTransport.js.map +1 -0
  57. package/dist/runtime/useExternalHistory.d.ts.map +1 -1
  58. package/dist/runtime/useExternalHistory.js +11 -105
  59. package/dist/runtime/useExternalHistory.js.map +1 -1
  60. package/dist/runtime/useResourceCleanup.js +1 -1
  61. package/dist/runtime/useStreamingTiming.js +2 -2
  62. package/dist/runtime/useStreamingTiming.js.map +1 -1
  63. package/dist/tools/generativeTools.d.ts +2 -1
  64. package/dist/tools/generativeTools.d.ts.map +1 -1
  65. package/dist/tools/generativeTools.js +17 -6
  66. package/dist/tools/generativeTools.js.map +1 -1
  67. package/dist/usage.js +1 -1
  68. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
  69. package/dist/utils/sliceMessagesUntil.js +1 -2
  70. package/dist/utils/sliceMessagesUntil.js.map +1 -1
  71. package/package.json +12 -10
  72. package/src/converters/convertMessage.test.ts +350 -2
  73. package/src/converters/convertMessage.tool-args-status.test.tsx +185 -0
  74. package/src/converters/convertMessage.ts +154 -23
  75. package/src/converters/toCreateMessage.test.ts +27 -0
  76. package/src/converters/toolApprovalAnswers.ts +27 -0
  77. package/src/index.native.ts +1 -1
  78. package/src/index.ts +1 -1
  79. package/src/model-context/injectInteractableContext.ts +3 -0
  80. package/src/model-context/injectQuoteContext.ts +1 -0
  81. package/src/runtime/AISDKChat.integration.test.tsx +57 -2
  82. package/src/runtime/AISDKThreads.cloud.test.ts +3 -0
  83. package/src/runtime/AISDKThreads.test.ts +181 -0
  84. package/src/runtime/AISDKThreads.ts +31 -4
  85. package/src/runtime/DynamicChatTransport.test.ts +203 -0
  86. package/src/runtime/DynamicChatTransport.ts +273 -0
  87. package/src/runtime/__tests__/controlled-transport.ts +3 -0
  88. package/src/runtime/getResumableAdapter.ts +16 -0
  89. package/src/runtime/toolHistoryCodec.test.ts +161 -0
  90. package/src/runtime/toolHistoryCodec.ts +207 -0
  91. package/src/runtime/useAISDKRuntime.approval.test.tsx +12 -0
  92. package/src/runtime/useAISDKRuntime.reload.test.tsx +219 -0
  93. package/src/runtime/useAISDKRuntime.test.ts +538 -5
  94. package/src/runtime/useAISDKRuntime.ts +161 -51
  95. package/src/runtime/useChatRuntime.integration.test.tsx +319 -4
  96. package/src/runtime/useChatRuntime.local-storage.test.tsx +123 -0
  97. package/src/runtime/useChatRuntime.test.ts +107 -1
  98. package/src/runtime/useChatRuntime.ts +26 -4
  99. package/src/runtime/useChatThread.binding.test.tsx +143 -0
  100. package/src/runtime/useChatThread.ts +98 -81
  101. package/src/runtime/useDynamicChatTransport.ts +26 -0
  102. package/src/runtime/useExternalHistory.test.ts +205 -0
  103. package/src/runtime/useExternalHistory.ts +14 -206
  104. package/src/runtime/useStreamingTiming.ts +2 -2
  105. package/src/tools/generativeTools.test.ts +190 -2
  106. package/src/tools/generativeTools.ts +28 -8
  107. package/src/utils/sliceMessagesUntil.test.ts +2 -6
  108. package/src/utils/sliceMessagesUntil.ts +1 -5
  109. package/dist/converters/modelContentEnvelope.d.ts +0 -14
  110. package/dist/converters/modelContentEnvelope.d.ts.map +0 -1
  111. package/dist/converters/modelContentEnvelope.js +0 -22
  112. package/dist/converters/modelContentEnvelope.js.map +0 -1
  113. package/dist/converters/toolOutputConversion.d.ts +0 -26
  114. package/dist/converters/toolOutputConversion.d.ts.map +0 -1
  115. package/dist/converters/toolOutputConversion.js +0 -31
  116. package/dist/converters/toolOutputConversion.js.map +0 -1
  117. package/dist/tools/frontendTools.d.ts +0 -30
  118. package/dist/tools/frontendTools.d.ts.map +0 -1
  119. package/dist/tools/frontendTools.js +0 -33
  120. package/dist/tools/frontendTools.js.map +0 -1
  121. package/src/converters/modelContentEnvelope.ts +0 -41
  122. package/src/converters/toolOutputConversion.ts +0 -26
  123. package/src/tools/frontendTools.test.ts +0 -205
  124. package/src/tools/frontendTools.ts +0 -83
@@ -2,7 +2,6 @@
2
2
  import { aiSDKV6FormatAdapter } from "../adapters/aiSDKFormatAdapter.js";
3
3
  import { vercelAttachmentAdapter } from "../adapters/vercelAttachmentAdapter.js";
4
4
  import { aiSDKExtras } from "../aiSDKExtras.js";
5
- import { wrapModelContentEnvelope } from "../converters/modelContentEnvelope.js";
6
5
  import { AISDKMessageConverter } from "../converters/convertMessage.js";
7
6
  import { toCreateMessage } from "../converters/toCreateMessage.js";
8
7
  import { sliceMessagesUntil } from "../utils/sliceMessagesUntil.js";
@@ -13,8 +12,8 @@ import { MessageRepository, appendToolInteraction, consumeSuggestionResult } fro
13
12
  import { generateId, getToolName, isToolUIPart } from "ai";
14
13
  import { useExternalStoreRuntime, useRuntimeAdapters } from "@assistant-ui/core/react";
15
14
  import { getExternalStoreMessages, pickExternalStoreSharedOptions } from "@assistant-ui/core";
16
- import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
17
- import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
15
+ import { wrapModelContentEnvelope } from "assistant-stream/internal";
16
+ import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "@assistant-ui/tap/react-shim";
18
17
  import { useReplaySafeEffect } from "@assistant-ui/store/internal";
19
18
  //#region src/runtime/useAISDKRuntime.ts
20
19
  const toUIMessage = (createMessage, fallbackRole) => ({
@@ -36,109 +35,83 @@ const toVoiceTranscriptUIMessage = (message) => ({
36
35
  });
37
36
  const EMPTY_SUGGESTIONS = [];
38
37
  const useGeneratedSuggestions = (suggestionAdapter, messages, isRunning) => {
39
- const $ = c(13);
40
38
  const [suggestions, setSuggestions] = useState(EMPTY_SUGGESTIONS);
41
39
  const controllerRef = useRef(null);
42
40
  const wasRunningRef = useRef(false);
43
41
  const messagesRef = useRef(messages);
44
- let t0;
45
- let t1;
46
- if ($[0] !== messages) {
47
- t0 = () => {
48
- messagesRef.current = messages;
49
- };
50
- t1 = [messages];
51
- $[0] = messages;
52
- $[1] = t0;
53
- $[2] = t1;
54
- } else {
55
- t0 = $[1];
56
- t1 = $[2];
57
- }
58
- useInsertionEffect(t0, t1);
42
+ useInsertionEffect(() => {
43
+ messagesRef.current = messages;
44
+ }, [messages]);
45
+ const adapterKey = suggestionAdapter?.key;
59
46
  const adapterRef = useRef(suggestionAdapter);
60
- let t2;
61
- let t3;
62
- if ($[3] !== suggestionAdapter) {
63
- t2 = () => {
64
- adapterRef.current = suggestionAdapter;
65
- };
66
- t3 = [suggestionAdapter];
67
- $[3] = suggestionAdapter;
68
- $[4] = t2;
69
- $[5] = t3;
70
- } else {
71
- t2 = $[4];
72
- t3 = $[5];
73
- }
74
- useInsertionEffect(t2, t3);
75
- const hasAdapter = suggestionAdapter != null;
76
- let t4;
77
- if ($[6] !== isRunning) {
78
- t4 = () => {
79
- const clearSuggestions = () => {
80
- controllerRef.current?.abort();
81
- controllerRef.current = null;
82
- setSuggestions(_temp);
83
- };
84
- const adapter = adapterRef.current;
85
- if (!adapter) {
86
- clearSuggestions();
87
- wasRunningRef.current = isRunning;
88
- return;
89
- }
90
- if (isRunning) {
91
- if (!wasRunningRef.current) clearSuggestions();
92
- wasRunningRef.current = true;
93
- return;
94
- }
95
- if (!wasRunningRef.current) return;
96
- wasRunningRef.current = false;
97
- const currentMessages = messagesRef.current;
98
- const last = currentMessages.at(-1);
99
- if (last?.role !== "assistant") return;
100
- if (last.status?.type === "requires-action") return;
101
- const controller = new AbortController();
102
- controllerRef.current = controller;
103
- const { signal } = controller;
104
- (async () => {
105
- try {
106
- const promiseOrGenerator = adapter.generate({
107
- messages: currentMessages,
108
- signal
109
- });
110
- await consumeSuggestionResult(promiseOrGenerator, {
111
- signal,
112
- onUpdate: setSuggestions
113
- });
114
- } catch {}
115
- })();
47
+ const lastAdapterKeyRef = useRef(adapterKey);
48
+ const adapterReplacedRef = useRef(false);
49
+ useInsertionEffect(() => {
50
+ const previousAdapter = adapterRef.current;
51
+ adapterRef.current = suggestionAdapter;
52
+ const adapterReplaced = previousAdapter !== void 0 && suggestionAdapter !== void 0 && lastAdapterKeyRef.current !== adapterKey;
53
+ adapterReplacedRef.current ||= adapterReplaced;
54
+ if (suggestionAdapter === void 0 || adapterReplaced) controllerRef.current?.abort();
55
+ lastAdapterKeyRef.current = adapterKey;
56
+ }, [adapterKey, suggestionAdapter]);
57
+ const hasAdapter = suggestionAdapter !== void 0;
58
+ const adapterPresentRef = useRef(hasAdapter);
59
+ useEffect(() => {
60
+ const clearSuggestions = () => {
61
+ controllerRef.current?.abort();
62
+ controllerRef.current = null;
63
+ setSuggestions((prev) => prev.length === 0 ? prev : EMPTY_SUGGESTIONS);
116
64
  };
117
- $[6] = isRunning;
118
- $[7] = t4;
119
- } else t4 = $[7];
120
- let t5;
121
- if ($[8] !== hasAdapter || $[9] !== isRunning) {
122
- t5 = [hasAdapter, isRunning];
123
- $[8] = hasAdapter;
124
- $[9] = isRunning;
125
- $[10] = t5;
126
- } else t5 = $[10];
127
- useEffect(t4, t5);
128
- let t6;
129
- let t7;
130
- if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
131
- t6 = () => () => {
65
+ const adapter = adapterRef.current;
66
+ const adapterRestored = hasAdapter && !adapterPresentRef.current;
67
+ adapterPresentRef.current = hasAdapter;
68
+ const adapterReplaced_0 = adapterReplacedRef.current;
69
+ adapterReplacedRef.current = false;
70
+ if (!adapter) {
71
+ clearSuggestions();
72
+ wasRunningRef.current = isRunning;
73
+ return;
74
+ }
75
+ if (adapterReplaced_0) clearSuggestions();
76
+ if (isRunning) {
77
+ if (!wasRunningRef.current) clearSuggestions();
78
+ wasRunningRef.current = true;
79
+ return;
80
+ }
81
+ if (!wasRunningRef.current && !adapterReplaced_0 && !adapterRestored) return;
82
+ wasRunningRef.current = false;
83
+ const currentMessages = messagesRef.current;
84
+ const last = currentMessages.at(-1);
85
+ if (last?.role !== "assistant") return;
86
+ if (last.status?.type === "requires-action") return;
87
+ const controller = new AbortController();
88
+ controllerRef.current = controller;
89
+ const { signal } = controller;
90
+ (async () => {
91
+ try {
92
+ const promiseOrGenerator = adapter.generate({
93
+ messages: currentMessages,
94
+ signal
95
+ });
96
+ await consumeSuggestionResult(promiseOrGenerator, {
97
+ signal,
98
+ onUpdate: (nextSuggestions) => {
99
+ if (controllerRef.current !== controller) return;
100
+ setSuggestions(nextSuggestions);
101
+ }
102
+ });
103
+ } catch {}
104
+ })();
105
+ }, [
106
+ adapterKey,
107
+ hasAdapter,
108
+ isRunning
109
+ ]);
110
+ useReplaySafeEffect(() => {
111
+ return () => {
132
112
  controllerRef.current?.abort();
133
113
  };
134
- t7 = [];
135
- $[11] = t6;
136
- $[12] = t7;
137
- } else {
138
- t6 = $[11];
139
- t7 = $[12];
140
- }
141
- useReplaySafeEffect(t6, t7);
114
+ }, []);
142
115
  return suggestions;
143
116
  };
144
117
  const NO_CANCELLED_MESSAGE_IDS = /* @__PURE__ */ new Set();
@@ -147,7 +120,19 @@ const NO_SUPERSEDED_APPROVAL_PROJECTION = Object.freeze({
147
120
  statusMessageIds: Object.freeze(/* @__PURE__ */ new Set())
148
121
  });
149
122
  const NO_TOOL_APPROVAL_RESPONSES = /* @__PURE__ */ new Map();
150
- const hostToolApprovalsByChat = /* @__PURE__ */ new WeakMap();
123
+ const chatRuntimeStates = /* @__PURE__ */ new WeakMap();
124
+ const getChatRuntimeState = (owner) => {
125
+ if (owner === void 0) return void 0;
126
+ let state = chatRuntimeStates.get(owner);
127
+ if (state === void 0) {
128
+ state = {
129
+ approvals: /* @__PURE__ */ new Map(),
130
+ cancelledMessageIds: NO_CANCELLED_MESSAGE_IDS
131
+ };
132
+ chatRuntimeStates.set(owner, state);
133
+ }
134
+ return state;
135
+ };
151
136
  const toApprovalResponses = (owned) => owned && owned.size > 0 ? new Map([...owned].map(([id, entry]) => [id, entry.response])) : NO_TOOL_APPROVAL_RESPONSES;
152
137
  /**
153
138
  * The answers live on the owner, but each mounted runtime renders them from
@@ -155,17 +140,17 @@ const toApprovalResponses = (owned) => owned && owned.size > 0 ? new Map([...own
155
140
  * may already be unmounted (a rollback resolving after a remount), and another
156
141
  * runtime may be mounted over the same owner.
157
142
  */
158
- const hostApprovalListenersByChat = /* @__PURE__ */ new WeakMap();
159
- const subscribeToHostApprovals = (owner, listener) => {
160
- const listeners = hostApprovalListenersByChat.get(owner) ?? /* @__PURE__ */ new Set();
161
- hostApprovalListenersByChat.set(owner, listeners);
143
+ const chatRuntimeListeners = /* @__PURE__ */ new WeakMap();
144
+ const subscribeToChatRuntimeState = (owner, listener) => {
145
+ const listeners = chatRuntimeListeners.get(owner) ?? /* @__PURE__ */ new Set();
146
+ chatRuntimeListeners.set(owner, listeners);
162
147
  listeners.add(listener);
163
148
  return () => {
164
149
  listeners.delete(listener);
165
150
  };
166
151
  };
167
- const notifyHostApprovals = (owner) => {
168
- for (const listener of [...hostApprovalListenersByChat.get(owner) ?? []]) listener();
152
+ const notifyChatRuntimeState = (owner) => {
153
+ for (const listener of [...chatRuntimeListeners.get(owner) ?? []]) listener();
169
154
  };
170
155
  const getSupersededApprovalProjection = (messages, hostApprovalIds, joinStrategy, isRunning) => {
171
156
  const approvalIds = /* @__PURE__ */ new Set();
@@ -176,7 +161,7 @@ const getSupersededApprovalProjection = (messages, hostApprovalIds, joinStrategy
176
161
  let previousWasVoice = false;
177
162
  const flush = () => {
178
163
  const hasOpenToolPart = lastAssistant === messages[lastIndex] && lastAssistant?.parts?.some((part) => {
179
- if (!isToolUIPart(part) || part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return false;
164
+ if (typeof part?.type !== "string" || !isToolUIPart(part) || part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return false;
180
165
  const approval = part.approval;
181
166
  const resolution = approval?.resolution ?? (approval?.descriptor)?.resolution;
182
167
  return resolution !== "cancelled" && resolution !== "expired";
@@ -196,7 +181,7 @@ const getSupersededApprovalProjection = (messages, hostApprovalIds, joinStrategy
196
181
  previousWasVoice = isVoice;
197
182
  }
198
183
  for (const part of message.parts ?? []) {
199
- if (!isToolUIPart(part) || part.state !== "approval-requested") continue;
184
+ if (typeof part?.type !== "string" || !isToolUIPart(part) || part.state !== "approval-requested") continue;
200
185
  const approval = part.approval;
201
186
  if (!approval) continue;
202
187
  const approvalId = approval.id;
@@ -217,7 +202,7 @@ const getSupersededApprovalProjection = (messages, hostApprovalIds, joinStrategy
217
202
  };
218
203
  };
219
204
  const findRawToolMessageIndex = (messages, messageId, toolCallId, joinStrategy) => {
220
- const containsToolCall = (message) => message.parts?.some((part) => isToolUIPart(part) && part.toolCallId === toolCallId) === true;
205
+ const containsToolCall = (message) => message.parts?.some((part) => typeof part?.type === "string" && isToolUIPart(part) && part.toolCallId === toolCallId) === true;
221
206
  const messageIndex = messages.findIndex((message) => message.id === messageId);
222
207
  if (messageIndex === -1) return -1;
223
208
  if (containsToolCall(messages[messageIndex])) return messageIndex;
@@ -243,11 +228,9 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
243
228
  const [toolStatuses, setToolStatuses] = useState({});
244
229
  const [cancelledMessages, setCancelledMessages] = useState(null);
245
230
  const approvalOwner = adapter.unstable_hostApprovalOwner;
246
- const ownedApprovals = approvalOwner ? hostToolApprovalsByChat.get(approvalOwner) ?? (() => {
247
- const created = /* @__PURE__ */ new Map();
248
- hostToolApprovalsByChat.set(approvalOwner, created);
249
- return created;
250
- })() : void 0;
231
+ const chatRuntimeState = getChatRuntimeState(approvalOwner);
232
+ const ownedApprovals = chatRuntimeState?.approvals;
233
+ const ownedCancelledMessageIds = useSyncExternalStore(useCallback((listener) => approvalOwner ? subscribeToChatRuntimeState(approvalOwner, listener) : () => {}, [approvalOwner]), useCallback(() => chatRuntimeState?.cancelledMessageIds ?? NO_CANCELLED_MESSAGE_IDS, [chatRuntimeState]), () => NO_CANCELLED_MESSAGE_IDS);
251
234
  const [toolApprovalResponses, setToolApprovalResponses] = useState(() => toApprovalResponses(ownedApprovals));
252
235
  const [toolArtifactEpoch, setToolArtifactEpoch] = useState(0);
253
236
  const [toolInteractionEpoch, setToolInteractionEpoch] = useState(0);
@@ -266,7 +249,7 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
266
249
  return prev.size === next.size && [...next].every(([id_1, response]) => prev.get(id_1) === response) ? prev : next;
267
250
  });
268
251
  };
269
- const unsubscribe = subscribeToHostApprovals(approvalOwner, sync);
252
+ const unsubscribe = subscribeToChatRuntimeState(approvalOwner, sync);
270
253
  sync();
271
254
  return unsubscribe;
272
255
  }, [approvalOwner, ownedApprovals]);
@@ -298,7 +281,7 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
298
281
  const messageTiming = useStreamingTiming$1(chatHelpers.messages, isRunning);
299
282
  const lastMessage = chatHelpers.messages.at(-1);
300
283
  const optimisticMessageId = isRunning && lastMessage?.role === "assistant" ? lastMessage.id : void 0;
301
- const cancelledMessageIds = cancelledMessages?.chatId === chatHelpers.id ? cancelledMessages.ids : NO_CANCELLED_MESSAGE_IDS;
284
+ const cancelledMessageIds = chatRuntimeState ? ownedCancelledMessageIds : cancelledMessages?.chatId === chatHelpers.id ? cancelledMessages.ids : NO_CANCELLED_MESSAGE_IDS;
302
285
  const supportsRichToolApprovalResponses = customOnRespondToToolApproval != null;
303
286
  const supersededApprovalProjectionRef = useRef(NO_SUPERSEDED_APPROVAL_PROJECTION);
304
287
  const supersededApprovalProjection = useMemo(() => {
@@ -323,16 +306,24 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
323
306
  return AISDKMessageConverter.toThreadMessages(sourceMessages, false, metadata);
324
307
  }, [supportsRichToolApprovalResponses]);
325
308
  const retractCancellation = useCallback((chatId, messageId) => {
309
+ if (chatRuntimeState !== void 0 && approvalOwner !== void 0) {
310
+ if (!chatRuntimeState.cancelledMessageIds.has(messageId)) return;
311
+ const ids = new Set(chatRuntimeState.cancelledMessageIds);
312
+ ids.delete(messageId);
313
+ chatRuntimeState.cancelledMessageIds = ids;
314
+ notifyChatRuntimeState(approvalOwner);
315
+ return;
316
+ }
326
317
  setCancelledMessages((prev_0) => {
327
318
  if (prev_0?.chatId !== chatId || !prev_0.ids.has(messageId)) return prev_0;
328
- const ids = new Set(prev_0.ids);
329
- ids.delete(messageId);
319
+ const ids_0 = new Set(prev_0.ids);
320
+ ids_0.delete(messageId);
330
321
  return {
331
322
  chatId,
332
- ids
323
+ ids: ids_0
333
324
  };
334
325
  });
335
- }, []);
326
+ }, [approvalOwner, chatRuntimeState]);
336
327
  const resumedMessageId = providerIsRunning && lastMessage?.role === "assistant" ? lastMessage.id : void 0;
337
328
  useEffect(() => {
338
329
  const wasProviderRunning = wasProviderRunningRef.current;
@@ -412,7 +403,7 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
412
403
  if (message.role !== "assistant") return message;
413
404
  let messageChanged = false;
414
405
  const parts = message.parts?.map((part) => {
415
- if (!isToolUIPart(part)) return part;
406
+ if (typeof part?.type !== "string" || !isToolUIPart(part)) return part;
416
407
  if (part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return part;
417
408
  messageChanged = true;
418
409
  const { approval: _approval, ...rest } = part;
@@ -441,7 +432,7 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
441
432
  }));
442
433
  const respondViaHost = async (onRespond, response_0) => {
443
434
  const { approvalId: approvalId_0 } = response_0;
444
- const requested = chatHelpers.messages.flatMap((message_0) => message_0.parts.flatMap((part_0) => isToolUIPart(part_0) ? [{
435
+ const requested = chatHelpers.messages.flatMap((message_0) => message_0.parts.flatMap((part_0) => typeof part_0?.type === "string" && isToolUIPart(part_0) ? [{
445
436
  messageId: message_0.id,
446
437
  part: part_0
447
438
  }] : [])).find(({ part: part_1 }) => part_1.state === "approval-requested" && part_1.approval?.id === approvalId_0);
@@ -455,7 +446,7 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
455
446
  if (applied) startedWith?.set(approvalId_0, { response: response_0 });
456
447
  else startedWith?.delete(approvalId_0);
457
448
  if (startedOwner) {
458
- notifyHostApprovals(startedOwner);
449
+ notifyChatRuntimeState(startedOwner);
459
450
  return;
460
451
  }
461
452
  if (lastApprovalOwnerRef.current !== startedOwner) return;
@@ -485,6 +476,11 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
485
476
  const remainsApplied = startedWith?.has(approvalId_0) ?? startedResponses.has(approvalId_0);
486
477
  if (isApplied && remainsApplied) await persistToolApprovalResponses(requested.messageId);
487
478
  };
479
+ const lastAISDKMessageId = (threadMessageId) => {
480
+ if (threadMessageId == null) return null;
481
+ const threadMessage = runtimeRef.current.thread.getState().messages.find((message_1) => message_1.id === threadMessageId);
482
+ return (threadMessage && getVercelAIMessages(threadMessage).at(-1)?.id) ?? threadMessageId;
483
+ };
488
484
  const hasSeededRepositoryRef = useRef(false);
489
485
  const shouldFeedRepository = exportedMessageRepository != null && !hasSeededRepositoryRef.current && messages.length === 0;
490
486
  const runtime = useExternalStoreRuntime({
@@ -495,7 +491,7 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
495
491
  setToolStatuses,
496
492
  setMessages: (messages_2) => chatHelpers.setMessages(messages_2.map(getVercelAIMessages).filter(Boolean).flat()),
497
493
  onImport: (messages_3) => chatHelpers.setMessages(messages_3.map(getVercelAIMessages).filter(Boolean).flat()),
498
- onVoiceTranscript: (message_1) => chatHelpers.setMessages((current) => [...current, toVoiceTranscriptUIMessage(message_1)]),
494
+ onVoiceTranscript: (message_2) => chatHelpers.setMessages((current) => [...current, toVoiceTranscriptUIMessage(message_2)]),
499
495
  onExportExternalState: () => {
500
496
  const exported = runtimeRef.current.thread.export();
501
497
  const expandedMessages = [];
@@ -521,12 +517,15 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
521
517
  runtimeRef.current.thread.import(exportedRepo);
522
518
  },
523
519
  onCancel: async () => {
524
- const message_2 = chatHelpers.messages.at(-1);
525
- const cancelledId = isRunning && message_2?.role === "assistant" ? message_2.id : void 0;
520
+ const message_3 = chatHelpers.messages.at(-1);
521
+ const cancelledId = isRunning && message_3?.role === "assistant" ? message_3.id : void 0;
526
522
  if (cancelledId) {
527
523
  const liveIds = new Set(chatHelpers.messages.map((m) => m.id));
528
- setCancelledMessages((prev_1) => {
529
- const kept = prev_1?.chatId === chatHelpers.id ? [...prev_1.ids].filter((id_5) => liveIds.has(id_5)) : [];
524
+ if (chatRuntimeState !== void 0 && approvalOwner !== void 0) {
525
+ chatRuntimeState.cancelledMessageIds = /* @__PURE__ */ new Set([...[...chatRuntimeState.cancelledMessageIds].filter((id_5) => liveIds.has(id_5)), cancelledId]);
526
+ notifyChatRuntimeState(approvalOwner);
527
+ } else setCancelledMessages((prev_1) => {
528
+ const kept = prev_1?.chatId === chatHelpers.id ? [...prev_1.ids].filter((id_6) => liveIds.has(id_6)) : [];
530
529
  return {
531
530
  chatId: chatHelpers.id,
532
531
  ids: /* @__PURE__ */ new Set([...kept, cancelledId])
@@ -542,29 +541,30 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
542
541
  }
543
542
  }
544
543
  },
545
- onNew: async (message_3) => {
546
- const createMessage = (customToCreateMessage ?? toCreateMessage)(message_3);
547
- if (!(message_3.startRun ?? message_3.role === "user")) {
548
- chatHelpers.setMessages((current_0) => [...current_0, toUIMessage(createMessage, message_3.role)]);
544
+ onNew: async (message_4) => {
545
+ const createMessage = (customToCreateMessage ?? toCreateMessage)(message_4);
546
+ if (!(message_4.startRun ?? message_4.role === "user")) {
547
+ chatHelpers.setMessages((current_0) => [...current_0, toUIMessage(createMessage, message_4.role)]);
549
548
  return;
550
549
  }
551
- lastRunConfigRef.current = message_3.runConfig;
550
+ lastRunConfigRef.current = message_4.runConfig;
552
551
  await completePendingToolCalls();
553
- await chatHelpers.sendMessage(createMessage, { metadata: message_3.runConfig });
552
+ await chatHelpers.sendMessage(createMessage, { metadata: message_4.runConfig });
554
553
  },
555
- onEdit: async (message_4) => {
556
- const createMessage_0 = (customToCreateMessage ?? toCreateMessage)(message_4);
557
- if (!(message_4.startRun ?? message_4.role === "user")) {
558
- chatHelpers.setMessages((current_1) => [...sliceMessagesUntil(current_1, message_4.parentId), toUIMessage(createMessage_0, message_4.role)]);
554
+ onEdit: async (message_5) => {
555
+ const createMessage_0 = (customToCreateMessage ?? toCreateMessage)(message_5);
556
+ const parentId_0 = lastAISDKMessageId(message_5.parentId);
557
+ if (!(message_5.startRun ?? message_5.role === "user")) {
558
+ chatHelpers.setMessages((current_1) => [...sliceMessagesUntil(current_1, parentId_0), toUIMessage(createMessage_0, message_5.role)]);
559
559
  return;
560
560
  }
561
- lastRunConfigRef.current = message_4.runConfig;
562
- chatHelpers.setMessages((current_2) => sliceMessagesUntil(current_2, message_4.parentId));
563
- await chatHelpers.sendMessage(createMessage_0, { metadata: message_4.runConfig });
561
+ lastRunConfigRef.current = message_5.runConfig;
562
+ chatHelpers.setMessages((current_2) => sliceMessagesUntil(current_2, parentId_0));
563
+ await chatHelpers.sendMessage(createMessage_0, { metadata: message_5.runConfig });
564
564
  },
565
565
  onDelete: async (messageId_0) => {
566
566
  const threadMessages = runtimeRef.current.thread.getState().messages;
567
- const messageIndex = threadMessages.findIndex((message_5) => message_5.id === messageId_0);
567
+ const messageIndex = threadMessages.findIndex((message_6) => message_6.id === messageId_0);
568
568
  if (messageIndex === -1) return;
569
569
  await deleteHistoryMessage(messageId_0);
570
570
  let removedToolArtifact = false;
@@ -583,15 +583,17 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
583
583
  if (removedToolArtifact) markToolArtifactsChanged();
584
584
  if (removedToolInteractions) markToolInteractionsChanged();
585
585
  if (removedToolApprovalResponse || removedHostApprovalId) {
586
- if (approvalOwner) notifyHostApprovals(approvalOwner);
586
+ if (approvalOwner) notifyChatRuntimeState(approvalOwner);
587
587
  setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
588
588
  }
589
- const deleteIds = new Set(getExternalStoreMessages(threadMessages[messageIndex]).map((message_6) => message_6.id));
590
- chatHelpers.setMessages((current_3) => current_3.filter((message_7) => !deleteIds.has(message_7.id)));
589
+ const deleteIds = new Set(getExternalStoreMessages(threadMessages[messageIndex]).map((message_7) => message_7.id));
590
+ chatHelpers.setMessages((current_3) => current_3.filter((message_8) => !deleteIds.has(message_8.id)));
591
591
  },
592
- onReload: async (parentId_0, config) => {
592
+ onReload: async (parentId_1, config) => {
593
593
  lastRunConfigRef.current = config.runConfig;
594
- const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId_0);
594
+ const newMessages = sliceMessagesUntil(chatHelpers.messages, lastAISDKMessageId(parentId_1));
595
+ const reloaded = chatHelpers.messages[newMessages.length];
596
+ if (reloaded?.role === "assistant") newMessages.push(reloaded);
595
597
  chatHelpers.setMessages(newMessages);
596
598
  await chatHelpers.regenerate({ metadata: config.runConfig });
597
599
  },
@@ -605,15 +607,15 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
605
607
  const output = !isError && modelContent !== void 0 ? wrapModelContentEnvelope(result_0, modelContent) : result_0;
606
608
  if (targetIndex >= 0 && targetIndex !== chatHelpers.messages.length - 1) {
607
609
  const target = chatHelpers.messages[targetIndex];
608
- const targetPart = target.parts.find((part_3) => isToolUIPart(part_3) && part_3.toolCallId === toolCallId);
610
+ const targetPart = target.parts.find((part_3) => typeof part_3?.type === "string" && isToolUIPart(part_3) && part_3.toolCallId === toolCallId);
609
611
  if (targetPart?.state === "output-error" || targetPart?.state === "output-denied" || targetPart?.state === "output-available" && !targetPart.preliminary) return Promise.resolve();
610
612
  const targetMessageId = target.id;
611
- chatHelpers.setMessages((current_4) => current_4.map((message_8) => {
612
- if (message_8.id !== targetMessageId) return message_8;
613
+ chatHelpers.setMessages((current_4) => current_4.map((message_9) => {
614
+ if (message_9.id !== targetMessageId) return message_9;
613
615
  return {
614
- ...message_8,
615
- parts: message_8.parts.map((part_4) => {
616
- if (!isToolUIPart(part_4) || part_4.toolCallId !== toolCallId) return part_4;
616
+ ...message_9,
617
+ parts: message_9.parts.map((part_4) => {
618
+ if (typeof part_4?.type !== "string" || !isToolUIPart(part_4) || part_4.toolCallId !== toolCallId) return part_4;
617
619
  const { preliminary: _preliminary, ...finalPart } = part_4;
618
620
  return {
619
621
  ...finalPart,
@@ -682,9 +684,6 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
682
684
  }, [exportedMessageRepository, chatHelpers.messages.length]);
683
685
  return runtime;
684
686
  };
685
- function _temp(prev) {
686
- return prev.length === 0 ? prev : EMPTY_SUGGESTIONS;
687
- }
688
687
  //#endregion
689
688
  export { useAISDKRuntime };
690
689