@assistant-ui/ai-sdk 0.0.6 → 0.0.8

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 (107) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/aiSDKFormatAdapter.d.ts +2 -2
  3. package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
  4. package/dist/adapters/vercelAttachmentAdapter.d.ts +2 -5
  5. package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -1
  6. package/dist/adapters/vercelAttachmentAdapter.js +3 -3
  7. package/dist/adapters/vercelAttachmentAdapter.js.map +1 -1
  8. package/dist/aiSDKExtras.d.ts +6 -9
  9. package/dist/aiSDKExtras.d.ts.map +1 -1
  10. package/dist/converters/convertMessage.d.ts +34 -25
  11. package/dist/converters/convertMessage.d.ts.map +1 -1
  12. package/dist/converters/convertMessage.js +78 -8
  13. package/dist/converters/convertMessage.js.map +1 -1
  14. package/dist/converters/modelContentEnvelope.d.ts +10 -12
  15. package/dist/converters/modelContentEnvelope.d.ts.map +1 -1
  16. package/dist/converters/toCreateMessage.d.ts +3 -6
  17. package/dist/converters/toCreateMessage.d.ts.map +1 -1
  18. package/dist/converters/toolOutputConversion.d.ts +22 -25
  19. package/dist/converters/toolOutputConversion.d.ts.map +1 -1
  20. package/dist/hooks.d.ts +4 -7
  21. package/dist/hooks.d.ts.map +1 -1
  22. package/dist/index.d.ts +17 -14
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.native.d.ts +15 -12
  25. package/dist/index.native.d.ts.map +1 -0
  26. package/dist/model-context/injectInteractableContext.d.ts +3 -6
  27. package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
  28. package/dist/model-context/injectQuoteContext.d.ts +2 -5
  29. package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
  30. package/dist/runtime/AISDKChat.d.ts +4 -7
  31. package/dist/runtime/AISDKChat.d.ts.map +1 -1
  32. package/dist/runtime/AISDKChat.js +1 -1
  33. package/dist/runtime/AISDKThreads.d.ts +29 -32
  34. package/dist/runtime/AISDKThreads.d.ts.map +1 -1
  35. package/dist/runtime/AISDKThreads.js +26 -33
  36. package/dist/runtime/AISDKThreads.js.map +1 -1
  37. package/dist/runtime/sdkIdentity.d.ts +2 -5
  38. package/dist/runtime/sdkIdentity.d.ts.map +1 -1
  39. package/dist/runtime/sdkIdentity.js +1 -1
  40. package/dist/runtime/useAISDKRuntime.d.ts +72 -61
  41. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
  42. package/dist/runtime/useAISDKRuntime.js +170 -56
  43. package/dist/runtime/useAISDKRuntime.js.map +1 -1
  44. package/dist/runtime/useChatRuntime.d.ts +8 -11
  45. package/dist/runtime/useChatRuntime.d.ts.map +1 -1
  46. package/dist/runtime/useChatRuntime.js +14 -10
  47. package/dist/runtime/useChatRuntime.js.map +1 -1
  48. package/dist/runtime/useChatThread.d.ts +100 -81
  49. package/dist/runtime/useChatThread.d.ts.map +1 -1
  50. package/dist/runtime/useChatThread.js +33 -13
  51. package/dist/runtime/useChatThread.js.map +1 -1
  52. package/dist/runtime/useExternalHistory.d.ts +8 -9
  53. package/dist/runtime/useExternalHistory.d.ts.map +1 -1
  54. package/dist/runtime/useExternalHistory.js +236 -58
  55. package/dist/runtime/useExternalHistory.js.map +1 -1
  56. package/dist/runtime/useResourceCleanup.d.ts +1 -4
  57. package/dist/runtime/useResourceCleanup.d.ts.map +1 -1
  58. package/dist/runtime/useResourceCleanup.js +38 -14
  59. package/dist/runtime/useResourceCleanup.js.map +1 -1
  60. package/dist/runtime/useStreamingTiming.d.ts +5 -8
  61. package/dist/runtime/useStreamingTiming.d.ts.map +1 -1
  62. package/dist/runtime/useStreamingTiming.js +1 -1
  63. package/dist/tools/frontendTools.d.ts +24 -27
  64. package/dist/tools/frontendTools.d.ts.map +1 -1
  65. package/dist/tools/generativeTools.d.ts +29 -32
  66. package/dist/tools/generativeTools.d.ts.map +1 -1
  67. package/dist/tools/mcp-stdio.node.d.ts +2 -2
  68. package/dist/tools/mcp-stdio.node.d.ts.map +1 -0
  69. package/dist/tools/mcp-stdio.unsupported.d.ts +2 -5
  70. package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -1
  71. package/dist/transport/AssistantChatTransport.d.ts +17 -20
  72. package/dist/transport/AssistantChatTransport.d.ts.map +1 -1
  73. package/dist/transport/resumable.d.ts +29 -32
  74. package/dist/transport/resumable.d.ts.map +1 -1
  75. package/dist/usage.d.ts +12 -15
  76. package/dist/usage.d.ts.map +1 -1
  77. package/dist/usage.js.map +1 -1
  78. package/dist/utils/getVercelAIMessages.d.ts +3 -6
  79. package/dist/utils/getVercelAIMessages.d.ts.map +1 -1
  80. package/dist/utils/sliceMessagesUntil.d.ts +2 -5
  81. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
  82. package/package.json +16 -14
  83. package/src/adapters/vercelAttachmentAdapter.test.ts +23 -0
  84. package/src/adapters/vercelAttachmentAdapter.ts +2 -2
  85. package/src/converters/convertMessage.test.ts +616 -3
  86. package/src/converters/convertMessage.ts +187 -12
  87. package/src/index.native.ts +1 -1
  88. package/src/index.ts +1 -1
  89. package/src/runtime/AISDKChat.test.ts +4 -5
  90. package/src/runtime/AISDKThreads.cloud.test.ts +60 -2
  91. package/src/runtime/AISDKThreads.test.ts +88 -51
  92. package/src/runtime/AISDKThreads.ts +4 -13
  93. package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +338 -0
  94. package/src/runtime/useAISDKRuntime.approval.test.tsx +476 -4
  95. package/src/runtime/useAISDKRuntime.test.ts +282 -8
  96. package/src/runtime/useAISDKRuntime.ts +274 -46
  97. package/src/runtime/useAISDKRuntime.voice.test.tsx +319 -0
  98. package/src/runtime/useChatRuntime.integration.test.tsx +56 -0
  99. package/src/runtime/useChatRuntime.test.ts +77 -5
  100. package/src/runtime/useChatRuntime.ts +6 -2
  101. package/src/runtime/useChatThread.transport.test.tsx +149 -0
  102. package/src/runtime/useChatThread.ts +77 -10
  103. package/src/runtime/useExternalHistory.test.ts +989 -3
  104. package/src/runtime/useExternalHistory.ts +390 -24
  105. package/src/runtime/useResourceCleanup.ts +41 -15
  106. package/src/tools/generativeTools.test.ts +7 -1
  107. package/src/usage.ts +1 -1
@@ -1,64 +1,75 @@
1
- import { JoinStrategy } from "@assistant-ui/core/react";
2
- import { AppendMessage, AssistantRuntime, ExternalStoreAdapter, ExternalStoreSharedOptions, MessageFormatRepository, SuggestionAdapter, ThreadHistoryAdapter } from "@assistant-ui/core";
1
+ import type { UIMessage, useChat, CreateUIMessage } from "@ai-sdk/react";
2
+ import { type JoinStrategy } from "@assistant-ui/core/react";
3
+ import type { SuggestionAdapter } from "@assistant-ui/core";
4
+ import type { ExternalStoreAdapter, ExternalStoreSharedOptions, ThreadHistoryAdapter, AssistantRuntime, MessageFormatRepository, AppendMessage, RespondToToolApprovalOptions } from "@assistant-ui/core";
3
5
  import { MessageRepository } from "@assistant-ui/core/internal";
4
- import { CreateUIMessage, UIMessage, useChat } from "@ai-sdk/react";
5
- //#region src/runtime/useAISDKRuntime.d.ts
6
- type CustomToCreateMessageFunction = <UI_MESSAGE extends UIMessage = UIMessage>(message: AppendMessage) => CreateUIMessage<UI_MESSAGE>;
7
- type AISDKRuntimeAdapter<UI_MESSAGE extends UIMessage = UIMessage> = ExternalStoreSharedOptions & {
8
- adapters?: (NonNullable<ExternalStoreAdapter["adapters"]> & {
9
- history?: ThreadHistoryAdapter | undefined;
10
- suggestion?: SuggestionAdapter | undefined;
11
- }) | undefined;
12
- toCreateMessage?: CustomToCreateMessageFunction;
13
- unstable_messageRepositoryInstance?: MessageRepository | undefined;
14
- /**
15
- * Whether to automatically cancel pending interactive tool calls when the user sends a new message.
16
- *
17
- * When enabled (default), the pending tool calls will be marked as failed with an error message
18
- * indicating the user cancelled the tool call by sending a new message.
19
- *
20
- * @default true
21
- */
22
- cancelPendingToolCallsOnSend?: boolean | undefined;
23
- /**
24
- * Called when `runtime.thread.resumeRun(config)` is invoked.
25
- *
26
- * When omitted, `resumeRun` throws `"Runtime does not support resuming runs."`.
27
- * Provide this to bridge resume invocations into a custom replay channel
28
- * (for example, an SSE reconnect endpoint keyed by turn id).
29
- */
30
- onResume?: ExternalStoreAdapter["onResume"];
31
- /**
32
- * Called when `runtime.thread.resumeToolCall(options)` is invoked for a tool call the in-process tracker does not own.
33
- *
34
- * When omitted, `resumeToolCall` throws `"Tool call ${toolCallId} is not waiting for resume."`.
35
- * Provide this to bridge resume-tool-call invocations into a custom handler.
36
- */
37
- onResumeToolCall?: ExternalStoreAdapter["onResumeToolCall"];
38
- /**
39
- * How consecutive assistant messages are rendered.
40
- *
41
- * `"concat-content"` (the default) merges them into a single thread message.
42
- * `"none"` keeps each assistant message as its own thread message, which is
43
- * useful when a backend persists proactive or consecutive assistant messages
44
- * as separate entries.
45
- */
46
- joinStrategy?: JoinStrategy | undefined;
47
- /**
48
- * A branch-aware AI SDK message tree seeded once when `useChat` is empty.
49
- * After that seed, live updates come only from `useChat`. A later empty
50
- * chat or a new object identity does not reload the tree.
51
- */
52
- messageRepository?: MessageFormatRepository<UI_MESSAGE>;
53
- /**
54
- * Called after an explicit `switchToBranch` (for example a BranchPicker
55
- * click). Complements `setMessages` and does not enable switching by itself.
56
- *
57
- * @deprecated This API is still under active development and might change without notice.
58
- */
59
- unstable_onBranchChange?: ExternalStoreAdapter["unstable_onBranchChange"];
6
+ export type CustomToCreateMessageFunction = <UI_MESSAGE extends UIMessage = UIMessage>(message: AppendMessage) => CreateUIMessage<UI_MESSAGE>;
7
+ export type AISDKRuntimeAdapter<UI_MESSAGE extends UIMessage = UIMessage> = ExternalStoreSharedOptions & {
8
+ adapters?: (NonNullable<ExternalStoreAdapter["adapters"]> & {
9
+ history?: ThreadHistoryAdapter | undefined;
10
+ suggestion?: SuggestionAdapter | undefined;
11
+ }) | undefined;
12
+ toCreateMessage?: CustomToCreateMessageFunction;
13
+ unstable_messageRepositoryInstance?: MessageRepository | undefined;
14
+ /**
15
+ * Whether to automatically cancel pending interactive tool calls when the user sends a new message.
16
+ *
17
+ * When enabled (default), the pending tool calls will be marked as failed with an error message
18
+ * indicating the user cancelled the tool call by sending a new message.
19
+ *
20
+ * @default true
21
+ */
22
+ cancelPendingToolCallsOnSend?: boolean | undefined;
23
+ /**
24
+ * Called when `runtime.thread.resumeRun(config)` is invoked.
25
+ *
26
+ * When omitted, `resumeRun` throws `"Runtime does not support resuming runs."`.
27
+ * Provide this to bridge resume invocations into a custom replay channel
28
+ * (for example, an SSE reconnect endpoint keyed by turn id).
29
+ */
30
+ onResume?: ExternalStoreAdapter["onResume"];
31
+ /**
32
+ * Called when `runtime.thread.resumeToolCall(options)` is invoked for a tool call the in-process tracker does not own.
33
+ *
34
+ * When omitted, `resumeToolCall` throws `"Tool call ${toolCallId} is not waiting for resume."`.
35
+ * Provide this to bridge resume-tool-call invocations into a custom handler.
36
+ */
37
+ onResumeToolCall?: ExternalStoreAdapter["onResumeToolCall"];
38
+ /**
39
+ * Answers tool approval requests through a host-owned channel instead of the AI SDK's `addToolApprovalResponse`.
40
+ *
41
+ * Called for every approval request in the thread with the complete response, including option and free-form answers. Hand requests the host does not own to `respondViaAISDK`, which is what runs when this option is omitted. The answer applies to the approval when the handler starts and is removed if it throws. It is never written into the `useChat` messages, so `sendAutomaticallyWhen` cannot forward it. With a history adapter, the answer is stored with its message once the handler resolves and returns on reload, and a second response to the same request rejects; without one it lasts as long as this runtime, and a runtime mounted again over the same chat shows the request open until the resumed run records its resolution in the chat.
42
+ *
43
+ * While a handler is set, an approval's `display`, `allowFreeform`, `dismissible` and `options` reach the renderer, because the handler can receive answers the AI SDK cannot carry. A stream declares them through the `approvalDescriptor` of its `tool-approval-request` chunk, the one approval field the AI SDK keeps opaque; the converter reads the request and answer fields from that descriptor when the approval itself lacks them.
44
+ */
45
+ onRespondToToolApproval?: ((response: RespondToToolApprovalOptions, context: {
46
+ toolCallId: string;
47
+ toolName: string;
48
+ /** Sends this response through the AI SDK's `addToolApprovalResponse`, which carries only `approved` and `reason`. */
49
+ respondViaAISDK: () => Promise<void>;
50
+ }) => Promise<void> | void) | undefined;
51
+ /**
52
+ * How consecutive assistant messages are rendered.
53
+ *
54
+ * `"concat-content"` (the default) merges them into a single thread message.
55
+ * `"none"` keeps each assistant message as its own thread message, which is
56
+ * useful when a backend persists proactive or consecutive assistant messages
57
+ * as separate entries.
58
+ */
59
+ joinStrategy?: JoinStrategy | undefined;
60
+ /**
61
+ * A branch-aware AI SDK message tree seeded once when `useChat` is empty.
62
+ * After that seed, live updates come only from `useChat`. A later empty
63
+ * chat or a new object identity does not reload the tree.
64
+ */
65
+ messageRepository?: MessageFormatRepository<UI_MESSAGE>;
66
+ /**
67
+ * Called after an explicit `switchToBranch` (for example a BranchPicker
68
+ * click). Complements `setMessages` and does not enable switching by itself.
69
+ *
70
+ * @deprecated This API is still under active development and might change without notice.
71
+ */
72
+ unstable_onBranchChange?: ExternalStoreAdapter["unstable_onBranchChange"];
60
73
  };
61
- declare const useAISDKRuntime: <UI_MESSAGE extends UIMessage = UIMessage>(chatHelpers: ReturnType<typeof useChat<UI_MESSAGE>>, adapter?: AISDKRuntimeAdapter<UI_MESSAGE>) => AssistantRuntime;
62
- //#endregion
63
- export { AISDKRuntimeAdapter, CustomToCreateMessageFunction, useAISDKRuntime };
74
+ export declare const useAISDKRuntime: <UI_MESSAGE extends UIMessage = UIMessage>(chatHelpers: ReturnType<typeof useChat<UI_MESSAGE>>, adapter?: AISDKRuntimeAdapter<UI_MESSAGE>) => AssistantRuntime;
64
75
  //# sourceMappingURL=useAISDKRuntime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAISDKRuntime.d.ts","names":[],"sources":["../../src/runtime/useAISDKRuntime.ts"],"mappings":";;;;;KAmEY,iCACV,mBAAmB,YAAY,WAE/B,SAAS,kBACN,gBAAgB;KAYT,oBAAoB,mBAAmB,YAAY,aAC7D;EACE,YACK,YAAY;IACX,UAAU;IACV,aAAa;;EAGnB,kBAAkB;EAClB,qCAAqC;;;;;;;;;EASrC;;;;;;;;EAQA,WAAW;;;;;;;EAOX,mBAAmB;;;;;;;;;EASnB,eAAe;;;;;;EAMf,oBAAoB,wBAAwB;;;;;;;EAO5C,0BAA0B;;cAiGjB,kBAAmB,mBAAmB,YAAY,WAC7D,aAAa,kBAAkB,QAAQ,cACvC,UAAS,oBAAoB,gBAAgB"}
1
+ {"version":3,"file":"useAISDKRuntime.d.ts","sourceRoot":"","sources":["../../src/runtime/useAISDKRuntime.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EACP,eAAe,EAEhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,gBAAgB,EAIhB,uBAAuB,EACvB,aAAa,EAGb,4BAA4B,EAE7B,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAGL,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAuBrC,MAAM,MAAM,6BAA6B,GAAG,CAC1C,UAAU,SAAS,SAAS,GAAG,SAAS,EAExC,OAAO,EAAE,aAAa,KACnB,eAAe,CAAC,UAAU,CAAC,CAAC;AA6BjC,MAAM,MAAM,mBAAmB,CAAC,UAAU,SAAS,SAAS,GAAG,SAAS,IACtE,0BAA0B,GAAG;IAC3B,QAAQ,CAAC,EACL,CAAC,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,GAAG;QAC/C,OAAO,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;QAC3C,UAAU,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;KAC5C,CAAC,GACF,SAAS,CAAC;IACd,eAAe,CAAC,EAAE,6BAA6B,CAAC;IAChD,kCAAkC,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACnE;;;;;;;OAOG;IACH,4BAA4B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAC5C;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;IAC5D;;;;;;OAMG;IACH,uBAAuB,CAAC,EACpB,CAAC,CACC,QAAQ,EAAE,4BAA4B,EACtC,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,sHAAsH;QACtH,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KACtC,KACE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAC1B,SAAS,CAAC;IACd;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,uBAAuB,CAAC,UAAU,CAAC,CAAC;IACxD;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,oBAAoB,CAAC,yBAAyB,CAAC,CAAC;CAC3E,CAAC;AAqGJ,eAAO,MAAM,eAAe,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EACtE,aAAa,UAAU,CAAC,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC,EACnD,UAAS,mBAAmB,CAAC,UAAU,CAAM,qBAonB9C,CAAC"}
@@ -1,26 +1,38 @@
1
1
  "use client";
2
- import { aiSDKExtras } from "../aiSDKExtras.js";
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 { getVercelAIMessages } from "../utils/getVercelAIMessages.js";
4
+ import { aiSDKExtras } from "../aiSDKExtras.js";
7
5
  import { wrapModelContentEnvelope } from "../converters/modelContentEnvelope.js";
8
6
  import { AISDKMessageConverter } from "../converters/convertMessage.js";
9
- import { aiSDKV6FormatAdapter } from "../adapters/aiSDKFormatAdapter.js";
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 { generateId, isToolUIPart } from "ai";
16
- import { getExternalStoreMessages, pickExternalStoreSharedOptions } from "@assistant-ui/core";
17
- import { MessageRepository, consumeSuggestionResult } from "@assistant-ui/core/internal";
18
18
  //#region src/runtime/useAISDKRuntime.ts
19
19
  const toUIMessage = (createMessage, fallbackRole) => ({
20
20
  ...createMessage,
21
21
  id: createMessage.id ?? generateId(),
22
22
  role: createMessage.role ?? fallbackRole
23
23
  });
24
+ const toVoiceTranscriptUIMessage = (message) => ({
25
+ id: message.id,
26
+ role: message.role,
27
+ parts: message.content.filter((part) => part.type === "text").map((part) => ({
28
+ type: "text",
29
+ text: part.text
30
+ })),
31
+ metadata: {
32
+ ...message.metadata.modality && { modality: message.metadata.modality },
33
+ ...Object.keys(message.metadata.custom).length > 0 && { custom: message.metadata.custom }
34
+ }
35
+ });
24
36
  const EMPTY_SUGGESTIONS = [];
25
37
  const useGeneratedSuggestions = (suggestionAdapter, messages, isRunning) => {
26
38
  const $ = c(13);
@@ -129,6 +141,7 @@ const useGeneratedSuggestions = (suggestionAdapter, messages, isRunning) => {
129
141
  return suggestions;
130
142
  };
131
143
  const NO_CANCELLED_MESSAGE_IDS = /* @__PURE__ */ new Set();
144
+ const NO_TOOL_APPROVAL_RESPONSES = /* @__PURE__ */ new Map();
132
145
  const toChatError = (error) => {
133
146
  const code = error.code;
134
147
  return {
@@ -137,15 +150,29 @@ const toChatError = (error) => {
137
150
  };
138
151
  };
139
152
  const useAISDKRuntime = (chatHelpers, adapter = {}) => {
140
- const { adapters, toCreateMessage: customToCreateMessage, cancelPendingToolCallsOnSend = true, onResume, onResumeToolCall, joinStrategy, messageRepository, unstable_onBranchChange } = adapter;
153
+ const { adapters, toCreateMessage: customToCreateMessage, cancelPendingToolCallsOnSend = true, onResume, onResumeToolCall, onRespondToToolApproval: customOnRespondToToolApproval, joinStrategy, messageRepository, unstable_onBranchChange } = adapter;
141
154
  const suggestionAdapter = adapters?.suggestion;
142
155
  const contextAdapters = useRuntimeAdapters();
143
156
  const [toolStatuses, setToolStatuses] = useState({});
144
157
  const [cancelledMessages, setCancelledMessages] = useState(null);
158
+ const [toolApprovalResponses, setToolApprovalResponses] = useState(NO_TOOL_APPROVAL_RESPONSES);
159
+ const [toolArtifactEpoch, setToolArtifactEpoch] = useState(0);
160
+ const [toolInteractionEpoch, setToolInteractionEpoch] = useState(0);
161
+ const hostApprovalIdsRef = useRef(/* @__PURE__ */ new Set());
162
+ const toolApprovalResponsesRef = useRef(/* @__PURE__ */ new Map());
145
163
  const toolArgsKeyOrderCacheRef = useRef(/* @__PURE__ */ new Map());
146
164
  const toolLastInputCacheRef = useRef(/* @__PURE__ */ new Map());
165
+ const toolArgsTextCacheRef = useRef(/* @__PURE__ */ new WeakMap());
147
166
  const mcpAppMetadataCacheRef = useRef(/* @__PURE__ */ new Map());
167
+ const toolArtifactsRef = useRef(/* @__PURE__ */ new Map());
168
+ const toolInteractionsRef = useRef(/* @__PURE__ */ new Map());
148
169
  const lastRunConfigRef = useRef(void 0);
170
+ const markToolArtifactsChanged = useCallback(() => {
171
+ setToolArtifactEpoch((epoch) => epoch + 1);
172
+ }, []);
173
+ const markToolInteractionsChanged = useCallback(() => {
174
+ setToolInteractionEpoch((epoch_0) => epoch_0 + 1);
175
+ }, []);
149
176
  const hasExecutingTools = Object.values(toolStatuses).some((s) => s?.type === "executing");
150
177
  const providerIsRunning = chatHelpers.status === "submitted" || chatHelpers.status === "streaming";
151
178
  const isRunning = providerIsRunning || hasExecutingTools;
@@ -154,6 +181,16 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
154
181
  const lastMessage = chatHelpers.messages.at(-1);
155
182
  const optimisticMessageId = isRunning && lastMessage?.role === "assistant" ? lastMessage.id : void 0;
156
183
  const cancelledMessageIds = cancelledMessages?.chatId === chatHelpers.id ? cancelledMessages.ids : NO_CANCELLED_MESSAGE_IDS;
184
+ const supportsRichToolApprovalResponses = customOnRespondToToolApproval != null;
185
+ const toThreadMessages = useCallback((sourceMessages) => {
186
+ const metadata = {
187
+ supportsRichToolApprovalResponses,
188
+ toolArtifacts: toolArtifactsRef.current,
189
+ toolInteractions: toolInteractionsRef.current,
190
+ toolApprovalResponses: toolApprovalResponsesRef.current
191
+ };
192
+ return AISDKMessageConverter.toThreadMessages(sourceMessages, false, metadata);
193
+ }, [supportsRichToolApprovalResponses]);
157
194
  const retractCancellation = useCallback((chatId, messageId) => {
158
195
  setCancelledMessages((prev) => {
159
196
  if (prev?.chatId !== chatId || !prev.ids.has(messageId)) return prev;
@@ -185,30 +222,42 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
185
222
  toolStatuses,
186
223
  messageTiming,
187
224
  toolArgsKeyOrderCache: toolArgsKeyOrderCacheRef.current,
225
+ toolArgsTextCache: toolArgsTextCacheRef.current,
188
226
  toolLastInputCache: toolLastInputCacheRef.current,
189
227
  mcpAppMetadataCache: mcpAppMetadataCacheRef.current,
228
+ toolArtifacts: toolArtifactsRef.current,
229
+ toolInteractions: toolInteractionsRef.current,
230
+ supportsRichToolApprovalResponses,
190
231
  ...optimisticMessageId && { optimisticMessageId },
191
232
  ...chatHelpers.error && { error: toChatError(chatHelpers.error) },
192
- ...cancelledMessageIds.size > 0 && { cancelledMessageIds }
233
+ ...cancelledMessageIds.size > 0 && { cancelledMessageIds },
234
+ ...toolApprovalResponses.size > 0 && { toolApprovalResponses }
193
235
  }), [
194
236
  toolStatuses,
195
237
  messageTiming,
196
238
  optimisticMessageId,
197
239
  chatHelpers.error,
198
- cancelledMessageIds
240
+ cancelledMessageIds,
241
+ toolApprovalResponses,
242
+ supportsRichToolApprovalResponses,
243
+ toolArtifactEpoch,
244
+ toolInteractionEpoch
199
245
  ])
200
246
  });
201
247
  const exportedMessageRepository = useMemo(() => {
202
248
  if (!messageRepository) return void 0;
203
- const converted = toExportedMessageRepository(AISDKMessageConverter.toThreadMessages, messageRepository);
249
+ const converted = toExportedMessageRepository(toThreadMessages, messageRepository);
204
250
  return converted.messages.length > 0 ? converted : void 0;
205
- }, [messageRepository]);
251
+ }, [messageRepository, toThreadMessages]);
206
252
  const generatedSuggestions = useGeneratedSuggestions(suggestionAdapter, messages, isRunning);
207
253
  const [runtimeRef] = useState(() => ({ get current() {
208
254
  return runtime;
209
255
  } }));
210
- const { isLoading, deleteMessage: deleteHistoryMessage } = useExternalHistory(runtimeRef, adapters?.history ?? contextAdapters?.history, AISDKMessageConverter.toThreadMessages, aiSDKV6FormatAdapter, (messages_0) => {
256
+ const { isLoading, deleteMessage: deleteHistoryMessage, persistToolInteractions, persistToolApprovalResponses } = useExternalHistory(runtimeRef, adapters?.history ?? contextAdapters?.history, toThreadMessages, aiSDKV6FormatAdapter, (messages_0) => {
211
257
  chatHelpers.setMessages(messages_0);
258
+ }, toolArtifactsRef.current, markToolArtifactsChanged, toolInteractionsRef.current, markToolInteractionsChanged, toolApprovalResponsesRef.current, () => {
259
+ hostApprovalIdsRef.current = new Set(toolApprovalResponsesRef.current.keys());
260
+ setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
212
261
  });
213
262
  const { id: chatId_0, messages: chatMessages, status: chatStatus, error } = chatHelpers;
214
263
  const extras = useMemo(() => aiSDKExtras.provide({
@@ -223,36 +272,82 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
223
272
  const completePendingToolCalls = async () => {
224
273
  if (!cancelPendingToolCallsOnSend) return;
225
274
  chatHelpers.setMessages((messages_1) => {
226
- const lastMessage_0 = messages_1.at(-1);
227
- if (lastMessage_0?.role !== "assistant") return messages_1;
228
275
  let hasChanges = false;
229
- const parts = lastMessage_0.parts?.map((part) => {
230
- if (!isToolUIPart(part)) return part;
231
- if (part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return part;
276
+ const next = messages_1.map((message) => {
277
+ if (message.role !== "assistant") return message;
278
+ let messageChanged = false;
279
+ const parts = message.parts?.map((part) => {
280
+ if (!isToolUIPart(part)) return part;
281
+ if (part.state === "output-available" || part.state === "output-error" || part.state === "output-denied") return part;
282
+ messageChanged = true;
283
+ const { approval: _approval, ...rest } = part;
284
+ return {
285
+ ...rest,
286
+ state: "output-error",
287
+ errorText: "User cancelled tool call by sending a new message."
288
+ };
289
+ });
290
+ if (!messageChanged) return message;
232
291
  hasChanges = true;
233
- const { approval: _approval, ...rest } = part;
234
292
  return {
235
- ...rest,
236
- state: "output-error",
237
- errorText: "User cancelled tool call by sending a new message."
293
+ ...message,
294
+ parts
238
295
  };
239
296
  });
240
297
  if (!hasChanges) return messages_1;
241
- return [...messages_1.slice(0, -1), {
242
- ...lastMessage_0,
243
- parts
244
- }];
298
+ return next;
245
299
  });
246
300
  };
301
+ const respondViaAISDK = ({ approvalId, approved, reason }) => Promise.resolve(chatHelpers.addToolApprovalResponse({
302
+ id: approvalId,
303
+ approved,
304
+ ...reason != null && { reason },
305
+ options: { metadata: lastRunConfigRef.current }
306
+ }));
307
+ const respondViaHost = async (onRespond, response) => {
308
+ const { approvalId: approvalId_0 } = response;
309
+ const requested = chatHelpers.messages.flatMap((message_0) => message_0.parts.flatMap((part_0) => isToolUIPart(part_0) ? [{
310
+ messageId: message_0.id,
311
+ part: part_0
312
+ }] : [])).find(({ part: part_1 }) => part_1.state === "approval-requested" && part_1.approval.id === approvalId_0);
313
+ if (!requested || hostApprovalIdsRef.current.has(approvalId_0)) throw new Error(`Tool approval ${approvalId_0} is not waiting for a response.`);
314
+ const applyResponse = (applied) => {
315
+ if (applied) hostApprovalIdsRef.current.add(approvalId_0);
316
+ else hostApprovalIdsRef.current.delete(approvalId_0);
317
+ if (applied) toolApprovalResponsesRef.current.set(approvalId_0, response);
318
+ else toolApprovalResponsesRef.current.delete(approvalId_0);
319
+ setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
320
+ };
321
+ applyResponse(true);
322
+ try {
323
+ await onRespond(response, {
324
+ toolCallId: requested.part.toolCallId,
325
+ toolName: getToolName(requested.part),
326
+ respondViaAISDK: async () => {
327
+ try {
328
+ await respondViaAISDK(response);
329
+ } finally {
330
+ applyResponse(false);
331
+ }
332
+ }
333
+ });
334
+ } catch (error_0) {
335
+ if (hostApprovalIdsRef.current.has(approvalId_0)) applyResponse(false);
336
+ throw error_0;
337
+ }
338
+ if (hostApprovalIdsRef.current.has(approvalId_0)) await persistToolApprovalResponses(requested.messageId);
339
+ };
247
340
  const hasSeededRepositoryRef = useRef(false);
248
341
  const shouldFeedRepository = exportedMessageRepository != null && !hasSeededRepositoryRef.current && messages.length === 0;
249
342
  const runtime = useExternalStoreRuntime({
343
+ unstable_persistsHistory: true,
250
344
  isRunning: providerIsRunning,
251
345
  ...shouldFeedRepository ? { messageRepository: exportedMessageRepository } : { messages },
252
346
  unstable_enableToolInvocations: true,
253
347
  setToolStatuses,
254
348
  setMessages: (messages_2) => chatHelpers.setMessages(messages_2.map(getVercelAIMessages).filter(Boolean).flat()),
255
349
  onImport: (messages_3) => chatHelpers.setMessages(messages_3.map(getVercelAIMessages).filter(Boolean).flat()),
350
+ onVoiceTranscript: (message_1) => chatHelpers.setMessages((current) => [...current, toVoiceTranscriptUIMessage(message_1)]),
256
351
  onExportExternalState: () => {
257
352
  const exported = runtimeRef.current.thread.export();
258
353
  const expandedMessages = [];
@@ -274,12 +369,12 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
274
369
  return result;
275
370
  },
276
371
  onLoadExternalState: (repo) => {
277
- const exportedRepo = toExportedMessageRepository(AISDKMessageConverter.toThreadMessages, repo);
372
+ const exportedRepo = toExportedMessageRepository(toThreadMessages, repo);
278
373
  runtimeRef.current.thread.import(exportedRepo);
279
374
  },
280
375
  onCancel: async () => {
281
- const message = chatHelpers.messages.at(-1);
282
- const cancelledId = isRunning && message?.role === "assistant" ? message.id : void 0;
376
+ const message_2 = chatHelpers.messages.at(-1);
377
+ const cancelledId = isRunning && message_2?.role === "assistant" ? message_2.id : void 0;
283
378
  if (cancelledId) {
284
379
  const liveIds = new Set(chatHelpers.messages.map((m) => m.id));
285
380
  setCancelledMessages((prev_0) => {
@@ -292,40 +387,55 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
292
387
  }
293
388
  try {
294
389
  await chatHelpers.stop();
295
- } catch (error_0) {
296
- if (!(error_0 instanceof Error && error_0.name === "AbortError")) {
390
+ } catch (error_1) {
391
+ if (!(error_1 instanceof Error && error_1.name === "AbortError")) {
297
392
  if (cancelledId) retractCancellation(chatHelpers.id, cancelledId);
298
- throw error_0;
393
+ throw error_1;
299
394
  }
300
395
  }
301
396
  },
302
- onNew: async (message_0) => {
303
- const createMessage = (customToCreateMessage ?? toCreateMessage)(message_0);
304
- if (!(message_0.startRun ?? message_0.role === "user")) {
305
- chatHelpers.setMessages((current) => [...current, toUIMessage(createMessage, message_0.role)]);
397
+ onNew: async (message_3) => {
398
+ const createMessage = (customToCreateMessage ?? toCreateMessage)(message_3);
399
+ if (!(message_3.startRun ?? message_3.role === "user")) {
400
+ chatHelpers.setMessages((current_0) => [...current_0, toUIMessage(createMessage, message_3.role)]);
306
401
  return;
307
402
  }
308
- lastRunConfigRef.current = message_0.runConfig;
403
+ lastRunConfigRef.current = message_3.runConfig;
309
404
  await completePendingToolCalls();
310
- await chatHelpers.sendMessage(createMessage, { metadata: message_0.runConfig });
405
+ await chatHelpers.sendMessage(createMessage, { metadata: message_3.runConfig });
311
406
  },
312
- onEdit: async (message_1) => {
313
- const createMessage_0 = (customToCreateMessage ?? toCreateMessage)(message_1);
314
- if (!(message_1.startRun ?? message_1.role === "user")) {
315
- chatHelpers.setMessages((current_0) => [...sliceMessagesUntil(current_0, message_1.parentId), toUIMessage(createMessage_0, message_1.role)]);
407
+ onEdit: async (message_4) => {
408
+ const createMessage_0 = (customToCreateMessage ?? toCreateMessage)(message_4);
409
+ if (!(message_4.startRun ?? message_4.role === "user")) {
410
+ chatHelpers.setMessages((current_1) => [...sliceMessagesUntil(current_1, message_4.parentId), toUIMessage(createMessage_0, message_4.role)]);
316
411
  return;
317
412
  }
318
- lastRunConfigRef.current = message_1.runConfig;
319
- chatHelpers.setMessages((current_1) => sliceMessagesUntil(current_1, message_1.parentId));
320
- await chatHelpers.sendMessage(createMessage_0, { metadata: message_1.runConfig });
413
+ lastRunConfigRef.current = message_4.runConfig;
414
+ chatHelpers.setMessages((current_2) => sliceMessagesUntil(current_2, message_4.parentId));
415
+ await chatHelpers.sendMessage(createMessage_0, { metadata: message_4.runConfig });
321
416
  },
322
417
  onDelete: async (messageId_0) => {
323
418
  const threadMessages = runtimeRef.current.thread.getState().messages;
324
- const messageIndex = threadMessages.findIndex((message_2) => message_2.id === messageId_0);
419
+ const messageIndex = threadMessages.findIndex((message_5) => message_5.id === messageId_0);
325
420
  if (messageIndex === -1) return;
326
421
  await deleteHistoryMessage(messageId_0);
327
- const deleteIds = new Set(getExternalStoreMessages(threadMessages[messageIndex]).map((message_3) => message_3.id));
328
- chatHelpers.setMessages((current_2) => current_2.filter((message_4) => !deleteIds.has(message_4.id)));
422
+ let removedToolArtifact = false;
423
+ let removedToolInteractions = false;
424
+ let removedToolApprovalResponse = false;
425
+ let removedHostApprovalId = false;
426
+ for (const part_2 of threadMessages[messageIndex].content) if (part_2.type === "tool-call") {
427
+ removedToolArtifact = toolArtifactsRef.current.delete(part_2.toolCallId) || removedToolArtifact;
428
+ removedToolInteractions = toolInteractionsRef.current.delete(part_2.toolCallId) || removedToolInteractions;
429
+ if (part_2.approval) {
430
+ removedToolApprovalResponse = toolApprovalResponsesRef.current.delete(part_2.approval.id) || removedToolApprovalResponse;
431
+ removedHostApprovalId = hostApprovalIdsRef.current.delete(part_2.approval.id) || removedHostApprovalId;
432
+ }
433
+ }
434
+ if (removedToolArtifact) markToolArtifactsChanged();
435
+ if (removedToolInteractions) markToolInteractionsChanged();
436
+ if (removedToolApprovalResponse || removedHostApprovalId) setToolApprovalResponses(new Map(toolApprovalResponsesRef.current));
437
+ const deleteIds = new Set(getExternalStoreMessages(threadMessages[messageIndex]).map((message_6) => message_6.id));
438
+ chatHelpers.setMessages((current_3) => current_3.filter((message_7) => !deleteIds.has(message_7.id)));
329
439
  },
330
440
  onReload: async (parentId_0, config) => {
331
441
  lastRunConfigRef.current = config.runConfig;
@@ -333,7 +443,11 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
333
443
  chatHelpers.setMessages(newMessages);
334
444
  await chatHelpers.regenerate({ metadata: config.runConfig });
335
445
  },
336
- onAddToolResult: ({ toolCallId, toolName, result: result_0, isError, modelContent }) => {
446
+ onAddToolResult: ({ toolCallId, toolName, result: result_0, isError, artifact, modelContent }) => {
447
+ if (artifact !== void 0) {
448
+ toolArtifactsRef.current.set(toolCallId, artifact);
449
+ markToolArtifactsChanged();
450
+ }
337
451
  const options = { metadata: lastRunConfigRef.current };
338
452
  if (isError) return Promise.resolve(chatHelpers.addToolOutput({
339
453
  state: "output-error",
@@ -352,12 +466,12 @@ const useAISDKRuntime = (chatHelpers, adapter = {}) => {
352
466
  }));
353
467
  }
354
468
  },
355
- onRespondToToolApproval: ({ approvalId, approved, reason }) => Promise.resolve(chatHelpers.addToolApprovalResponse({
356
- id: approvalId,
357
- approved,
358
- ...reason != null && { reason },
359
- options: { metadata: lastRunConfigRef.current }
360
- })),
469
+ onRespondToToolApproval: customOnRespondToToolApproval ? (response_0) => respondViaHost(customOnRespondToToolApproval, response_0) : respondViaAISDK,
470
+ unstable_onRecordToolInteraction: ({ messageId: messageId_1, toolCallId: toolCallId_0, interaction }) => {
471
+ toolInteractionsRef.current.set(toolCallId_0, appendToolInteraction(toolInteractionsRef.current.get(toolCallId_0), interaction));
472
+ markToolInteractionsChanged();
473
+ return persistToolInteractions(messageId_1);
474
+ },
361
475
  ...pickExternalStoreSharedOptions(adapter),
362
476
  ...adapter.unstable_messageRepositoryInstance && { unstable_messageRepositoryInstance: adapter.unstable_messageRepositoryInstance },
363
477
  ...suggestionAdapter ? { suggestions: generatedSuggestions } : {},