@assistant-ui/ai-sdk 0.0.7 → 0.0.9
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.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/adapters/aiSDKFormatAdapter.d.ts +2 -2
- package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts +1 -3
- package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -1
- package/dist/adapters/vercelAttachmentAdapter.js +3 -3
- package/dist/adapters/vercelAttachmentAdapter.js.map +1 -1
- package/dist/aiSDKExtras.d.ts +4 -6
- package/dist/aiSDKExtras.d.ts.map +1 -1
- package/dist/converters/convertMessage.d.ts +34 -24
- package/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/convertMessage.js +56 -12
- package/dist/converters/convertMessage.js.map +1 -1
- package/dist/converters/modelContentEnvelope.d.ts +6 -7
- package/dist/converters/modelContentEnvelope.d.ts.map +1 -1
- package/dist/converters/toCreateMessage.d.ts +2 -4
- package/dist/converters/toCreateMessage.d.ts.map +1 -1
- package/dist/converters/toolOutputConversion.d.ts +20 -22
- package/dist/converters/toolOutputConversion.d.ts.map +1 -1
- package/dist/hooks.d.ts +2 -4
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +17 -14
- package/dist/index.d.ts.map +1 -0
- package/dist/index.native.d.ts +15 -12
- package/dist/index.native.d.ts.map +1 -0
- package/dist/model-context/injectInteractableContext.d.ts +2 -4
- package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
- package/dist/model-context/injectQuoteContext.d.ts +1 -3
- package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.d.ts +2 -4
- package/dist/runtime/AISDKChat.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.js +1 -1
- package/dist/runtime/AISDKChat.js.map +1 -1
- package/dist/runtime/AISDKThreads.d.ts +29 -31
- package/dist/runtime/AISDKThreads.d.ts.map +1 -1
- package/dist/runtime/AISDKThreads.js +22 -23
- package/dist/runtime/AISDKThreads.js.map +1 -1
- package/dist/runtime/sdkIdentity.d.ts +1 -3
- package/dist/runtime/sdkIdentity.d.ts.map +1 -1
- package/dist/runtime/sdkIdentity.js +1 -1
- package/dist/runtime/useAISDKRuntime.d.ts +76 -70
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +311 -77
- package/dist/runtime/useAISDKRuntime.js.map +1 -1
- package/dist/runtime/useChatRuntime.d.ts +6 -8
- package/dist/runtime/useChatRuntime.d.ts.map +1 -1
- package/dist/runtime/useChatRuntime.js +14 -10
- package/dist/runtime/useChatRuntime.js.map +1 -1
- package/dist/runtime/useChatThread.d.ts +94 -89
- package/dist/runtime/useChatThread.d.ts.map +1 -1
- package/dist/runtime/useChatThread.js +44 -15
- package/dist/runtime/useChatThread.js.map +1 -1
- package/dist/runtime/useExternalHistory.d.ts +7 -7
- package/dist/runtime/useExternalHistory.d.ts.map +1 -1
- package/dist/runtime/useExternalHistory.js +231 -60
- package/dist/runtime/useExternalHistory.js.map +1 -1
- package/dist/runtime/useResourceCleanup.d.ts +1 -3
- package/dist/runtime/useResourceCleanup.d.ts.map +1 -1
- package/dist/runtime/useResourceCleanup.js +38 -14
- package/dist/runtime/useResourceCleanup.js.map +1 -1
- package/dist/runtime/useStreamingTiming.d.ts +3 -5
- package/dist/runtime/useStreamingTiming.d.ts.map +1 -1
- package/dist/runtime/useStreamingTiming.js +1 -1
- package/dist/tools/frontendTools.d.ts +21 -23
- package/dist/tools/frontendTools.d.ts.map +1 -1
- package/dist/tools/generativeTools.d.ts +24 -26
- package/dist/tools/generativeTools.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.node.d.ts +2 -2
- package/dist/tools/mcp-stdio.node.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts +2 -4
- package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -1
- package/dist/transport/AssistantChatTransport.d.ts +14 -16
- package/dist/transport/AssistantChatTransport.d.ts.map +1 -1
- package/dist/transport/AssistantChatTransport.js +9 -2
- package/dist/transport/AssistantChatTransport.js.map +1 -1
- package/dist/transport/resumable.d.ts +25 -27
- package/dist/transport/resumable.d.ts.map +1 -1
- package/dist/usage.d.ts +8 -10
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js.map +1 -1
- package/dist/utils/getVercelAIMessages.d.ts +2 -4
- package/dist/utils/getVercelAIMessages.d.ts.map +1 -1
- package/dist/utils/sliceMessagesUntil.d.ts +1 -3
- package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
- package/package.json +13 -9
- package/src/adapters/vercelAttachmentAdapter.test.ts +23 -0
- package/src/adapters/vercelAttachmentAdapter.ts +2 -2
- package/src/converters/convertMessage.test.ts +541 -3
- package/src/converters/convertMessage.ts +123 -15
- package/src/index.native.ts +1 -1
- package/src/index.ts +1 -1
- package/src/runtime/AISDKChat.ts +0 -4
- package/src/runtime/AISDKThreads.cloud.test.ts +60 -2
- package/src/runtime/AISDKThreads.test.ts +105 -46
- package/src/runtime/AISDKThreads.ts +1 -1
- package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +1639 -19
- package/src/runtime/useAISDKRuntime.approval.test.tsx +252 -9
- package/src/runtime/useAISDKRuntime.fast-refresh.test.tsx +182 -0
- package/src/runtime/useAISDKRuntime.test.ts +275 -5
- package/src/runtime/useAISDKRuntime.ts +533 -60
- package/src/runtime/useAISDKRuntime.voice.test.tsx +50 -1
- package/src/runtime/useChatRuntime.fast-refresh.test.tsx +94 -0
- package/src/runtime/useChatRuntime.integration.test.tsx +143 -65
- package/src/runtime/useChatRuntime.test.ts +11 -7
- package/src/runtime/useChatRuntime.ts +6 -2
- package/src/runtime/useChatThread.test.ts +166 -2
- package/src/runtime/useChatThread.transport.test.tsx +152 -0
- package/src/runtime/useChatThread.ts +89 -22
- package/src/runtime/useExternalHistory.test.ts +943 -3
- package/src/runtime/useExternalHistory.ts +378 -15
- package/src/runtime/useResourceCleanup.ts +41 -15
- package/src/transport/AssistantChatTransport.test.ts +164 -0
- package/src/transport/AssistantChatTransport.ts +22 -2
- package/src/usage.ts +1 -1
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { sliceMessagesUntil } from "../utils/sliceMessagesUntil.js";
|
|
4
|
-
import { toCreateMessage } from "../converters/toCreateMessage.js";
|
|
2
|
+
import { aiSDKV6FormatAdapter } from "../adapters/aiSDKFormatAdapter.js";
|
|
5
3
|
import { vercelAttachmentAdapter } from "../adapters/vercelAttachmentAdapter.js";
|
|
6
|
-
import {
|
|
4
|
+
import { aiSDKExtras } from "../aiSDKExtras.js";
|
|
7
5
|
import { wrapModelContentEnvelope } from "../converters/modelContentEnvelope.js";
|
|
8
6
|
import { AISDKMessageConverter } from "../converters/convertMessage.js";
|
|
9
|
-
import {
|
|
7
|
+
import { toCreateMessage } from "../converters/toCreateMessage.js";
|
|
8
|
+
import { sliceMessagesUntil } from "../utils/sliceMessagesUntil.js";
|
|
9
|
+
import { getVercelAIMessages } from "../utils/getVercelAIMessages.js";
|
|
10
10
|
import { toExportedMessageRepository, useExternalHistory } from "./useExternalHistory.js";
|
|
11
11
|
import { useStreamingTiming as useStreamingTiming$1 } from "./useStreamingTiming.js";
|
|
12
|
+
import { MessageRepository, appendToolInteraction, consumeSuggestionResult } from "@assistant-ui/core/internal";
|
|
13
|
+
import { generateId, getToolName, isToolUIPart } from "ai";
|
|
12
14
|
import { useExternalStoreRuntime, useRuntimeAdapters } from "@assistant-ui/core/react";
|
|
15
|
+
import { getExternalStoreMessages, pickExternalStoreSharedOptions } from "@assistant-ui/core";
|
|
13
16
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
14
17
|
import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
|
|
15
|
-
import {
|
|
16
|
-
import { getExternalStoreMessages, pickExternalStoreSharedOptions } from "@assistant-ui/core";
|
|
17
|
-
import { MessageRepository, consumeSuggestionResult } from "@assistant-ui/core/internal";
|
|
18
|
+
import { useReplaySafeEffect } from "@assistant-ui/store/internal";
|
|
18
19
|
//#region src/runtime/useAISDKRuntime.ts
|
|
19
20
|
const toUIMessage = (createMessage, fallbackRole) => ({
|
|
20
21
|
...createMessage,
|
|
@@ -29,7 +30,7 @@ const toVoiceTranscriptUIMessage = (message) => ({
|
|
|
29
30
|
text: part.text
|
|
30
31
|
})),
|
|
31
32
|
metadata: {
|
|
32
|
-
modality:
|
|
33
|
+
...message.metadata.modality && { modality: message.metadata.modality },
|
|
33
34
|
...Object.keys(message.metadata.custom).length > 0 && { custom: message.metadata.custom }
|
|
34
35
|
}
|
|
35
36
|
});
|
|
@@ -137,11 +138,97 @@ const useGeneratedSuggestions = (suggestionAdapter, messages, isRunning) => {
|
|
|
137
138
|
t6 = $[11];
|
|
138
139
|
t7 = $[12];
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
+
useReplaySafeEffect(t6, t7);
|
|
141
142
|
return suggestions;
|
|
142
143
|
};
|
|
143
144
|
const NO_CANCELLED_MESSAGE_IDS = /* @__PURE__ */ new Set();
|
|
145
|
+
const NO_SUPERSEDED_APPROVAL_PROJECTION = Object.freeze({
|
|
146
|
+
approvalIds: Object.freeze(/* @__PURE__ */ new Set()),
|
|
147
|
+
statusMessageIds: Object.freeze(/* @__PURE__ */ new Set())
|
|
148
|
+
});
|
|
144
149
|
const NO_TOOL_APPROVAL_RESPONSES = /* @__PURE__ */ new Map();
|
|
150
|
+
const hostToolApprovalsByChat = /* @__PURE__ */ new WeakMap();
|
|
151
|
+
const toApprovalResponses = (owned) => owned && owned.size > 0 ? new Map([...owned].map(([id, entry]) => [id, entry.response])) : NO_TOOL_APPROVAL_RESPONSES;
|
|
152
|
+
/**
|
|
153
|
+
* The answers live on the owner, but each mounted runtime renders them from
|
|
154
|
+
* its own state, so a write has to be announced: the runtime that performed it
|
|
155
|
+
* may already be unmounted (a rollback resolving after a remount), and another
|
|
156
|
+
* runtime may be mounted over the same owner.
|
|
157
|
+
*/
|
|
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);
|
|
162
|
+
listeners.add(listener);
|
|
163
|
+
return () => {
|
|
164
|
+
listeners.delete(listener);
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
const notifyHostApprovals = (owner) => {
|
|
168
|
+
for (const listener of [...hostApprovalListenersByChat.get(owner) ?? []]) listener();
|
|
169
|
+
};
|
|
170
|
+
const getSupersededApprovalProjection = (messages, hostApprovalIds, joinStrategy, isRunning) => {
|
|
171
|
+
const approvalIds = /* @__PURE__ */ new Set();
|
|
172
|
+
const statusMessageIds = /* @__PURE__ */ new Set();
|
|
173
|
+
const lastIndex = messages.length - 1;
|
|
174
|
+
let lastAssistant;
|
|
175
|
+
let hasSupersededApproval = false;
|
|
176
|
+
let previousWasVoice = false;
|
|
177
|
+
const flush = () => {
|
|
178
|
+
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;
|
|
180
|
+
const approval = part.approval;
|
|
181
|
+
const resolution = approval?.resolution ?? (approval?.descriptor)?.resolution;
|
|
182
|
+
return resolution !== "cancelled" && resolution !== "expired";
|
|
183
|
+
});
|
|
184
|
+
if (lastAssistant && hasSupersededApproval && !hasOpenToolPart && !(isRunning && lastAssistant === messages[lastIndex])) statusMessageIds.add(lastAssistant.id);
|
|
185
|
+
lastAssistant = void 0;
|
|
186
|
+
hasSupersededApproval = false;
|
|
187
|
+
previousWasVoice = false;
|
|
188
|
+
};
|
|
189
|
+
for (let index = 0; index < messages.length; index++) {
|
|
190
|
+
const message = messages[index];
|
|
191
|
+
if (message.role !== "assistant") flush();
|
|
192
|
+
else {
|
|
193
|
+
const isVoice = message.metadata?.modality === "voice";
|
|
194
|
+
if (isVoice || previousWasVoice || joinStrategy === "none") flush();
|
|
195
|
+
lastAssistant = message;
|
|
196
|
+
previousWasVoice = isVoice;
|
|
197
|
+
}
|
|
198
|
+
for (const part of message.parts ?? []) {
|
|
199
|
+
if (!isToolUIPart(part) || part.state !== "approval-requested") continue;
|
|
200
|
+
const approval = part.approval;
|
|
201
|
+
if (!approval) continue;
|
|
202
|
+
const approvalId = approval.id;
|
|
203
|
+
if (hostApprovalIds.has(approvalId)) continue;
|
|
204
|
+
const resolution = approval.resolution ?? approval.descriptor?.resolution;
|
|
205
|
+
if (resolution === "cancelled" || resolution === "expired") continue;
|
|
206
|
+
if (index !== lastIndex) {
|
|
207
|
+
approvalIds.add(approvalId);
|
|
208
|
+
if (message.role === "assistant") hasSupersededApproval = true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
flush();
|
|
213
|
+
if (approvalIds.size === 0 && statusMessageIds.size === 0) return NO_SUPERSEDED_APPROVAL_PROJECTION;
|
|
214
|
+
return {
|
|
215
|
+
approvalIds,
|
|
216
|
+
statusMessageIds
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
const findRawToolMessageIndex = (messages, messageId, toolCallId, joinStrategy) => {
|
|
220
|
+
const containsToolCall = (message) => message.parts?.some((part) => isToolUIPart(part) && part.toolCallId === toolCallId) === true;
|
|
221
|
+
const messageIndex = messages.findIndex((message) => message.id === messageId);
|
|
222
|
+
if (messageIndex === -1) return -1;
|
|
223
|
+
if (containsToolCall(messages[messageIndex])) return messageIndex;
|
|
224
|
+
if (joinStrategy === "none" || messages[messageIndex]?.role !== "assistant") return -1;
|
|
225
|
+
let start = messageIndex;
|
|
226
|
+
while (start > 0 && messages[start - 1]?.role === "assistant") start--;
|
|
227
|
+
let end = messageIndex;
|
|
228
|
+
while (end + 1 < messages.length && messages[end + 1]?.role === "assistant") end++;
|
|
229
|
+
for (let index = start; index <= end; index++) if (containsToolCall(messages[index])) return index;
|
|
230
|
+
return -1;
|
|
231
|
+
};
|
|
145
232
|
const toChatError = (error) => {
|
|
146
233
|
const code = error.code;
|
|
147
234
|
return {
|
|
@@ -155,12 +242,55 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
155
242
|
const contextAdapters = useRuntimeAdapters();
|
|
156
243
|
const [toolStatuses, setToolStatuses] = useState({});
|
|
157
244
|
const [cancelledMessages, setCancelledMessages] = useState(null);
|
|
158
|
-
const
|
|
159
|
-
const
|
|
245
|
+
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;
|
|
251
|
+
const [toolApprovalResponses, setToolApprovalResponses] = useState(() => toApprovalResponses(ownedApprovals));
|
|
252
|
+
const [toolArtifactEpoch, setToolArtifactEpoch] = useState(0);
|
|
253
|
+
const [toolInteractionEpoch, setToolInteractionEpoch] = useState(0);
|
|
254
|
+
const toolApprovalResponsesRef = useRef(new Map(toApprovalResponses(ownedApprovals)));
|
|
255
|
+
const ownedApprovalIdsRef = useRef(new Set(ownedApprovals?.keys()));
|
|
256
|
+
const hostApprovalIdsRef = useRef(new Set(ownedApprovals?.keys()));
|
|
257
|
+
useEffect(() => {
|
|
258
|
+
if (!approvalOwner || !ownedApprovals) return void 0;
|
|
259
|
+
const sync = () => {
|
|
260
|
+
for (const id of ownedApprovalIdsRef.current) toolApprovalResponsesRef.current.delete(id);
|
|
261
|
+
for (const [id_0, entry] of ownedApprovals) toolApprovalResponsesRef.current.set(id_0, entry.response);
|
|
262
|
+
ownedApprovalIdsRef.current = new Set(ownedApprovals.keys());
|
|
263
|
+
hostApprovalIdsRef.current = new Set(toolApprovalResponsesRef.current.keys());
|
|
264
|
+
setToolApprovalResponses((prev) => {
|
|
265
|
+
const next = new Map(toolApprovalResponsesRef.current);
|
|
266
|
+
return prev.size === next.size && [...next].every(([id_1, response]) => prev.get(id_1) === response) ? prev : next;
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
const unsubscribe = subscribeToHostApprovals(approvalOwner, sync);
|
|
270
|
+
sync();
|
|
271
|
+
return unsubscribe;
|
|
272
|
+
}, [approvalOwner, ownedApprovals]);
|
|
273
|
+
const lastApprovalOwnerRef = useRef(approvalOwner);
|
|
274
|
+
if (lastApprovalOwnerRef.current !== approvalOwner) {
|
|
275
|
+
lastApprovalOwnerRef.current = approvalOwner;
|
|
276
|
+
hostApprovalIdsRef.current = new Set(ownedApprovals?.keys());
|
|
277
|
+
toolApprovalResponsesRef.current = new Map(toApprovalResponses(ownedApprovals));
|
|
278
|
+
ownedApprovalIdsRef.current = new Set(ownedApprovals?.keys());
|
|
279
|
+
setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
|
|
280
|
+
}
|
|
160
281
|
const toolArgsKeyOrderCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
161
282
|
const toolLastInputCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
283
|
+
const toolArgsTextCacheRef = useRef(/* @__PURE__ */ new WeakMap());
|
|
162
284
|
const mcpAppMetadataCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
285
|
+
const toolArtifactsRef = useRef(/* @__PURE__ */ new Map());
|
|
286
|
+
const toolInteractionsRef = useRef(/* @__PURE__ */ new Map());
|
|
163
287
|
const lastRunConfigRef = useRef(void 0);
|
|
288
|
+
const markToolArtifactsChanged = useCallback(() => {
|
|
289
|
+
setToolArtifactEpoch((epoch) => epoch + 1);
|
|
290
|
+
}, []);
|
|
291
|
+
const markToolInteractionsChanged = useCallback(() => {
|
|
292
|
+
setToolInteractionEpoch((epoch_0) => epoch_0 + 1);
|
|
293
|
+
}, []);
|
|
164
294
|
const hasExecutingTools = Object.values(toolStatuses).some((s) => s?.type === "executing");
|
|
165
295
|
const providerIsRunning = chatHelpers.status === "submitted" || chatHelpers.status === "streaming";
|
|
166
296
|
const isRunning = providerIsRunning || hasExecutingTools;
|
|
@@ -170,14 +300,32 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
170
300
|
const optimisticMessageId = isRunning && lastMessage?.role === "assistant" ? lastMessage.id : void 0;
|
|
171
301
|
const cancelledMessageIds = cancelledMessages?.chatId === chatHelpers.id ? cancelledMessages.ids : NO_CANCELLED_MESSAGE_IDS;
|
|
172
302
|
const supportsRichToolApprovalResponses = customOnRespondToToolApproval != null;
|
|
303
|
+
const supersededApprovalProjectionRef = useRef(NO_SUPERSEDED_APPROVAL_PROJECTION);
|
|
304
|
+
const supersededApprovalProjection = useMemo(() => {
|
|
305
|
+
const projection = getSupersededApprovalProjection(chatHelpers.messages, hostApprovalIdsRef.current, joinStrategy, isRunning);
|
|
306
|
+
const previous = supersededApprovalProjectionRef.current;
|
|
307
|
+
if (projection.approvalIds.size === previous.approvalIds.size && projection.statusMessageIds.size === previous.statusMessageIds.size && [...projection.approvalIds].every((id_3) => previous.approvalIds.has(id_3)) && [...projection.statusMessageIds].every((id_2) => previous.statusMessageIds.has(id_2))) return previous;
|
|
308
|
+
supersededApprovalProjectionRef.current = projection;
|
|
309
|
+
return projection;
|
|
310
|
+
}, [
|
|
311
|
+
chatHelpers.messages,
|
|
312
|
+
joinStrategy,
|
|
313
|
+
isRunning,
|
|
314
|
+
toolApprovalResponses
|
|
315
|
+
]);
|
|
173
316
|
const toThreadMessages = useCallback((sourceMessages) => {
|
|
174
|
-
const metadata = {
|
|
317
|
+
const metadata = {
|
|
318
|
+
supportsRichToolApprovalResponses,
|
|
319
|
+
toolArtifacts: toolArtifactsRef.current,
|
|
320
|
+
toolInteractions: toolInteractionsRef.current,
|
|
321
|
+
toolApprovalResponses: toolApprovalResponsesRef.current
|
|
322
|
+
};
|
|
175
323
|
return AISDKMessageConverter.toThreadMessages(sourceMessages, false, metadata);
|
|
176
324
|
}, [supportsRichToolApprovalResponses]);
|
|
177
325
|
const retractCancellation = useCallback((chatId, messageId) => {
|
|
178
|
-
setCancelledMessages((
|
|
179
|
-
if (
|
|
180
|
-
const ids = new Set(
|
|
326
|
+
setCancelledMessages((prev_0) => {
|
|
327
|
+
if (prev_0?.chatId !== chatId || !prev_0.ids.has(messageId)) return prev_0;
|
|
328
|
+
const ids = new Set(prev_0.ids);
|
|
181
329
|
ids.delete(messageId);
|
|
182
330
|
return {
|
|
183
331
|
chatId,
|
|
@@ -205,9 +353,14 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
205
353
|
toolStatuses,
|
|
206
354
|
messageTiming,
|
|
207
355
|
toolArgsKeyOrderCache: toolArgsKeyOrderCacheRef.current,
|
|
356
|
+
toolArgsTextCache: toolArgsTextCacheRef.current,
|
|
208
357
|
toolLastInputCache: toolLastInputCacheRef.current,
|
|
209
358
|
mcpAppMetadataCache: mcpAppMetadataCacheRef.current,
|
|
359
|
+
toolArtifacts: toolArtifactsRef.current,
|
|
360
|
+
toolInteractions: toolInteractionsRef.current,
|
|
210
361
|
supportsRichToolApprovalResponses,
|
|
362
|
+
cancelledToolApprovalIds: supersededApprovalProjection.approvalIds,
|
|
363
|
+
cancelledStatusMessageIds: supersededApprovalProjection.statusMessageIds,
|
|
211
364
|
...optimisticMessageId && { optimisticMessageId },
|
|
212
365
|
...chatHelpers.error && { error: toChatError(chatHelpers.error) },
|
|
213
366
|
...cancelledMessageIds.size > 0 && { cancelledMessageIds },
|
|
@@ -219,7 +372,10 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
219
372
|
chatHelpers.error,
|
|
220
373
|
cancelledMessageIds,
|
|
221
374
|
toolApprovalResponses,
|
|
222
|
-
supportsRichToolApprovalResponses
|
|
375
|
+
supportsRichToolApprovalResponses,
|
|
376
|
+
supersededApprovalProjection,
|
|
377
|
+
toolArtifactEpoch,
|
|
378
|
+
toolInteractionEpoch
|
|
223
379
|
])
|
|
224
380
|
});
|
|
225
381
|
const exportedMessageRepository = useMemo(() => {
|
|
@@ -231,8 +387,12 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
231
387
|
const [runtimeRef] = useState(() => ({ get current() {
|
|
232
388
|
return runtime;
|
|
233
389
|
} }));
|
|
234
|
-
const { isLoading, deleteMessage: deleteHistoryMessage } = useExternalHistory(runtimeRef, adapters?.history ?? contextAdapters?.history, toThreadMessages, aiSDKV6FormatAdapter, (messages_0) => {
|
|
390
|
+
const { isLoading, deleteMessage: deleteHistoryMessage, persistToolInteractions, persistToolApprovalResponses } = useExternalHistory(runtimeRef, adapters?.history ?? contextAdapters?.history, toThreadMessages, aiSDKV6FormatAdapter, (messages_0) => {
|
|
235
391
|
chatHelpers.setMessages(messages_0);
|
|
392
|
+
}, toolArtifactsRef.current, markToolArtifactsChanged, toolInteractionsRef.current, markToolInteractionsChanged, toolApprovalResponsesRef.current, () => {
|
|
393
|
+
for (const [id_4, entry_0] of ownedApprovals ?? []) toolApprovalResponsesRef.current.set(id_4, entry_0.response);
|
|
394
|
+
hostApprovalIdsRef.current = new Set(toolApprovalResponsesRef.current.keys());
|
|
395
|
+
setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
|
|
236
396
|
});
|
|
237
397
|
const { id: chatId_0, messages: chatMessages, status: chatStatus, error } = chatHelpers;
|
|
238
398
|
const extras = useMemo(() => aiSDKExtras.provide({
|
|
@@ -247,25 +407,30 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
247
407
|
const completePendingToolCalls = async () => {
|
|
248
408
|
if (!cancelPendingToolCallsOnSend) return;
|
|
249
409
|
chatHelpers.setMessages((messages_1) => {
|
|
250
|
-
const lastMessage_0 = messages_1.at(-1);
|
|
251
|
-
if (lastMessage_0?.role !== "assistant") return messages_1;
|
|
252
410
|
let hasChanges = false;
|
|
253
|
-
const
|
|
254
|
-
if (
|
|
255
|
-
|
|
411
|
+
const next_0 = messages_1.map((message) => {
|
|
412
|
+
if (message.role !== "assistant") return message;
|
|
413
|
+
let messageChanged = false;
|
|
414
|
+
const parts = message.parts?.map((part) => {
|
|
415
|
+
if (!isToolUIPart(part)) return part;
|
|
416
|
+
if (part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return part;
|
|
417
|
+
messageChanged = true;
|
|
418
|
+
const { approval: _approval, ...rest } = part;
|
|
419
|
+
return {
|
|
420
|
+
...rest,
|
|
421
|
+
state: "output-error",
|
|
422
|
+
errorText: "User cancelled tool call by sending a new message."
|
|
423
|
+
};
|
|
424
|
+
});
|
|
425
|
+
if (!messageChanged) return message;
|
|
256
426
|
hasChanges = true;
|
|
257
|
-
const { approval: _approval, ...rest } = part;
|
|
258
427
|
return {
|
|
259
|
-
...
|
|
260
|
-
|
|
261
|
-
errorText: "User cancelled tool call by sending a new message."
|
|
428
|
+
...message,
|
|
429
|
+
parts
|
|
262
430
|
};
|
|
263
431
|
});
|
|
264
432
|
if (!hasChanges) return messages_1;
|
|
265
|
-
return
|
|
266
|
-
...lastMessage_0,
|
|
267
|
-
parts
|
|
268
|
-
}];
|
|
433
|
+
return next_0;
|
|
269
434
|
});
|
|
270
435
|
};
|
|
271
436
|
const respondViaAISDK = ({ approvalId, approved, reason }) => Promise.resolve(chatHelpers.addToolApprovalResponse({
|
|
@@ -274,48 +439,63 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
274
439
|
...reason != null && { reason },
|
|
275
440
|
options: { metadata: lastRunConfigRef.current }
|
|
276
441
|
}));
|
|
277
|
-
const respondViaHost = async (onRespond,
|
|
278
|
-
const { approvalId: approvalId_0 } =
|
|
279
|
-
const requested = chatHelpers.messages.flatMap((
|
|
442
|
+
const respondViaHost = async (onRespond, response_0) => {
|
|
443
|
+
const { approvalId: approvalId_0 } = response_0;
|
|
444
|
+
const requested = chatHelpers.messages.flatMap((message_0) => message_0.parts.flatMap((part_0) => isToolUIPart(part_0) ? [{
|
|
445
|
+
messageId: message_0.id,
|
|
446
|
+
part: part_0
|
|
447
|
+
}] : [])).find(({ part: part_1 }) => part_1.state === "approval-requested" && part_1.approval?.id === approvalId_0);
|
|
280
448
|
if (!requested || hostApprovalIdsRef.current.has(approvalId_0)) throw new Error(`Tool approval ${approvalId_0} is not waiting for a response.`);
|
|
449
|
+
const startedWith = ownedApprovals;
|
|
450
|
+
const startedOwner = approvalOwner;
|
|
451
|
+
const startedResponses = toolApprovalResponsesRef.current;
|
|
452
|
+
let isApplied = false;
|
|
281
453
|
const applyResponse = (applied) => {
|
|
454
|
+
isApplied = applied;
|
|
455
|
+
if (applied) startedWith?.set(approvalId_0, { response: response_0 });
|
|
456
|
+
else startedWith?.delete(approvalId_0);
|
|
457
|
+
if (startedOwner) {
|
|
458
|
+
notifyHostApprovals(startedOwner);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (lastApprovalOwnerRef.current !== startedOwner) return;
|
|
282
462
|
if (applied) hostApprovalIdsRef.current.add(approvalId_0);
|
|
283
463
|
else hostApprovalIdsRef.current.delete(approvalId_0);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
else responses.delete(approvalId_0);
|
|
288
|
-
return responses;
|
|
289
|
-
});
|
|
464
|
+
if (applied) toolApprovalResponsesRef.current.set(approvalId_0, response_0);
|
|
465
|
+
else toolApprovalResponsesRef.current.delete(approvalId_0);
|
|
466
|
+
setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
|
|
290
467
|
};
|
|
291
468
|
applyResponse(true);
|
|
292
469
|
try {
|
|
293
|
-
await onRespond(
|
|
294
|
-
toolCallId: requested.toolCallId,
|
|
295
|
-
toolName: getToolName(requested),
|
|
470
|
+
await onRespond(response_0, {
|
|
471
|
+
toolCallId: requested.part.toolCallId,
|
|
472
|
+
toolName: getToolName(requested.part),
|
|
296
473
|
respondViaAISDK: async () => {
|
|
297
474
|
try {
|
|
298
|
-
await respondViaAISDK(
|
|
475
|
+
await respondViaAISDK(response_0);
|
|
299
476
|
} finally {
|
|
300
477
|
applyResponse(false);
|
|
301
478
|
}
|
|
302
479
|
}
|
|
303
480
|
});
|
|
304
481
|
} catch (error_0) {
|
|
305
|
-
if (
|
|
482
|
+
if (isApplied) applyResponse(false);
|
|
306
483
|
throw error_0;
|
|
307
484
|
}
|
|
485
|
+
const remainsApplied = startedWith?.has(approvalId_0) ?? startedResponses.has(approvalId_0);
|
|
486
|
+
if (isApplied && remainsApplied) await persistToolApprovalResponses(requested.messageId);
|
|
308
487
|
};
|
|
309
488
|
const hasSeededRepositoryRef = useRef(false);
|
|
310
489
|
const shouldFeedRepository = exportedMessageRepository != null && !hasSeededRepositoryRef.current && messages.length === 0;
|
|
311
490
|
const runtime = useExternalStoreRuntime({
|
|
491
|
+
unstable_persistsHistory: true,
|
|
312
492
|
isRunning: providerIsRunning,
|
|
313
493
|
...shouldFeedRepository ? { messageRepository: exportedMessageRepository } : { messages },
|
|
314
494
|
unstable_enableToolInvocations: true,
|
|
315
495
|
setToolStatuses,
|
|
316
496
|
setMessages: (messages_2) => chatHelpers.setMessages(messages_2.map(getVercelAIMessages).filter(Boolean).flat()),
|
|
317
497
|
onImport: (messages_3) => chatHelpers.setMessages(messages_3.map(getVercelAIMessages).filter(Boolean).flat()),
|
|
318
|
-
onVoiceTranscript: (
|
|
498
|
+
onVoiceTranscript: (message_1) => chatHelpers.setMessages((current) => [...current, toVoiceTranscriptUIMessage(message_1)]),
|
|
319
499
|
onExportExternalState: () => {
|
|
320
500
|
const exported = runtimeRef.current.thread.export();
|
|
321
501
|
const expandedMessages = [];
|
|
@@ -341,12 +521,12 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
341
521
|
runtimeRef.current.thread.import(exportedRepo);
|
|
342
522
|
},
|
|
343
523
|
onCancel: async () => {
|
|
344
|
-
const
|
|
345
|
-
const cancelledId = isRunning &&
|
|
524
|
+
const message_2 = chatHelpers.messages.at(-1);
|
|
525
|
+
const cancelledId = isRunning && message_2?.role === "assistant" ? message_2.id : void 0;
|
|
346
526
|
if (cancelledId) {
|
|
347
527
|
const liveIds = new Set(chatHelpers.messages.map((m) => m.id));
|
|
348
528
|
setCancelledMessages((prev_1) => {
|
|
349
|
-
const kept = prev_1?.chatId === chatHelpers.id ? [...prev_1.ids].filter((
|
|
529
|
+
const kept = prev_1?.chatId === chatHelpers.id ? [...prev_1.ids].filter((id_5) => liveIds.has(id_5)) : [];
|
|
350
530
|
return {
|
|
351
531
|
chatId: chatHelpers.id,
|
|
352
532
|
ids: /* @__PURE__ */ new Set([...kept, cancelledId])
|
|
@@ -362,33 +542,52 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
362
542
|
}
|
|
363
543
|
}
|
|
364
544
|
},
|
|
365
|
-
onNew: async (
|
|
366
|
-
const createMessage = (customToCreateMessage ?? toCreateMessage)(
|
|
367
|
-
if (!(
|
|
368
|
-
chatHelpers.setMessages((current_0) => [...current_0, toUIMessage(createMessage,
|
|
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)]);
|
|
369
549
|
return;
|
|
370
550
|
}
|
|
371
|
-
lastRunConfigRef.current =
|
|
551
|
+
lastRunConfigRef.current = message_3.runConfig;
|
|
372
552
|
await completePendingToolCalls();
|
|
373
|
-
await chatHelpers.sendMessage(createMessage, { metadata:
|
|
553
|
+
await chatHelpers.sendMessage(createMessage, { metadata: message_3.runConfig });
|
|
374
554
|
},
|
|
375
|
-
onEdit: async (
|
|
376
|
-
const createMessage_0 = (customToCreateMessage ?? toCreateMessage)(
|
|
377
|
-
if (!(
|
|
378
|
-
chatHelpers.setMessages((current_1) => [...sliceMessagesUntil(current_1,
|
|
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)]);
|
|
379
559
|
return;
|
|
380
560
|
}
|
|
381
|
-
lastRunConfigRef.current =
|
|
382
|
-
chatHelpers.setMessages((current_2) => sliceMessagesUntil(current_2,
|
|
383
|
-
await chatHelpers.sendMessage(createMessage_0, { metadata:
|
|
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 });
|
|
384
564
|
},
|
|
385
565
|
onDelete: async (messageId_0) => {
|
|
386
566
|
const threadMessages = runtimeRef.current.thread.getState().messages;
|
|
387
|
-
const messageIndex = threadMessages.findIndex((
|
|
567
|
+
const messageIndex = threadMessages.findIndex((message_5) => message_5.id === messageId_0);
|
|
388
568
|
if (messageIndex === -1) return;
|
|
389
569
|
await deleteHistoryMessage(messageId_0);
|
|
390
|
-
|
|
391
|
-
|
|
570
|
+
let removedToolArtifact = false;
|
|
571
|
+
let removedToolInteractions = false;
|
|
572
|
+
let removedToolApprovalResponse = false;
|
|
573
|
+
let removedHostApprovalId = false;
|
|
574
|
+
for (const part_2 of threadMessages[messageIndex].content) if (part_2.type === "tool-call") {
|
|
575
|
+
removedToolArtifact = toolArtifactsRef.current.delete(part_2.toolCallId) || removedToolArtifact;
|
|
576
|
+
removedToolInteractions = toolInteractionsRef.current.delete(part_2.toolCallId) || removedToolInteractions;
|
|
577
|
+
if (part_2.approval) {
|
|
578
|
+
ownedApprovals?.delete(part_2.approval.id);
|
|
579
|
+
removedToolApprovalResponse = toolApprovalResponsesRef.current.delete(part_2.approval.id) || removedToolApprovalResponse;
|
|
580
|
+
removedHostApprovalId = hostApprovalIdsRef.current.delete(part_2.approval.id) || removedHostApprovalId;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (removedToolArtifact) markToolArtifactsChanged();
|
|
584
|
+
if (removedToolInteractions) markToolInteractionsChanged();
|
|
585
|
+
if (removedToolApprovalResponse || removedHostApprovalId) {
|
|
586
|
+
if (approvalOwner) notifyHostApprovals(approvalOwner);
|
|
587
|
+
setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
|
|
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)));
|
|
392
591
|
},
|
|
393
592
|
onReload: async (parentId_0, config) => {
|
|
394
593
|
lastRunConfigRef.current = config.runConfig;
|
|
@@ -396,26 +595,61 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
396
595
|
chatHelpers.setMessages(newMessages);
|
|
397
596
|
await chatHelpers.regenerate({ metadata: config.runConfig });
|
|
398
597
|
},
|
|
399
|
-
onAddToolResult: ({ toolCallId, toolName, result: result_0, isError, modelContent }) => {
|
|
598
|
+
onAddToolResult: ({ messageId: messageId_1, toolCallId, toolName, result: result_0, isError, artifact, modelContent }) => {
|
|
599
|
+
if (artifact !== void 0) {
|
|
600
|
+
toolArtifactsRef.current.set(toolCallId, artifact);
|
|
601
|
+
markToolArtifactsChanged();
|
|
602
|
+
}
|
|
603
|
+
const targetIndex = findRawToolMessageIndex(chatHelpers.messages, messageId_1, toolCallId, joinStrategy);
|
|
604
|
+
const errorText = typeof result_0 === "string" ? result_0 : JSON.stringify(result_0);
|
|
605
|
+
const output = !isError && modelContent !== void 0 ? wrapModelContentEnvelope(result_0, modelContent) : result_0;
|
|
606
|
+
if (targetIndex >= 0 && targetIndex !== chatHelpers.messages.length - 1) {
|
|
607
|
+
const target = chatHelpers.messages[targetIndex];
|
|
608
|
+
const targetPart = target.parts.find((part_3) => isToolUIPart(part_3) && part_3.toolCallId === toolCallId);
|
|
609
|
+
if (targetPart?.state === "output-error" || targetPart?.state === "output-denied" || targetPart?.state === "output-available" && !targetPart.preliminary) return Promise.resolve();
|
|
610
|
+
const targetMessageId = target.id;
|
|
611
|
+
chatHelpers.setMessages((current_4) => current_4.map((message_8) => {
|
|
612
|
+
if (message_8.id !== targetMessageId) return message_8;
|
|
613
|
+
return {
|
|
614
|
+
...message_8,
|
|
615
|
+
parts: message_8.parts.map((part_4) => {
|
|
616
|
+
if (!isToolUIPart(part_4) || part_4.toolCallId !== toolCallId) return part_4;
|
|
617
|
+
const { preliminary: _preliminary, ...finalPart } = part_4;
|
|
618
|
+
return {
|
|
619
|
+
...finalPart,
|
|
620
|
+
state: isError ? "output-error" : "output-available",
|
|
621
|
+
output: isError ? void 0 : output,
|
|
622
|
+
errorText: isError ? errorText : void 0
|
|
623
|
+
};
|
|
624
|
+
})
|
|
625
|
+
};
|
|
626
|
+
}));
|
|
627
|
+
return Promise.resolve();
|
|
628
|
+
}
|
|
400
629
|
const options = { metadata: lastRunConfigRef.current };
|
|
401
630
|
if (isError) return Promise.resolve(chatHelpers.addToolOutput({
|
|
402
631
|
state: "output-error",
|
|
403
632
|
tool: toolName ?? toolCallId,
|
|
404
633
|
toolCallId,
|
|
405
|
-
errorText
|
|
634
|
+
errorText,
|
|
406
635
|
options
|
|
407
636
|
}));
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
637
|
+
return Promise.resolve(chatHelpers.addToolOutput({
|
|
638
|
+
tool: toolName,
|
|
639
|
+
toolCallId,
|
|
640
|
+
output,
|
|
641
|
+
options
|
|
642
|
+
}));
|
|
643
|
+
},
|
|
644
|
+
onRespondToToolApproval: (response_1) => {
|
|
645
|
+
if (supersededApprovalProjection.approvalIds.has(response_1.approvalId)) return Promise.reject(/* @__PURE__ */ new Error(`Tool approval ${response_1.approvalId} is not waiting for a response.`));
|
|
646
|
+
return customOnRespondToToolApproval ? respondViaHost(customOnRespondToToolApproval, response_1) : respondViaAISDK(response_1);
|
|
647
|
+
},
|
|
648
|
+
unstable_onRecordToolInteraction: ({ messageId: messageId_2, toolCallId: toolCallId_0, interaction }) => {
|
|
649
|
+
toolInteractionsRef.current.set(toolCallId_0, appendToolInteraction(toolInteractionsRef.current.get(toolCallId_0), interaction));
|
|
650
|
+
markToolInteractionsChanged();
|
|
651
|
+
return persistToolInteractions(messageId_2);
|
|
417
652
|
},
|
|
418
|
-
onRespondToToolApproval: customOnRespondToToolApproval ? (response_0) => respondViaHost(customOnRespondToToolApproval, response_0) : respondViaAISDK,
|
|
419
653
|
...pickExternalStoreSharedOptions(adapter),
|
|
420
654
|
...adapter.unstable_messageRepositoryInstance && { unstable_messageRepositoryInstance: adapter.unstable_messageRepositoryInstance },
|
|
421
655
|
...suggestionAdapter ? { suggestions: generatedSuggestions } : {},
|