@assistant-ui/react 0.11.20 → 0.11.22

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 (201) hide show
  1. package/dist/augmentations.d.ts +20 -9
  2. package/dist/augmentations.d.ts.map +1 -1
  3. package/dist/client/AssistantClient.d.ts +6 -7
  4. package/dist/client/AssistantClient.d.ts.map +1 -1
  5. package/dist/client/AssistantClient.js +9 -4
  6. package/dist/client/AssistantClient.js.map +1 -1
  7. package/dist/client/NoOpComposerClient.d.ts +9 -0
  8. package/dist/client/NoOpComposerClient.d.ts.map +1 -0
  9. package/dist/client/NoOpComposerClient.js +59 -0
  10. package/dist/client/NoOpComposerClient.js.map +1 -0
  11. package/dist/client/ThreadMessageClient.d.ts +14 -0
  12. package/dist/client/ThreadMessageClient.d.ts.map +1 -0
  13. package/dist/client/ThreadMessageClient.js +140 -0
  14. package/dist/client/ThreadMessageClient.js.map +1 -0
  15. package/dist/client/types/Part.d.ts +5 -0
  16. package/dist/client/types/Part.d.ts.map +1 -1
  17. package/dist/client/types/Thread.d.ts +8 -0
  18. package/dist/client/types/Thread.d.ts.map +1 -1
  19. package/dist/context/providers/MessageProvider.d.ts +1 -8
  20. package/dist/context/providers/MessageProvider.d.ts.map +1 -1
  21. package/dist/context/providers/MessageProvider.js +4 -181
  22. package/dist/context/providers/MessageProvider.js.map +1 -1
  23. package/dist/context/providers/TextMessagePartProvider.d.ts.map +1 -1
  24. package/dist/context/providers/TextMessagePartProvider.js +3 -1
  25. package/dist/context/providers/TextMessagePartProvider.js.map +1 -1
  26. package/dist/context/react/AssistantApiContext.d.ts +3 -1
  27. package/dist/context/react/AssistantApiContext.d.ts.map +1 -1
  28. package/dist/context/react/AssistantApiContext.js +17 -4
  29. package/dist/context/react/AssistantApiContext.js.map +1 -1
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/internal.d.ts +1 -0
  34. package/dist/internal.d.ts.map +1 -1
  35. package/dist/internal.js +5 -1
  36. package/dist/internal.js.map +1 -1
  37. package/dist/legacy-runtime/client/MessagePartRuntimeClient.d.ts.map +1 -1
  38. package/dist/legacy-runtime/client/MessagePartRuntimeClient.js +9 -10
  39. package/dist/legacy-runtime/client/MessagePartRuntimeClient.js.map +1 -1
  40. package/dist/legacy-runtime/client/ThreadRuntimeClient.d.ts.map +1 -1
  41. package/dist/legacy-runtime/client/ThreadRuntimeClient.js +6 -0
  42. package/dist/legacy-runtime/client/ThreadRuntimeClient.js.map +1 -1
  43. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts +1 -1
  44. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts.map +1 -1
  45. package/dist/legacy-runtime/hooks/AssistantContext.js +1 -1
  46. package/dist/legacy-runtime/hooks/AssistantContext.js.map +1 -1
  47. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts.map +1 -1
  48. package/dist/legacy-runtime/hooks/AttachmentContext.js +1 -1
  49. package/dist/legacy-runtime/hooks/AttachmentContext.js.map +1 -1
  50. package/dist/legacy-runtime/hooks/ComposerContext.d.ts.map +1 -1
  51. package/dist/legacy-runtime/hooks/ComposerContext.js +1 -1
  52. package/dist/legacy-runtime/hooks/ComposerContext.js.map +1 -1
  53. package/dist/legacy-runtime/hooks/MessageContext.d.ts.map +1 -1
  54. package/dist/legacy-runtime/hooks/MessageContext.js +1 -1
  55. package/dist/legacy-runtime/hooks/MessageContext.js.map +1 -1
  56. package/dist/legacy-runtime/hooks/MessagePartContext.js +1 -1
  57. package/dist/legacy-runtime/hooks/MessagePartContext.js.map +1 -1
  58. package/dist/legacy-runtime/hooks/ThreadContext.js +1 -1
  59. package/dist/legacy-runtime/hooks/ThreadContext.js.map +1 -1
  60. package/dist/legacy-runtime/hooks/ThreadListItemContext.js +1 -1
  61. package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +1 -1
  62. package/dist/legacy-runtime/runtime/MessagePartRuntime.d.ts +6 -0
  63. package/dist/legacy-runtime/runtime/MessagePartRuntime.d.ts.map +1 -1
  64. package/dist/legacy-runtime/runtime/MessagePartRuntime.js +13 -0
  65. package/dist/legacy-runtime/runtime/MessagePartRuntime.js.map +1 -1
  66. package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts +1 -0
  67. package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts.map +1 -1
  68. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +2 -0
  69. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -1
  70. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  71. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +5 -2
  72. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  73. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +2 -1
  74. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts.map +1 -1
  75. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +3 -3
  76. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
  77. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts +9 -1
  78. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts.map +1 -1
  79. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +48 -7
  80. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js.map +1 -1
  81. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts +2 -1
  82. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts.map +1 -1
  83. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.js.map +1 -1
  84. package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts +7 -1
  85. package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts.map +1 -1
  86. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.d.ts +5 -1
  87. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.d.ts.map +1 -1
  88. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.d.ts +2 -1
  89. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.d.ts.map +1 -1
  90. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.js +12 -1
  91. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.js.map +1 -1
  92. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.d.ts +4 -1
  93. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.d.ts.map +1 -1
  94. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.js +5 -1
  95. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.js.map +1 -1
  96. package/dist/legacy-runtime/runtime-cores/external-store/createMessageConverter.d.ts +3 -2
  97. package/dist/legacy-runtime/runtime-cores/external-store/createMessageConverter.d.ts.map +1 -1
  98. package/dist/legacy-runtime/runtime-cores/external-store/createMessageConverter.js +6 -4
  99. package/dist/legacy-runtime/runtime-cores/external-store/createMessageConverter.js.map +1 -1
  100. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.d.ts +8 -3
  101. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.d.ts.map +1 -1
  102. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.js +20 -6
  103. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.js.map +1 -1
  104. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.d.ts +3 -1
  105. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.d.ts.map +1 -1
  106. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.js +4 -0
  107. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.js.map +1 -1
  108. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.d.ts +1 -1
  109. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.d.ts.map +1 -1
  110. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.d.ts.map +1 -1
  111. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.js +4 -0
  112. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.js.map +1 -1
  113. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts +2 -0
  114. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  115. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.js +1 -3
  116. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.js.map +1 -1
  117. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts +2 -0
  118. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  119. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.d.ts +3 -3
  120. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.d.ts.map +1 -1
  121. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.js.map +1 -1
  122. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.d.ts +2 -2
  123. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.d.ts.map +1 -1
  124. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.js.map +1 -1
  125. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js +1 -1
  126. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js.map +1 -1
  127. package/dist/model-context/frame/AssistantFrameProvider.d.ts.map +1 -1
  128. package/dist/model-context/frame/AssistantFrameProvider.js +6 -1
  129. package/dist/model-context/frame/AssistantFrameProvider.js.map +1 -1
  130. package/dist/primitives/branchPicker/BranchPickerNext.d.ts.map +1 -1
  131. package/dist/primitives/branchPicker/BranchPickerNext.js +7 -3
  132. package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
  133. package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts.map +1 -1
  134. package/dist/primitives/branchPicker/BranchPickerPrevious.js +7 -3
  135. package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
  136. package/dist/primitives/message/MessageParts.d.ts.map +1 -1
  137. package/dist/primitives/message/MessageParts.js +12 -3
  138. package/dist/primitives/message/MessageParts.js.map +1 -1
  139. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  140. package/dist/primitives/message/MessagePartsGrouped.js +11 -2
  141. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  142. package/dist/tests/setup.js +8 -8
  143. package/dist/tests/setup.js.map +1 -1
  144. package/dist/types/AssistantTypes.d.ts +2 -2
  145. package/dist/types/AssistantTypes.d.ts.map +1 -1
  146. package/dist/types/MessagePartComponentTypes.d.ts +1 -0
  147. package/dist/types/MessagePartComponentTypes.d.ts.map +1 -1
  148. package/dist/types/MessagePartTypes.d.ts +1 -0
  149. package/dist/types/MessagePartTypes.d.ts.map +1 -1
  150. package/dist/utils/smooth/SmoothContext.d.ts +6 -6
  151. package/package.json +10 -10
  152. package/src/augmentations.ts +21 -24
  153. package/src/client/AssistantClient.ts +15 -8
  154. package/src/client/NoOpComposerClient.tsx +56 -0
  155. package/src/client/ThreadMessageClient.tsx +161 -0
  156. package/src/client/types/Attachment.ts +1 -1
  157. package/src/client/types/Composer.ts +1 -1
  158. package/src/client/types/Message.ts +1 -1
  159. package/src/client/types/Part.ts +7 -1
  160. package/src/client/types/Thread.ts +11 -1
  161. package/src/client/types/ThreadListItem.ts +1 -1
  162. package/src/context/providers/MessageProvider.tsx +4 -222
  163. package/src/context/providers/TextMessagePartProvider.tsx +3 -1
  164. package/src/context/react/AssistantApiContext.tsx +27 -5
  165. package/src/index.ts +3 -0
  166. package/src/internal.ts +4 -0
  167. package/src/legacy-runtime/client/MessagePartRuntimeClient.ts +12 -13
  168. package/src/legacy-runtime/client/ThreadRuntimeClient.ts +6 -0
  169. package/src/legacy-runtime/hooks/AssistantContext.ts +1 -1
  170. package/src/legacy-runtime/hooks/AttachmentContext.ts +3 -1
  171. package/src/legacy-runtime/hooks/ComposerContext.ts +3 -1
  172. package/src/legacy-runtime/hooks/MessageContext.ts +3 -1
  173. package/src/legacy-runtime/hooks/MessagePartContext.ts +1 -1
  174. package/src/legacy-runtime/hooks/ThreadContext.ts +1 -1
  175. package/src/legacy-runtime/hooks/ThreadListItemContext.ts +1 -1
  176. package/src/legacy-runtime/runtime/MessagePartRuntime.ts +23 -0
  177. package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +2 -0
  178. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.tsx +10 -3
  179. package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +4 -2
  180. package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.ts +73 -6
  181. package/src/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.tsx +2 -0
  182. package/src/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.tsx +8 -1
  183. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.tsx +4 -1
  184. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.tsx +15 -1
  185. package/src/legacy-runtime/runtime-cores/external-store/auto-status.tsx +11 -3
  186. package/src/legacy-runtime/runtime-cores/external-store/createMessageConverter.tsx +9 -2
  187. package/src/legacy-runtime/runtime-cores/external-store/external-message-converter.tsx +33 -5
  188. package/src/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.tsx +6 -0
  189. package/src/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.tsx +5 -0
  190. package/src/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.tsx +1 -4
  191. package/src/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.tsx +3 -3
  192. package/src/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.tsx +2 -1
  193. package/src/legacy-runtime/runtime-cores/utils/MessageRepository.tsx +1 -1
  194. package/src/model-context/frame/AssistantFrameProvider.ts +5 -0
  195. package/src/primitives/branchPicker/BranchPickerNext.tsx +11 -3
  196. package/src/primitives/branchPicker/BranchPickerPrevious.tsx +11 -3
  197. package/src/primitives/message/MessageParts.tsx +11 -3
  198. package/src/primitives/message/MessagePartsGrouped.tsx +10 -2
  199. package/src/types/AssistantTypes.ts +2 -2
  200. package/src/types/MessagePartComponentTypes.tsx +1 -0
  201. package/src/types/MessagePartTypes.ts +1 -0
@@ -12,7 +12,7 @@ import type {
12
12
  } from "./types";
13
13
  import {
14
14
  AssistantMetaTransformStream,
15
- ReadonlyJSONValue,
15
+ type ReadonlyJSONValue,
16
16
  } from "assistant-stream/utils";
17
17
 
18
18
  const isArgsTextComplete = (argsText: string) => {
@@ -28,12 +28,24 @@ type UseToolInvocationsParams = {
28
28
  state: AssistantTransportState;
29
29
  getTools: () => Record<string, Tool> | undefined;
30
30
  onResult: (command: AssistantTransportCommand) => void;
31
+ setToolStatuses: (
32
+ updater:
33
+ | Record<string, ToolExecutionStatus>
34
+ | ((
35
+ prev: Record<string, ToolExecutionStatus>,
36
+ ) => Record<string, ToolExecutionStatus>),
37
+ ) => void;
31
38
  };
32
39
 
40
+ export type ToolExecutionStatus =
41
+ | { type: "executing" }
42
+ | { type: "interrupt"; payload: unknown };
43
+
33
44
  export function useToolInvocations({
34
45
  state,
35
46
  getTools,
36
47
  onResult,
48
+ setToolStatuses,
37
49
  }: UseToolInvocationsParams) {
38
50
  const lastToolStates = useRef<
39
51
  Record<
@@ -46,12 +58,39 @@ export function useToolInvocations({
46
58
  >
47
59
  >({});
48
60
 
61
+ const interruptedToolsRef = useRef<
62
+ Map<
63
+ string,
64
+ {
65
+ resolve: (payload: unknown) => void;
66
+ reject: (reason: unknown) => void;
67
+ }
68
+ >
69
+ >(new Map());
70
+
49
71
  const acRef = useRef<AbortController>(new AbortController());
50
72
  const [controller] = useState(() => {
51
73
  const [stream, controller] = createAssistantStreamController();
52
74
  const transform = unstable_toolResultStream(
53
75
  getTools,
54
76
  () => acRef.current?.signal ?? new AbortController().signal,
77
+ (toolCallId: string, payload: unknown) => {
78
+ return new Promise<unknown>((resolve, reject) => {
79
+ // Reject previous interrupt if it exists
80
+ const previous = interruptedToolsRef.current.get(toolCallId);
81
+ if (previous) {
82
+ previous.reject(
83
+ new Error("Interrupt was superseded by a new interrupt"),
84
+ );
85
+ }
86
+
87
+ interruptedToolsRef.current.set(toolCallId, { resolve, reject });
88
+ setToolStatuses((prev) => ({
89
+ ...prev,
90
+ [toolCallId]: { type: "interrupt", payload },
91
+ }));
92
+ });
93
+ },
55
94
  );
56
95
  stream
57
96
  .pipeThrough(transform)
@@ -72,6 +111,13 @@ export function useToolInvocations({
72
111
  isError: chunk.isError,
73
112
  ...(chunk.artifact && { artifact: chunk.artifact }),
74
113
  });
114
+
115
+ // Clear status when result is set
116
+ setToolStatuses((prev) => {
117
+ const next = { ...prev };
118
+ delete next[chunk.meta.toolCallId];
119
+ return next;
120
+ });
75
121
  }
76
122
  },
77
123
  }),
@@ -159,15 +205,36 @@ export function useToolInvocations({
159
205
  }
160
206
  }, [state, controller, onResult]);
161
207
 
208
+ const abort = () => {
209
+ interruptedToolsRef.current.forEach(({ reject }) => {
210
+ reject(new Error("Tool execution aborted"));
211
+ });
212
+ interruptedToolsRef.current.clear();
213
+ setToolStatuses({});
214
+
215
+ acRef.current.abort();
216
+ acRef.current = new AbortController();
217
+ };
218
+
162
219
  return {
163
220
  reset: () => {
164
- acRef.current.abort();
165
- acRef.current = new AbortController();
221
+ abort();
166
222
  isInititialState.current = true;
167
223
  },
168
- abort: () => {
169
- acRef.current.abort();
170
- acRef.current = new AbortController();
224
+ abort,
225
+ resume: (toolCallId: string, payload: unknown) => {
226
+ const handlers = interruptedToolsRef.current.get(toolCallId);
227
+ if (handlers) {
228
+ interruptedToolsRef.current.delete(toolCallId);
229
+ setToolStatuses((prev) => {
230
+ const next = { ...prev };
231
+ delete next[toolCallId];
232
+ return next;
233
+ });
234
+ handlers.resolve(payload);
235
+ } else {
236
+ throw new Error(`Tool call ${toolCallId} is not interrupted`);
237
+ }
171
238
  },
172
239
  };
173
240
  }
@@ -6,6 +6,7 @@ import {
6
6
  import { DefaultThreadComposerRuntimeCore } from "../composer/DefaultThreadComposerRuntimeCore";
7
7
  import {
8
8
  AddToolResultOptions,
9
+ ResumeToolCallOptions,
9
10
  ThreadSuggestion,
10
11
  SubmitFeedbackOptions,
11
12
  ThreadRuntimeCore,
@@ -46,6 +47,7 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
46
47
  public abstract startRun(config: StartRunConfig): void;
47
48
  public abstract resumeRun(config: ResumeRunConfig): void;
48
49
  public abstract addToolResult(options: AddToolResultOptions): void;
50
+ public abstract resumeToolCall(options: ResumeToolCallOptions): void;
49
51
  public abstract cancelRun(): void;
50
52
  public abstract unstable_loadExternalState(state: any): void;
51
53
 
@@ -1,4 +1,4 @@
1
- import { ReadonlyJSONValue } from "assistant-stream/utils";
1
+ import type { ReadonlyJSONValue } from "assistant-stream/utils";
2
2
  import { ModelContext } from "../../../model-context";
3
3
  import { AppendMessage, ThreadMessage } from "../../../types";
4
4
  import { RunConfig } from "../../../types/AssistantTypes";
@@ -14,6 +14,7 @@ import {
14
14
 
15
15
  export type RuntimeCapabilities = {
16
16
  readonly switchToBranch: boolean;
17
+ readonly switchBranchDuringRun: boolean;
17
18
  readonly edit: boolean;
18
19
  readonly reload: boolean;
19
20
  readonly cancel: boolean;
@@ -32,6 +33,11 @@ export type AddToolResultOptions = {
32
33
  artifact?: ReadonlyJSONValue | undefined;
33
34
  };
34
35
 
36
+ export type ResumeToolCallOptions = {
37
+ toolCallId: string;
38
+ payload: unknown;
39
+ };
40
+
35
41
  export type SubmitFeedbackOptions = {
36
42
  messageId: string;
37
43
  type: "negative" | "positive";
@@ -85,6 +91,7 @@ export type ThreadRuntimeCore = Readonly<{
85
91
  cancelRun: () => void;
86
92
 
87
93
  addToolResult: (options: AddToolResultOptions) => void;
94
+ resumeToolCall: (options: ResumeToolCallOptions) => void;
88
95
 
89
96
  speak: (messageId: string) => void;
90
97
  stopSpeaking: () => void;
@@ -10,7 +10,7 @@ import { FeedbackAdapter } from "../adapters/feedback/FeedbackAdapter";
10
10
  import { SpeechSynthesisAdapter } from "../adapters/speech/SpeechAdapterTypes";
11
11
  import { ThreadMessageLike } from "./ThreadMessageLike";
12
12
  import { ExportedMessageRepository } from "../utils/MessageRepository";
13
- import { ReadonlyJSONValue } from "assistant-stream/utils";
13
+ import type { ReadonlyJSONValue } from "assistant-stream/utils";
14
14
 
15
15
  export type ExternalStoreThreadData<TState extends "regular" | "archived"> = {
16
16
  status: TState;
@@ -77,6 +77,9 @@ type ExternalStoreAdapterBase<T> = {
77
77
  onAddToolResult?:
78
78
  | ((options: AddToolResultOptions) => Promise<void> | void)
79
79
  | undefined;
80
+ onResumeToolCall?:
81
+ | ((options: { toolCallId: string; payload: unknown }) => void)
82
+ | undefined;
80
83
  convertMessage?: ExternalStoreMessageConverter<T> | undefined;
81
84
  adapters?:
82
85
  | {
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AddToolResultOptions,
3
3
  ResumeRunConfig,
4
+ ResumeToolCallOptions,
4
5
  StartRunConfig,
5
6
  ThreadSuggestion,
6
7
  } from "../core/ThreadRuntimeCore";
@@ -43,6 +44,7 @@ export class ExternalStoreThreadRuntimeCore
43
44
 
44
45
  private _capabilities: RuntimeCapabilities = {
45
46
  switchToBranch: false,
47
+ switchBranchDuringRun: false,
46
48
  edit: false,
47
49
  reload: false,
48
50
  cancel: false,
@@ -108,6 +110,7 @@ export class ExternalStoreThreadRuntimeCore
108
110
  this.suggestions = store.suggestions ?? EMPTY_ARRAY;
109
111
  this._capabilities = {
110
112
  switchToBranch: this._store.setMessages !== undefined,
113
+ switchBranchDuringRun: false, // External store never supports branch switching during run
111
114
  edit: this._store.onEdit !== undefined,
112
115
  reload: this._store.onReload !== undefined,
113
116
  cancel: this._store.onCancel !== undefined,
@@ -159,7 +162,7 @@ export class ExternalStoreThreadRuntimeCore
159
162
  if (!store.convertMessage) return m;
160
163
 
161
164
  const isLast = idx === store.messages!.length - 1;
162
- const autoStatus = getAutoStatus(isLast, isRunning, false);
165
+ const autoStatus = getAutoStatus(isLast, isRunning, false, false);
163
166
 
164
167
  if (
165
168
  cache &&
@@ -228,6 +231,11 @@ export class ExternalStoreThreadRuntimeCore
228
231
  if (!this._store.setMessages)
229
232
  throw new Error("Runtime does not support switching branches.");
230
233
 
234
+ // Silently ignore branch switches while running
235
+ if (this._store.isRunning) {
236
+ return;
237
+ }
238
+
231
239
  this.repository.switchToBranch(branchId);
232
240
  this.updateMessages(this.repository.getMessages());
233
241
  }
@@ -302,6 +310,12 @@ export class ExternalStoreThreadRuntimeCore
302
310
  this._store.onAddToolResult?.(options);
303
311
  }
304
312
 
313
+ public resumeToolCall(options: ResumeToolCallOptions) {
314
+ if (!this._store.onResumeToolCall)
315
+ throw new Error("Runtime does not support resuming tool calls.");
316
+ this._store.onResumeToolCall(options);
317
+ }
318
+
305
319
  public override reset(initialMessages?: readonly ThreadMessageLike[]) {
306
320
  const repo = new MessageRepository();
307
321
  repo.import(ExportedMessageRepository.fromArray(initialMessages ?? []));
@@ -11,16 +11,24 @@ const AUTO_STATUS_PENDING = Object.freeze({
11
11
  reason: "tool-calls",
12
12
  });
13
13
 
14
+ const AUTO_STATUS_SUSPENDED = Object.freeze({
15
+ type: "requires-action",
16
+ reason: "interrupt",
17
+ });
18
+
14
19
  export const isAutoStatus = (status: MessageStatus) =>
15
20
  status === AUTO_STATUS_RUNNING || status === AUTO_STATUS_COMPLETE;
16
21
 
17
22
  export const getAutoStatus = (
18
23
  isLast: boolean,
19
24
  isRunning: boolean,
25
+ hasSuspendedToolCalls: boolean,
20
26
  hasPendingToolCalls: boolean,
21
27
  ) =>
22
28
  isLast && isRunning
23
29
  ? AUTO_STATUS_RUNNING
24
- : hasPendingToolCalls
25
- ? AUTO_STATUS_PENDING
26
- : AUTO_STATUS_COMPLETE;
30
+ : hasSuspendedToolCalls
31
+ ? AUTO_STATUS_SUSPENDED
32
+ : hasPendingToolCalls
33
+ ? AUTO_STATUS_PENDING
34
+ : AUTO_STATUS_COMPLETE;
@@ -16,20 +16,27 @@ export const createMessageConverter = <T extends object>(
16
16
  messages,
17
17
  isRunning,
18
18
  joinStrategy,
19
+ metadata,
19
20
  }: {
20
21
  messages: T[];
21
22
  isRunning: boolean;
22
23
  joinStrategy?: "concat-content" | "none" | undefined;
24
+ metadata?: useExternalMessageConverter.Metadata;
23
25
  }) => {
24
26
  return useExternalMessageConverter<T>({
25
27
  callback,
26
28
  messages,
27
29
  isRunning,
28
30
  joinStrategy,
31
+ metadata,
29
32
  });
30
33
  },
31
- toThreadMessages: (messages: T[], isRunning = false) => {
32
- return convertExternalMessages(messages, callback, isRunning);
34
+ toThreadMessages: (
35
+ messages: T[],
36
+ isRunning = false,
37
+ metadata: useExternalMessageConverter.Metadata = {},
38
+ ) => {
39
+ return convertExternalMessages(messages, callback, isRunning, metadata);
33
40
  },
34
41
  toOriginalMessages: (
35
42
  input: ThreadState | ThreadMessage | ThreadMessage["content"][number],
@@ -9,6 +9,7 @@ import {
9
9
  import { fromThreadMessageLike, ThreadMessageLike } from "./ThreadMessageLike";
10
10
  import { getAutoStatus, isAutoStatus } from "./auto-status";
11
11
  import { ToolCallMessagePart } from "../../../types";
12
+ import { ToolExecutionStatus } from "../assistant-transport/useToolInvocations";
12
13
 
13
14
  export namespace useExternalMessageConverter {
14
15
  export type Message =
@@ -26,7 +27,14 @@ export namespace useExternalMessageConverter {
26
27
  isError?: boolean;
27
28
  };
28
29
 
29
- export type Callback<T> = (message: T) => Message | Message[];
30
+ export type Metadata = {
31
+ readonly toolStatuses?: Record<string, ToolExecutionStatus>;
32
+ };
33
+
34
+ export type Callback<T> = (
35
+ message: T,
36
+ metadata: Metadata,
37
+ ) => Message | Message[];
30
38
  }
31
39
 
32
40
  type CallbackResult<T> = {
@@ -218,10 +226,11 @@ export const convertExternalMessages = <T extends WeakKey>(
218
226
  messages: T[],
219
227
  callback: useExternalMessageConverter.Callback<T>,
220
228
  isRunning: boolean,
229
+ metadata: useExternalMessageConverter.Metadata,
221
230
  ) => {
222
231
  const callbackResults: CallbackResult<T>[] = [];
223
232
  for (const message of messages) {
224
- const output = callback(message);
233
+ const output = callback(message, metadata);
225
234
  const outputs = Array.isArray(output) ? output : [output];
226
235
  const result = { input: message, outputs };
227
236
  callbackResults.push(result);
@@ -232,12 +241,22 @@ export const convertExternalMessages = <T extends WeakKey>(
232
241
  return chunks.map((message, idx) => {
233
242
  const isLast = idx === chunks.length - 1;
234
243
  const joined = joinExternalMessages(message.outputs);
244
+ const hasSuspendedToolCalls =
245
+ typeof joined.content === "object" &&
246
+ joined.content.some(
247
+ (c) => c.type === "tool-call" && c.result === undefined,
248
+ );
235
249
  const hasPendingToolCalls =
236
250
  typeof joined.content === "object" &&
237
251
  joined.content.some(
238
252
  (c) => c.type === "tool-call" && c.result === undefined,
239
253
  );
240
- const autoStatus = getAutoStatus(isLast, isRunning, hasPendingToolCalls);
254
+ const autoStatus = getAutoStatus(
255
+ isLast,
256
+ isRunning,
257
+ hasSuspendedToolCalls,
258
+ hasPendingToolCalls,
259
+ );
241
260
  const newMessage = fromThreadMessageLike(
242
261
  joined,
243
262
  idx.toString(),
@@ -253,14 +272,17 @@ export const useExternalMessageConverter = <T extends WeakKey>({
253
272
  messages,
254
273
  isRunning,
255
274
  joinStrategy,
275
+ metadata,
256
276
  }: {
257
277
  callback: useExternalMessageConverter.Callback<T>;
258
278
  messages: T[];
259
279
  isRunning: boolean;
260
280
  joinStrategy?: "concat-content" | "none" | undefined;
281
+ metadata?: useExternalMessageConverter.Metadata | undefined;
261
282
  }) => {
262
283
  const state = useMemo(
263
284
  () => ({
285
+ metadata: metadata ?? {},
264
286
  callback,
265
287
  callbackCache: new WeakMap<T, CallbackResult<T>>(),
266
288
  chunkCache: new WeakMap<
@@ -269,7 +291,7 @@ export const useExternalMessageConverter = <T extends WeakKey>({
269
291
  >(),
270
292
  converterCache: new ThreadMessageConverter(),
271
293
  }),
272
- [callback],
294
+ [callback, metadata],
273
295
  );
274
296
 
275
297
  return useMemo(() => {
@@ -277,7 +299,7 @@ export const useExternalMessageConverter = <T extends WeakKey>({
277
299
  for (const message of messages) {
278
300
  let result = state.callbackCache.get(message);
279
301
  if (!result) {
280
- const output = state.callback(message);
302
+ const output = state.callback(message, state.metadata);
281
303
  const outputs = Array.isArray(output) ? output : [output];
282
304
  result = { input: message, outputs };
283
305
  state.callbackCache.set(message, result);
@@ -304,6 +326,11 @@ export const useExternalMessageConverter = <T extends WeakKey>({
304
326
  const isLast = idx === chunks.length - 1;
305
327
 
306
328
  const joined = joinExternalMessages(message.outputs);
329
+ const hasSuspendedToolCalls =
330
+ typeof joined.content === "object" &&
331
+ joined.content.some(
332
+ (c) => c.type === "tool-call" && c.result === undefined,
333
+ );
307
334
  const hasPendingToolCalls =
308
335
  typeof joined.content === "object" &&
309
336
  joined.content.some(
@@ -312,6 +339,7 @@ export const useExternalMessageConverter = <T extends WeakKey>({
312
339
  const autoStatus = getAutoStatus(
313
340
  isLast,
314
341
  isRunning,
342
+ hasSuspendedToolCalls,
315
343
  hasPendingToolCalls,
316
344
  );
317
345
 
@@ -5,6 +5,7 @@ import { shouldContinue } from "./shouldContinue";
5
5
  import { LocalRuntimeOptionsBase } from "./LocalRuntimeOptions";
6
6
  import {
7
7
  AddToolResultOptions,
8
+ ResumeToolCallOptions,
8
9
  ThreadSuggestion,
9
10
  ThreadRuntimeCore,
10
11
  StartRunConfig,
@@ -30,6 +31,7 @@ export class LocalThreadRuntimeCore
30
31
  {
31
32
  public readonly capabilities = {
32
33
  switchToBranch: true,
34
+ switchBranchDuringRun: true,
33
35
  edit: true,
34
36
  reload: true,
35
37
  cancel: true,
@@ -462,4 +464,8 @@ export class LocalThreadRuntimeCore
462
464
  this.performRoundtrip(parentId, message, this._lastRunConfig);
463
465
  }
464
466
  }
467
+
468
+ public resumeToolCall(_options: ResumeToolCallOptions) {
469
+ throw new Error("Local runtime does not support resuming tool calls.");
470
+ }
465
471
  }
@@ -36,6 +36,10 @@ export const EMPTY_THREAD_CORE: ThreadRuntimeCore = {
36
36
  throw EMPTY_THREAD_ERROR;
37
37
  },
38
38
 
39
+ resumeToolCall() {
40
+ throw EMPTY_THREAD_ERROR;
41
+ },
42
+
39
43
  speak() {
40
44
  throw EMPTY_THREAD_ERROR;
41
45
  },
@@ -132,6 +136,7 @@ export const EMPTY_THREAD_CORE: ThreadRuntimeCore = {
132
136
 
133
137
  capabilities: {
134
138
  switchToBranch: false,
139
+ switchBranchDuringRun: false,
135
140
  edit: false,
136
141
  reload: false,
137
142
  cancel: false,
@@ -90,10 +90,7 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
90
90
  throw new Error("Thread not found. This is a bug in assistant-ui.");
91
91
 
92
92
  aliveThread.runtime = threadBinding.getState();
93
-
94
- if (isMounted) {
95
- this._notifySubscribers();
96
- }
93
+ this._notifySubscribers();
97
94
  }, [threadId, threadBinding]);
98
95
 
99
96
  const isMounted = useRef(false);
@@ -16,14 +16,14 @@ import { InMemoryThreadListAdapter } from "./in-memory";
16
16
  import { CloudFileAttachmentAdapter } from "../../adapters";
17
17
 
18
18
  type ThreadData = {
19
- externalId: string;
19
+ externalId: string | undefined;
20
20
  };
21
21
 
22
22
  type CloudThreadListAdapterOptions = {
23
23
  cloud?: AssistantCloud | undefined;
24
24
 
25
- create?(): Promise<ThreadData>;
26
- delete?(threadId: string): Promise<void>;
25
+ create?: (() => Promise<ThreadData>) | undefined;
26
+ delete?: ((threadId: string) => Promise<void>) | undefined;
27
27
  };
28
28
 
29
29
  const baseUrl =
@@ -6,6 +6,7 @@ import { RemoteThreadListThreadListRuntimeCore } from "./RemoteThreadListThreadL
6
6
  import { RemoteThreadListOptions } from "./types";
7
7
  import { AssistantRuntimeImpl } from "../../../internal";
8
8
  import { AssistantRuntimeCore } from "../core/AssistantRuntimeCore";
9
+ import { AssistantRuntime } from "../../runtime/AssistantRuntime";
9
10
 
10
11
  class RemoteThreadListRuntimeCore
11
12
  extends BaseAssistantRuntimeCore
@@ -28,7 +29,7 @@ class RemoteThreadListRuntimeCore
28
29
 
29
30
  export const useRemoteThreadListRuntime = (
30
31
  options: RemoteThreadListOptions,
31
- ) => {
32
+ ): AssistantRuntime => {
32
33
  const [runtime] = useState(() => new RemoteThreadListRuntimeCore(options));
33
34
  useEffect(() => {
34
35
  runtime.threads.__internal_setOptions(options);
@@ -67,7 +67,7 @@ export const ExportedMessageRepository = {
67
67
  fromThreadMessageLike(
68
68
  m,
69
69
  generateId(),
70
- getAutoStatus(false, false, false),
70
+ getAutoStatus(false, false, false, false),
71
71
  ),
72
72
  );
73
73
 
@@ -133,6 +133,11 @@ export class AssistantFrameProvider {
133
133
  ? await tool.execute(message.args, {
134
134
  toolCallId: message.id,
135
135
  abortSignal: new AbortController().signal,
136
+ interrupt: async () => {
137
+ throw new Error(
138
+ "Tool interrupt is not supported in frame context",
139
+ );
140
+ },
136
141
  })
137
142
  : undefined;
138
143
  } catch (e) {
@@ -10,9 +10,17 @@ import { useAssistantState, useAssistantApi } from "../../context";
10
10
 
11
11
  const useBranchPickerNext = () => {
12
12
  const api = useAssistantApi();
13
- const disabled = useAssistantState(
14
- ({ message }) => message.branchNumber >= message.branchCount,
15
- );
13
+ const disabled = useAssistantState(({ thread, message }) => {
14
+ // Disabled if no next branch
15
+ if (message.branchNumber >= message.branchCount) return true;
16
+
17
+ // Disabled if running and capability not supported
18
+ if (thread.isRunning && !thread.capabilities.switchBranchDuringRun) {
19
+ return true;
20
+ }
21
+
22
+ return false;
23
+ });
16
24
 
17
25
  const callback = useCallback(() => {
18
26
  api.message().switchToBranch({ position: "next" });
@@ -31,9 +31,17 @@ import { useAssistantState, useAssistantApi } from "../../context";
31
31
  */
32
32
  const useBranchPickerPrevious = () => {
33
33
  const api = useAssistantApi();
34
- const disabled = useAssistantState(
35
- ({ message }) => message.branchNumber <= 1,
36
- );
34
+ const disabled = useAssistantState(({ thread, message }) => {
35
+ // Disabled if no previous branch
36
+ if (message.branchNumber <= 1) return true;
37
+
38
+ // Disabled if running and capability not supported
39
+ if (thread.isRunning && !thread.capabilities.switchBranchDuringRun) {
40
+ return true;
41
+ }
42
+
43
+ return false;
44
+ });
37
45
 
38
46
  const callback = useCallback(() => {
39
47
  api.message().switchToBranch({ position: "previous" });
@@ -241,11 +241,19 @@ const MessagePartComponent: FC<MessagePartComponentProps> = ({
241
241
 
242
242
  const type = part.type;
243
243
  if (type === "tool-call") {
244
- const addResult = (result: any) => api.part().addToolResult(result);
244
+ const addResult = api.part().addToolResult;
245
+ const resume = api.part().resumeToolCall;
245
246
  if ("Override" in tools)
246
- return <tools.Override {...part} addResult={addResult} />;
247
+ return <tools.Override {...part} addResult={addResult} resume={resume} />;
247
248
  const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;
248
- return <ToolUIDisplay {...part} Fallback={Tool} addResult={addResult} />;
249
+ return (
250
+ <ToolUIDisplay
251
+ {...part}
252
+ Fallback={Tool}
253
+ addResult={addResult}
254
+ resume={resume}
255
+ />
256
+ );
249
257
  }
250
258
 
251
259
  if (part.status?.type === "requires-action")
@@ -267,10 +267,18 @@ const MessagePartComponent: FC<MessagePartComponentProps> = ({
267
267
  const type = part.type;
268
268
  if (type === "tool-call") {
269
269
  const addResult = (result: any) => api.part().addToolResult(result);
270
+ const resume = api.part().resumeToolCall;
270
271
  if ("Override" in tools)
271
- return <tools.Override {...part} addResult={addResult} />;
272
+ return <tools.Override {...part} addResult={addResult} resume={resume} />;
272
273
  const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;
273
- return <ToolUIDisplay {...part} Fallback={Tool} addResult={addResult} />;
274
+ return (
275
+ <ToolUIDisplay
276
+ {...part}
277
+ Fallback={Tool}
278
+ addResult={addResult}
279
+ resume={resume}
280
+ />
281
+ );
274
282
  }
275
283
 
276
284
  if (part.status?.type === "requires-action")
@@ -64,7 +64,7 @@ export type MessagePartStatus =
64
64
  export type ToolCallMessagePartStatus =
65
65
  | {
66
66
  readonly type: "requires-action";
67
- readonly reason: "tool-calls";
67
+ readonly reason: "interrupt";
68
68
  }
69
69
  | MessagePartStatus;
70
70
 
@@ -74,7 +74,7 @@ export type MessageStatus =
74
74
  }
75
75
  | {
76
76
  readonly type: "requires-action";
77
- readonly reason: "tool-calls";
77
+ readonly reason: "tool-calls" | "interrupt";
78
78
  }
79
79
  | {
80
80
  readonly type: "complete";
@@ -44,6 +44,7 @@ export type ToolCallMessagePartProps<
44
44
  > = MessagePartState &
45
45
  ToolCallMessagePart<TArgs, TResult> & {
46
46
  addResult: (result: TResult | ToolResponse<TResult>) => void;
47
+ resume: (payload: unknown) => void;
47
48
  };
48
49
 
49
50
  export type ToolCallMessagePartComponent<
@@ -54,6 +54,7 @@ export type ToolCallMessagePart<
54
54
  readonly isError?: boolean | undefined;
55
55
  readonly argsText: string;
56
56
  readonly artifact?: unknown;
57
+ readonly interrupt?: unknown;
57
58
  readonly parentId?: string;
58
59
  };
59
60