@assistant-ui/react-ai-sdk 1.3.28 → 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 -3
  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 +22 -35
  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 -1
  50. package/src/ui/use-chat/useAISDKRuntime.test.ts +36 -0
  51. package/src/ui/use-chat/useAISDKRuntime.ts +41 -52
  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 +34 -6
  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
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="@assistant-ui/core/store" />
2
1
  /// <reference types="@assistant-ui/core/react" />
3
2
 
4
3
  export { useAISDKRuntime } from "./ui/use-chat/useAISDKRuntime";
@@ -14,6 +13,10 @@ export type {
14
13
  ResumableClientStorage,
15
14
  } from "./ui/resumable";
16
15
  export { frontendTools } from "./frontendTools";
16
+ export {
17
+ generativeTools,
18
+ type GenerativeToolsOptions,
19
+ } from "./generativeTools";
17
20
  export { injectQuoteContext } from "./injectQuoteContext";
18
21
  export type { ThreadTokenUsage, TokenUsageExtractableMessage } from "./usage";
19
22
  export { getThreadMessageTokenUsage, useThreadTokenUsage } from "./usage";
@@ -354,4 +354,40 @@ describe("useAISDKRuntime", () => {
354
354
  metadata: { custom: { maxTokens: 100 } },
355
355
  });
356
356
  });
357
+
358
+ it("forwards isDisabled to thread state", () => {
359
+ const chat = createChatHelpers();
360
+ const { result } = renderHook(() =>
361
+ useAISDKRuntime(chat, { isDisabled: true }),
362
+ );
363
+ expect(result.current.thread.getState().isDisabled).toBe(true);
364
+ });
365
+
366
+ it("forwards isSendDisabled to the composer canSend gate", () => {
367
+ const chat = createChatHelpers();
368
+ const { result } = renderHook(() =>
369
+ useAISDKRuntime(chat, { isSendDisabled: true }),
370
+ );
371
+ act(() => {
372
+ result.current.thread.composer.setText("hello");
373
+ });
374
+ expect(result.current.thread.composer.getState().canSend).toBe(false);
375
+ });
376
+
377
+ it("forwards unstable_capabilities to thread capabilities", () => {
378
+ const chat = createChatHelpers();
379
+ const { result } = renderHook(() =>
380
+ useAISDKRuntime(chat, { unstable_capabilities: { copy: false } }),
381
+ );
382
+ expect(result.current.thread.getState().capabilities.unstable_copy).toBe(
383
+ false,
384
+ );
385
+ });
386
+
387
+ it("forwards suggestions to thread state", () => {
388
+ const chat = createChatHelpers();
389
+ const suggestions = [{ prompt: "tell me a joke" }];
390
+ const { result } = renderHook(() => useAISDKRuntime(chat, { suggestions }));
391
+ expect(result.current.thread.getState().suggestions).toEqual(suggestions);
392
+ });
357
393
  });
@@ -1,20 +1,19 @@
1
1
  "use client";
2
2
 
3
- import { useState, useMemo, useRef } from "react";
3
+ import { useMemo, useRef, useState } from "react";
4
4
  import type { UIMessage, useChat, CreateUIMessage } from "@ai-sdk/react";
5
5
  import { isToolUIPart, generateId } from "ai";
6
6
  import {
7
7
  useExternalStoreRuntime,
8
8
  useRuntimeAdapters,
9
- useToolInvocations,
10
- type ToolExecutionStatus,
11
9
  } from "@assistant-ui/core/react";
10
+ import type { ToolExecutionStatus } from "@assistant-ui/core";
12
11
  import type {
13
12
  ExternalStoreAdapter,
13
+ ExternalStoreSharedOptions,
14
14
  ThreadHistoryAdapter,
15
15
  AssistantRuntime,
16
16
  ThreadMessage,
17
- ThreadSuggestion,
18
17
  MessageFormatAdapter,
19
18
  MessageFormatItem,
20
19
  MessageFormatRepository,
@@ -22,7 +21,10 @@ import type {
22
21
  RunConfig,
23
22
  McpAppMetadata,
24
23
  } from "@assistant-ui/core";
25
- import { getExternalStoreMessages } from "@assistant-ui/core";
24
+ import {
25
+ getExternalStoreMessages,
26
+ pickExternalStoreSharedOptions,
27
+ } from "@assistant-ui/core";
26
28
  import type { ReadonlyJSONObject } from "assistant-stream/utils";
27
29
  import { sliceMessagesUntil } from "../utils/sliceMessagesUntil";
28
30
  import { toCreateMessage } from "../utils/toCreateMessage";
@@ -56,7 +58,7 @@ const toUIMessage = <UI_MESSAGE extends UIMessage>(
56
58
  role: createMessage.role ?? fallbackRole,
57
59
  }) as UI_MESSAGE;
58
60
 
59
- export type AISDKRuntimeAdapter = {
61
+ export type AISDKRuntimeAdapter = ExternalStoreSharedOptions & {
60
62
  adapters?:
61
63
  | (NonNullable<ExternalStoreAdapter["adapters"]> & {
62
64
  history?: ThreadHistoryAdapter | undefined;
@@ -80,25 +82,18 @@ export type AISDKRuntimeAdapter = {
80
82
  * (for example, an SSE reconnect endpoint keyed by turn id).
81
83
  */
82
84
  onResume?: ExternalStoreAdapter["onResume"];
83
- /**
84
- * Follow up suggestions to surface on the thread. Use this to drive
85
- * dynamic suggestions from application state, tool results, or backend
86
- * responses; flows into `thread.suggestions` and is rendered by
87
- * components that read it (such as the shadcn `ThreadFollowupSuggestions`).
88
- */
89
- suggestions?: readonly ThreadSuggestion[] | undefined;
90
85
  };
91
86
 
92
87
  export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
93
88
  chatHelpers: ReturnType<typeof useChat<UI_MESSAGE>>,
94
- {
89
+ adapter: AISDKRuntimeAdapter = {},
90
+ ) => {
91
+ const {
95
92
  adapters,
96
93
  toCreateMessage: customToCreateMessage,
97
94
  cancelPendingToolCallsOnSend = true,
98
95
  onResume,
99
- suggestions,
100
- }: AISDKRuntimeAdapter = {},
101
- ) => {
96
+ } = adapter;
102
97
  const contextAdapters = useRuntimeAdapters();
103
98
  const [toolStatuses, setToolStatuses] = useState<
104
99
  Record<string, ToolExecutionStatus>
@@ -122,6 +117,12 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
122
117
 
123
118
  const messageTiming = useStreamingTiming(chatHelpers.messages, isRunning);
124
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
+
125
126
  const messages = AISDKMessageConverter.useThreadMessages({
126
127
  isRunning,
127
128
  messages: chatHelpers.messages,
@@ -132,9 +133,10 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
132
133
  toolArgsKeyOrderCache: toolArgsKeyOrderCacheRef.current,
133
134
  toolLastInputCache: toolLastInputCacheRef.current,
134
135
  mcpAppMetadataCache: mcpAppMetadataCacheRef.current,
136
+ ...(optimisticMessageId && { optimisticMessageId }),
135
137
  ...(chatHelpers.error && { error: chatHelpers.error.message }),
136
138
  }),
137
- [toolStatuses, messageTiming, chatHelpers.error],
139
+ [toolStatuses, messageTiming, optimisticMessageId, chatHelpers.error],
138
140
  ),
139
141
  });
140
142
 
@@ -144,29 +146,6 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
144
146
  },
145
147
  }));
146
148
 
147
- const toolInvocations = useToolInvocations({
148
- state: {
149
- messages,
150
- isRunning,
151
- },
152
- getTools: () => runtimeRef.current.thread.getModelContext().tools,
153
- onResult: (command) => {
154
- if (command.type === "add-tool-result") {
155
- const output =
156
- command.modelContent !== undefined
157
- ? wrapModelContentEnvelope(command.result, command.modelContent)
158
- : command.result;
159
- chatHelpers.addToolResult({
160
- tool: command.toolName,
161
- toolCallId: command.toolCallId,
162
- output,
163
- options: { metadata: lastRunConfigRef.current },
164
- });
165
- }
166
- },
167
- setToolStatuses,
168
- });
169
-
170
149
  const isLoading = useExternalHistory(
171
150
  runtimeRef,
172
151
  adapters?.history ?? contextAdapters?.history,
@@ -185,7 +164,9 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
185
164
  const completePendingToolCalls = async () => {
186
165
  if (!cancelPendingToolCallsOnSend) return;
187
166
 
188
- await toolInvocations.abort();
167
+ // The runtime auto-aborts in-flight tool invocations when a new run
168
+ // is dispatched (append() / startRun()). All we need to do here is
169
+ // mark any tool without a result as cancelled in the UI message list.
189
170
 
190
171
  // Mark any tool without a result as cancelled (uses setMessages to avoid triggering sendAutomaticallyWhen)
191
172
  chatHelpers.setMessages((messages) => {
@@ -214,6 +195,8 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
214
195
  const runtime = useExternalStoreRuntime({
215
196
  isRunning,
216
197
  messages,
198
+ unstable_enableToolInvocations: true,
199
+ setToolStatuses,
217
200
  setMessages: (messages) =>
218
201
  chatHelpers.setMessages(
219
202
  messages
@@ -278,7 +261,6 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
278
261
  },
279
262
  onCancel: async () => {
280
263
  chatHelpers.stop();
281
- await toolInvocations.abort();
282
264
  },
283
265
  onNew: async (message) => {
284
266
  const createMessage = (
@@ -327,29 +309,36 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
327
309
 
328
310
  await chatHelpers.regenerate({ metadata: config.runConfig });
329
311
  },
330
- onAddToolResult: ({ toolCallId, result, isError }) => {
312
+ onAddToolResult: ({
313
+ toolCallId,
314
+ toolName,
315
+ result,
316
+ isError,
317
+ modelContent,
318
+ }) => {
331
319
  const options = { metadata: lastRunConfigRef.current };
332
320
  if (isError) {
333
321
  chatHelpers.addToolOutput({
334
322
  state: "output-error",
335
- tool: toolCallId,
323
+ tool: toolName ?? toolCallId,
336
324
  toolCallId,
337
325
  errorText:
338
326
  typeof result === "string" ? result : JSON.stringify(result),
339
327
  options,
340
328
  });
341
329
  } else {
342
- chatHelpers.addToolOutput({
343
- state: "output-available",
344
- tool: toolCallId,
330
+ const output =
331
+ modelContent !== undefined
332
+ ? wrapModelContentEnvelope(result, modelContent)
333
+ : result;
334
+ chatHelpers.addToolResult({
335
+ tool: toolName,
345
336
  toolCallId,
346
- output: result,
337
+ output,
347
338
  options,
348
339
  });
349
340
  }
350
341
  },
351
- onResumeToolCall: (options) =>
352
- toolInvocations.resume(options.toolCallId, options.payload),
353
342
  onRespondToToolApproval: ({ approvalId, approved, reason }) => {
354
343
  void chatHelpers.addToolApprovalResponse({
355
344
  id: approvalId,
@@ -358,8 +347,8 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
358
347
  options: { metadata: lastRunConfigRef.current },
359
348
  });
360
349
  },
350
+ ...pickExternalStoreSharedOptions(adapter),
361
351
  ...(onResume && { onResume }),
362
- ...(suggestions && { suggestions }),
363
352
  adapters: {
364
353
  attachments: vercelAttachmentAdapter,
365
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
 
@@ -1,8 +1,36 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import type { ReadonlyJSONObject } from "assistant-stream/utils";
3
- import { AISDKMessageConverter } from "./convertMessage";
3
+ import {
4
+ AISDKMessageConverter,
5
+ type AISDKMessageConverterMetadata,
6
+ } from "./convertMessage";
4
7
 
5
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
+
6
34
  it("converts user files into attachments and keeps text content", () => {
7
35
  const converted = AISDKMessageConverter.toThreadMessages([
8
36
  {
@@ -338,7 +366,7 @@ describe("AISDKMessageConverter", () => {
338
366
  });
339
367
 
340
368
  it("keeps observed key order from streaming snapshots for final tool args", () => {
341
- const metadata = {
369
+ const metadata: AISDKMessageConverterMetadata = {
342
370
  toolArgsKeyOrderCache: new Map<string, Map<string, string[]>>(),
343
371
  };
344
372
 
@@ -410,7 +438,7 @@ describe("AISDKMessageConverter", () => {
410
438
  });
411
439
 
412
440
  it("merges duplicate toolCallId across assistant snapshots", () => {
413
- const metadata = {
441
+ const metadata: AISDKMessageConverterMetadata = {
414
442
  toolArgsKeyOrderCache: new Map<string, Map<string, string[]>>(),
415
443
  };
416
444
 
@@ -463,7 +491,7 @@ describe("AISDKMessageConverter", () => {
463
491
  });
464
492
 
465
493
  it("preserves last good input when AI SDK briefly emits null input", () => {
466
- const metadata = {
494
+ const metadata: AISDKMessageConverterMetadata = {
467
495
  toolArgsKeyOrderCache: new Map<string, Map<string, string[]>>(),
468
496
  toolLastInputCache: new Map<string, ReadonlyJSONObject>(),
469
497
  };
@@ -506,7 +534,7 @@ describe("AISDKMessageConverter", () => {
506
534
  });
507
535
 
508
536
  it("preserves last good input across terminal state transitions", () => {
509
- const metadata = {
537
+ const metadata: AISDKMessageConverterMetadata = {
510
538
  toolArgsKeyOrderCache: new Map<string, Map<string, string[]>>(),
511
539
  toolLastInputCache: new Map<string, ReadonlyJSONObject>(),
512
540
  };
@@ -690,7 +718,7 @@ describe("AISDKMessageConverter", () => {
690
718
  });
691
719
 
692
720
  it("memoizes MCP app metadata across conversions by resourceUri", () => {
693
- const metadata = {
721
+ const metadata: AISDKMessageConverterMetadata = {
694
722
  mcpAppMetadataCache: new Map(),
695
723
  };
696
724
 
@@ -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"}