@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.
- package/dist/client/ExternalThread.js +4 -3
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/client/InMemoryThreadList.js +3 -3
- package/dist/client/InMemoryThreadList.js.map +1 -1
- package/dist/context/index.d.ts +1 -8
- package/dist/context/index.js +1 -8
- package/dist/hooks/useToolCallElapsed.js +24 -29
- package/dist/hooks/useToolCallElapsed.js.map +1 -1
- package/dist/index.d.ts +1 -8
- package/dist/index.js +1 -8
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +3 -3
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js +1 -1
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +26 -25
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js +3 -3
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js +3 -3
- package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +3 -3
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +3 -3
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +13 -13
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js +1 -1
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.js +3 -3
- package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.js +3 -3
- package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +67 -89
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/message/MessageRoot.js +3 -3
- package/dist/primitives/message/MessageRoot.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js +3 -3
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js +3 -3
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js +16 -15
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
- package/dist/unstable/useComposerInput.js +2 -2
- package/dist/unstable/useComposerInput.js.map +1 -1
- package/dist/unstable/useComposerInputHistory.js +6 -6
- package/dist/unstable/useComposerInputHistory.js.map +1 -1
- package/dist/unstable/useMentionAdapter.js +1 -1
- package/dist/unstable/useMentionAdapter.js.map +1 -1
- package/dist/utils/smooth/SmoothContext.js +3 -3
- package/dist/utils/smooth/SmoothContext.js.map +1 -1
- package/dist/utils/smooth/useSmooth.js +1 -1
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
- package/package.json +5 -5
- package/src/client/ExternalThread.ts +5 -3
- package/src/client/InMemoryThreadList.ts +3 -3
- package/src/context/index.ts +0 -44
- package/src/hooks/useToolCallElapsed.ts +9 -12
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +5 -9
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +1 -1
- package/src/mcp-apps/McpAppRenderer.tsx +1 -1
- package/src/model-context/makeAssistantVisible.test.tsx +1 -1
- package/src/model-context/makeAssistantVisible.tsx +1 -1
- package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +1 -1
- package/src/primitives/attachment/AttachmentRemove.ts +1 -1
- package/src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.ts +1 -1
- package/src/primitives/composer/ComposerAddAttachment.test.tsx +2 -2
- package/src/primitives/composer/ComposerAddAttachment.ts +1 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +2 -2
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +1 -1
- package/src/primitives/composer/ComposerInput.test.tsx +7 -7
- package/src/primitives/composer/ComposerInput.tsx +13 -13
- package/src/primitives/composer/ComposerQuote.tsx +1 -1
- package/src/primitives/composer/ComposerStopDictation.ts +1 -1
- package/src/primitives/composer/trigger/triggerSelectionResource.ts +7 -9
- package/src/primitives/message/MessagePartsGrouped.tsx +6 -8
- package/src/primitives/message/MessageRoot.tsx +1 -1
- package/src/primitives/queueItem/QueueItemRemove.ts +1 -1
- package/src/primitives/queueItem/QueueItemSteer.ts +1 -1
- package/src/primitives/selectionToolbar/SelectionToolbarQuote.tsx +1 -1
- package/src/primitives/suggestion/SuggestionTrigger.ts +9 -9
- package/src/tests/DataRenderers.test.tsx +1 -1
- package/src/tests/RemoteThreadListRuntime.deferredProvider.test.tsx +3 -3
- package/src/tests/external-thread-attachments.test.tsx +21 -23
- package/src/tests/external-thread-branches.test.tsx +10 -10
- package/src/tests/external-thread-parity.test.tsx +18 -20
- package/src/tests/local-runtime-queue.test.tsx +29 -34
- package/src/tests/messagePartSwitchRace.test.tsx +30 -13
- package/src/unstable/useComposerInput.test.tsx +2 -2
- package/src/unstable/useComposerInput.ts +2 -2
- package/src/unstable/useComposerInputHistory.test.tsx +3 -3
- package/src/unstable/useComposerInputHistory.ts +6 -6
- package/src/unstable/useMentionAdapter.ts +1 -1
- package/src/utils/smooth/SmoothContext.tsx +1 -1
- package/src/utils/smooth/useSmooth.test.tsx +1 -1
- package/src/utils/smooth/useSmooth.ts +1 -1
- package/dist/context/react/utils/createStateHookForRuntime.d.ts +0 -34
- package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +0 -1
- package/dist/context/react/utils/createStateHookForRuntime.js +0 -21
- package/dist/context/react/utils/createStateHookForRuntime.js.map +0 -1
- package/dist/legacy-runtime/hooks/AssistantContext.d.ts +0 -74
- package/dist/legacy-runtime/hooks/AssistantContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/AssistantContext.js +0 -29
- package/dist/legacy-runtime/hooks/AssistantContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/AttachmentContext.d.ts +0 -1007
- package/dist/legacy-runtime/hooks/AttachmentContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/AttachmentContext.js +0 -47
- package/dist/legacy-runtime/hooks/AttachmentContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/ComposerContext.d.ts +0 -141
- package/dist/legacy-runtime/hooks/ComposerContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/ComposerContext.js +0 -65
- package/dist/legacy-runtime/hooks/ComposerContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/MessageContext.d.ts +0 -155
- package/dist/legacy-runtime/hooks/MessageContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/MessageContext.js +0 -68
- package/dist/legacy-runtime/hooks/MessageContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/MessagePartContext.d.ts +0 -44
- package/dist/legacy-runtime/hooks/MessagePartContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/MessagePartContext.js +0 -26
- package/dist/legacy-runtime/hooks/MessagePartContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadContext.d.ts +0 -139
- package/dist/legacy-runtime/hooks/ThreadContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadContext.js +0 -84
- package/dist/legacy-runtime/hooks/ThreadContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts +0 -44
- package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadListItemContext.js +0 -26
- package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +0 -1
- package/src/context/react/utils/createStateHookForRuntime.ts +0 -82
- package/src/legacy-runtime/hooks/AssistantContext.ts +0 -67
- package/src/legacy-runtime/hooks/AttachmentContext.ts +0 -99
- package/src/legacy-runtime/hooks/ComposerContext.ts +0 -131
- package/src/legacy-runtime/hooks/MessageContext.ts +0 -127
- package/src/legacy-runtime/hooks/MessagePartContext.ts +0 -32
- package/src/legacy-runtime/hooks/ThreadContext.ts +0 -120
- 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
|
|
165
|
+
aui.thread.append({ content: [{ type: "text", text }] });
|
|
166
166
|
return { ok: true };
|
|
167
167
|
},
|
|
168
168
|
callTool: (params) =>
|
|
@@ -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
|
|
107
|
+
return aui.modelContext.register({
|
|
108
108
|
getModelContext: () => {
|
|
109
109
|
return {
|
|
110
110
|
tools: {
|
|
@@ -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
|
|
16
|
+
aui.chainOfThought.setCollapsed(!collapsed);
|
|
17
17
|
}, [aui, collapsed]);
|
|
18
18
|
|
|
19
19
|
return callback;
|
|
@@ -24,7 +24,7 @@ const useComposerAddAttachment = ({
|
|
|
24
24
|
input.multiple = multiple;
|
|
25
25
|
input.hidden = true;
|
|
26
26
|
|
|
27
|
-
const attachmentAccept = aui.composer
|
|
27
|
+
const attachmentAccept = aui.composer.getState().attachmentAccept;
|
|
28
28
|
if (attachmentAccept !== "*") {
|
|
29
29
|
input.accept = attachmentAccept;
|
|
30
30
|
}
|
|
@@ -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
|
|
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
|
|
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
|
|
241
|
+
aui.composer.getState().canSend
|
|
242
242
|
) {
|
|
243
243
|
e.preventDefault();
|
|
244
|
-
aui.composer
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
399
|
+
if (!aui.composer.getState().isEditing) return;
|
|
400
400
|
const target = e.target as HTMLTextAreaElement;
|
|
401
401
|
flushTapSync(() => {
|
|
402
|
-
aui.composer
|
|
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
|
|
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
|
|
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
|
|
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
|
-
.
|
|
85
|
-
|
|
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
|
-
.
|
|
97
|
-
|
|
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(
|
|
234
|
-
|
|
235
|
-
|
|
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
|
|
296
|
-
const resume = aui.part
|
|
297
|
-
const respondToApproval = aui.part
|
|
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) => {
|
|
@@ -34,24 +34,24 @@ const useSuggestionTrigger = ({
|
|
|
34
34
|
const resolvedSend = send ?? false;
|
|
35
35
|
|
|
36
36
|
const callback = useCallback(() => {
|
|
37
|
-
const isRunning = aui.thread
|
|
37
|
+
const isRunning = aui.thread.getState().isRunning;
|
|
38
38
|
|
|
39
39
|
if (resolvedSend && !isRunning) {
|
|
40
|
-
aui.thread
|
|
40
|
+
aui.thread.append({
|
|
41
41
|
content: [{ type: "text", text: prompt }],
|
|
42
|
-
runConfig: aui.composer
|
|
42
|
+
runConfig: aui.composer.getState().runConfig,
|
|
43
43
|
});
|
|
44
44
|
if (clearComposer) {
|
|
45
|
-
aui.composer
|
|
45
|
+
aui.composer.setText("");
|
|
46
46
|
}
|
|
47
47
|
} else {
|
|
48
48
|
if (clearComposer) {
|
|
49
|
-
aui.composer
|
|
49
|
+
aui.composer.setText(prompt);
|
|
50
50
|
} else {
|
|
51
|
-
const currentText = aui.composer
|
|
52
|
-
aui
|
|
53
|
-
.
|
|
54
|
-
|
|
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
|
|
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
|
|
38
|
-
runtimeRef.current =
|
|
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
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
95
|
+
await act(() => aui().thread.composer().addAttachment(foreignFile));
|
|
98
96
|
|
|
99
|
-
expect(aui().thread
|
|
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
|
|
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
|
|
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
|
|
193
|
+
aui().thread.composer().setText("hello");
|
|
196
194
|
});
|
|
197
195
|
await act(async () => {
|
|
198
|
-
aui().thread
|
|
196
|
+
aui().thread.composer().send();
|
|
199
197
|
});
|
|
200
198
|
|
|
201
199
|
// The draft is cleared optimistically while the upload runs.
|
|
202
|
-
expect(aui().thread
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
353
|
-
aui().thread
|
|
350
|
+
aui().thread.composer().setText("hello");
|
|
351
|
+
aui().thread.composer().send();
|
|
354
352
|
});
|
|
355
353
|
await act(async () => {
|
|
356
|
-
aui().thread
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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]);
|