@assistant-ui/react 0.15.14 → 0.15.15

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 (160) hide show
  1. package/dist/assistant-transport.d.ts +32 -0
  2. package/dist/assistant-transport.d.ts.map +1 -0
  3. package/dist/assistant-transport.js +27 -0
  4. package/dist/assistant-transport.js.map +1 -0
  5. package/dist/context/providers/MessageProvider.js +1 -1
  6. package/dist/hooks/useMessageQuote.js +1 -1
  7. package/dist/hooks/useToolCallElapsed.js +1 -1
  8. package/dist/index.d.ts +4 -7
  9. package/dist/index.js +6 -8
  10. package/dist/legacy-runtime/AssistantRuntimeProvider.js +1 -1
  11. package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -1
  12. package/dist/mcp-apps/McpAppsRemoteHost.js +18 -2
  13. package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
  14. package/dist/mcp-apps/bridge.js +39 -24
  15. package/dist/mcp-apps/bridge.js.map +1 -1
  16. package/dist/model-context/makeAssistantVisible.js +1 -1
  17. package/dist/primitives/actionBar/ActionBarCopy.js +1 -1
  18. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +1 -1
  19. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +1 -1
  20. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +1 -1
  21. package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -1
  22. package/dist/primitives/actionBar/ActionBarStopSpeaking.js +24 -37
  23. package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
  24. package/dist/primitives/actionBar/useActionBarFloatStatus.js +1 -1
  25. package/dist/primitives/assistantModal/AssistantModalRoot.js +1 -1
  26. package/dist/primitives/attachment/AttachmentName.js +1 -1
  27. package/dist/primitives/attachment/AttachmentRemove.js +1 -1
  28. package/dist/primitives/attachment/AttachmentThumb.js +1 -1
  29. package/dist/primitives/branchPicker/BranchPickerCount.js +1 -1
  30. package/dist/primitives/branchPicker/BranchPickerNumber.js +1 -1
  31. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +1 -1
  32. package/dist/primitives/composer/ComposerAddAttachment.js +1 -1
  33. package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
  34. package/dist/primitives/composer/ComposerDictationTranscript.js +1 -1
  35. package/dist/primitives/composer/ComposerInput.js +2 -2
  36. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  37. package/dist/primitives/composer/ComposerQuote.js +1 -1
  38. package/dist/primitives/composer/ComposerRoot.js +1 -1
  39. package/dist/primitives/composer/ComposerStopDictation.js +1 -1
  40. package/dist/primitives/composer/trigger/TriggerPopover.js +1 -1
  41. package/dist/primitives/composer/useComposerInputState.js +1 -1
  42. package/dist/primitives/message/MessageIf.js +1 -1
  43. package/dist/primitives/message/MessagePartsGrouped.js +1 -1
  44. package/dist/primitives/message/MessageRoot.js +1 -1
  45. package/dist/primitives/queueItem/QueueItemRemove.js +1 -1
  46. package/dist/primitives/queueItem/QueueItemSteer.js +1 -1
  47. package/dist/primitives/queueItem/QueueItemText.js +1 -1
  48. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
  49. package/dist/primitives/suggestion/SuggestionDescription.js +1 -1
  50. package/dist/primitives/suggestion/SuggestionTitle.js +1 -1
  51. package/dist/primitives/suggestion/SuggestionTrigger.js +1 -1
  52. package/dist/primitives/thread/ThreadIf.js +1 -1
  53. package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
  54. package/dist/primitives/thread/ThreadRoot.js +41 -9
  55. package/dist/primitives/thread/ThreadRoot.js.map +1 -1
  56. package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
  57. package/dist/primitives/thread/ThreadViewport.js +2 -13
  58. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  59. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
  60. package/dist/primitives/thread/useThreadViewportAutoScroll.js +23 -8
  61. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  62. package/dist/primitives/threadList/ThreadListNew.js +1 -1
  63. package/dist/sandbox-host/SandboxHost.js +2 -0
  64. package/dist/sandbox-host/SandboxHost.js.map +1 -1
  65. package/dist/unstable/useComposerInput.d.ts +2 -2
  66. package/dist/unstable/useComposerInput.js +1 -1
  67. package/dist/unstable/useComposerInput.js.map +1 -1
  68. package/dist/unstable/useComposerInputHistory.js +1 -1
  69. package/dist/unstable/useComposerInputHistory.js.map +1 -1
  70. package/dist/unstable/useLiveCompletionAdapter.d.ts +5 -0
  71. package/dist/unstable/useLiveCompletionAdapter.d.ts.map +1 -1
  72. package/dist/unstable/useLiveCompletionAdapter.js +16 -3
  73. package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
  74. package/dist/unstable/useMentionAdapter.d.ts +1 -1
  75. package/dist/utils/smooth/SmoothContext.js +1 -1
  76. package/dist/utils/useToolArgsFieldStatus.js +1 -1
  77. package/package.json +9 -9
  78. package/src/assistant-transport.ts +94 -0
  79. package/src/index.ts +8 -4
  80. package/src/mcp-apps/McpAppsRemoteHost.test.ts +29 -3
  81. package/src/mcp-apps/McpAppsRemoteHost.ts +42 -7
  82. package/src/mcp-apps/bridge.test.ts +132 -0
  83. package/src/mcp-apps/bridge.ts +54 -30
  84. package/src/primitives/actionBar/ActionBarStopSpeaking.tsx +0 -9
  85. package/src/primitives/composer/ComposerInput.tsx +1 -1
  86. package/src/primitives/thread/ThreadRoot.test.tsx +251 -0
  87. package/src/primitives/thread/ThreadRoot.tsx +30 -0
  88. package/src/primitives/thread/ThreadViewport.tsx +1 -8
  89. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +152 -0
  90. package/src/primitives/thread/useThreadViewportAutoScroll.ts +26 -19
  91. package/src/sandbox-host/SandboxHost.test.tsx +29 -0
  92. package/src/sandbox-host/SandboxHost.tsx +2 -0
  93. package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +24 -3
  94. package/src/tests/RemoteThreadListRuntime.reloadMainThread.test.tsx +1 -2
  95. package/src/tests/augmentations.test.ts +49 -0
  96. package/src/tests/in-memory-thread-list.test.tsx +1 -1
  97. package/src/tests/threadMessageById.test.tsx +61 -1
  98. package/src/unstable/useComposerInput.ts +2 -2
  99. package/src/unstable/useComposerInputHistory.test.tsx +4 -4
  100. package/src/unstable/useComposerInputHistory.ts +1 -1
  101. package/src/unstable/useLiveCompletionAdapter.test.tsx +100 -0
  102. package/src/unstable/useLiveCompletionAdapter.ts +41 -17
  103. package/dist/client/InMemoryThreadList.d.ts +0 -12
  104. package/dist/client/InMemoryThreadList.d.ts.map +0 -1
  105. package/dist/client/InMemoryThreadList.js +0 -374
  106. package/dist/client/InMemoryThreadList.js.map +0 -1
  107. package/dist/legacy-runtime/cloud/auiV0.d.ts +0 -119
  108. package/dist/legacy-runtime/cloud/auiV0.d.ts.map +0 -1
  109. package/dist/legacy-runtime/cloud/auiV0.js +0 -137
  110. package/dist/legacy-runtime/cloud/auiV0.js.map +0 -1
  111. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts +0 -16
  112. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts.map +0 -1
  113. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js +0 -36
  114. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js.map +0 -1
  115. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +0 -17
  116. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +0 -1
  117. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +0 -55
  118. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +0 -1
  119. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.d.ts +0 -11
  120. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.d.ts.map +0 -1
  121. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js +0 -165
  122. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js.map +0 -1
  123. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts +0 -15
  124. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +0 -1
  125. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +0 -81
  126. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +0 -1
  127. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +0 -126
  128. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +0 -1
  129. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.js +0 -0
  130. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts +0 -14
  131. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +0 -1
  132. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +0 -290
  133. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +0 -1
  134. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +0 -7
  135. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts.map +0 -1
  136. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +0 -25
  137. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +0 -1
  138. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.d.ts +0 -7
  139. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.d.ts.map +0 -1
  140. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js +0 -27
  141. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js.map +0 -1
  142. package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.d.ts +0 -2
  143. package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.js +0 -2
  144. package/src/client/InMemoryThreadList.ts +0 -247
  145. package/src/legacy-runtime/cloud/auiV0.ts +0 -331
  146. package/src/legacy-runtime/cloud/useCloudThreadListRuntime.ts +0 -31
  147. package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +0 -68
  148. package/src/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.test.ts +0 -455
  149. package/src/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.ts +0 -172
  150. package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +0 -113
  151. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-delivery.test.tsx +0 -79
  152. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-parentid.test.tsx +0 -97
  153. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +0 -307
  154. package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +0 -170
  155. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +0 -141
  156. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +0 -585
  157. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +0 -476
  158. package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +0 -20
  159. package/src/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.ts +0 -9
  160. package/src/legacy-runtime/runtime-cores/assistant-transport/utils.ts +0 -1
@@ -1,476 +0,0 @@
1
- "use client";
2
-
3
- import type { AppendMessage } from "@assistant-ui/core";
4
- import {
5
- type ReadonlyJSONObject,
6
- type ReadonlyJSONValue,
7
- asAsyncIterableStream,
8
- } from "assistant-stream/utils";
9
- import { useExternalStoreRuntime } from "../external-store/useExternalStoreRuntime";
10
- import type { AssistantRuntime } from "../../runtime/AssistantRuntime";
11
- import type { AddToolResultOptions } from "@assistant-ui/core";
12
- import { useMemo, useRef, useState } from "react";
13
- import {
14
- AssistantMessageAccumulator,
15
- DataStreamDecoder,
16
- AssistantTransportDecoder,
17
- unstable_createInitialMessage as createInitialMessage,
18
- toToolsJSONSchema,
19
- } from "assistant-stream";
20
- import type {
21
- AssistantTransportOptions,
22
- AddMessageCommand,
23
- AddToolResultCommand,
24
- UserMessagePart,
25
- QueuedCommand,
26
- AssistantTransportCommand,
27
- SendCommandsRequestBody,
28
- } from "./types";
29
- import { useCommandQueue } from "./commandQueue";
30
- import {
31
- createReplayBoundaryStream,
32
- useReplayRenderWait,
33
- } from "./replayBoundaryStream";
34
- import { useRunManager } from "./runManager";
35
- import { useConvertedState } from "./useConvertedState";
36
- import type { ToolExecutionStatus } from "@assistant-ui/core";
37
- import { createRequestHeaders } from "@assistant-ui/core";
38
- import { useRemoteThreadListRuntime } from "../remote-thread-list/useRemoteThreadListRuntime";
39
- import { InMemoryThreadListAdapter } from "@assistant-ui/core";
40
- import { useAui, useAuiState } from "@assistant-ui/store";
41
- import type { UserExternalState } from "../../../augmentations";
42
-
43
- const convertAppendMessageToCommand = (
44
- message: AppendMessage,
45
- ): AddMessageCommand | null => {
46
- if (message.role !== "user")
47
- throw new Error("Only user messages are supported");
48
-
49
- const parts: UserMessagePart[] = [];
50
- const content = [
51
- ...message.content,
52
- ...(message.attachments?.flatMap((a) => a.content) ?? []),
53
- ];
54
- for (const contentPart of content) {
55
- if (contentPart.type === "text") {
56
- parts.push({ type: "text", text: contentPart.text });
57
- } else if (contentPart.type === "image") {
58
- parts.push({ type: "image", image: contentPart.image });
59
- }
60
- }
61
-
62
- if (parts.length === 0) return null;
63
-
64
- return {
65
- type: "add-message",
66
- message: {
67
- role: "user",
68
- parts,
69
- },
70
- parentId: message.parentId,
71
- sourceId: message.sourceId,
72
- };
73
- };
74
-
75
- const readResumeState = async <T>(
76
- response: Response,
77
- ): Promise<{ runId: string; state: T } | null> => {
78
- if (response.status === 204) return null;
79
- if (!response.ok) {
80
- throw new Error(
81
- `Resume state request failed with status ${response.status}: ${await response.text()}`,
82
- );
83
- }
84
-
85
- let value: unknown;
86
- try {
87
- value = await response.json();
88
- } catch {
89
- throw new Error("Resume state response was not valid JSON");
90
- }
91
-
92
- if (
93
- typeof value !== "object" ||
94
- value === null ||
95
- !("state" in value) ||
96
- !("runId" in value) ||
97
- typeof value.runId !== "string"
98
- ) {
99
- throw new Error("Resume state response must contain state and runId");
100
- }
101
-
102
- return { runId: value.runId, state: value.state as T };
103
- };
104
-
105
- const symbolAssistantTransportExtras = Symbol("assistant-transport-extras");
106
- type AssistantTransportExtras = {
107
- [symbolAssistantTransportExtras]: true;
108
- sendCommand: (command: AssistantTransportCommand) => void;
109
- state: UserExternalState;
110
- };
111
-
112
- const asAssistantTransportExtras = (
113
- extras: unknown,
114
- ): AssistantTransportExtras => {
115
- if (
116
- typeof extras !== "object" ||
117
- extras == null ||
118
- !(symbolAssistantTransportExtras in extras)
119
- )
120
- throw new Error(
121
- "This method can only be called when you are using useAssistantTransportRuntime",
122
- );
123
-
124
- return extras as AssistantTransportExtras;
125
- };
126
-
127
- export const useAssistantTransportSendCommand = () => {
128
- const aui = useAui();
129
-
130
- return (command: AssistantTransportCommand) => {
131
- const extras = aui.thread.getState().extras;
132
- const transportExtras = asAssistantTransportExtras(extras);
133
- transportExtras.sendCommand(command);
134
- };
135
- };
136
-
137
- export function useAssistantTransportState(): UserExternalState;
138
- export function useAssistantTransportState<T>(
139
- selector: (state: UserExternalState) => T,
140
- ): T;
141
- export function useAssistantTransportState<T>(
142
- selector: (state: UserExternalState) => T = (t) => t as T,
143
- ): T | UserExternalState {
144
- return useAuiState((s) =>
145
- selector(asAssistantTransportExtras(s.thread.extras).state),
146
- );
147
- }
148
-
149
- const useAssistantTransportThreadRuntime = <T>(
150
- options: AssistantTransportOptions<T>,
151
- ): AssistantRuntime => {
152
- const agentStateRef = useRef(options.initialState);
153
- const [, rerender] = useState(0);
154
- const resumeFlagRef = useRef(false);
155
- const [isReplaying, setIsReplaying] = useState(false);
156
- const waitForReplayRender = useReplayRenderWait();
157
- const parentIdRef = useRef<string | null | undefined>(undefined);
158
- const commandQueue = useCommandQueue({
159
- onQueue: () => runManager.schedule(),
160
- });
161
-
162
- const enqueueAppendMessage = (message: AppendMessage) => {
163
- const command = convertAppendMessageToCommand(message);
164
- if (!command) {
165
- console.warn(
166
- "[assistant-ui] Skipped add-message command with no supported parts",
167
- );
168
- return;
169
- }
170
- parentIdRef.current = message.parentId;
171
- commandQueue.enqueue(command, {
172
- schedule: message.startRun ?? message.role === "user",
173
- });
174
- };
175
-
176
- const threadId = useAuiState((s) => s.threadListItem.remoteId);
177
-
178
- const runManager = useRunManager({
179
- onRun: async (signal: AbortSignal) => {
180
- const isResume = resumeFlagRef.current;
181
- resumeFlagRef.current = false;
182
- setIsReplaying(false);
183
- const commands: QueuedCommand[] = isResume ? [] : commandQueue.flush();
184
- if (commands.length === 0 && !isResume) return;
185
-
186
- // The flushed batch consumes the parentId; read it alongside the flush
187
- // (before any awaits) so a mid-run append keeps its own value. Resume
188
- // runs send no commands, so they neither send nor consume it.
189
- const parentId = isResume ? undefined : parentIdRef.current;
190
- if (!isResume) parentIdRef.current = undefined;
191
-
192
- const headers = await createRequestHeaders(options.headers);
193
- let resumeState: { runId: string; state: T } | undefined;
194
- if (isResume && options.resumeStateApi) {
195
- const resumeStateResponse = await fetch(options.resumeStateApi, {
196
- method: "POST",
197
- headers,
198
- body: JSON.stringify({ threadId }),
199
- signal,
200
- });
201
- const retained = await readResumeState<T>(resumeStateResponse);
202
- if (retained === null) {
203
- if (commandQueue.state.queued.length > 0) {
204
- runManager.schedule();
205
- }
206
- return;
207
- }
208
- resumeState = retained;
209
- }
210
-
211
- const bodyValue =
212
- typeof options.body === "function"
213
- ? await options.body()
214
- : options.body;
215
- const context = runtime.thread.getModelContext();
216
-
217
- let requestBody: Record<string, unknown> = {
218
- commands,
219
- ...(resumeState === undefined && { state: agentStateRef.current }),
220
- system: context.system,
221
- tools: context.tools ? toToolsJSONSchema(context.tools) : undefined,
222
- threadId,
223
- ...(parentId !== undefined && {
224
- parentId,
225
- }),
226
- // nested (new format, aligned with AssistantChatTransport)
227
- callSettings: context.callSettings,
228
- config: context.config,
229
- // @deprecated spread at top level — use nested `callSettings`/`config` instead. Will be removed in a future version.
230
- ...context.callSettings,
231
- ...context.config,
232
- ...(bodyValue ?? {}),
233
- };
234
-
235
- if (options.prepareSendCommandsRequest) {
236
- requestBody = await options.prepareSendCommandsRequest(
237
- requestBody as SendCommandsRequestBody,
238
- );
239
- }
240
-
241
- if (resumeState !== undefined) {
242
- // The server replays a resume from the snapshot it retained for this
243
- // runId. Body overrides and prepare hooks can neither substitute a
244
- // state nor drop the ID the server validates against.
245
- requestBody = { ...requestBody, runId: resumeState.runId };
246
- delete requestBody["state"];
247
- }
248
-
249
- const response = await fetch(
250
- isResume ? options.resumeApi! : options.api,
251
- {
252
- method: "POST",
253
- headers,
254
- body: JSON.stringify(requestBody),
255
- signal,
256
- },
257
- );
258
-
259
- options.onResponse?.(response);
260
-
261
- if (!response.ok) {
262
- throw new Error(`Status ${response.status}: ${await response.text()}`);
263
- }
264
-
265
- if (!response.body) {
266
- throw new Error("Response body is null");
267
- }
268
-
269
- if (resumeState !== undefined) {
270
- agentStateRef.current = resumeState.state;
271
- rerender((prev) => prev + 1);
272
- }
273
-
274
- const body = await createReplayBoundaryStream(response, {
275
- setReplaying: setIsReplaying,
276
- waitForRender: waitForReplayRender,
277
- });
278
-
279
- // Select decoder based on protocol option
280
- const protocol = options.protocol ?? "data-stream";
281
- // Resume replays a best-effort buffer; always reconcile leniently.
282
- const strict = isResume ? false : (options.strict ?? true);
283
- const decoder =
284
- protocol === "assistant-transport"
285
- ? new AssistantTransportDecoder({ strict })
286
- : new DataStreamDecoder({ strict });
287
-
288
- let err: string | undefined;
289
- const stream = body.pipeThrough(decoder).pipeThrough(
290
- new AssistantMessageAccumulator({
291
- initialMessage: createInitialMessage({
292
- unstable_state:
293
- (agentStateRef.current as ReadonlyJSONValue) ?? null,
294
- }),
295
- throttle: isResume,
296
- strict,
297
- onError: (error) => {
298
- err = error;
299
- },
300
- }),
301
- );
302
-
303
- let markedDelivered = false;
304
-
305
- for await (const chunk of asAsyncIterableStream(stream)) {
306
- if (chunk.metadata.unstable_state === agentStateRef.current) continue;
307
-
308
- if (!markedDelivered) {
309
- commandQueue.markDelivered();
310
- markedDelivered = true;
311
- }
312
-
313
- agentStateRef.current = chunk.metadata.unstable_state as T;
314
- rerender((prev) => prev + 1);
315
- }
316
-
317
- if (err) {
318
- throw new Error(err);
319
- }
320
-
321
- // A successful run confirms delivery even when no state-changing
322
- // chunk was observed.
323
- if (!markedDelivered) {
324
- commandQueue.markDelivered();
325
- }
326
-
327
- // commands that coalesced into this resume run must not starve
328
- if (isResume && commandQueue.state.queued.length > 0) {
329
- runManager.schedule();
330
- }
331
- },
332
- onFinish: options.onFinish,
333
- onCancel: () => {
334
- setIsReplaying(false);
335
- const cmds = [
336
- ...commandQueue.state.inTransit,
337
- ...commandQueue.state.queued,
338
- ];
339
-
340
- commandQueue.reset();
341
-
342
- options.onCancel?.({
343
- commands: cmds,
344
- updateState: (updater) => {
345
- agentStateRef.current = updater(agentStateRef.current);
346
- rerender((prev) => prev + 1);
347
- },
348
- });
349
- },
350
- onError: async (error) => {
351
- resumeFlagRef.current = false;
352
- setIsReplaying(false);
353
- const inTransitCmds = [...commandQueue.state.inTransit];
354
- const queuedCmds = [...commandQueue.state.queued];
355
-
356
- commandQueue.reset();
357
-
358
- try {
359
- await options.onError?.(error as Error, {
360
- commands: inTransitCmds,
361
- updateState: (updater) => {
362
- agentStateRef.current = updater(agentStateRef.current);
363
- rerender((prev) => prev + 1);
364
- },
365
- });
366
- } finally {
367
- options.onCancel?.({
368
- commands: queuedCmds,
369
- updateState: (updater) => {
370
- agentStateRef.current = updater(agentStateRef.current);
371
- rerender((prev) => prev + 1);
372
- },
373
- error: error as Error,
374
- });
375
- }
376
- },
377
- });
378
-
379
- // Tool execution status state
380
- const [toolStatuses, setToolStatuses] = useState<
381
- Record<string, ToolExecutionStatus>
382
- >({});
383
-
384
- // Reactive conversion of agent state + connection metadata → UI state
385
- const pendingCommands = useMemo(
386
- () => [...commandQueue.state.inTransit, ...commandQueue.state.queued],
387
- [commandQueue.state],
388
- );
389
- const converted = useConvertedState(
390
- options.converter,
391
- agentStateRef.current,
392
- pendingCommands,
393
- runManager.isRunning,
394
- toolStatuses,
395
- );
396
-
397
- // Create runtime
398
- const runtime = useExternalStoreRuntime({
399
- messages: converted.messages,
400
- state: converted.state,
401
- isRunning: converted.isRunning,
402
- isLoading: isReplaying,
403
- adapters: options.adapters,
404
- unstable_enableToolInvocations: true,
405
- setToolStatuses,
406
- extras: {
407
- [symbolAssistantTransportExtras]: true,
408
- sendCommand: (command: AssistantTransportCommand) => {
409
- commandQueue.enqueue(command);
410
- },
411
- state: agentStateRef.current as UserExternalState,
412
- } satisfies AssistantTransportExtras,
413
- onNew: async (message: AppendMessage): Promise<void> =>
414
- enqueueAppendMessage(message),
415
- ...(options.capabilities?.edit && {
416
- onEdit: async (message: AppendMessage): Promise<void> =>
417
- enqueueAppendMessage(message),
418
- }),
419
- ...(commandQueue.state.queued.length > 0 && {
420
- onReload: async (parentId: string | null) => {
421
- parentIdRef.current = parentId;
422
- runManager.schedule();
423
- },
424
- }),
425
- onCancel: async () => {
426
- resumeFlagRef.current = false;
427
- runManager.cancel();
428
- },
429
- onResume: async () => {
430
- if (!options.resumeApi)
431
- throw new Error("Must pass resumeApi to options to resume runs");
432
-
433
- resumeFlagRef.current = true;
434
- runManager.schedule();
435
- },
436
- onAddToolResult: async (
437
- toolOptions: AddToolResultOptions,
438
- ): Promise<void> => {
439
- const command: AddToolResultCommand = {
440
- type: "add-tool-result",
441
- toolCallId: toolOptions.toolCallId,
442
- result: toolOptions.result as ReadonlyJSONObject,
443
- toolName: toolOptions.toolName,
444
- isError: toolOptions.isError,
445
- ...(toolOptions.artifact && { artifact: toolOptions.artifact }),
446
- ...(toolOptions.modelContent !== undefined && {
447
- modelContent: toolOptions.modelContent,
448
- }),
449
- };
450
-
451
- commandQueue.enqueue(command);
452
- },
453
- onLoadExternalState: async (state) => {
454
- agentStateRef.current = state as T;
455
- rerender((prev) => prev + 1);
456
- },
457
- });
458
-
459
- return runtime;
460
- };
461
-
462
- /**
463
- * @alpha This is an experimental API that is subject to change.
464
- */
465
- export const useAssistantTransportRuntime = <T>(
466
- options: AssistantTransportOptions<T>,
467
- ): AssistantRuntime => {
468
- const runtime = useRemoteThreadListRuntime({
469
- runtimeHook: function RuntimeHook() {
470
- return useAssistantTransportThreadRuntime(options);
471
- },
472
- adapter: new InMemoryThreadListAdapter(),
473
- allowNesting: true,
474
- });
475
- return runtime;
476
- };
@@ -1,20 +0,0 @@
1
- import { useMemo } from "react";
2
- import type {
3
- AssistantTransportCommand,
4
- AssistantTransportState,
5
- AssistantTransportStateConverter,
6
- } from "./types";
7
- import type { ToolExecutionStatus } from "@assistant-ui/core";
8
-
9
- export function useConvertedState<T>(
10
- converter: AssistantTransportStateConverter<T>,
11
- agentState: T,
12
- pendingCommands: AssistantTransportCommand[],
13
- isSending: boolean,
14
- toolStatuses: Record<string, ToolExecutionStatus>,
15
- ): AssistantTransportState {
16
- return useMemo(
17
- () => converter(agentState, { pendingCommands, isSending, toolStatuses }),
18
- [converter, agentState, pendingCommands, isSending, toolStatuses],
19
- );
20
- }
@@ -1,9 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- export function useLatestRef<T>(value: T) {
4
- const ref = useRef(value);
5
- useEffect(() => {
6
- ref.current = value;
7
- }, [value]);
8
- return ref as { current: T };
9
- }
@@ -1 +0,0 @@
1
- export { createRequestHeaders } from "@assistant-ui/core";