@assistant-ui/react-ai-sdk 1.3.30 → 1.3.31

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 (85) hide show
  1. package/dist/assistant-stream/dist/core/tool/schema-utils.d.ts +15 -0
  2. package/dist/assistant-stream/dist/core/tool/schema-utils.d.ts.map +1 -0
  3. package/dist/{packages/assistant-stream → assistant-stream}/dist/core/tool/schema-utils.js +9 -3
  4. package/dist/assistant-stream/dist/core/tool/schema-utils.js.map +1 -0
  5. package/dist/{packages/assistant-stream → assistant-stream}/dist/core/tool/tool-types.d.ts +27 -1
  6. package/dist/assistant-stream/dist/core/tool/tool-types.d.ts.map +1 -0
  7. package/dist/assistant-stream/dist/index.d.ts +2 -0
  8. package/dist/{node_modules → assistant-stream/dist/node_modules}/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts +4 -5
  9. package/dist/assistant-stream/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -0
  10. package/dist/assistant-stream/dist/resumable/createResumableAssistantStreamResponse.d.ts.map +1 -0
  11. package/dist/assistant-stream/dist/resumable/createResumableAssistantStreamResponse.js.map +1 -0
  12. package/dist/assistant-stream/dist/utils/json/json-value.d.ts.map +1 -0
  13. package/dist/frontendTools.d.ts +33 -6
  14. package/dist/frontendTools.d.ts.map +1 -1
  15. package/dist/frontendTools.js +3 -2
  16. package/dist/frontendTools.js.map +1 -1
  17. package/dist/generativeTools.d.ts +44 -0
  18. package/dist/generativeTools.d.ts.map +1 -0
  19. package/dist/generativeTools.js +55 -0
  20. package/dist/generativeTools.js.map +1 -0
  21. package/dist/index.d.ts +3 -2
  22. package/dist/index.js +3 -2
  23. package/dist/modelContentEnvelope.d.ts +1 -1
  24. package/dist/modelContentEnvelope.d.ts.map +1 -1
  25. package/dist/ui/resumable.d.ts +1 -1
  26. package/dist/ui/resumable.js +1 -1
  27. package/dist/ui/use-chat/AssistantChatTransport.d.ts.map +1 -1
  28. package/dist/ui/use-chat/AssistantChatTransport.js +2 -2
  29. package/dist/ui/use-chat/useAISDKRuntime.d.ts +3 -16
  30. package/dist/ui/use-chat/useAISDKRuntime.d.ts.map +1 -1
  31. package/dist/ui/use-chat/useAISDKRuntime.js +8 -3
  32. package/dist/ui/use-chat/useAISDKRuntime.js.map +1 -1
  33. package/dist/ui/use-chat/useChatRuntime.d.ts +2 -3
  34. package/dist/ui/use-chat/useChatRuntime.d.ts.map +1 -1
  35. package/dist/ui/use-chat/useChatRuntime.js +4 -3
  36. package/dist/ui/use-chat/useChatRuntime.js.map +1 -1
  37. package/dist/ui/use-chat/useExternalHistory.d.ts.map +1 -1
  38. package/dist/ui/use-chat/useExternalHistory.js +16 -8
  39. package/dist/ui/use-chat/useExternalHistory.js.map +1 -1
  40. package/dist/ui/utils/convertMessage.d.ts +3 -2
  41. package/dist/ui/utils/convertMessage.d.ts.map +1 -1
  42. package/dist/ui/utils/convertMessage.js +3 -1
  43. package/dist/ui/utils/convertMessage.js.map +1 -1
  44. package/dist/usage.d.ts.map +1 -1
  45. package/package.json +7 -7
  46. package/src/frontendTools.test.ts +24 -0
  47. package/src/frontendTools.ts +4 -6
  48. package/src/generativeTools.ts +90 -0
  49. package/src/index.ts +4 -0
  50. package/src/ui/use-chat/useAISDKRuntime.test.ts +36 -0
  51. package/src/ui/use-chat/useAISDKRuntime.ts +19 -16
  52. package/src/ui/use-chat/useChatRuntime.ts +22 -21
  53. package/src/ui/use-chat/useExternalHistory.test.ts +60 -1
  54. package/src/ui/use-chat/useExternalHistory.ts +17 -8
  55. package/src/ui/utils/convertMessage.test.ts +25 -0
  56. package/src/ui/utils/convertMessage.ts +6 -0
  57. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +0 -1
  58. package/dist/packages/assistant-stream/dist/core/tool/schema-utils.d.ts +0 -1
  59. package/dist/packages/assistant-stream/dist/core/tool/schema-utils.js.map +0 -1
  60. package/dist/packages/assistant-stream/dist/core/tool/tool-types.d.ts.map +0 -1
  61. package/dist/packages/assistant-stream/dist/index.d.ts +0 -1
  62. package/dist/packages/assistant-stream/dist/resumable/createResumableAssistantStreamResponse.d.ts.map +0 -1
  63. package/dist/packages/assistant-stream/dist/resumable/createResumableAssistantStreamResponse.js.map +0 -1
  64. package/dist/packages/assistant-stream/dist/utils/json/json-value.d.ts.map +0 -1
  65. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/AssistantStream.d.ts +0 -0
  66. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/AssistantStreamChunk.d.ts +0 -0
  67. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/accumulators/AssistantMessageStream.d.ts +0 -0
  68. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/accumulators/assistant-message-accumulator.d.ts +0 -0
  69. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/modules/assistant-stream.d.ts +0 -0
  70. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/modules/text.d.ts +0 -0
  71. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/modules/tool-call.d.ts +0 -0
  72. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/serialization/PlainText.d.ts +0 -0
  73. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/serialization/assistant-transport/AssistantTransport.d.ts +0 -0
  74. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/serialization/data-stream/DataStream.d.ts +0 -0
  75. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts +0 -0
  76. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/tool/ToolExecutionStream.d.ts +0 -0
  77. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/tool/toolResultStream.d.ts +0 -0
  78. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/utils/stream/AssistantMetaTransformStream.d.ts +0 -0
  79. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/utils/stream/AssistantTransformStream.d.ts +0 -0
  80. /package/dist/{packages/assistant-stream → assistant-stream}/dist/core/utils/types.d.ts +0 -0
  81. /package/dist/{packages/assistant-stream → assistant-stream}/dist/resumable/createResumableAssistantStreamResponse.d.ts +0 -0
  82. /package/dist/{packages/assistant-stream → assistant-stream}/dist/resumable/createResumableAssistantStreamResponse.js +0 -0
  83. /package/dist/{packages/assistant-stream → assistant-stream}/dist/resumable/index.d.ts +0 -0
  84. /package/dist/{packages/assistant-stream → assistant-stream}/dist/utils/json/json-value.d.ts +0 -0
  85. /package/dist/{packages/assistant-stream → assistant-stream}/dist/utils.d.ts +0 -0
@@ -10,10 +10,10 @@ import {
10
10
  import type { ToolExecutionStatus } from "@assistant-ui/core";
11
11
  import type {
12
12
  ExternalStoreAdapter,
13
+ ExternalStoreSharedOptions,
13
14
  ThreadHistoryAdapter,
14
15
  AssistantRuntime,
15
16
  ThreadMessage,
16
- ThreadSuggestion,
17
17
  MessageFormatAdapter,
18
18
  MessageFormatItem,
19
19
  MessageFormatRepository,
@@ -21,7 +21,10 @@ import type {
21
21
  RunConfig,
22
22
  McpAppMetadata,
23
23
  } from "@assistant-ui/core";
24
- import { getExternalStoreMessages } from "@assistant-ui/core";
24
+ import {
25
+ getExternalStoreMessages,
26
+ pickExternalStoreSharedOptions,
27
+ } from "@assistant-ui/core";
25
28
  import type { ReadonlyJSONObject } from "assistant-stream/utils";
26
29
  import { sliceMessagesUntil } from "../utils/sliceMessagesUntil";
27
30
  import { toCreateMessage } from "../utils/toCreateMessage";
@@ -55,7 +58,7 @@ const toUIMessage = <UI_MESSAGE extends UIMessage>(
55
58
  role: createMessage.role ?? fallbackRole,
56
59
  }) as UI_MESSAGE;
57
60
 
58
- export type AISDKRuntimeAdapter = {
61
+ export type AISDKRuntimeAdapter = ExternalStoreSharedOptions & {
59
62
  adapters?:
60
63
  | (NonNullable<ExternalStoreAdapter["adapters"]> & {
61
64
  history?: ThreadHistoryAdapter | undefined;
@@ -79,25 +82,18 @@ export type AISDKRuntimeAdapter = {
79
82
  * (for example, an SSE reconnect endpoint keyed by turn id).
80
83
  */
81
84
  onResume?: ExternalStoreAdapter["onResume"];
82
- /**
83
- * Follow up suggestions to surface on the thread. Use this to drive
84
- * dynamic suggestions from application state, tool results, or backend
85
- * responses; flows into `thread.suggestions` and is rendered by
86
- * components that read it (such as the shadcn `ThreadFollowupSuggestions`).
87
- */
88
- suggestions?: readonly ThreadSuggestion[] | undefined;
89
85
  };
90
86
 
91
87
  export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
92
88
  chatHelpers: ReturnType<typeof useChat<UI_MESSAGE>>,
93
- {
89
+ adapter: AISDKRuntimeAdapter = {},
90
+ ) => {
91
+ const {
94
92
  adapters,
95
93
  toCreateMessage: customToCreateMessage,
96
94
  cancelPendingToolCallsOnSend = true,
97
95
  onResume,
98
- suggestions,
99
- }: AISDKRuntimeAdapter = {},
100
- ) => {
96
+ } = adapter;
101
97
  const contextAdapters = useRuntimeAdapters();
102
98
  const [toolStatuses, setToolStatuses] = useState<
103
99
  Record<string, ToolExecutionStatus>
@@ -121,6 +117,12 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
121
117
 
122
118
  const messageTiming = useStreamingTiming(chatHelpers.messages, isRunning);
123
119
 
120
+ // Flag the streaming message optimistic: its id can be swapped for a server
121
+ // id mid-run, and the repository then drops the orphaned pre-swap id (#4037).
122
+ const lastMessage = chatHelpers.messages.at(-1);
123
+ const optimisticMessageId =
124
+ isRunning && lastMessage?.role === "assistant" ? lastMessage.id : undefined;
125
+
124
126
  const messages = AISDKMessageConverter.useThreadMessages({
125
127
  isRunning,
126
128
  messages: chatHelpers.messages,
@@ -131,9 +133,10 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
131
133
  toolArgsKeyOrderCache: toolArgsKeyOrderCacheRef.current,
132
134
  toolLastInputCache: toolLastInputCacheRef.current,
133
135
  mcpAppMetadataCache: mcpAppMetadataCacheRef.current,
136
+ ...(optimisticMessageId && { optimisticMessageId }),
134
137
  ...(chatHelpers.error && { error: chatHelpers.error.message }),
135
138
  }),
136
- [toolStatuses, messageTiming, chatHelpers.error],
139
+ [toolStatuses, messageTiming, optimisticMessageId, chatHelpers.error],
137
140
  ),
138
141
  });
139
142
 
@@ -344,8 +347,8 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
344
347
  options: { metadata: lastRunConfigRef.current },
345
348
  });
346
349
  },
350
+ ...pickExternalStoreSharedOptions(adapter),
347
351
  ...(onResume && { onResume }),
348
- ...(suggestions && { suggestions }),
349
352
  adapters: {
350
353
  attachments: vercelAttachmentAdapter,
351
354
  ...contextAdapters,
@@ -2,7 +2,11 @@
2
2
 
3
3
  import { useChat, type UIMessage } from "@ai-sdk/react";
4
4
  import type { AssistantCloud } from "assistant-cloud";
5
- import type { AssistantRuntime } from "@assistant-ui/core";
5
+ import {
6
+ pickExternalStoreSharedOptions,
7
+ type AssistantRuntime,
8
+ type ExternalStoreSharedOptions,
9
+ } from "@assistant-ui/core";
6
10
  import {
7
11
  useCloudThreadListAdapter,
8
12
  useRemoteThreadListRuntime,
@@ -19,24 +23,21 @@ import type { AssistantChatResumableOptions } from "../resumable";
19
23
  import { useEffect, useMemo, useRef } from "react";
20
24
 
21
25
  export type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =
22
- ChatInit<UI_MESSAGE> & {
23
- cloud?: AssistantCloud | undefined;
24
- adapters?: AISDKRuntimeAdapter["adapters"] | undefined;
25
- toCreateMessage?: CustomToCreateMessageFunction;
26
- onResume?: AISDKRuntimeAdapter["onResume"];
27
- suggestions?: AISDKRuntimeAdapter["suggestions"];
28
- };
26
+ ChatInit<UI_MESSAGE> &
27
+ ExternalStoreSharedOptions & {
28
+ cloud?: AssistantCloud | undefined;
29
+ adapters?: AISDKRuntimeAdapter["adapters"] | undefined;
30
+ toCreateMessage?: CustomToCreateMessageFunction;
31
+ onResume?: AISDKRuntimeAdapter["onResume"];
32
+ };
29
33
 
30
34
  const useDynamicChatTransport = <UI_MESSAGE extends UIMessage = UIMessage>(
31
35
  transport: ChatTransport<UI_MESSAGE>,
32
36
  ): ChatTransport<UI_MESSAGE> => {
33
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
34
37
  const transportRef = useRef<ChatTransport<UI_MESSAGE>>(transport);
35
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
36
38
  useEffect(() => {
37
39
  transportRef.current = transport;
38
40
  });
39
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
40
41
  const dynamicTransport = useMemo(
41
42
  () =>
42
43
  new Proxy(transportRef.current, {
@@ -72,33 +73,36 @@ const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
72
73
  adapters,
73
74
  transport: transportOptions,
74
75
  toCreateMessage,
76
+ isDisabled: _isDisabled,
77
+ isSendDisabled: _isSendDisabled,
78
+ unstable_capabilities: _unstable_capabilities,
79
+ suggestions: _suggestions,
75
80
  onResume,
76
- suggestions,
77
81
  ...chatOptions
78
82
  } = options ?? {};
83
+ // peel guard: any shared key left in `chatOptions` collapses this to `never`
84
+ true satisfies keyof typeof chatOptions &
85
+ keyof ExternalStoreSharedOptions extends never
86
+ ? true
87
+ : never;
79
88
 
80
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
81
89
  const transport = useDynamicChatTransport(
82
90
  transportOptions ?? new AssistantChatTransport(),
83
91
  );
84
92
 
85
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
86
93
  const id = useAuiState((s) => s.threadListItem.id);
87
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
88
94
  const aui = useAui();
89
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
90
95
  const chat = useChat({
91
96
  ...chatOptions,
92
97
  id,
93
98
  transport,
94
99
  });
95
100
 
96
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
97
101
  const runtime = useAISDKRuntime(chat, {
98
102
  adapters,
103
+ ...pickExternalStoreSharedOptions(options ?? {}),
99
104
  ...(toCreateMessage && { toCreateMessage }),
100
105
  ...(onResume && { onResume }),
101
- ...(suggestions && { suggestions }),
102
106
  });
103
107
 
104
108
  if (transport instanceof AssistantChatTransport) {
@@ -108,9 +112,7 @@ const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
108
112
  );
109
113
  }
110
114
 
111
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
112
115
  const resumeFiredRef = useRef(false);
113
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
114
116
  useEffect(() => {
115
117
  if (resumeFiredRef.current) return;
116
118
  const adapter = getResumableAdapter(transport);
@@ -137,7 +139,6 @@ export const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>({
137
139
  const cloudAdapter = useCloudThreadListAdapter({ cloud });
138
140
  return useRemoteThreadListRuntime({
139
141
  runtimeHook: function RuntimeHook() {
140
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
141
142
  return useChatThreadRuntime(options);
142
143
  },
143
144
  adapter: cloudAdapter,
@@ -5,6 +5,7 @@ import { describe, expect, it, vi } from "vitest";
5
5
  import type {
6
6
  AssistantRuntime,
7
7
  MessageFormatAdapter,
8
+ MessageFormatRepository,
8
9
  ThreadHistoryAdapter,
9
10
  ThreadMessage,
10
11
  } from "@assistant-ui/core";
@@ -15,7 +16,11 @@ vi.mock("@assistant-ui/store", () => ({
15
16
  }),
16
17
  }));
17
18
 
18
- import { useExternalHistory } from "./useExternalHistory";
19
+ import { MessageRepository } from "@assistant-ui/core/internal";
20
+ import {
21
+ toExportedMessageRepository,
22
+ useExternalHistory,
23
+ } from "./useExternalHistory";
19
24
 
20
25
  const noopThread = {
21
26
  subscribe: () => () => {},
@@ -105,3 +110,57 @@ describe("useExternalHistory withFormat contract", () => {
105
110
  expect(adapter.withFormat).toHaveBeenCalledWith(storageFormat);
106
111
  });
107
112
  });
113
+
114
+ describe("toExportedMessageRepository", () => {
115
+ const convert = (items: { id: string; ok: boolean }[]): ThreadMessage[] =>
116
+ items[0]!.ok ? [{ id: items[0]!.id } as ThreadMessage] : [];
117
+
118
+ it("drops a malformed row together with its now-orphaned descendants", () => {
119
+ const repo: MessageFormatRepository<{ id: string; ok: boolean }> = {
120
+ headId: "c",
121
+ messages: [
122
+ { parentId: null, message: { id: "a", ok: true } },
123
+ { parentId: "a", message: { id: "b", ok: false } },
124
+ { parentId: "b", message: { id: "c", ok: true } },
125
+ ],
126
+ };
127
+
128
+ const result = toExportedMessageRepository(convert, repo);
129
+
130
+ expect(result.messages.map((m) => m.message.id)).toEqual(["a"]);
131
+ expect(result.headId).toBeNull();
132
+ expect(() => new MessageRepository().import(result)).not.toThrow();
133
+ });
134
+
135
+ it("drops a headId that points at a filtered row", () => {
136
+ const repo: MessageFormatRepository<{ id: string; ok: boolean }> = {
137
+ headId: "b",
138
+ messages: [
139
+ { parentId: null, message: { id: "a", ok: true } },
140
+ { parentId: "a", message: { id: "b", ok: false } },
141
+ ],
142
+ };
143
+
144
+ const result = toExportedMessageRepository(convert, repo);
145
+
146
+ expect(result.messages.map((m) => m.message.id)).toEqual(["a"]);
147
+ expect(result.headId).toBeNull();
148
+ expect(() => new MessageRepository().import(result)).not.toThrow();
149
+ });
150
+
151
+ it("drops a malformed root and its entire subtree", () => {
152
+ const repo: MessageFormatRepository<{ id: string; ok: boolean }> = {
153
+ headId: "b",
154
+ messages: [
155
+ { parentId: null, message: { id: "a", ok: false } },
156
+ { parentId: "a", message: { id: "b", ok: true } },
157
+ ],
158
+ };
159
+
160
+ const result = toExportedMessageRepository(convert, repo);
161
+
162
+ expect(result.messages).toHaveLength(0);
163
+ expect(result.headId).toBeNull();
164
+ expect(() => new MessageRepository().import(result)).not.toThrow();
165
+ });
166
+ });
@@ -24,15 +24,24 @@ export const toExportedMessageRepository = <TMessage>(
24
24
  toThreadMessages: (messages: TMessage[]) => ThreadMessage[],
25
25
  messages: MessageFormatRepository<TMessage>,
26
26
  ): ExportedMessageRepository => {
27
+ const survivingIds = new Set<string>();
28
+ const survivors = messages.messages.flatMap((m) => {
29
+ const message = toThreadMessages([m.message])[0];
30
+ if (!message) {
31
+ console.warn("Skipping a stored message that could not be loaded.");
32
+ return [];
33
+ }
34
+ if (m.parentId && !survivingIds.has(m.parentId)) return [];
35
+ survivingIds.add(message.id);
36
+ return [{ ...m, message }];
37
+ });
38
+
27
39
  return {
28
- headId: messages.headId!,
29
- messages: messages.messages.map((m) => {
30
- const message = toThreadMessages([m.message])[0]!;
31
- return {
32
- ...m,
33
- message,
34
- };
35
- }),
40
+ headId:
41
+ messages.headId && survivingIds.has(messages.headId)
42
+ ? messages.headId
43
+ : null,
44
+ messages: survivors,
36
45
  };
37
46
  };
38
47
 
@@ -6,6 +6,31 @@ import {
6
6
  } from "./convertMessage";
7
7
 
8
8
  describe("AISDKMessageConverter", () => {
9
+ it("flags the streaming assistant message as optimistic", () => {
10
+ const metadata: AISDKMessageConverterMetadata = {
11
+ optimisticMessageId: "a1",
12
+ };
13
+ const converted = AISDKMessageConverter.toThreadMessages(
14
+ [
15
+ { id: "u1", role: "user", parts: [{ type: "text", text: "hi" }] },
16
+ { id: "a1", role: "assistant", parts: [{ type: "text", text: "yo" }] },
17
+ ] as any,
18
+ true,
19
+ metadata,
20
+ );
21
+
22
+ expect(converted[0]?.metadata.isOptimistic).toBeFalsy();
23
+ expect(converted[1]?.metadata.isOptimistic).toBe(true);
24
+ });
25
+
26
+ it("does not flag messages when no optimistic id is provided", () => {
27
+ const converted = AISDKMessageConverter.toThreadMessages([
28
+ { id: "a1", role: "assistant", parts: [{ type: "text", text: "yo" }] },
29
+ ] as any);
30
+
31
+ expect(converted[0]?.metadata.isOptimistic).toBeFalsy();
32
+ });
33
+
9
34
  it("converts user files into attachments and keeps text content", () => {
10
35
  const converted = AISDKMessageConverter.toThreadMessages([
11
36
  {
@@ -24,6 +24,8 @@ export type AISDKMessageConverterMetadata =
24
24
  toolArgsKeyOrderCache?: Map<string, Map<string, string[]>>;
25
25
  toolLastInputCache?: Map<string, ReadonlyJSONObject>;
26
26
  mcpAppMetadataCache?: Map<string, McpAppMetadata>;
27
+ /** Id of the currently-streaming message, flagged optimistic (#4037). */
28
+ optimisticMessageId?: string | undefined;
27
29
  };
28
30
 
29
31
  function stripClosingDelimiters(json: string): string {
@@ -393,6 +395,9 @@ export const AISDKMessageConverter = unstable_createMessageConverter(
393
395
  case "system":
394
396
  case "assistant": {
395
397
  const timing = metadata.messageTiming?.[message.id];
398
+ const isOptimistic =
399
+ message.role === "assistant" &&
400
+ message.id === metadata.optimisticMessageId;
396
401
  return {
397
402
  role: message.role,
398
403
  id: message.id,
@@ -401,6 +406,7 @@ export const AISDKMessageConverter = unstable_createMessageConverter(
401
406
  metadata: {
402
407
  ...(message.metadata as MessageMetadata),
403
408
  ...(timing && { timing }),
409
+ ...(isOptimistic && { isOptimistic: true }),
404
410
  },
405
411
  };
406
412
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","names":["JSONSchema4TypeName","JSONSchema4Type","JSONSchema4Object","JSONSchema4Array","key","Array","JSONSchema4Version","JSONSchema4","id","$ref","$schema","title","description","default","multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","additionalItems","items","maxItems","minItems","uniqueItems","maxProperties","minProperties","required","additionalProperties","definitions","k","properties","patternProperties","dependencies","enum","type","allOf","anyOf","oneOf","not","extends","format","JSONSchema6TypeName","JSONSchema6Type","JSONSchema6Object","JSONSchema6Array","JSONSchema6Version","JSONSchema6Definition","JSONSchema6","$id","contains","propertyNames","const","examples","JSONSchema7TypeName","JSONSchema7Type","JSONSchema7Object","JSONSchema7Array","JSONSchema7Version","JSONSchema7Definition","JSONSchema7","$comment","$defs","if","then","else","contentMediaType","contentEncoding","readOnly","writeOnly","ValidationResult","ValidationError","valid","errors","property","message","validate","instance","schema","checkPropertyChange","value","mustBeValid","result"],"sources":["../../../../../../../../../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;KA+iBYwD,mBAAAA;AAAAA;;;;;KAaAC,eAAAA;AAAAA,qBAINC,iBAAAA,GACAC,gBAAgB;AAAA;AAAA,UAILD,iBAAAA;EAAAA,CACZtD,GAAAA,WAAcqD,eAAe;AAAA;AAAA;AAAA;AAAA,UAKjBE,gBAAAA,SAAyBtD,KAAK,CAACoD,eAAAA;;;;;;;;;;;;KAapCG,kBAAAA;;;;;KAMAC,qBAAAA,GAAwBC,WAAW;AAAA,UAC9BA,WAAAA;EACbX,GAAAA;EACA1C,IAAAA;EACAC,OAAAA,GAAUkD,kBAAAA;EACVG,QAAAA;;;;;EAMAC,KAAAA;IAAAA,CACK5D,GAAAA,WAAcyD,qBAAAA;EAAAA;;;;EAMnBxB,IAAAA,GAAOmB,mBAAAA,GAAsBA,mBAAAA;EAC7BpB,IAAAA,GAAOqB,eAAAA;EACPH,KAAAA,GAAQG,eAAAA;;;;EAKR3C,UAAAA;EACAC,OAAAA;EACAC,gBAAAA;EACAC,OAAAA;EACAC,gBAAAA;;;;EAKAC,SAAAA;EACAC,SAAAA;EACAC,OAAAA;;;;EAKAE,KAAAA,GAAQsC,qBAAAA,GAAwBA,qBAAAA;EAChCvC,eAAAA,GAAkBuC,qBAAAA;EAClBrC,QAAAA;EACAC,QAAAA;EACAC,WAAAA;EACA0B,QAAAA,GAAWS,qBAAAA;;;;EAKXlC,aAAAA;EACAC,aAAAA;EACAC,QAAAA;EACAI,UAAAA;IAAAA,CACK7B,GAAAA,WAAcyD,qBAAAA;EAAAA;EAEnB3B,iBAAAA;IAAAA,CACK9B,GAAAA,WAAcyD,qBAAAA;EAAAA;EAEnB/B,oBAAAA,GAAuB+B,qBAAAA;EACvB1B,YAAAA;IAAAA,CACK/B,GAAAA,WAAcyD,qBAAAA;EAAAA;EAEnBR,aAAAA,GAAgBQ,qBAAAA;;;;EAKhBI,EAAAA,GAAKJ,qBAAAA;EACLK,IAAAA,GAAOL,qBAAAA;EACPM,IAAAA,GAAON,qBAAAA;;;;EAKPvB,KAAAA,GAAQuB,qBAAAA;EACRtB,KAAAA,GAAQsB,qBAAAA;EACRrB,KAAAA,GAAQqB,qBAAAA;EACRpB,GAAAA,GAAMoB,qBAAAA;;;;EAKNlB,MAAAA;;;;EAKAyB,gBAAAA;EACAC,eAAAA;;;;EAKAtC,WAAAA;IAAAA,CACK3B,GAAAA,WAAcyD,qBAAAA;EAAAA;;;;EAMnBlD,KAAAA;EACAC,WAAAA;EACAC,OAAAA,GAAU4C,eAAAA;EACVa,QAAAA;EACAC,SAAAA;EACAhB,QAAAA,GAAWE,eAAAA;AAAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema-utils.js","names":[],"sources":["../../../../../../../assistant-stream/dist/core/tool/schema-utils.js"],"sourcesContent":["//#region src/core/tool/schema-utils.ts\nfunction isStandardSchema(schema) {\n\treturn typeof schema === \"object\" && schema !== null && \"~standard\" in schema && typeof schema[\"~standard\"] === \"object\";\n}\nfunction hasToJSONSchemaMethod(schema) {\n\treturn typeof schema === \"object\" && schema !== null && \"toJSONSchema\" in schema && typeof schema.toJSONSchema === \"function\";\n}\nfunction hasToJSONMethod(schema) {\n\treturn typeof schema === \"object\" && schema !== null && \"toJSON\" in schema && typeof schema.toJSON === \"function\";\n}\n/**\n* Converts a schema to JSONSchema7.\n* Supports:\n* - StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n* - StandardSchemaV1 with ~standard.jsonSchema.input() (e.g., Zod v4)\n* - Objects with toJSONSchema() method (e.g., Zod v4)\n* - Objects with toJSON() method\n* - Plain JSONSchema7 objects (must have a \"type\" property)\n*/\nfunction toJSONSchema(schema) {\n\tif (isStandardSchema(schema)) {\n\t\tconst toJSONSchemaMethod = schema[\"~standard\"].toJSONSchema;\n\t\tif (typeof toJSONSchemaMethod === \"function\") return toJSONSchemaMethod();\n\t\tconst jsonSchema = schema[\"~standard\"].jsonSchema;\n\t\tif (typeof jsonSchema === \"object\" && jsonSchema !== null && typeof jsonSchema.input === \"function\") return jsonSchema.input();\n\t}\n\tif (hasToJSONSchemaMethod(schema)) return schema.toJSONSchema();\n\tif (hasToJSONMethod(schema)) return schema.toJSON();\n\tif (isStandardSchema(schema)) throw new Error(\"Could not convert schema to JSON Schema. The schema implements Standard Schema but does not support JSON Schema conversion. If you are using Zod, please upgrade to Zod v4 (npm install zod@latest). Alternatively, pass a plain JSON Schema object instead.\");\n\treturn schema;\n}\n/**\n* Returns a copy of the JSON Schema with `required` removed recursively,\n* making every property optional. Array item schemas are left unchanged.\n*/\nfunction toPartialJSONSchema(schema) {\n\tconst { required: _, ...result } = schema;\n\tif (result.properties) result.properties = Object.fromEntries(Object.entries(result.properties).map(([key, prop]) => {\n\t\tif (typeof prop === \"object\" && prop !== null && !Array.isArray(prop)) {\n\t\t\tconst p = prop;\n\t\t\treturn [key, p.properties != null ? toPartialJSONSchema(p) : prop];\n\t\t}\n\t\treturn [key, prop];\n\t}));\n\treturn result;\n}\nfunction defaultToolFilter(_name, tool) {\n\treturn !tool.disabled && tool.type !== \"backend\";\n}\n/**\n* Converts a record of tools to a record of tool definitions with JSON Schema parameters.\n* By default, filters out disabled tools and backend tools.\n*/\nfunction toToolsJSONSchema(tools, options = {}) {\n\tif (!tools) return {};\n\tconst filter = options.filter ?? defaultToolFilter;\n\treturn Object.fromEntries(Object.entries(tools).filter(([name, tool]) => filter(name, tool) && tool.parameters).map(([name, tool]) => [name, {\n\t\t...tool.description && { description: tool.description },\n\t\tparameters: toJSONSchema(tool.parameters)\n\t}]));\n}\n//#endregion\nexport { toJSONSchema, toPartialJSONSchema, toToolsJSONSchema };\n\n//# sourceMappingURL=schema-utils.js.map"],"mappings":";AACA,SAAS,iBAAiB,QAAQ;CACjC,OAAO,OAAO,WAAW,YAAY,WAAW,QAAQ,eAAe,UAAU,OAAO,OAAO,iBAAiB;AACjH;AACA,SAAS,sBAAsB,QAAQ;CACtC,OAAO,OAAO,WAAW,YAAY,WAAW,QAAQ,kBAAkB,UAAU,OAAO,OAAO,iBAAiB;AACpH;AACA,SAAS,gBAAgB,QAAQ;CAChC,OAAO,OAAO,WAAW,YAAY,WAAW,QAAQ,YAAY,UAAU,OAAO,OAAO,WAAW;AACxG;;;;;;;;;;AAUA,SAAS,aAAa,QAAQ;CAC7B,IAAI,iBAAiB,MAAM,GAAG;EAC7B,MAAM,qBAAqB,OAAO,aAAa;EAC/C,IAAI,OAAO,uBAAuB,YAAY,OAAO,mBAAmB;EACxE,MAAM,aAAa,OAAO,aAAa;EACvC,IAAI,OAAO,eAAe,YAAY,eAAe,QAAQ,OAAO,WAAW,UAAU,YAAY,OAAO,WAAW,MAAM;CAC9H;CACA,IAAI,sBAAsB,MAAM,GAAG,OAAO,OAAO,aAAa;CAC9D,IAAI,gBAAgB,MAAM,GAAG,OAAO,OAAO,OAAO;CAClD,IAAI,iBAAiB,MAAM,GAAG,MAAM,IAAI,MAAM,8PAA8P;CAC5S,OAAO;AACR;AAgBA,SAAS,kBAAkB,OAAO,MAAM;CACvC,OAAO,CAAC,KAAK,YAAY,KAAK,SAAS;AACxC;;;;;AAKA,SAAS,kBAAkB,OAAO,UAAU,CAAC,GAAG;CAC/C,IAAI,CAAC,OAAO,OAAO,CAAC;CACpB,MAAM,SAAS,QAAQ,UAAU;CACjC,OAAO,OAAO,YAAY,OAAO,QAAQ,KAAK,EAAE,QAAQ,CAAC,MAAM,UAAU,OAAO,MAAM,IAAI,KAAK,KAAK,UAAU,EAAE,KAAK,CAAC,MAAM,UAAU,CAAC,MAAM;EAC5I,GAAG,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;EACvD,YAAY,aAAa,KAAK,UAAU;CACzC,CAAC,CAAC,CAAC;AACJ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-types.d.ts","names":["AsyncIterableStream","JSONSchema7","DeepPartial","TypeAtPath","TypePath","ToolResponse","StandardSchemaV1","ToolModelContentPart","type","text","data","mediaType","filename","ToolModelOutputFunction","TArgs","TResult","Promise","toolCallId","input","output","options","ToolCallArgsReader","PathT","Record","U","Array","get","fieldPath","streamValues","streamText","forEach","ToolCallResponseReader","ToolCallReader","args","response","result","ToolExecutionContext","AbortSignal","abortSignal","human","payload","ToolExecuteFunction","context","ToolStreamCallFunction","reader","OnSchemaValidationErrorFunction","ToolBase","streamCall","BackendTool","description","parameters","disabled","execute","toModelOutput","experimental_onSchemaValidationError","FrontendTool","HumanTool","Tool","ToolWithoutType","Omit"],"sources":["../../../../../../../assistant-stream/dist/core/tool/tool-types.d.ts"],"mappings":";;KAOKO,oBAAAA;EAEME,4EADmED,IAAAA,UACnEC;EAAAA,SAAAA,IAAAA;AAAAA;EAQAE,4EANmEH,IAAAA;EAOnEI;;AAAQ;;EAARA,SAFAF,IAAAA;WACAC,SAAAA;WACAC,QAAAA;AAAAA"}
@@ -1 +0,0 @@
1
- import { ToolModelContentPart } from "./core/tool/tool-types.js";
@@ -1 +0,0 @@
1
- {"version":3,"file":"createResumableAssistantStreamResponse.d.ts","names":["AssistantStreamController","AssistantStreamEncoder","ResumableStreamContext","RESUMABLE_STREAM_ID_HEADER","CreateResumableAssistantStreamResponseOptions","PromiseLike","HeadersInit","context","streamId","callback","controller","encoder","headers","createResumableAssistantStreamResponse","Response","Promise","options","CreateResumeAssistantStreamResponseOptions","missingResponse","createResumeAssistantStreamResponse"],"sources":["../../../../../../assistant-stream/dist/resumable/createResumableAssistantStreamResponse.d.ts"],"mappings":";;cAKcG,0BAAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"createResumableAssistantStreamResponse.js","names":[],"sources":["../../../../../../assistant-stream/dist/resumable/createResumableAssistantStreamResponse.js"],"sourcesContent":["import { DataStreamEncoder } from \"../core/serialization/data-stream/DataStream.js\";\nimport { createAssistantStream } from \"../core/modules/assistant-stream.js\";\n//#region src/resumable/createResumableAssistantStreamResponse.ts\nconst RESUMABLE_STREAM_ID_HEADER = \"x-resumable-stream-id\";\nasync function createResumableAssistantStreamResponse(options) {\n\tconst encoder = (options.encoder ?? (() => new DataStreamEncoder()))();\n\tconst stream = await options.context.run(options.streamId, () => {\n\t\treturn createAssistantStream(options.callback).pipeThrough(encoder);\n\t});\n\treturn new Response(stream, { headers: mergeHeaders(encoder.headers, options.headers, options.streamId) });\n}\nasync function createResumeAssistantStreamResponse(options) {\n\tconst stream = await options.context.resume(options.streamId);\n\tif (!stream) return options.missingResponse?.() ?? defaultMissingResponse();\n\tconst encoder = (options.encoder ?? (() => new DataStreamEncoder()))();\n\treturn new Response(stream, { headers: mergeHeaders(encoder.headers, options.headers, options.streamId) });\n}\nfunction defaultMissingResponse() {\n\treturn new Response(JSON.stringify({ error: \"stream not found\" }), {\n\t\tstatus: 404,\n\t\theaders: { \"Content-Type\": \"application/json\" }\n\t});\n}\nfunction mergeHeaders(encoderHeaders, extra, streamId) {\n\tconst merged = new Headers(encoderHeaders ?? {});\n\tif (extra) for (const [key, value] of new Headers(extra)) merged.set(key, value);\n\tmerged.set(RESUMABLE_STREAM_ID_HEADER, streamId);\n\treturn merged;\n}\n//#endregion\nexport { RESUMABLE_STREAM_ID_HEADER, createResumableAssistantStreamResponse, createResumeAssistantStreamResponse };\n\n//# sourceMappingURL=createResumableAssistantStreamResponse.js.map"],"mappings":";AAGA,MAAM,6BAA6B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"json-value.d.ts","names":["ReadonlyJSONValue","ReadonlyJSONObject","ReadonlyJSONArray","key"],"sources":["../../../../../../../assistant-stream/dist/utils/json/json-value.d.ts"],"mappings":";;KACKA,iBAAAA,sCAAuDC,kBAAAA,GAAqBC,iBAAiB;AAAA,KAC7FD,kBAAAA;EAAAA,UACOE,GAAAA,WAAcH,iBAAiB;AAAA;AAAA,KAEtCE,iBAAAA,YAA6BF,iBAAiB"}