@assistant-ui/react 0.15.13 → 0.15.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant-transport.d.ts +32 -0
- package/dist/assistant-transport.d.ts.map +1 -0
- package/dist/assistant-transport.js +27 -0
- package/dist/assistant-transport.js.map +1 -0
- package/dist/context/providers/MessageProvider.js +1 -1
- package/dist/hooks/useMessageQuote.js +1 -1
- package/dist/hooks/useToolCallElapsed.js +1 -1
- package/dist/index.d.ts +5 -8
- package/dist/index.js +7 -9
- package/dist/legacy-runtime/AssistantRuntimeProvider.js +1 -1
- package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -1
- package/dist/mcp-apps/McpAppsRemoteHost.js +18 -2
- package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
- package/dist/mcp-apps/bridge.js +39 -24
- package/dist/mcp-apps/bridge.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +1 -1
- package/dist/primitives/actionBar/ActionBarCopy.js +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js +24 -37
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.js +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js +1 -1
- package/dist/primitives/attachment/AttachmentName.js +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js +1 -1
- package/dist/primitives/attachment/AttachmentThumb.js +1 -1
- package/dist/primitives/branchPicker/BranchPickerCount.js +1 -1
- package/dist/primitives/branchPicker/BranchPickerNumber.js +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.js +1 -1
- package/dist/primitives/composer/ComposerInput.js +2 -2
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js +1 -1
- package/dist/primitives/composer/ComposerRoot.js +1 -1
- package/dist/primitives/composer/ComposerStopDictation.js +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.js +1 -1
- package/dist/primitives/composer/useComposerInputState.js +1 -1
- package/dist/primitives/message/MessageIf.js +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +1 -1
- package/dist/primitives/message/MessageRoot.js +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js +1 -1
- package/dist/primitives/queueItem/QueueItemText.js +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.js +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.js +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js +1 -1
- package/dist/primitives/thread/ThreadIf.js +1 -1
- package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadRoot.js +41 -9
- package/dist/primitives/thread/ThreadRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +2 -13
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +23 -8
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.js +1 -1
- package/dist/sandbox-host/SandboxHost.js +2 -0
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/unstable/useComposerInput.d.ts +2 -2
- package/dist/unstable/useComposerInput.js +1 -1
- package/dist/unstable/useComposerInput.js.map +1 -1
- package/dist/unstable/useComposerInputHistory.js +1 -1
- package/dist/unstable/useComposerInputHistory.js.map +1 -1
- package/dist/unstable/useLiveCompletionAdapter.d.ts +5 -0
- package/dist/unstable/useLiveCompletionAdapter.d.ts.map +1 -1
- package/dist/unstable/useLiveCompletionAdapter.js +16 -3
- package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
- package/dist/unstable/useMentionAdapter.d.ts +1 -1
- package/dist/utils/smooth/SmoothContext.js +1 -1
- package/dist/utils/useToolArgsFieldStatus.js +1 -1
- package/package.json +9 -9
- package/src/assistant-transport.ts +94 -0
- package/src/index.ts +9 -4
- package/src/mcp-apps/McpAppsRemoteHost.test.ts +29 -3
- package/src/mcp-apps/McpAppsRemoteHost.ts +42 -7
- package/src/mcp-apps/bridge.test.ts +132 -0
- package/src/mcp-apps/bridge.ts +54 -30
- package/src/primitives/actionBar/ActionBarStopSpeaking.tsx +0 -9
- package/src/primitives/composer/ComposerInput.tsx +1 -1
- package/src/primitives/thread/ThreadRoot.test.tsx +251 -0
- package/src/primitives/thread/ThreadRoot.tsx +30 -0
- package/src/primitives/thread/ThreadViewport.tsx +1 -8
- package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +152 -0
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +26 -19
- package/src/sandbox-host/SandboxHost.test.tsx +29 -0
- package/src/sandbox-host/SandboxHost.tsx +2 -0
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +24 -3
- package/src/tests/RemoteThreadListRuntime.reloadMainThread.test.tsx +1 -2
- package/src/tests/augmentations.test.ts +49 -0
- package/src/tests/in-memory-thread-list.test.tsx +87 -3
- package/src/tests/threadMessageById.test.tsx +61 -1
- package/src/unstable/useComposerInput.ts +2 -2
- package/src/unstable/useComposerInputHistory.test.tsx +4 -4
- package/src/unstable/useComposerInputHistory.ts +1 -1
- package/src/unstable/useLiveCompletionAdapter.test.tsx +100 -0
- package/src/unstable/useLiveCompletionAdapter.ts +41 -17
- package/dist/client/InMemoryThreadList.d.ts +0 -12
- package/dist/client/InMemoryThreadList.d.ts.map +0 -1
- package/dist/client/InMemoryThreadList.js +0 -365
- package/dist/client/InMemoryThreadList.js.map +0 -1
- package/dist/legacy-runtime/cloud/auiV0.d.ts +0 -119
- package/dist/legacy-runtime/cloud/auiV0.d.ts.map +0 -1
- package/dist/legacy-runtime/cloud/auiV0.js +0 -137
- package/dist/legacy-runtime/cloud/auiV0.js.map +0 -1
- package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts +0 -16
- package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts.map +0 -1
- package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js +0 -36
- package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +0 -17
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +0 -55
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.d.ts +0 -11
- package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.d.ts.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js +0 -141
- package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts +0 -15
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +0 -81
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +0 -126
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.js +0 -0
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts +0 -14
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +0 -290
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +0 -7
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +0 -25
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.d.ts +0 -7
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.d.ts.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js +0 -27
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js.map +0 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.d.ts +0 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.js +0 -2
- package/src/client/InMemoryThreadList.ts +0 -241
- package/src/legacy-runtime/cloud/auiV0.ts +0 -331
- package/src/legacy-runtime/cloud/useCloudThreadListRuntime.ts +0 -31
- package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +0 -68
- package/src/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.test.ts +0 -430
- package/src/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.ts +0 -146
- package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +0 -113
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-delivery.test.tsx +0 -79
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-parentid.test.tsx +0 -97
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +0 -307
- package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +0 -170
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +0 -141
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +0 -585
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +0 -476
- package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +0 -20
- package/src/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.ts +0 -9
- package/src/legacy-runtime/runtime-cores/assistant-transport/utils.ts +0 -1
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
|
|
3
|
-
import { act, render, waitFor } from "@testing-library/react";
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import type { FC } from "react";
|
|
6
|
-
import { useAssistantTransportRuntime } from "./useAssistantTransportRuntime";
|
|
7
|
-
import type { AssistantRuntime } from "../../runtime/AssistantRuntime";
|
|
8
|
-
import { AssistantRuntimeProvider } from "../../../context";
|
|
9
|
-
import type {
|
|
10
|
-
AssistantTransportCommand,
|
|
11
|
-
AssistantTransportStateConverter,
|
|
12
|
-
} from "./types";
|
|
13
|
-
|
|
14
|
-
const emptySuccessfulResponse = () =>
|
|
15
|
-
new Response(
|
|
16
|
-
new ReadableStream<Uint8Array>({
|
|
17
|
-
start(controller) {
|
|
18
|
-
controller.close();
|
|
19
|
-
},
|
|
20
|
-
}),
|
|
21
|
-
{ status: 200 },
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const setupRuntime = () => {
|
|
25
|
-
const fetchMock = vi.fn(async () => emptySuccessfulResponse());
|
|
26
|
-
vi.stubGlobal("fetch", fetchMock);
|
|
27
|
-
|
|
28
|
-
const pendingRef: { current: AssistantTransportCommand[] } = { current: [] };
|
|
29
|
-
const converter: AssistantTransportStateConverter<Record<string, never>> = (
|
|
30
|
-
_state,
|
|
31
|
-
{ pendingCommands, isSending },
|
|
32
|
-
) => {
|
|
33
|
-
pendingRef.current = pendingCommands;
|
|
34
|
-
return { messages: [], isRunning: isSending };
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const runtimeRef: { current: AssistantRuntime | null } = { current: null };
|
|
38
|
-
const App: FC = () => {
|
|
39
|
-
const runtime = useAssistantTransportRuntime({
|
|
40
|
-
initialState: {},
|
|
41
|
-
api: "http://localhost/api",
|
|
42
|
-
converter,
|
|
43
|
-
headers: {},
|
|
44
|
-
});
|
|
45
|
-
runtimeRef.current = runtime;
|
|
46
|
-
return (
|
|
47
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
48
|
-
{null}
|
|
49
|
-
</AssistantRuntimeProvider>
|
|
50
|
-
);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
return { App, fetchMock, pendingRef, runtimeRef };
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
describe("assistant transport delivery contracts", () => {
|
|
57
|
-
afterEach(() => {
|
|
58
|
-
vi.unstubAllGlobals();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("clears in-transit commands when a run succeeds without state chunks", async () => {
|
|
62
|
-
const { App, fetchMock, pendingRef, runtimeRef } = setupRuntime();
|
|
63
|
-
|
|
64
|
-
await act(async () => {
|
|
65
|
-
render(<App />);
|
|
66
|
-
});
|
|
67
|
-
await waitFor(() => expect(runtimeRef.current).not.toBeNull());
|
|
68
|
-
|
|
69
|
-
await act(async () => {
|
|
70
|
-
runtimeRef.current!.thread.append("m1");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
|
|
74
|
-
await waitFor(() =>
|
|
75
|
-
expect(runtimeRef.current!.thread.getState().isRunning).toBe(false),
|
|
76
|
-
);
|
|
77
|
-
await waitFor(() => expect(pendingRef.current).toHaveLength(0));
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
|
|
3
|
-
import { act, render, waitFor } from "@testing-library/react";
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import type { FC } from "react";
|
|
6
|
-
import { useAssistantTransportRuntime } from "./useAssistantTransportRuntime";
|
|
7
|
-
import type { AssistantRuntime } from "../../runtime/AssistantRuntime";
|
|
8
|
-
import { AssistantRuntimeProvider } from "../../../context";
|
|
9
|
-
import type {
|
|
10
|
-
AssistantTransportCommand,
|
|
11
|
-
AssistantTransportStateConverter,
|
|
12
|
-
} from "./types";
|
|
13
|
-
|
|
14
|
-
const emptySuccessfulResponse = () =>
|
|
15
|
-
new Response(
|
|
16
|
-
new ReadableStream<Uint8Array>({
|
|
17
|
-
start(controller) {
|
|
18
|
-
controller.close();
|
|
19
|
-
},
|
|
20
|
-
}),
|
|
21
|
-
{ status: 200 },
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const setupRuntime = () => {
|
|
25
|
-
const requestBodies: Record<string, unknown>[] = [];
|
|
26
|
-
const fetchMock = vi.fn(
|
|
27
|
-
async (_url: RequestInfo | URL, init?: RequestInit) => {
|
|
28
|
-
requestBodies.push(JSON.parse(init!.body as string));
|
|
29
|
-
return emptySuccessfulResponse();
|
|
30
|
-
},
|
|
31
|
-
);
|
|
32
|
-
vi.stubGlobal("fetch", fetchMock);
|
|
33
|
-
|
|
34
|
-
const converter: AssistantTransportStateConverter<Record<string, never>> = (
|
|
35
|
-
_state,
|
|
36
|
-
{ isSending },
|
|
37
|
-
) => ({ messages: [], isRunning: isSending });
|
|
38
|
-
|
|
39
|
-
const runtimeRef: { current: AssistantRuntime | null } = { current: null };
|
|
40
|
-
const App: FC = () => {
|
|
41
|
-
const runtime = useAssistantTransportRuntime({
|
|
42
|
-
initialState: {},
|
|
43
|
-
api: "http://localhost/api",
|
|
44
|
-
converter,
|
|
45
|
-
headers: {},
|
|
46
|
-
});
|
|
47
|
-
runtimeRef.current = runtime;
|
|
48
|
-
return (
|
|
49
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
50
|
-
{null}
|
|
51
|
-
</AssistantRuntimeProvider>
|
|
52
|
-
);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
return { App, fetchMock, requestBodies, runtimeRef };
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
describe("assistant transport parentId lifetime", () => {
|
|
59
|
-
afterEach(() => {
|
|
60
|
-
vi.unstubAllGlobals();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("consumes parentId per run: the append's run carries it, a later sendCommand run omits it", async () => {
|
|
64
|
-
const { App, fetchMock, requestBodies, runtimeRef } = setupRuntime();
|
|
65
|
-
|
|
66
|
-
await act(async () => {
|
|
67
|
-
render(<App />);
|
|
68
|
-
});
|
|
69
|
-
await waitFor(() => expect(runtimeRef.current).not.toBeNull());
|
|
70
|
-
|
|
71
|
-
await act(async () => {
|
|
72
|
-
runtimeRef.current!.thread.append("m1");
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
|
|
76
|
-
await waitFor(() =>
|
|
77
|
-
expect(runtimeRef.current!.thread.getState().isRunning).toBe(false),
|
|
78
|
-
);
|
|
79
|
-
expect(Object.hasOwn(requestBodies[0]!, "parentId")).toBe(true);
|
|
80
|
-
|
|
81
|
-
const extras = runtimeRef.current!.thread.getState().extras as {
|
|
82
|
-
sendCommand: (command: AssistantTransportCommand) => void;
|
|
83
|
-
};
|
|
84
|
-
await act(async () => {
|
|
85
|
-
extras.sendCommand({
|
|
86
|
-
type: "add-tool-result",
|
|
87
|
-
toolCallId: "t1",
|
|
88
|
-
toolName: "tool",
|
|
89
|
-
result: {},
|
|
90
|
-
isError: false,
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(2));
|
|
95
|
-
expect(Object.hasOwn(requestBodies[1]!, "parentId")).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
|
|
3
|
-
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
4
|
-
import { describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { createElement, StrictMode, useRef } from "react";
|
|
6
|
-
import { useCommandQueue } from "./commandQueue";
|
|
7
|
-
import { useRunManager } from "./runManager";
|
|
8
|
-
import type { AssistantTransportCommand } from "./types";
|
|
9
|
-
|
|
10
|
-
const createMessageCommand = (id: string): AssistantTransportCommand => ({
|
|
11
|
-
type: "add-message",
|
|
12
|
-
message: {
|
|
13
|
-
role: "user",
|
|
14
|
-
parts: [{ type: "text", text: id }],
|
|
15
|
-
},
|
|
16
|
-
parentId: null,
|
|
17
|
-
sourceId: null,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const createDeferred = () => {
|
|
21
|
-
let resolve!: () => void;
|
|
22
|
-
const promise = new Promise<void>((res) => {
|
|
23
|
-
resolve = res;
|
|
24
|
-
});
|
|
25
|
-
return { promise, resolve };
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const useTransportSchedulingHarness = (
|
|
29
|
-
opts: {
|
|
30
|
-
onRun?: (signal: AbortSignal) => Promise<void> | void;
|
|
31
|
-
onCancel?: (commands: AssistantTransportCommand[]) => void;
|
|
32
|
-
onError?: (commands: AssistantTransportCommand[]) => void;
|
|
33
|
-
onFinish?: () => void;
|
|
34
|
-
} = {},
|
|
35
|
-
) => {
|
|
36
|
-
const commandQueueRef = useRef<ReturnType<typeof useCommandQueue> | null>(
|
|
37
|
-
null,
|
|
38
|
-
);
|
|
39
|
-
const runBatchesRef = useRef<AssistantTransportCommand[][]>([]);
|
|
40
|
-
|
|
41
|
-
const runManager = useRunManager({
|
|
42
|
-
onRun: async (signal) => {
|
|
43
|
-
const batch = commandQueueRef.current!.flush();
|
|
44
|
-
runBatchesRef.current.push(batch);
|
|
45
|
-
await opts.onRun?.(signal);
|
|
46
|
-
},
|
|
47
|
-
onCancel: () => {
|
|
48
|
-
const queue = commandQueueRef.current!;
|
|
49
|
-
const commands = [...queue.state.inTransit, ...queue.state.queued];
|
|
50
|
-
queue.reset();
|
|
51
|
-
opts.onCancel?.(commands);
|
|
52
|
-
},
|
|
53
|
-
onError: async () => {
|
|
54
|
-
const queue = commandQueueRef.current!;
|
|
55
|
-
opts.onError?.([...queue.state.inTransit]);
|
|
56
|
-
},
|
|
57
|
-
onFinish: () => opts.onFinish?.(),
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const commandQueue = useCommandQueue({
|
|
61
|
-
onQueue: () => runManager.schedule(),
|
|
62
|
-
});
|
|
63
|
-
commandQueueRef.current = commandQueue;
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
commandQueue,
|
|
67
|
-
runManager,
|
|
68
|
-
runBatchesRef,
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
describe("assistant transport scheduling contracts", () => {
|
|
73
|
-
it("runs in single-flight mode and schedules exactly one follow-up run", async () => {
|
|
74
|
-
const gate = createDeferred();
|
|
75
|
-
const { result } = renderHook(() =>
|
|
76
|
-
useTransportSchedulingHarness({
|
|
77
|
-
onRun: () => gate.promise,
|
|
78
|
-
}),
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
act(() => {
|
|
82
|
-
result.current.commandQueue.enqueue(createMessageCommand("m1"));
|
|
83
|
-
result.current.commandQueue.enqueue(createMessageCommand("m2"));
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
await waitFor(() => {
|
|
87
|
-
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
88
|
-
});
|
|
89
|
-
expect(result.current.runBatchesRef.current[0]).toHaveLength(2);
|
|
90
|
-
|
|
91
|
-
act(() => {
|
|
92
|
-
result.current.commandQueue.enqueue(createMessageCommand("m3"));
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
await Promise.resolve();
|
|
96
|
-
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
97
|
-
|
|
98
|
-
gate.resolve();
|
|
99
|
-
|
|
100
|
-
await waitFor(() => {
|
|
101
|
-
expect(result.current.runBatchesRef.current).toHaveLength(2);
|
|
102
|
-
});
|
|
103
|
-
expect(result.current.runBatchesRef.current[1]).toHaveLength(1);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("can enqueue without scheduling until a run is started", async () => {
|
|
107
|
-
const { result } = renderHook(() => useTransportSchedulingHarness());
|
|
108
|
-
|
|
109
|
-
act(() => {
|
|
110
|
-
result.current.commandQueue.enqueue(createMessageCommand("staged"), {
|
|
111
|
-
schedule: false,
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
await Promise.resolve();
|
|
116
|
-
expect(result.current.runBatchesRef.current).toHaveLength(0);
|
|
117
|
-
|
|
118
|
-
act(() => {
|
|
119
|
-
result.current.runManager.schedule();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
await waitFor(() => {
|
|
123
|
-
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
124
|
-
});
|
|
125
|
-
expect(result.current.runBatchesRef.current[0]).toHaveLength(1);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("onError receives the live in-transit commands at error time", async () => {
|
|
129
|
-
const seen: AssistantTransportCommand[][] = [];
|
|
130
|
-
const { result } = renderHook(() =>
|
|
131
|
-
useTransportSchedulingHarness({
|
|
132
|
-
onRun: () => {
|
|
133
|
-
throw new Error("network error");
|
|
134
|
-
},
|
|
135
|
-
onError: (commands) => seen.push(commands),
|
|
136
|
-
}),
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
act(() => {
|
|
140
|
-
result.current.commandQueue.enqueue(createMessageCommand("m1"));
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// The flush that moved m1 into transit has not re-rendered yet when the
|
|
144
|
-
// error fires; the queue state must be read live, not from a render snapshot.
|
|
145
|
-
await waitFor(() => expect(seen).toHaveLength(1));
|
|
146
|
-
expect(seen[0]).toEqual([createMessageCommand("m1")]);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("settles the run when onFinish throws", async () => {
|
|
150
|
-
const error = new Error("telemetry failed");
|
|
151
|
-
const onFinish = vi.fn<() => void>().mockImplementationOnce(() => {
|
|
152
|
-
throw error;
|
|
153
|
-
});
|
|
154
|
-
const consoleError = vi
|
|
155
|
-
.spyOn(console, "error")
|
|
156
|
-
.mockImplementation(() => {});
|
|
157
|
-
|
|
158
|
-
try {
|
|
159
|
-
const { result } = renderHook(() =>
|
|
160
|
-
useTransportSchedulingHarness({ onFinish }),
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
act(() => {
|
|
164
|
-
result.current.commandQueue.enqueue(createMessageCommand("m1"));
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
await waitFor(() => {
|
|
168
|
-
expect(result.current.runManager.isRunning).toBe(false);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
act(() => {
|
|
172
|
-
result.current.commandQueue.enqueue(createMessageCommand("m2"));
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
await waitFor(() => {
|
|
176
|
-
expect(result.current.runBatchesRef.current).toHaveLength(2);
|
|
177
|
-
});
|
|
178
|
-
expect(consoleError).toHaveBeenCalledWith(
|
|
179
|
-
"[assistant-ui] Assistant transport onFinish callback threw an error",
|
|
180
|
-
error,
|
|
181
|
-
);
|
|
182
|
-
} finally {
|
|
183
|
-
consoleError.mockRestore();
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("cancel returns combined in-flight and queued commands", async () => {
|
|
188
|
-
const onCancel = vi.fn();
|
|
189
|
-
const { result } = renderHook(() =>
|
|
190
|
-
useTransportSchedulingHarness({
|
|
191
|
-
onRun: (signal) =>
|
|
192
|
-
new Promise<void>((_resolve, reject) => {
|
|
193
|
-
signal.addEventListener(
|
|
194
|
-
"abort",
|
|
195
|
-
() => reject(new Error("aborted")),
|
|
196
|
-
{ once: true },
|
|
197
|
-
);
|
|
198
|
-
}),
|
|
199
|
-
onCancel,
|
|
200
|
-
}),
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
act(() => {
|
|
204
|
-
result.current.commandQueue.enqueue(createMessageCommand("in-flight"));
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
await waitFor(() => {
|
|
208
|
-
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
act(() => {
|
|
212
|
-
result.current.commandQueue.enqueue(createMessageCommand("queued"));
|
|
213
|
-
result.current.runManager.cancel();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
await waitFor(() => {
|
|
217
|
-
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
218
|
-
});
|
|
219
|
-
expect(onCancel.mock.calls[0]?.[0]).toHaveLength(2);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it("unmount aborts the in-flight run without invoking callbacks", async () => {
|
|
223
|
-
let aborted = false;
|
|
224
|
-
const onCancel = vi.fn();
|
|
225
|
-
const onError = vi.fn();
|
|
226
|
-
const onFinish = vi.fn();
|
|
227
|
-
const { result, unmount } = renderHook(() =>
|
|
228
|
-
useTransportSchedulingHarness({
|
|
229
|
-
onRun: (signal) =>
|
|
230
|
-
new Promise<void>((_resolve, reject) => {
|
|
231
|
-
signal.addEventListener(
|
|
232
|
-
"abort",
|
|
233
|
-
() => {
|
|
234
|
-
aborted = true;
|
|
235
|
-
reject(new Error("aborted"));
|
|
236
|
-
},
|
|
237
|
-
{ once: true },
|
|
238
|
-
);
|
|
239
|
-
}),
|
|
240
|
-
onCancel,
|
|
241
|
-
onError,
|
|
242
|
-
onFinish,
|
|
243
|
-
}),
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
act(() => {
|
|
247
|
-
result.current.commandQueue.enqueue(createMessageCommand("in-flight"));
|
|
248
|
-
});
|
|
249
|
-
await waitFor(() => {
|
|
250
|
-
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
unmount();
|
|
254
|
-
|
|
255
|
-
expect(aborted).toBe(true);
|
|
256
|
-
await act(async () => {});
|
|
257
|
-
expect(onCancel).not.toHaveBeenCalled();
|
|
258
|
-
expect(onError).not.toHaveBeenCalled();
|
|
259
|
-
expect(onFinish).not.toHaveBeenCalled();
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("ignores schedules after unmount", async () => {
|
|
263
|
-
const { result, unmount } = renderHook(() =>
|
|
264
|
-
useTransportSchedulingHarness(),
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
unmount();
|
|
268
|
-
act(() => {
|
|
269
|
-
result.current.commandQueue.enqueue(createMessageCommand("late"));
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
await act(async () => {});
|
|
273
|
-
expect(result.current.runBatchesRef.current).toHaveLength(0);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it("survives StrictMode double-mounting", async () => {
|
|
277
|
-
const gate = createDeferred();
|
|
278
|
-
const onFinish = vi.fn();
|
|
279
|
-
const { result } = renderHook(
|
|
280
|
-
() =>
|
|
281
|
-
useTransportSchedulingHarness({ onRun: () => gate.promise, onFinish }),
|
|
282
|
-
{ wrapper: ({ children }) => createElement(StrictMode, null, children) },
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
act(() => {
|
|
286
|
-
result.current.commandQueue.enqueue(createMessageCommand("m1"));
|
|
287
|
-
});
|
|
288
|
-
await waitFor(() => {
|
|
289
|
-
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// enqueued while the first run is in flight — must restart as a follow-up
|
|
293
|
-
act(() => {
|
|
294
|
-
result.current.commandQueue.enqueue(createMessageCommand("m2"));
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
gate.resolve();
|
|
298
|
-
await waitFor(() => {
|
|
299
|
-
expect(result.current.runBatchesRef.current).toHaveLength(2);
|
|
300
|
-
});
|
|
301
|
-
expect(result.current.runBatchesRef.current[1]).toHaveLength(1);
|
|
302
|
-
expect(onFinish).toHaveBeenCalled();
|
|
303
|
-
await waitFor(() => {
|
|
304
|
-
expect(result.current.runManager.isRunning).toBe(false);
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import type { ToolModelContentPart } from "assistant-stream";
|
|
2
|
-
import type { ThreadMessage } from "@assistant-ui/core";
|
|
3
|
-
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
4
|
-
import type {
|
|
5
|
-
AttachmentAdapter,
|
|
6
|
-
ThreadHistoryAdapter,
|
|
7
|
-
LanguageModelV1CallSettings,
|
|
8
|
-
LanguageModelConfig,
|
|
9
|
-
} from "@assistant-ui/core";
|
|
10
|
-
import type { UserCommands } from "../../../augmentations";
|
|
11
|
-
import type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
12
|
-
|
|
13
|
-
// Message part types
|
|
14
|
-
export type TextPart = {
|
|
15
|
-
readonly type: "text";
|
|
16
|
-
readonly text: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type ImagePart = {
|
|
20
|
-
readonly type: "image";
|
|
21
|
-
readonly image: string;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type UserMessagePart = TextPart | ImagePart;
|
|
25
|
-
|
|
26
|
-
export type UserMessage = {
|
|
27
|
-
readonly role: "user";
|
|
28
|
-
readonly parts: readonly UserMessagePart[];
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export type AssistantMessage = {
|
|
32
|
-
readonly role: "assistant";
|
|
33
|
-
readonly parts: readonly TextPart[];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Command types
|
|
37
|
-
export type AddMessageCommand = {
|
|
38
|
-
readonly type: "add-message";
|
|
39
|
-
readonly message: UserMessage | AssistantMessage;
|
|
40
|
-
readonly parentId: string | null;
|
|
41
|
-
readonly sourceId: string | null;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export type AddToolResultCommand = {
|
|
45
|
-
readonly type: "add-tool-result";
|
|
46
|
-
readonly toolCallId: string;
|
|
47
|
-
readonly toolName: string;
|
|
48
|
-
readonly result: ReadonlyJSONValue;
|
|
49
|
-
readonly isError: boolean;
|
|
50
|
-
readonly artifact?: ReadonlyJSONValue;
|
|
51
|
-
readonly modelContent?: readonly ToolModelContentPart[];
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type AssistantTransportCommand =
|
|
55
|
-
| AddMessageCommand
|
|
56
|
-
| AddToolResultCommand
|
|
57
|
-
| UserCommands;
|
|
58
|
-
|
|
59
|
-
// State types
|
|
60
|
-
export type AssistantTransportState = {
|
|
61
|
-
readonly messages: readonly ThreadMessage[];
|
|
62
|
-
readonly state?: ReadonlyJSONValue;
|
|
63
|
-
readonly isRunning: boolean;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export type AssistantTransportConnectionMetadata = {
|
|
67
|
-
pendingCommands: AssistantTransportCommand[];
|
|
68
|
-
isSending: boolean;
|
|
69
|
-
toolStatuses: Record<string, ToolExecutionStatus>;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export type AssistantTransportStateConverter<T> = (
|
|
73
|
-
state: T,
|
|
74
|
-
connectionMetadata: AssistantTransportConnectionMetadata,
|
|
75
|
-
) => AssistantTransportState;
|
|
76
|
-
|
|
77
|
-
// Queue types
|
|
78
|
-
export type CommandQueueState = {
|
|
79
|
-
queued: AssistantTransportCommand[];
|
|
80
|
-
inTransit: AssistantTransportCommand[];
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// For now, queued items are plain commands (runConfig not supported)
|
|
84
|
-
export type QueuedCommand = AssistantTransportCommand;
|
|
85
|
-
|
|
86
|
-
// Task types
|
|
87
|
-
|
|
88
|
-
// Options types
|
|
89
|
-
export type HeadersValue = Record<string, string> | Headers;
|
|
90
|
-
|
|
91
|
-
export type AssistantTransportProtocol = "data-stream" | "assistant-transport";
|
|
92
|
-
|
|
93
|
-
export type SendCommandsRequestBody = {
|
|
94
|
-
commands: QueuedCommand[];
|
|
95
|
-
/** Absent on a resume with `resumeStateApi`; the server replays from its retained snapshot. */
|
|
96
|
-
state?: unknown;
|
|
97
|
-
runId?: string;
|
|
98
|
-
system: string | undefined;
|
|
99
|
-
tools: Record<string, unknown> | undefined;
|
|
100
|
-
callSettings: LanguageModelV1CallSettings | undefined;
|
|
101
|
-
config: LanguageModelConfig | undefined;
|
|
102
|
-
threadId: string | null;
|
|
103
|
-
parentId?: string | null;
|
|
104
|
-
// `callSettings` and `config` fields are also spread at the top level for
|
|
105
|
-
// backward compatibility (e.g. `body.modelName`). Use the nested objects
|
|
106
|
-
// instead. The top-level fields will be removed in a future version.
|
|
107
|
-
[key: string]: unknown;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export type AssistantTransportOptions<T> = {
|
|
111
|
-
initialState: T;
|
|
112
|
-
api: string;
|
|
113
|
-
resumeApi?: string;
|
|
114
|
-
/** Endpoint that returns the retained initial state and run ID for a resume stream. A 204 response means no run is active and the resume is skipped. */
|
|
115
|
-
resumeStateApi?: string;
|
|
116
|
-
protocol?: AssistantTransportProtocol;
|
|
117
|
-
/**
|
|
118
|
-
* When `false`, stream decoding and state reconciliation tolerate malformed
|
|
119
|
-
* input (invalid chunks are dropped with a console log) instead of throwing.
|
|
120
|
-
* Resume runs always decode leniently. Defaults to `true`.
|
|
121
|
-
*/
|
|
122
|
-
strict?: boolean;
|
|
123
|
-
converter: AssistantTransportStateConverter<T>;
|
|
124
|
-
headers: HeadersValue | (() => Promise<HeadersValue>);
|
|
125
|
-
body?: object | (() => Promise<object | undefined>);
|
|
126
|
-
/**
|
|
127
|
-
* Transform the request body before it is sent to the API.
|
|
128
|
-
* Receives the fully assembled body and returns the (potentially transformed) body.
|
|
129
|
-
*
|
|
130
|
-
* @example
|
|
131
|
-
* ```ts
|
|
132
|
-
* prepareSendCommandsRequest: (body) => ({
|
|
133
|
-
* ...body,
|
|
134
|
-
* trackingId: crypto.randomUUID(),
|
|
135
|
-
* })
|
|
136
|
-
* ```
|
|
137
|
-
*/
|
|
138
|
-
prepareSendCommandsRequest?: (
|
|
139
|
-
body: SendCommandsRequestBody,
|
|
140
|
-
) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
141
|
-
onResponse?: (response: Response) => void;
|
|
142
|
-
onFinish?: () => void;
|
|
143
|
-
onError?: (
|
|
144
|
-
error: Error,
|
|
145
|
-
params: {
|
|
146
|
-
commands: AssistantTransportCommand[];
|
|
147
|
-
updateState: (updater: (state: T) => T) => void;
|
|
148
|
-
},
|
|
149
|
-
) => void | Promise<void>;
|
|
150
|
-
/**
|
|
151
|
-
* Called when commands are cancelled.
|
|
152
|
-
*
|
|
153
|
-
* When an error occurs, queued commands are automatically cancelled after `onError` settles.
|
|
154
|
-
* In this case, the `error` parameter contains the error that caused the cancellation.
|
|
155
|
-
*/
|
|
156
|
-
onCancel?: (params: {
|
|
157
|
-
commands: AssistantTransportCommand[];
|
|
158
|
-
updateState: (updater: (state: T) => T) => void;
|
|
159
|
-
error?: Error;
|
|
160
|
-
}) => void;
|
|
161
|
-
capabilities?: {
|
|
162
|
-
edit?: boolean;
|
|
163
|
-
};
|
|
164
|
-
adapters?: {
|
|
165
|
-
attachments?: AttachmentAdapter | undefined;
|
|
166
|
-
history?: ThreadHistoryAdapter | undefined;
|
|
167
|
-
};
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
// (no task or stream-specific types needed in this module)
|