@assistant-ui/react 0.14.29 → 0.15.1

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 (142) hide show
  1. package/dist/client/ExternalThread.js +4 -3
  2. package/dist/client/ExternalThread.js.map +1 -1
  3. package/dist/client/InMemoryThreadList.js +3 -3
  4. package/dist/client/InMemoryThreadList.js.map +1 -1
  5. package/dist/context/index.d.ts +1 -8
  6. package/dist/context/index.js +1 -8
  7. package/dist/hooks/useToolCallElapsed.js +24 -29
  8. package/dist/hooks/useToolCallElapsed.js.map +1 -1
  9. package/dist/index.d.ts +1 -8
  10. package/dist/index.js +1 -8
  11. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +3 -3
  12. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  13. package/dist/mcp-apps/McpAppRenderer.js +1 -1
  14. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  15. package/dist/model-context/makeAssistantVisible.js +26 -25
  16. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  17. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +3 -3
  18. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
  19. package/dist/primitives/attachment/AttachmentRemove.js +3 -3
  20. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  21. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +3 -3
  22. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  23. package/dist/primitives/composer/ComposerAddAttachment.js +3 -3
  24. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  25. package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
  26. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  27. package/dist/primitives/composer/ComposerInput.js +13 -13
  28. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  29. package/dist/primitives/composer/ComposerQuote.js +1 -1
  30. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  31. package/dist/primitives/composer/ComposerStopDictation.js +3 -3
  32. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
  33. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  34. package/dist/primitives/composer/trigger/triggerSelectionResource.js +3 -3
  35. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  36. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  37. package/dist/primitives/message/MessagePartsGrouped.js +67 -89
  38. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  39. package/dist/primitives/message/MessageRoot.js +3 -3
  40. package/dist/primitives/message/MessageRoot.js.map +1 -1
  41. package/dist/primitives/queueItem/QueueItemRemove.js +3 -3
  42. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  43. package/dist/primitives/queueItem/QueueItemSteer.js +3 -3
  44. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  45. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
  46. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
  47. package/dist/primitives/suggestion/SuggestionTrigger.js +16 -15
  48. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  49. package/dist/unstable/useComposerInput.js +2 -2
  50. package/dist/unstable/useComposerInput.js.map +1 -1
  51. package/dist/unstable/useComposerInputHistory.js +6 -6
  52. package/dist/unstable/useComposerInputHistory.js.map +1 -1
  53. package/dist/unstable/useMentionAdapter.js +1 -1
  54. package/dist/unstable/useMentionAdapter.js.map +1 -1
  55. package/dist/utils/smooth/SmoothContext.js +3 -3
  56. package/dist/utils/smooth/SmoothContext.js.map +1 -1
  57. package/dist/utils/smooth/useSmooth.js +1 -1
  58. package/dist/utils/smooth/useSmooth.js.map +1 -1
  59. package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
  60. package/package.json +5 -5
  61. package/src/client/ExternalThread.ts +5 -3
  62. package/src/client/InMemoryThreadList.ts +3 -3
  63. package/src/context/index.ts +0 -44
  64. package/src/hooks/useToolCallElapsed.ts +9 -12
  65. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +5 -9
  66. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +1 -1
  67. package/src/mcp-apps/McpAppRenderer.tsx +1 -1
  68. package/src/model-context/makeAssistantVisible.test.tsx +1 -1
  69. package/src/model-context/makeAssistantVisible.tsx +1 -1
  70. package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +1 -1
  71. package/src/primitives/attachment/AttachmentRemove.ts +1 -1
  72. package/src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.ts +1 -1
  73. package/src/primitives/composer/ComposerAddAttachment.test.tsx +2 -2
  74. package/src/primitives/composer/ComposerAddAttachment.ts +1 -1
  75. package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +2 -2
  76. package/src/primitives/composer/ComposerAttachmentDropzone.tsx +1 -1
  77. package/src/primitives/composer/ComposerInput.test.tsx +7 -7
  78. package/src/primitives/composer/ComposerInput.tsx +13 -13
  79. package/src/primitives/composer/ComposerQuote.tsx +1 -1
  80. package/src/primitives/composer/ComposerStopDictation.ts +1 -1
  81. package/src/primitives/composer/trigger/triggerSelectionResource.ts +7 -9
  82. package/src/primitives/message/MessagePartsGrouped.tsx +6 -8
  83. package/src/primitives/message/MessageRoot.tsx +1 -1
  84. package/src/primitives/queueItem/QueueItemRemove.ts +1 -1
  85. package/src/primitives/queueItem/QueueItemSteer.ts +1 -1
  86. package/src/primitives/selectionToolbar/SelectionToolbarQuote.tsx +1 -1
  87. package/src/primitives/suggestion/SuggestionTrigger.ts +9 -9
  88. package/src/tests/DataRenderers.test.tsx +1 -1
  89. package/src/tests/RemoteThreadListRuntime.deferredProvider.test.tsx +3 -3
  90. package/src/tests/external-thread-attachments.test.tsx +21 -23
  91. package/src/tests/external-thread-branches.test.tsx +10 -10
  92. package/src/tests/external-thread-parity.test.tsx +18 -20
  93. package/src/tests/local-runtime-queue.test.tsx +29 -34
  94. package/src/tests/messagePartSwitchRace.test.tsx +30 -13
  95. package/src/unstable/useComposerInput.test.tsx +2 -2
  96. package/src/unstable/useComposerInput.ts +2 -2
  97. package/src/unstable/useComposerInputHistory.test.tsx +3 -3
  98. package/src/unstable/useComposerInputHistory.ts +6 -6
  99. package/src/unstable/useMentionAdapter.ts +1 -1
  100. package/src/utils/smooth/SmoothContext.tsx +1 -1
  101. package/src/utils/smooth/useSmooth.test.tsx +1 -1
  102. package/src/utils/smooth/useSmooth.ts +1 -1
  103. package/dist/context/react/utils/createStateHookForRuntime.d.ts +0 -34
  104. package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +0 -1
  105. package/dist/context/react/utils/createStateHookForRuntime.js +0 -21
  106. package/dist/context/react/utils/createStateHookForRuntime.js.map +0 -1
  107. package/dist/legacy-runtime/hooks/AssistantContext.d.ts +0 -74
  108. package/dist/legacy-runtime/hooks/AssistantContext.d.ts.map +0 -1
  109. package/dist/legacy-runtime/hooks/AssistantContext.js +0 -29
  110. package/dist/legacy-runtime/hooks/AssistantContext.js.map +0 -1
  111. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts +0 -1007
  112. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts.map +0 -1
  113. package/dist/legacy-runtime/hooks/AttachmentContext.js +0 -47
  114. package/dist/legacy-runtime/hooks/AttachmentContext.js.map +0 -1
  115. package/dist/legacy-runtime/hooks/ComposerContext.d.ts +0 -141
  116. package/dist/legacy-runtime/hooks/ComposerContext.d.ts.map +0 -1
  117. package/dist/legacy-runtime/hooks/ComposerContext.js +0 -65
  118. package/dist/legacy-runtime/hooks/ComposerContext.js.map +0 -1
  119. package/dist/legacy-runtime/hooks/MessageContext.d.ts +0 -155
  120. package/dist/legacy-runtime/hooks/MessageContext.d.ts.map +0 -1
  121. package/dist/legacy-runtime/hooks/MessageContext.js +0 -68
  122. package/dist/legacy-runtime/hooks/MessageContext.js.map +0 -1
  123. package/dist/legacy-runtime/hooks/MessagePartContext.d.ts +0 -44
  124. package/dist/legacy-runtime/hooks/MessagePartContext.d.ts.map +0 -1
  125. package/dist/legacy-runtime/hooks/MessagePartContext.js +0 -26
  126. package/dist/legacy-runtime/hooks/MessagePartContext.js.map +0 -1
  127. package/dist/legacy-runtime/hooks/ThreadContext.d.ts +0 -139
  128. package/dist/legacy-runtime/hooks/ThreadContext.d.ts.map +0 -1
  129. package/dist/legacy-runtime/hooks/ThreadContext.js +0 -84
  130. package/dist/legacy-runtime/hooks/ThreadContext.js.map +0 -1
  131. package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts +0 -44
  132. package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts.map +0 -1
  133. package/dist/legacy-runtime/hooks/ThreadListItemContext.js +0 -26
  134. package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +0 -1
  135. package/src/context/react/utils/createStateHookForRuntime.ts +0 -82
  136. package/src/legacy-runtime/hooks/AssistantContext.ts +0 -67
  137. package/src/legacy-runtime/hooks/AttachmentContext.ts +0 -99
  138. package/src/legacy-runtime/hooks/ComposerContext.ts +0 -131
  139. package/src/legacy-runtime/hooks/MessageContext.ts +0 -127
  140. package/src/legacy-runtime/hooks/MessagePartContext.ts +0 -32
  141. package/src/legacy-runtime/hooks/ThreadContext.ts +0 -120
  142. package/src/legacy-runtime/hooks/ThreadListItemContext.ts +0 -36
@@ -162,7 +162,7 @@ function InlineRenderer({
162
162
  sendMessage: (params) => {
163
163
  const text = extractSendMessageText(params);
164
164
  if (!text) return { ok: false, reason: "unrecognised params shape" };
165
- aui.thread().append({ content: [{ type: "text", text }] });
165
+ aui.thread.append({ content: [{ type: "text", text }] });
166
166
  return { ok: true };
167
167
  },
168
168
  callTool: (params) =>
@@ -14,7 +14,7 @@ const { registerMock, auiMock } = vi.hoisted(() => {
14
14
  return {
15
15
  registerMock: register,
16
16
  auiMock: {
17
- modelContext: () => ({ register }),
17
+ modelContext: { register },
18
18
  },
19
19
  };
20
20
  });
@@ -104,7 +104,7 @@ export const makeAssistantVisible = <T extends ComponentType<any>>(
104
104
 
105
105
  const { clickable, editable } = config ?? {};
106
106
  useEffect(() => {
107
- return aui.modelContext().register({
107
+ return aui.modelContext.register({
108
108
  getModelContext: () => {
109
109
  return {
110
110
  tools: {
@@ -23,7 +23,7 @@ const useActionBarExportMarkdown = ({
23
23
  });
24
24
 
25
25
  const callback = useCallback(async () => {
26
- const content = aui.message().getCopyText();
26
+ const content = aui.message.getCopyText();
27
27
  if (!content) return;
28
28
 
29
29
  if (onExport) {
@@ -12,7 +12,7 @@ const useAttachmentRemove = () => {
12
12
  const aui = useAui();
13
13
 
14
14
  const handleRemoveAttachment = useCallback(() => {
15
- aui.attachment().remove();
15
+ aui.attachment.remove();
16
16
  }, [aui]);
17
17
 
18
18
  return handleRemoveAttachment;
@@ -13,7 +13,7 @@ const useChainOfThoughtAccordionTrigger = () => {
13
13
  const collapsed = useAuiState((s) => s.chainOfThought.collapsed);
14
14
 
15
15
  const callback = useCallback(() => {
16
- aui.chainOfThought().setCollapsed(!collapsed);
16
+ aui.chainOfThought.setCollapsed(!collapsed);
17
17
  }, [aui, collapsed]);
18
18
 
19
19
  return callback;
@@ -30,9 +30,9 @@ vi.mock("@assistant-ui/store", async (importOriginal) => {
30
30
  return {
31
31
  ...actual,
32
32
  useAui: () => ({
33
- composer: () => ({
33
+ composer: {
34
34
  getState: () => ({ attachmentAccept: "*" }),
35
- }),
35
+ },
36
36
  }),
37
37
  };
38
38
  });
@@ -24,7 +24,7 @@ const useComposerAddAttachment = ({
24
24
  input.multiple = multiple;
25
25
  input.hidden = true;
26
26
 
27
- const attachmentAccept = aui.composer().getState().attachmentAccept;
27
+ const attachmentAccept = aui.composer.getState().attachmentAccept;
28
28
  if (attachmentAccept !== "*") {
29
29
  input.accept = attachmentAccept;
30
30
  }
@@ -15,9 +15,9 @@ vi.mock("@assistant-ui/store", async (importOriginal) => {
15
15
  return {
16
16
  ...actual,
17
17
  useAui: () => ({
18
- composer: () => ({
18
+ composer: {
19
19
  addAttachment,
20
- }),
20
+ },
21
21
  }),
22
22
  };
23
23
  });
@@ -70,7 +70,7 @@ export const ComposerPrimitiveAttachmentDropzone = forwardRef<
70
70
  await Promise.all(
71
71
  files.map(async (file) => {
72
72
  try {
73
- await aui.composer().addAttachment(file);
73
+ await aui.composer.addAttachment(file);
74
74
  } catch {
75
75
  // The composer runtime emits composer.attachmentAddError before rejecting;
76
76
  // the readonly and empty-thread stubs only throw.
@@ -38,16 +38,16 @@ let pluginRegistry: { getPlugins: () => (typeof plugin)[] } | null = null;
38
38
 
39
39
  vi.mock("@assistant-ui/store", () => {
40
40
  const aui = {
41
- composer: () => ({
42
- setText,
41
+ composer: {
42
+ setText: (text: string) => setText(text),
43
43
  getState: () => composerState,
44
44
  cancel: () => {},
45
- send: sendSpy,
46
- addAttachment,
47
- }),
48
- thread: () => ({
45
+ send: (options?: { steer?: boolean }) => sendSpy(options),
46
+ addAttachment: (file: File) => addAttachment(file),
47
+ },
48
+ thread: {
49
49
  getState: () => threadState,
50
- }),
50
+ },
51
51
  on: () => () => {},
52
52
  };
53
53
  type Selector<T> = (s: {
@@ -208,7 +208,7 @@ export const ComposerPrimitiveInput = forwardRef<
208
208
 
209
209
  if (!cancelOnEscape) return;
210
210
 
211
- const composer = aui.composer();
211
+ const composer = aui.composer;
212
212
  if (composer.getState().canCancel) {
213
213
  composer.cancel();
214
214
  e.preventDefault();
@@ -229,7 +229,7 @@ export const ComposerPrimitiveInput = forwardRef<
229
229
  }
230
230
 
231
231
  if (e.key === "Enter") {
232
- const threadState = aui.thread().getState();
232
+ const threadState = aui.thread.getState();
233
233
  const hasQueue = threadState.capabilities.queue;
234
234
 
235
235
  // Steer hotkey: Cmd/Ctrl+Shift+Enter (respects submitMode="none" and canSend)
@@ -238,10 +238,10 @@ export const ComposerPrimitiveInput = forwardRef<
238
238
  (e.ctrlKey || e.metaKey) &&
239
239
  hasQueue &&
240
240
  declaredSubmitMode !== "none" &&
241
- aui.composer().getState().canSend
241
+ aui.composer.getState().canSend
242
242
  ) {
243
243
  e.preventDefault();
244
- aui.composer().send({ steer: true });
244
+ aui.composer.send({ steer: true });
245
245
  return;
246
246
  }
247
247
 
@@ -267,7 +267,7 @@ export const ComposerPrimitiveInput = forwardRef<
267
267
 
268
268
  const handlePaste = async (e: ClipboardEvent<HTMLTextAreaElement>) => {
269
269
  if (!addAttachmentOnPaste) return;
270
- const threadCapabilities = aui.thread().getState().capabilities;
270
+ const threadCapabilities = aui.thread.getState().capabilities;
271
271
  const files = Array.from(e.clipboardData?.files || []);
272
272
 
273
273
  if (threadCapabilities.attachments && files.length > 0) {
@@ -275,7 +275,7 @@ export const ComposerPrimitiveInput = forwardRef<
275
275
  await Promise.all(
276
276
  files.map(async (file) => {
277
277
  try {
278
- await aui.composer().addAttachment(file);
278
+ await aui.composer.addAttachment(file);
279
279
  } catch {
280
280
  // The composer runtime emits composer.attachmentAddError before rejecting.
281
281
  }
@@ -319,7 +319,7 @@ export const ComposerPrimitiveInput = forwardRef<
319
319
 
320
320
  useOnScrollToBottom(() => {
321
321
  if (
322
- aui.composer().getState().type === "thread" &&
322
+ aui.composer.getState().type === "thread" &&
323
323
  unstable_focusOnScrollToBottom
324
324
  ) {
325
325
  focus();
@@ -328,7 +328,7 @@ export const ComposerPrimitiveInput = forwardRef<
328
328
 
329
329
  useEffect(() => {
330
330
  if (
331
- aui.composer().getState().type !== "thread" ||
331
+ aui.composer.getState().type !== "thread" ||
332
332
  !unstable_focusOnRunStart
333
333
  )
334
334
  return undefined;
@@ -338,7 +338,7 @@ export const ComposerPrimitiveInput = forwardRef<
338
338
 
339
339
  useEffect(() => {
340
340
  if (
341
- aui.composer().getState().type !== "thread" ||
341
+ aui.composer.getState().type !== "thread" ||
342
342
  !unstable_focusOnThreadSwitched
343
343
  )
344
344
  return undefined;
@@ -362,7 +362,7 @@ export const ComposerPrimitiveInput = forwardRef<
362
362
  onChange: composeEventHandlers(
363
363
  onChange,
364
364
  (e: React.ChangeEvent<HTMLTextAreaElement>) => {
365
- if (!aui.composer().getState().isEditing) return;
365
+ if (!aui.composer.getState().isEditing) return;
366
366
  const nativeIsComposing =
367
367
  (e.nativeEvent as { isComposing?: boolean }).isComposing === true;
368
368
  // recover stuck compositionRef when the browser drops compositionend
@@ -372,7 +372,7 @@ export const ComposerPrimitiveInput = forwardRef<
372
372
  const isComposing = nativeIsComposing || compositionRef.current;
373
373
  // keep controlled value in sync mid-IME so react does not reset the textarea to a stale value
374
374
  flushTapSync(() => {
375
- aui.composer().setText(e.target.value);
375
+ aui.composer.setText(e.target.value);
376
376
  });
377
377
  if (isComposing) return;
378
378
  const pos = e.target.selectionStart ?? e.target.value.length;
@@ -396,10 +396,10 @@ export const ComposerPrimitiveInput = forwardRef<
396
396
  .onCompositionEnd,
397
397
  (e: React.CompositionEvent<HTMLTextAreaElement>) => {
398
398
  compositionRef.current = false;
399
- if (!aui.composer().getState().isEditing) return;
399
+ if (!aui.composer.getState().isEditing) return;
400
400
  const target = e.target as HTMLTextAreaElement;
401
401
  flushTapSync(() => {
402
- aui.composer().setText(target.value);
402
+ aui.composer.setText(target.value);
403
403
  });
404
404
  const pos = target.selectionStart ?? target.value.length;
405
405
  if (pluginRegistry) {
@@ -93,7 +93,7 @@ export const ComposerPrimitiveQuoteDismiss = forwardRef<
93
93
  >(({ onClick, ...props }, forwardedRef) => {
94
94
  const aui = useAui();
95
95
  const handleDismiss = useCallback(() => {
96
- aui.composer().setQuote(undefined);
96
+ aui.composer.setQuote(undefined);
97
97
  }, [aui]);
98
98
 
99
99
  return (
@@ -14,7 +14,7 @@ const useComposerStopDictation = () => {
14
14
  const isDictating = useAuiState((s) => s.composer.dictation != null);
15
15
 
16
16
  const callback = useCallback(() => {
17
- aui.composer().stopDictation();
17
+ aui.composer.stopDictation();
18
18
  }, [aui]);
19
19
 
20
20
  if (!isDictating) return null;
@@ -72,7 +72,7 @@ const useTriggerSelectionResource = ({
72
72
  return;
73
73
  }
74
74
 
75
- const currentText = aui.composer().getState().text;
75
+ const currentText = aui.composer.getState().text;
76
76
  const before = currentText.slice(0, trigger.offset);
77
77
  const after = currentText.slice(
78
78
  trigger.offset + triggerChar.length + trigger.query.length,
@@ -80,11 +80,9 @@ const useTriggerSelectionResource = ({
80
80
 
81
81
  const insertDirective = () => {
82
82
  const directive = behavior.formatter.serialize(item);
83
- aui
84
- .composer()
85
- .setText(
86
- before + directive + (after.startsWith(" ") ? after : ` ${after}`),
87
- );
83
+ aui.composer.setText(
84
+ before + directive + (after.startsWith(" ") ? after : ` ${after}`),
85
+ );
88
86
  };
89
87
 
90
88
  if (behavior.kind === "directive") {
@@ -92,9 +90,9 @@ const useTriggerSelectionResource = ({
92
90
  behavior.onInserted?.(item);
93
91
  } else {
94
92
  if (behavior.removeOnExecute) {
95
- aui
96
- .composer()
97
- .setText(before + (after.startsWith(" ") ? after.slice(1) : after));
93
+ aui.composer.setText(
94
+ before + (after.startsWith(" ") ? after.slice(1) : after),
95
+ );
98
96
  } else {
99
97
  // Leave directive chip in the composer as an audit trail
100
98
  insertDirective();
@@ -230,11 +230,9 @@ const ToolUIDisplay = ({
230
230
  }: {
231
231
  Fallback: ToolCallMessagePartComponent | undefined;
232
232
  } & ToolCallMessagePartProps) => {
233
- const Render = useAuiState((s) => {
234
- const Render = s.tools.tools[props.toolName] ?? Fallback;
235
- if (Array.isArray(Render)) return Render[0] ?? Fallback;
236
- return Render;
237
- });
233
+ const Render = useAuiState(
234
+ (s) => s.tools.toolUIs[props.toolName]?.[0]?.render ?? Fallback,
235
+ );
238
236
  if (!Render) return null;
239
237
  return <Render {...props} />;
240
238
  };
@@ -292,9 +290,9 @@ const MessagePartComponent: FC<MessagePartComponentProps> = ({
292
290
 
293
291
  const type = part.type;
294
292
  if (type === "tool-call") {
295
- const addResult = aui.part().addToolResult;
296
- const resume = aui.part().resumeToolCall;
297
- const respondToApproval = aui.part().respondToToolApproval;
293
+ const addResult = aui.part.addToolResult;
294
+ const resume = aui.part.resumeToolCall;
295
+ const respondToApproval = aui.part.respondToToolApproval;
298
296
  if ("Override" in tools)
299
297
  return (
300
298
  <tools.Override
@@ -23,7 +23,7 @@ type ThreadViewportStore = NonNullable<
23
23
 
24
24
  const useIsHoveringRef = () => {
25
25
  const aui = useAui();
26
- const message = useAuiState(() => aui.message());
26
+ const message = useAuiState(() => aui.message);
27
27
 
28
28
  const callbackRef = useCallback(
29
29
  (el: HTMLElement) => {
@@ -12,7 +12,7 @@ const useQueueItemRemove = () => {
12
12
  const aui = useAui();
13
13
 
14
14
  const callback = useCallback(() => {
15
- aui.queueItem().remove();
15
+ aui.queueItem.remove();
16
16
  }, [aui]);
17
17
 
18
18
  return callback;
@@ -12,7 +12,7 @@ const useQueueItemSteer = () => {
12
12
  const aui = useAui();
13
13
 
14
14
  const callback = useCallback(() => {
15
- aui.queueItem().steer();
15
+ aui.queueItem.steer();
16
16
  }, [aui]);
17
17
 
18
18
  return callback;
@@ -40,7 +40,7 @@ export const SelectionToolbarPrimitiveQuote = forwardRef<
40
40
  const handleClick = useCallback(() => {
41
41
  if (!info) return;
42
42
 
43
- aui.thread().composer().setQuote({
43
+ aui.thread.composer().setQuote({
44
44
  text: info.text,
45
45
  messageId: info.messageId,
46
46
  });
@@ -34,24 +34,24 @@ const useSuggestionTrigger = ({
34
34
  const resolvedSend = send ?? false;
35
35
 
36
36
  const callback = useCallback(() => {
37
- const isRunning = aui.thread().getState().isRunning;
37
+ const isRunning = aui.thread.getState().isRunning;
38
38
 
39
39
  if (resolvedSend && !isRunning) {
40
- aui.thread().append({
40
+ aui.thread.append({
41
41
  content: [{ type: "text", text: prompt }],
42
- runConfig: aui.composer().getState().runConfig,
42
+ runConfig: aui.composer.getState().runConfig,
43
43
  });
44
44
  if (clearComposer) {
45
- aui.composer().setText("");
45
+ aui.composer.setText("");
46
46
  }
47
47
  } else {
48
48
  if (clearComposer) {
49
- aui.composer().setText(prompt);
49
+ aui.composer.setText(prompt);
50
50
  } else {
51
- const currentText = aui.composer().getState().text;
52
- aui
53
- .composer()
54
- .setText(currentText.trim() ? `${currentText} ${prompt}` : prompt);
51
+ const currentText = aui.composer.getState().text;
52
+ aui.composer.setText(
53
+ currentText.trim() ? `${currentText} ${prompt}` : prompt,
54
+ );
55
55
  }
56
56
  }
57
57
  }, [aui, resolvedSend, clearComposer, prompt]);
@@ -66,7 +66,7 @@ const GlobalFallbackDataUI: FC<{ render: DataMessagePartComponent }> = ({
66
66
  render,
67
67
  }) => {
68
68
  const aui = useAui();
69
- useEffect(() => aui.dataRenderers().setFallbackDataUI(render), [aui, render]);
69
+ useEffect(() => aui.dataRenderers.setFallbackDataUI(render), [aui, render]);
70
70
  return null;
71
71
  };
72
72
 
@@ -6,9 +6,9 @@ import { afterEach, describe, expect, it, vi } from "vitest";
6
6
  import { useRemoteThreadListRuntime } from "@assistant-ui/core/react";
7
7
  import { makeAdapter } from "./remote-thread-list-test-helpers";
8
8
  import type { AssistantRuntime } from "@assistant-ui/core";
9
+ import { useAui } from "@assistant-ui/store";
9
10
  import { AssistantRuntimeProvider } from "../context";
10
11
  import { useLocalRuntime } from "../legacy-runtime/runtime-cores/local/useLocalRuntime";
11
- import { useAssistantRuntime } from "../legacy-runtime/hooks/AssistantContext";
12
12
  import type { ChatModelAdapter, RemoteThreadListAdapter } from "../index";
13
13
 
14
14
  const noOpAdapter: ChatModelAdapter = {
@@ -34,8 +34,8 @@ const RuntimeProvider: FC<
34
34
  const RuntimeCapture: FC<{
35
35
  runtimeRef: { current: AssistantRuntime | null };
36
36
  }> = ({ runtimeRef }) => {
37
- const runtime = useAssistantRuntime();
38
- runtimeRef.current = runtime;
37
+ const aui = useAui();
38
+ runtimeRef.current = aui.threads.__internal_getAssistantRuntime?.() ?? null;
39
39
  return null;
40
40
  };
41
41
 
@@ -74,14 +74,12 @@ describe("ExternalThread attachments", () => {
74
74
  }
75
75
 
76
76
  await waitFor(() => {
77
- expect(aui().thread().composer().getState().attachments[0]).toMatchObject(
78
- {
79
- type: "document",
80
- name: "notes.txt",
81
- contentType: "text/plain",
82
- content: [{ type: "text", text: "hello" }],
83
- },
84
- );
77
+ expect(aui().thread.composer().getState().attachments[0]).toMatchObject({
78
+ type: "document",
79
+ name: "notes.txt",
80
+ contentType: "text/plain",
81
+ content: [{ type: "text", text: "hello" }],
82
+ });
85
83
  });
86
84
  });
87
85
 
@@ -94,9 +92,9 @@ describe("ExternalThread attachments", () => {
94
92
  content: [{ type: "text", text: "implementation detail" }],
95
93
  } as File;
96
94
 
97
- await act(() => aui().thread().composer().addAttachment(foreignFile));
95
+ await act(() => aui().thread.composer().addAttachment(foreignFile));
98
96
 
99
- expect(aui().thread().composer().getState().attachments[0]).toMatchObject({
97
+ expect(aui().thread.composer().getState().attachments[0]).toMatchObject({
100
98
  type: "file",
101
99
  name: "photo.png",
102
100
  contentType: "image/png",
@@ -126,7 +124,7 @@ describe("ExternalThread attachments", () => {
126
124
  "File type application/zip is not accepted. Accepted types: image/*",
127
125
  );
128
126
  expect(add).not.toHaveBeenCalled();
129
- expect(aui().thread().composer().getState().attachments).toHaveLength(0);
127
+ expect(aui().thread.composer().getState().attachments).toHaveLength(0);
130
128
  });
131
129
 
132
130
  it("rejects prepared attachments that do not match the adapter accept string", async () => {
@@ -151,7 +149,7 @@ describe("ExternalThread attachments", () => {
151
149
  ).rejects.toThrow(
152
150
  "File type text/plain is not accepted. Accepted types: .pdf",
153
151
  );
154
- expect(aui().thread().composer().getState().attachments).toHaveLength(0);
152
+ expect(aui().thread.composer().getState().attachments).toHaveLength(0);
155
153
  });
156
154
 
157
155
  it("restores the draft when an attachment upload fails on send", async () => {
@@ -192,21 +190,21 @@ describe("ExternalThread attachments", () => {
192
190
  .addAttachment(new File(["data"], "notes.txt", { type: "text/plain" })),
193
191
  );
194
192
  await act(async () => {
195
- aui().thread().composer().setText("hello");
193
+ aui().thread.composer().setText("hello");
196
194
  });
197
195
  await act(async () => {
198
- aui().thread().composer().send();
196
+ aui().thread.composer().send();
199
197
  });
200
198
 
201
199
  // The draft is cleared optimistically while the upload runs.
202
- expect(aui().thread().composer().getState().text).toBe("");
200
+ expect(aui().thread.composer().getState().text).toBe("");
203
201
 
204
202
  await act(async () => {
205
203
  rejectSend(new Error("upload failed"));
206
204
  });
207
205
 
208
206
  await waitFor(() => {
209
- const state = aui().thread().composer().getState();
207
+ const state = aui().thread.composer().getState();
210
208
  expect(state.text).toBe("hello");
211
209
  expect(state.attachments).toHaveLength(1);
212
210
  });
@@ -255,13 +253,13 @@ describe("ExternalThread attachments", () => {
255
253
  ),
256
254
  );
257
255
 
258
- await act(() => invoke(aui().thread().composer()));
256
+ await act(() => invoke(aui().thread.composer()));
259
257
 
260
258
  expect(remove).toHaveBeenCalledTimes(1);
261
259
  expect(remove).toHaveBeenCalledWith(
262
260
  expect.objectContaining({ id: "att-1" }),
263
261
  );
264
- expect(aui().thread().composer().getState().attachments).toHaveLength(0);
262
+ expect(aui().thread.composer().getState().attachments).toHaveLength(0);
265
263
  },
266
264
  );
267
265
 
@@ -297,7 +295,7 @@ describe("ExternalThread attachments", () => {
297
295
  },
298
296
  });
299
297
 
300
- const composer = () => aui().thread().message({ id: "u1" }).composer();
298
+ const composer = () => aui().thread.message({ id: "u1" }).composer();
301
299
  await act(async () => {
302
300
  composer().beginEdit();
303
301
  });
@@ -349,18 +347,18 @@ describe("ExternalThread attachments", () => {
349
347
  .addAttachment(new File(["data"], "notes.txt", { type: "text/plain" })),
350
348
  );
351
349
  await act(async () => {
352
- aui().thread().composer().setText("hello");
353
- aui().thread().composer().send();
350
+ aui().thread.composer().setText("hello");
351
+ aui().thread.composer().send();
354
352
  });
355
353
  await act(async () => {
356
- aui().thread().composer().setText("meanwhile");
354
+ aui().thread.composer().setText("meanwhile");
357
355
  });
358
356
 
359
357
  await act(async () => {
360
358
  rejectSend(new Error("upload failed"));
361
359
  });
362
360
 
363
- const state = aui().thread().composer().getState();
361
+ const state = aui().thread.composer().getState();
364
362
  expect(state.text).toBe("hello\nmeanwhile");
365
363
  expect(state.attachments).toHaveLength(1);
366
364
  expect(state.attachments[0]).toMatchObject({ id: "att-1" });
@@ -66,18 +66,18 @@ const adapterFor = (
66
66
  describe("ExternalThread branches", () => {
67
67
  it("defaults to single-branch state without an adapter", () => {
68
68
  const { aui } = renderThread(baseProps());
69
- const state = aui().thread().getState();
69
+ const state = aui().thread.getState();
70
70
  expect(state.messages[1]!.branchNumber).toBe(1);
71
71
  expect(state.messages[1]!.branchCount).toBe(1);
72
72
  expect(state.capabilities.switchToBranch).toBe(false);
73
73
  expect(() =>
74
- aui().thread().message({ index: 1 }).switchToBranch({ position: "next" }),
74
+ aui().thread.message({ index: 1 }).switchToBranch({ position: "next" }),
75
75
  ).not.toThrow();
76
76
  });
77
77
 
78
78
  it("derives branchNumber and branchCount from the adapter", () => {
79
79
  const { aui } = renderThread(baseProps(adapterFor(["a1", "a2", "a3"])));
80
- const state = aui().thread().getState();
80
+ const state = aui().thread.getState();
81
81
  expect(state.messages[1]!.branchNumber).toBe(2);
82
82
  expect(state.messages[1]!.branchCount).toBe(3);
83
83
  expect(state.capabilities.switchToBranch).toBe(true);
@@ -88,7 +88,7 @@ describe("ExternalThread branches", () => {
88
88
  const { aui } = renderThread(
89
89
  baseProps(adapterFor(["a1", "a2", "a3"], switchToBranch)),
90
90
  );
91
- const msg = () => aui().thread().message({ index: 1 });
91
+ const msg = () => aui().thread.message({ index: 1 });
92
92
 
93
93
  msg().switchToBranch({ position: "previous" });
94
94
  expect(switchToBranch).toHaveBeenLastCalledWith("a1");
@@ -101,7 +101,7 @@ describe("ExternalThread branches", () => {
101
101
  isRunning: false,
102
102
  branches: adapterFor(["a1", "a2"], switchToBranch),
103
103
  });
104
- edge.aui().thread().message({ index: 1 }).switchToBranch({
104
+ edge.aui().thread.message({ index: 1 }).switchToBranch({
105
105
  position: "previous",
106
106
  });
107
107
  expect(switchToBranch).not.toHaveBeenCalled();
@@ -112,7 +112,7 @@ describe("ExternalThread branches", () => {
112
112
  const { aui } = renderThread(
113
113
  baseProps(adapterFor(["a1", "a2"], switchToBranch)),
114
114
  );
115
- const msg = () => aui().thread().message({ index: 1 });
115
+ const msg = () => aui().thread.message({ index: 1 });
116
116
 
117
117
  msg().switchToBranch({ branchId: "not-in-the-list" });
118
118
  expect(switchToBranch).toHaveBeenLastCalledWith("not-in-the-list");
@@ -130,11 +130,11 @@ describe("ExternalThread branches", () => {
130
130
  switchToBranch,
131
131
  }),
132
132
  );
133
- const state = aui().thread().getState();
133
+ const state = aui().thread.getState();
134
134
  expect(state.messages[1]!.branchNumber).toBe(1);
135
135
  expect(state.messages[1]!.branchCount).toBe(1);
136
136
 
137
- aui().thread().message({ index: 1 }).switchToBranch({ position: "next" });
137
+ aui().thread.message({ index: 1 }).switchToBranch({ position: "next" });
138
138
  expect(switchToBranch).not.toHaveBeenCalled();
139
139
  });
140
140
 
@@ -145,14 +145,14 @@ describe("ExternalThread branches", () => {
145
145
  isRunning: false,
146
146
  branches: adapterFor(["a1", "a2", "a3"]),
147
147
  });
148
- const before = aui().thread().getState().messages[1];
148
+ const before = aui().thread.getState().messages[1];
149
149
 
150
150
  rerender({
151
151
  messages,
152
152
  isRunning: false,
153
153
  branches: adapterFor(["a1", "a2", "a3"]),
154
154
  });
155
- const after = aui().thread().getState().messages[1];
155
+ const after = aui().thread.getState().messages[1];
156
156
 
157
157
  expect(after!.branchNumber).toBe(2);
158
158
  expect(before!.parts[0]).toBe(after!.parts[0]);