@assistant-ui/core 0.2.0 → 0.2.3
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/README.md +45 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/model-context/tool.d.ts +25 -0
- package/dist/model-context/tool.d.ts.map +1 -1
- package/dist/model-context/tool.js +25 -0
- package/dist/model-context/tool.js.map +1 -1
- package/dist/react/AssistantRuntimeProvider.d.ts +33 -0
- package/dist/react/AssistantRuntimeProvider.d.ts.map +1 -1
- package/dist/react/AssistantRuntimeProvider.js +22 -0
- package/dist/react/AssistantRuntimeProvider.js.map +1 -1
- package/dist/react/client/DataRenderers.d.ts +7 -0
- package/dist/react/client/DataRenderers.d.ts.map +1 -1
- package/dist/react/client/DataRenderers.js +7 -0
- package/dist/react/client/DataRenderers.js.map +1 -1
- package/dist/react/client/Tools.d.ts +18 -1
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +24 -19
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/model-context/makeAssistantDataUI.d.ts +13 -0
- package/dist/react/model-context/makeAssistantDataUI.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantDataUI.js +6 -0
- package/dist/react/model-context/makeAssistantDataUI.js.map +1 -1
- package/dist/react/model-context/makeAssistantTool.d.ts +15 -0
- package/dist/react/model-context/makeAssistantTool.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantTool.js +8 -0
- package/dist/react/model-context/makeAssistantTool.js.map +1 -1
- package/dist/react/model-context/makeAssistantToolUI.d.ts +15 -0
- package/dist/react/model-context/makeAssistantToolUI.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantToolUI.js +8 -0
- package/dist/react/model-context/makeAssistantToolUI.js.map +1 -1
- package/dist/react/model-context/toolbox.d.ts +29 -0
- package/dist/react/model-context/toolbox.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantDataUI.d.ts +9 -0
- package/dist/react/model-context/useAssistantDataUI.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantDataUI.js +6 -0
- package/dist/react/model-context/useAssistantDataUI.js.map +1 -1
- package/dist/react/model-context/useAssistantTool.d.ts +34 -0
- package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantTool.js +30 -0
- package/dist/react/model-context/useAssistantTool.js.map +1 -1
- package/dist/react/model-context/useAssistantToolUI.d.ts +12 -0
- package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantToolUI.js +9 -0
- package/dist/react/model-context/useAssistantToolUI.js.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.d.ts +29 -0
- package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.js +24 -0
- package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarCopy.js +4 -3
- package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
- package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerSend.js +2 -3
- package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
- package/dist/react/primitives/message/MessageAttachments.js +1 -1
- package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
- package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +14 -10
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts +6 -0
- package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts.map +1 -0
- package/dist/react/primitives/messagePart/MessagePartInProgress.js +7 -0
- package/dist/react/primitives/messagePart/MessagePartInProgress.js.map +1 -0
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +2 -0
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +2 -0
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +10 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +21 -3
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/useToolInvocations.d.ts +11 -1
- package/dist/react/runtimes/useToolInvocations.d.ts.map +1 -1
- package/dist/react/runtimes/useToolInvocations.js +325 -256
- package/dist/react/runtimes/useToolInvocations.js.map +1 -1
- package/dist/react/types/MessagePartComponentTypes.d.ts +11 -0
- package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
- package/dist/react/types/scopes/tools.d.ts +4 -0
- package/dist/react/types/scopes/tools.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +1 -0
- package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.js +2 -0
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +2 -0
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -0
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +1 -0
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.js +3 -0
- package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -0
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js +12 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -0
- package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-adapter.d.ts +15 -0
- package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +14 -12
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -0
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +13 -0
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -0
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +1 -0
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +2 -0
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +2 -0
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.js +2 -0
- package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
- package/dist/store/clients/model-context-client.d.ts.map +1 -1
- package/dist/store/clients/model-context-client.js +24 -4
- package/dist/store/clients/model-context-client.js.map +1 -1
- package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
- package/dist/store/clients/no-op-composer-client.js +1 -0
- package/dist/store/clients/no-op-composer-client.js.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +1 -0
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/scopes/composer.d.ts +9 -0
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/model-context.d.ts +4 -1
- package/dist/store/scopes/model-context.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/message.d.ts +50 -1
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js +2 -1
- package/dist/types/message.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +6 -0
- package/src/model-context/tool.ts +25 -0
- package/src/react/AssistantRuntimeProvider.tsx +33 -0
- package/src/react/client/DataRenderers.ts +7 -0
- package/src/react/client/Tools.ts +56 -22
- package/src/react/index.ts +2 -1
- package/src/react/model-context/makeAssistantDataUI.ts +13 -0
- package/src/react/model-context/makeAssistantTool.ts +15 -0
- package/src/react/model-context/makeAssistantToolUI.ts +15 -0
- package/src/react/model-context/toolbox.ts +32 -1
- package/src/react/model-context/useAssistantDataUI.ts +9 -0
- package/src/react/model-context/useAssistantTool.ts +34 -0
- package/src/react/model-context/useAssistantToolUI.ts +12 -0
- package/src/react/model-context/useToolArgsStatus.ts +29 -0
- package/src/react/primitive-hooks/useActionBarCopy.ts +9 -5
- package/src/react/primitive-hooks/useComposerSend.ts +2 -3
- package/src/react/primitives/message/MessageAttachments.test.tsx +50 -0
- package/src/react/primitives/message/MessageAttachments.tsx +1 -1
- package/src/react/primitives/message/MessageParts.tsx +20 -9
- package/src/react/primitives/messagePart/MessagePartInProgress.ts +15 -0
- package/src/react/runtimes/cloud/auiV0.ts +37 -4
- package/src/react/runtimes/useToolInvocations.ts +422 -333
- package/src/react/types/MessagePartComponentTypes.ts +11 -0
- package/src/react/types/scopes/tools.ts +5 -0
- package/src/runtime/api/composer-runtime.ts +3 -0
- package/src/runtime/base/base-composer-runtime-core.ts +2 -1
- package/src/runtime/base/base-thread-runtime-core.ts +1 -0
- package/src/runtime/base/default-edit-composer-runtime-core.ts +4 -0
- package/src/runtime/base/default-thread-composer-runtime-core.ts +12 -1
- package/src/runtime/interfaces/composer-runtime-core.ts +1 -0
- package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
- package/src/runtimes/external-store/external-store-adapter.ts +15 -0
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +15 -9
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +13 -0
- package/src/runtimes/local/local-thread-runtime-core.ts +1 -0
- package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +2 -0
- package/src/runtimes/remote-thread-list/empty-thread-core.ts +2 -0
- package/src/store/clients/model-context-client.test.ts +108 -0
- package/src/store/clients/model-context-client.ts +36 -6
- package/src/store/clients/no-op-composer-client.ts +1 -0
- package/src/store/runtime-clients/composer-runtime-client.ts +1 -0
- package/src/store/scopes/composer.ts +9 -0
- package/src/store/scopes/model-context.ts +4 -1
- package/src/tests/auiV0Encode.test.ts +55 -0
- package/src/tests/composer-can-send.test.ts +112 -0
- package/src/tests/external-store-thread-list-runtime-core.test.ts +34 -0
- package/src/tests/external-store-thread-runtime-core.test.ts +113 -0
- package/src/types/index.ts +2 -0
- package/src/types/message.ts +66 -7
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { DefaultThreadComposerRuntimeCore } from "../runtime/base/default-thread-composer-runtime-core";
|
|
3
|
+
import { DefaultEditComposerRuntimeCore } from "../runtime/base/default-edit-composer-runtime-core";
|
|
4
|
+
import type { ThreadRuntimeCore } from "../runtime/interfaces/thread-runtime-core";
|
|
5
|
+
import type { ThreadMessage } from "../types/message";
|
|
6
|
+
|
|
7
|
+
type ThreadRuntimeStub = Omit<ThreadRuntimeCore, "composer"> & {
|
|
8
|
+
notify: () => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const makeRuntimeStub = (
|
|
12
|
+
overrides: Partial<ThreadRuntimeCore> = {},
|
|
13
|
+
): ThreadRuntimeStub => {
|
|
14
|
+
const subscribers = new Set<() => void>();
|
|
15
|
+
const stub = {
|
|
16
|
+
append: vi.fn(),
|
|
17
|
+
cancelRun: vi.fn(),
|
|
18
|
+
subscribe: (cb: () => void) => {
|
|
19
|
+
subscribers.add(cb);
|
|
20
|
+
return () => subscribers.delete(cb);
|
|
21
|
+
},
|
|
22
|
+
capabilities: { cancel: false },
|
|
23
|
+
messages: [],
|
|
24
|
+
isDisabled: false,
|
|
25
|
+
isSendDisabled: false,
|
|
26
|
+
isLoading: false,
|
|
27
|
+
composer: { runConfig: {} },
|
|
28
|
+
notify: () => {
|
|
29
|
+
for (const cb of subscribers) cb();
|
|
30
|
+
},
|
|
31
|
+
...overrides,
|
|
32
|
+
} as unknown as ThreadRuntimeStub;
|
|
33
|
+
return stub;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const makeUserMessage = (text = "old"): ThreadMessage =>
|
|
37
|
+
({
|
|
38
|
+
id: "msg-1",
|
|
39
|
+
role: "user",
|
|
40
|
+
createdAt: new Date(),
|
|
41
|
+
content: [{ type: "text", text }],
|
|
42
|
+
attachments: [],
|
|
43
|
+
metadata: { custom: {} },
|
|
44
|
+
}) as ThreadMessage;
|
|
45
|
+
|
|
46
|
+
describe("DefaultThreadComposerRuntimeCore.canSend", () => {
|
|
47
|
+
it("is false when the composer is empty", () => {
|
|
48
|
+
const composer = new DefaultThreadComposerRuntimeCore(makeRuntimeStub());
|
|
49
|
+
expect(composer.canSend).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("is true when in editing mode with non-empty text", () => {
|
|
53
|
+
const composer = new DefaultThreadComposerRuntimeCore(makeRuntimeStub());
|
|
54
|
+
composer.setText("hi");
|
|
55
|
+
expect(composer.canSend).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("is false when the runtime reports isSendDisabled", () => {
|
|
59
|
+
const composer = new DefaultThreadComposerRuntimeCore(
|
|
60
|
+
makeRuntimeStub({ isSendDisabled: true }),
|
|
61
|
+
);
|
|
62
|
+
composer.setText("hi");
|
|
63
|
+
expect(composer.canSend).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("notifies subscribers when isSendDisabled flips", () => {
|
|
67
|
+
const stub = makeRuntimeStub();
|
|
68
|
+
const composer = new DefaultThreadComposerRuntimeCore(stub);
|
|
69
|
+
composer.setText("hi");
|
|
70
|
+
const onChange = vi.fn();
|
|
71
|
+
composer.subscribe(onChange);
|
|
72
|
+
|
|
73
|
+
(stub as { isSendDisabled: boolean }).isSendDisabled = true;
|
|
74
|
+
stub.notify();
|
|
75
|
+
expect(onChange).toHaveBeenCalled();
|
|
76
|
+
expect(composer.canSend).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("BaseComposerRuntimeCore.send", () => {
|
|
81
|
+
it("is a no-op when canSend is false because of isSendDisabled", async () => {
|
|
82
|
+
const stub = makeRuntimeStub({ isSendDisabled: true });
|
|
83
|
+
const composer = new DefaultThreadComposerRuntimeCore(stub);
|
|
84
|
+
composer.setText("hi");
|
|
85
|
+
|
|
86
|
+
await composer.send();
|
|
87
|
+
|
|
88
|
+
expect(stub.append).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("dispatches when canSend is true", async () => {
|
|
92
|
+
const stub = makeRuntimeStub();
|
|
93
|
+
const composer = new DefaultThreadComposerRuntimeCore(stub);
|
|
94
|
+
composer.setText("hi");
|
|
95
|
+
|
|
96
|
+
await composer.send();
|
|
97
|
+
|
|
98
|
+
expect(stub.append).toHaveBeenCalledTimes(1);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("DefaultEditComposerRuntimeCore.canSend", () => {
|
|
103
|
+
it("ignores runtime.isSendDisabled (thread-scoped flag does not block edits)", () => {
|
|
104
|
+
const stub = makeRuntimeStub({ isSendDisabled: true });
|
|
105
|
+
const composer = new DefaultEditComposerRuntimeCore(stub, () => {}, {
|
|
106
|
+
parentId: null,
|
|
107
|
+
message: makeUserMessage("seed"),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(composer.canSend).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -146,6 +146,33 @@ describe("ExternalStoreThreadListRuntimeCore - __internal_setAdapter", () => {
|
|
|
146
146
|
core.__internal_setAdapter(makeAdapter({ threadId: "thread-beta" }));
|
|
147
147
|
expect(callback).toHaveBeenCalled();
|
|
148
148
|
});
|
|
149
|
+
|
|
150
|
+
it("synthesizes mainThreadId entry after a switch to a threadId not in the threads list (regression: #3971)", () => {
|
|
151
|
+
const core = new ExternalStoreThreadListRuntimeCore(
|
|
152
|
+
makeAdapter({ threadId: "thread-alpha" }),
|
|
153
|
+
makeFactory(),
|
|
154
|
+
);
|
|
155
|
+
core.__internal_setAdapter(makeAdapter({ threadId: "thread-beta" }));
|
|
156
|
+
const item = core.getItemById("thread-beta");
|
|
157
|
+
expect(item).toBeDefined();
|
|
158
|
+
expect(item?.id).toBe("thread-beta");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("does not retain stale synthesized entries across mainThreadId switches (regression: #3971)", () => {
|
|
162
|
+
const core = new ExternalStoreThreadListRuntimeCore(
|
|
163
|
+
makeAdapter({ threadId: "thread-alpha" }),
|
|
164
|
+
makeFactory(),
|
|
165
|
+
);
|
|
166
|
+
core.__internal_setAdapter(makeAdapter({ threadId: "thread-beta" }));
|
|
167
|
+
core.__internal_setAdapter(makeAdapter({ threadId: "thread-gamma" }));
|
|
168
|
+
expect(core.getItemById("thread-alpha")).toBeUndefined();
|
|
169
|
+
expect(core.getItemById("thread-beta")).toBeUndefined();
|
|
170
|
+
expect(core.getItemById("thread-gamma")).toBeDefined();
|
|
171
|
+
expect(Object.keys(core.threadItems).sort()).toEqual([
|
|
172
|
+
"DEFAULT_THREAD_ID",
|
|
173
|
+
"thread-gamma",
|
|
174
|
+
]);
|
|
175
|
+
});
|
|
149
176
|
});
|
|
150
177
|
|
|
151
178
|
describe("ExternalStoreThreadListRuntimeCore - isMain via ThreadListRuntimeImpl", () => {
|
|
@@ -187,6 +214,13 @@ describe("ExternalStoreThreadListRuntimeCore - isMain via ThreadListRuntimeImpl"
|
|
|
187
214
|
expect(state.mainThreadId).toBe("thread-alpha");
|
|
188
215
|
expect(state.threadIds).toEqual(["thread-alpha", "thread-beta"]);
|
|
189
216
|
});
|
|
217
|
+
|
|
218
|
+
it("does not throw when adapter.threadId has no matching threads entry (regression: #3971)", () => {
|
|
219
|
+
const impl = buildImpl({ threadId: "thread-alpha" });
|
|
220
|
+
expect(impl.mainItem.getState().id).toBe("thread-alpha");
|
|
221
|
+
expect(impl.mainItem.getState().isMain).toBe(true);
|
|
222
|
+
expect(impl.mainItem.getState().status).toBe("regular");
|
|
223
|
+
});
|
|
190
224
|
});
|
|
191
225
|
|
|
192
226
|
describe("ExternalStoreThreadListRuntimeCore - switchToThread", () => {
|
|
@@ -150,3 +150,116 @@ describe("ExternalStoreThreadRuntimeCore - state reference stability", () => {
|
|
|
150
150
|
expect(runtime.capabilities).toBe(capsBefore);
|
|
151
151
|
});
|
|
152
152
|
});
|
|
153
|
+
|
|
154
|
+
describe("ExternalStoreThreadRuntimeCore - messages reconciliation", () => {
|
|
155
|
+
const user = { id: "u", role: "user" as const, content: [] };
|
|
156
|
+
|
|
157
|
+
it("drops ids that disappear between syncs (same length, swapped assistant id)", () => {
|
|
158
|
+
const a1 = { id: "a1", role: "assistant" as const, content: [] };
|
|
159
|
+
const a2 = { id: "a2", role: "assistant" as const, content: [] };
|
|
160
|
+
|
|
161
|
+
const runtime = new ExternalStoreThreadRuntimeCore(
|
|
162
|
+
mockContextProvider,
|
|
163
|
+
makeStore({ messages: [user, a1] }),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
runtime.__internal_setAdapter(makeStore({ messages: [user, a2] }));
|
|
167
|
+
|
|
168
|
+
const exported = runtime.export();
|
|
169
|
+
expect(exported.messages.map((m) => m.message.id)).toEqual(["u", "a2"]);
|
|
170
|
+
const userChildren = exported.messages
|
|
171
|
+
.filter((m) => m.parentId === "u")
|
|
172
|
+
.map((m) => m.message.id);
|
|
173
|
+
expect(userChildren).toEqual(["a2"]);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("keeps prior ids when they remain in the new sync", () => {
|
|
177
|
+
const a = { id: "a", role: "assistant" as const, content: [] };
|
|
178
|
+
|
|
179
|
+
const runtime = new ExternalStoreThreadRuntimeCore(
|
|
180
|
+
mockContextProvider,
|
|
181
|
+
makeStore({ messages: [user, a] }),
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
runtime.__internal_setAdapter(makeStore({ messages: [user, a] }));
|
|
185
|
+
|
|
186
|
+
expect(runtime.export().messages.map((m) => m.message.id)).toEqual([
|
|
187
|
+
"u",
|
|
188
|
+
"a",
|
|
189
|
+
]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("removes trailing messages dropped from the new sync", () => {
|
|
193
|
+
const a = { id: "a", role: "assistant" as const, content: [] };
|
|
194
|
+
const u2 = { id: "u2", role: "user" as const, content: [] };
|
|
195
|
+
|
|
196
|
+
const runtime = new ExternalStoreThreadRuntimeCore(
|
|
197
|
+
mockContextProvider,
|
|
198
|
+
makeStore({ messages: [user, a, u2] }),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
runtime.__internal_setAdapter(makeStore({ messages: [user, a] }));
|
|
202
|
+
|
|
203
|
+
expect(runtime.export().messages.map((m) => m.message.id)).toEqual([
|
|
204
|
+
"u",
|
|
205
|
+
"a",
|
|
206
|
+
]);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("does not crash on the next sync after cancelRun removes a leaf user", () => {
|
|
210
|
+
const userWithText = {
|
|
211
|
+
id: "u",
|
|
212
|
+
role: "user" as const,
|
|
213
|
+
content: [{ type: "text" as const, text: "hi" }],
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const runtime = new ExternalStoreThreadRuntimeCore(
|
|
217
|
+
mockContextProvider,
|
|
218
|
+
makeStore({
|
|
219
|
+
messages: [userWithText],
|
|
220
|
+
onCancel: vi.fn(),
|
|
221
|
+
isRunning: true,
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
runtime.cancelRun();
|
|
226
|
+
|
|
227
|
+
expect(() => {
|
|
228
|
+
runtime.__internal_setAdapter(makeStore({ messages: [] }));
|
|
229
|
+
}).not.toThrow();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("drops phantom sibling when convertMessage swaps the assistant id", () => {
|
|
233
|
+
type Raw = { id: string; role: "user" | "assistant"; text: string };
|
|
234
|
+
const rawU: Raw = { id: "u", role: "user", text: "hi" };
|
|
235
|
+
const rawA1: Raw = { id: "client_id", role: "assistant", text: "" };
|
|
236
|
+
const rawA2: Raw = { id: "server_id", role: "assistant", text: "" };
|
|
237
|
+
|
|
238
|
+
const convertMessage = (m: Raw) => ({
|
|
239
|
+
id: m.id,
|
|
240
|
+
role: m.role,
|
|
241
|
+
content: [{ type: "text" as const, text: m.text }],
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const runtime = new ExternalStoreThreadRuntimeCore(
|
|
245
|
+
mockContextProvider,
|
|
246
|
+
makeStore({
|
|
247
|
+
messages: [rawU, rawA1] as any,
|
|
248
|
+
convertMessage: convertMessage as any,
|
|
249
|
+
}),
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
runtime.__internal_setAdapter(
|
|
253
|
+
makeStore({
|
|
254
|
+
messages: [rawU, rawA2] as any,
|
|
255
|
+
convertMessage: convertMessage as any,
|
|
256
|
+
}),
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const userChildren = runtime
|
|
260
|
+
.export()
|
|
261
|
+
.messages.filter((m) => m.parentId === "u")
|
|
262
|
+
.map((m) => m.message.id);
|
|
263
|
+
expect(userChildren).toEqual(["server_id"]);
|
|
264
|
+
});
|
|
265
|
+
});
|
package/src/types/index.ts
CHANGED
|
@@ -2,12 +2,14 @@ export type {
|
|
|
2
2
|
// Message parts
|
|
3
3
|
TextMessagePart,
|
|
4
4
|
ReasoningMessagePart,
|
|
5
|
+
SourceProviderMetadata,
|
|
5
6
|
SourceMessagePart,
|
|
6
7
|
ImageMessagePart,
|
|
7
8
|
FileMessagePart,
|
|
8
9
|
DataMessagePart,
|
|
9
10
|
Unstable_AudioMessagePart,
|
|
10
11
|
ToolCallMessagePart,
|
|
12
|
+
ToolModelContentPart,
|
|
11
13
|
ThreadUserMessagePart,
|
|
12
14
|
ThreadAssistantMessagePart,
|
|
13
15
|
// Message status
|
package/src/types/message.ts
CHANGED
|
@@ -2,8 +2,11 @@ import type {
|
|
|
2
2
|
ReadonlyJSONObject,
|
|
3
3
|
ReadonlyJSONValue,
|
|
4
4
|
} from "assistant-stream/utils";
|
|
5
|
+
import type { ToolModelContentPart } from "assistant-stream";
|
|
5
6
|
import type { CompleteAttachment } from "./attachment";
|
|
6
7
|
|
|
8
|
+
export type { ToolModelContentPart };
|
|
9
|
+
|
|
7
10
|
export type TextMessagePart = {
|
|
8
11
|
readonly type: "text";
|
|
9
12
|
readonly text: string;
|
|
@@ -16,15 +19,32 @@ export type ReasoningMessagePart = {
|
|
|
16
19
|
readonly parentId?: string;
|
|
17
20
|
};
|
|
18
21
|
|
|
19
|
-
export type
|
|
20
|
-
readonly
|
|
21
|
-
readonly sourceType: "url";
|
|
22
|
-
readonly id: string;
|
|
23
|
-
readonly url: string;
|
|
24
|
-
readonly title?: string;
|
|
25
|
-
readonly parentId?: string;
|
|
22
|
+
export type SourceProviderMetadata = {
|
|
23
|
+
readonly [providerName: string]: ReadonlyJSONObject;
|
|
26
24
|
};
|
|
27
25
|
|
|
26
|
+
export type SourceMessagePart =
|
|
27
|
+
| {
|
|
28
|
+
readonly type: "source";
|
|
29
|
+
readonly sourceType: "url";
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly url: string;
|
|
32
|
+
readonly title?: string;
|
|
33
|
+
readonly providerMetadata?: SourceProviderMetadata;
|
|
34
|
+
readonly parentId?: string;
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
readonly type: "source";
|
|
38
|
+
readonly sourceType: "document";
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly url?: undefined;
|
|
41
|
+
readonly title: string;
|
|
42
|
+
readonly mediaType: string;
|
|
43
|
+
readonly filename?: string;
|
|
44
|
+
readonly providerMetadata?: SourceProviderMetadata;
|
|
45
|
+
readonly parentId?: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
28
48
|
export type ImageMessagePart = {
|
|
29
49
|
readonly type: "image";
|
|
30
50
|
readonly image: string;
|
|
@@ -53,20 +73,57 @@ export type DataMessagePart<T = any> = {
|
|
|
53
73
|
readonly data: T;
|
|
54
74
|
};
|
|
55
75
|
|
|
76
|
+
export type McpAppMetadata = {
|
|
77
|
+
readonly resourceUri: string;
|
|
78
|
+
readonly mimeType?: string;
|
|
79
|
+
readonly visibility?: readonly ("model" | "app")[];
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const MCP_APP_URI_SCHEME = "ui://";
|
|
83
|
+
|
|
84
|
+
export const isMcpAppUri = (uri: string | undefined): boolean =>
|
|
85
|
+
!!uri?.startsWith(MCP_APP_URI_SCHEME);
|
|
86
|
+
|
|
87
|
+
export type ToolCallMessagePartMcpMetadata = {
|
|
88
|
+
readonly app?: McpAppMetadata;
|
|
89
|
+
};
|
|
90
|
+
|
|
56
91
|
export type ToolCallMessagePart<
|
|
57
92
|
TArgs = ReadonlyJSONObject,
|
|
58
93
|
TResult = unknown,
|
|
59
94
|
> = {
|
|
95
|
+
/** Identifies this part as a tool call. */
|
|
60
96
|
readonly type: "tool-call";
|
|
97
|
+
/** Stable identifier for this invocation of the tool. */
|
|
61
98
|
readonly toolCallId: string;
|
|
99
|
+
/** Name of the tool requested by the model. */
|
|
62
100
|
readonly toolName: string;
|
|
101
|
+
/**
|
|
102
|
+
* Arguments supplied by the model. During streaming this is a partial parse:
|
|
103
|
+
* fields may be missing or incomplete. From a tool-call renderer, use
|
|
104
|
+
* `useToolArgsStatus` to detect which fields are still arriving.
|
|
105
|
+
*/
|
|
63
106
|
readonly args: TArgs;
|
|
107
|
+
/** Result returned by the tool, if it has completed. */
|
|
64
108
|
readonly result?: TResult | undefined;
|
|
109
|
+
/** Whether the result represents a tool execution error. */
|
|
65
110
|
readonly isError?: boolean | undefined;
|
|
111
|
+
/** Raw JSON argument text streamed by the model. */
|
|
66
112
|
readonly argsText: string;
|
|
113
|
+
/** UI-only artifact associated with the tool result. */
|
|
67
114
|
readonly artifact?: unknown;
|
|
115
|
+
/** MCP app metadata associated with this tool call, when present. */
|
|
116
|
+
readonly mcp?: ToolCallMessagePartMcpMetadata;
|
|
117
|
+
/** Content returned to the model for this tool result. */
|
|
118
|
+
readonly modelContent?: readonly ToolModelContentPart[] | undefined;
|
|
119
|
+
/** Human-input request that must be resolved before the run can continue. */
|
|
68
120
|
readonly interrupt?: { type: "human"; payload: unknown };
|
|
121
|
+
/** Parent message-part ID when this part belongs to a nested structure. */
|
|
69
122
|
readonly parentId?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Nested thread messages produced by this tool call, for example a sub-agent
|
|
125
|
+
* conversation.
|
|
126
|
+
*/
|
|
70
127
|
readonly messages?: readonly ThreadMessage[];
|
|
71
128
|
};
|
|
72
129
|
|
|
@@ -106,7 +163,9 @@ export type MessagePartStatus =
|
|
|
106
163
|
|
|
107
164
|
export type ToolCallMessagePartStatus =
|
|
108
165
|
| {
|
|
166
|
+
/** The tool call is waiting for UI or human input before continuing. */
|
|
109
167
|
readonly type: "requires-action";
|
|
168
|
+
/** Reason the tool call requires action. */
|
|
110
169
|
readonly reason: "interrupt";
|
|
111
170
|
}
|
|
112
171
|
| MessagePartStatus;
|