@copilotkit/vue 1.62.0 → 1.62.2
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/PARITY.md +7 -7
- package/dist/index-By5nIKxT.js +1632 -0
- package/dist/index-By5nIKxT.js.map +1 -0
- package/dist/index-LFxZI63k.cjs +748 -0
- package/dist/index-LFxZI63k.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +70 -69
- package/dist/styles.css +1 -1
- package/dist/{use-render-activity-message-Bzh3Bckz.js → use-render-activity-message-DbGuwvBd.js} +3363 -3064
- package/dist/use-render-activity-message-DbGuwvBd.js.map +1 -0
- package/dist/use-render-activity-message-hlNcbV9l.cjs +85 -0
- package/dist/use-render-activity-message-hlNcbV9l.cjs.map +1 -0
- package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotThreadsDrawer.vue.d.ts +42 -0
- package/dist/v2/components/chat/CopilotThreadsDrawer.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/index.d.ts +1 -0
- package/dist/v2/components/chat/index.d.ts.map +1 -1
- package/dist/v2/components/icons/index.d.ts +1 -1
- package/dist/v2/components/icons/index.d.ts.map +1 -1
- package/dist/v2/hooks/use-agent.d.ts.map +1 -1
- package/dist/v2/hooks/use-threads.d.ts +12 -0
- package/dist/v2/hooks/use-threads.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +44 -43
- package/dist/v2/lib/is-mobile-viewport.d.ts +15 -0
- package/dist/v2/lib/is-mobile-viewport.d.ts.map +1 -0
- package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/types.d.ts +24 -0
- package/dist/v2/providers/types.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/v2/components/chat/CopilotChat.vue +22 -4
- package/src/v2/components/chat/CopilotModalHeader.vue +61 -2
- package/src/v2/components/chat/CopilotThreadsDrawer.vue +285 -0
- package/src/v2/components/chat/__tests__/CopilotChat.clearOnFresh.test.ts +160 -0
- package/src/v2/components/chat/__tests__/CopilotModalHeader.drawerLauncher.test.ts +125 -0
- package/src/v2/components/chat/__tests__/CopilotThreadsDrawer.ssr.test.ts +25 -0
- package/src/v2/components/chat/__tests__/CopilotThreadsDrawer.test.ts +835 -0
- package/src/v2/components/chat/index.ts +1 -0
- package/src/v2/components/icons/index.ts +1 -0
- package/src/v2/hooks/__tests__/use-agent.test.ts +60 -1
- package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.ts +4 -4
- package/src/v2/hooks/__tests__/use-threads.test.ts +303 -6
- package/src/v2/hooks/use-agent.ts +4 -1
- package/src/v2/hooks/use-threads.ts +137 -8
- package/src/v2/lib/__tests__/is-mobile-viewport.test.ts +48 -0
- package/src/v2/lib/is-mobile-viewport.ts +23 -0
- package/src/v2/providers/CopilotChatConfigurationProvider.vue +120 -4
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.ts +105 -1
- package/src/v2/providers/types.ts +25 -0
- package/dist/use-render-activity-message-Bzh3Bckz.js.map +0 -1
- package/dist/use-render-activity-message-CW3vipBB.cjs +0 -85
- package/dist/use-render-activity-message-CW3vipBB.cjs.map +0 -1
|
@@ -13,6 +13,7 @@ export { default as CopilotChatSuggestionView } from "./CopilotChatSuggestionVie
|
|
|
13
13
|
export { default as CopilotChatToolCallsView } from "./CopilotChatToolCallsView.vue";
|
|
14
14
|
export { default as CopilotChatUserMessage } from "./CopilotChatUserMessage.vue";
|
|
15
15
|
export { default as CopilotChatView } from "./CopilotChatView.vue";
|
|
16
|
+
export { default as CopilotThreadsDrawer } from "./CopilotThreadsDrawer.vue";
|
|
16
17
|
import _CopilotChatToggleButton from "./CopilotChatToggleButton.vue";
|
|
17
18
|
import CopilotChatToggleButtonCloseIcon from "./CopilotChatToggleButtonCloseIcon";
|
|
18
19
|
import CopilotChatToggleButtonOpenIcon from "./CopilotChatToggleButtonOpenIcon";
|
|
@@ -4,7 +4,10 @@ import type { AbstractAgent, Message } from "@ag-ui/client";
|
|
|
4
4
|
import { randomUUID } from "@copilotkit/shared";
|
|
5
5
|
import { useAgent } from "../use-agent";
|
|
6
6
|
import { useCopilotKit } from "../../providers/useCopilotKit";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
mountWithProvider,
|
|
9
|
+
renderWithCopilotKit,
|
|
10
|
+
} from "../../__tests__/utils/mount";
|
|
8
11
|
import { StateCapturingAgent } from "../../__tests__/utils/agents";
|
|
9
12
|
|
|
10
13
|
describe("useAgent", () => {
|
|
@@ -37,6 +40,62 @@ describe("useAgent", () => {
|
|
|
37
40
|
expect(wrapper.find("[data-testid=agent-id]").text()).toBe("test-agent");
|
|
38
41
|
});
|
|
39
42
|
|
|
43
|
+
it("uses agentId from chat configuration when no agentId prop is provided", () => {
|
|
44
|
+
const mockAgent = new StateCapturingAgent([], "configured-agent");
|
|
45
|
+
|
|
46
|
+
const Child = defineComponent({
|
|
47
|
+
setup() {
|
|
48
|
+
const { agent } = useAgent();
|
|
49
|
+
return () =>
|
|
50
|
+
h(
|
|
51
|
+
"span",
|
|
52
|
+
{ "data-testid": "agent-id" },
|
|
53
|
+
agent.value?.agentId ?? "none",
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const { wrapper } = renderWithCopilotKit(() => h(Child), {
|
|
59
|
+
agentId: "configured-agent",
|
|
60
|
+
agents: {
|
|
61
|
+
"configured-agent": mockAgent as unknown as AbstractAgent,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(wrapper.find("[data-testid=agent-id]").text()).toBe(
|
|
66
|
+
"configured-agent",
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("uses explicit agentId over chat configuration", () => {
|
|
71
|
+
const configuredAgent = new StateCapturingAgent([], "configured-agent");
|
|
72
|
+
const explicitAgent = new StateCapturingAgent([], "explicit-agent");
|
|
73
|
+
|
|
74
|
+
const Child = defineComponent({
|
|
75
|
+
setup() {
|
|
76
|
+
const { agent } = useAgent({ agentId: "explicit-agent" });
|
|
77
|
+
return () =>
|
|
78
|
+
h(
|
|
79
|
+
"span",
|
|
80
|
+
{ "data-testid": "agent-id" },
|
|
81
|
+
agent.value?.agentId ?? "none",
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const { wrapper } = renderWithCopilotKit(() => h(Child), {
|
|
87
|
+
agentId: "configured-agent",
|
|
88
|
+
agents: {
|
|
89
|
+
"configured-agent": configuredAgent as unknown as AbstractAgent,
|
|
90
|
+
"explicit-agent": explicitAgent as unknown as AbstractAgent,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(wrapper.find("[data-testid=agent-id]").text()).toBe(
|
|
95
|
+
"explicit-agent",
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
40
99
|
it("passes state set through useAgent into run input", async () => {
|
|
41
100
|
const mockAgent = new StateCapturingAgent(
|
|
42
101
|
[{ newMessages: [] }],
|
|
@@ -944,13 +944,13 @@ describe("HITL Thread Reconnection Bug", () => {
|
|
|
944
944
|
expect(screen.getByTestId("hitl-action").textContent).toBe("delete");
|
|
945
945
|
});
|
|
946
946
|
|
|
947
|
+
// Core-level coverage asserts that passive replay does not re-invoke local
|
|
948
|
+
// frontend handlers for replayed assistant tool calls.
|
|
947
949
|
await waitFor(() => {
|
|
948
|
-
expect(screen.getByTestId("hitl-status").textContent).
|
|
949
|
-
|
|
950
|
+
expect(screen.getByTestId("hitl-status").textContent).toMatch(
|
|
951
|
+
/^(executing|inProgress)$/,
|
|
950
952
|
);
|
|
951
953
|
});
|
|
952
|
-
|
|
953
|
-
expect(screen.getByTestId("hitl-respond")).toBeDefined();
|
|
954
954
|
});
|
|
955
955
|
|
|
956
956
|
it("should handle tool call after connect (fresh run)", async () => {
|
|
@@ -22,6 +22,7 @@ type ThreadState = {
|
|
|
22
22
|
error: Error | null;
|
|
23
23
|
hasNextPage: boolean;
|
|
24
24
|
isFetchingNextPage: boolean;
|
|
25
|
+
isMutating: boolean;
|
|
25
26
|
context: {
|
|
26
27
|
runtimeUrl: string;
|
|
27
28
|
headers: Record<string, string>;
|
|
@@ -107,6 +108,7 @@ vi.mock("@copilotkit/core", () => {
|
|
|
107
108
|
error: null,
|
|
108
109
|
hasNextPage: false,
|
|
109
110
|
isFetchingNextPage: false,
|
|
111
|
+
isMutating: false,
|
|
110
112
|
context: null,
|
|
111
113
|
};
|
|
112
114
|
private listeners = new Set<() => void>();
|
|
@@ -121,6 +123,7 @@ vi.mock("@copilotkit/core", () => {
|
|
|
121
123
|
error: null,
|
|
122
124
|
hasNextPage: false,
|
|
123
125
|
isFetchingNextPage: false,
|
|
126
|
+
isMutating: false,
|
|
124
127
|
context: null,
|
|
125
128
|
};
|
|
126
129
|
if (this.socket) {
|
|
@@ -266,6 +269,27 @@ vi.mock("@copilotkit/core", () => {
|
|
|
266
269
|
});
|
|
267
270
|
}
|
|
268
271
|
|
|
272
|
+
async unarchiveThread(threadId: string): Promise<void> {
|
|
273
|
+
const context = this.requireContext();
|
|
274
|
+
await fetchMock(`${context.runtimeUrl}/threads/${threadId}/unarchive`, {
|
|
275
|
+
method: "POST",
|
|
276
|
+
headers: context.headers,
|
|
277
|
+
body: JSON.stringify({
|
|
278
|
+
agentId: context.agentId,
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
refetchThreads(): void {
|
|
284
|
+
if (!this.state.context) return;
|
|
285
|
+
void this.fetchThreads(this.state.context);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
startNewThread(): void {
|
|
289
|
+
// Test double: mirrors the real store's "reset to a fresh thread" no-op
|
|
290
|
+
// shape closely enough for surface-presence assertions.
|
|
291
|
+
}
|
|
292
|
+
|
|
269
293
|
fetchNextPage(): void {
|
|
270
294
|
this.state.isFetchingNextPage = true;
|
|
271
295
|
this.notify();
|
|
@@ -319,6 +343,7 @@ vi.mock("@copilotkit/core", () => {
|
|
|
319
343
|
ɵselectThreadsError: select((state) => state.error),
|
|
320
344
|
ɵselectHasNextPage: select((state) => state.hasNextPage),
|
|
321
345
|
ɵselectIsFetchingNextPage: select((state) => state.isFetchingNextPage),
|
|
346
|
+
ɵselectIsMutating: select((state) => state.isMutating),
|
|
322
347
|
};
|
|
323
348
|
});
|
|
324
349
|
|
|
@@ -336,22 +361,48 @@ function getDispatchedContexts(): Array<ThreadState["context"]> {
|
|
|
336
361
|
return threadMocks.dispatchedContexts;
|
|
337
362
|
}
|
|
338
363
|
|
|
364
|
+
type ThreadEndpointRuntimeInfo = {
|
|
365
|
+
list: boolean;
|
|
366
|
+
inspect: boolean;
|
|
367
|
+
mutations: boolean;
|
|
368
|
+
realtimeMetadata: boolean;
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const supportedThreadEndpoints: ThreadEndpointRuntimeInfo = {
|
|
372
|
+
list: true,
|
|
373
|
+
inspect: true,
|
|
374
|
+
mutations: true,
|
|
375
|
+
realtimeMetadata: true,
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
type MockCopilotKit = {
|
|
379
|
+
runtimeUrl: string | undefined;
|
|
380
|
+
runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus;
|
|
381
|
+
headers: Record<string, string>;
|
|
382
|
+
intelligence: { wsUrl?: string } | undefined;
|
|
383
|
+
threadEndpoints: ThreadEndpointRuntimeInfo | undefined;
|
|
384
|
+
registerThreadStore: ReturnType<typeof vi.fn>;
|
|
385
|
+
unregisterThreadStore: ReturnType<typeof vi.fn>;
|
|
386
|
+
};
|
|
387
|
+
|
|
339
388
|
function setupCopilotKit(
|
|
340
389
|
runtimeUrl: string | undefined = "http://localhost:4000",
|
|
341
390
|
runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Connected,
|
|
391
|
+
overrides: Partial<
|
|
392
|
+
Pick<MockCopilotKit, "threadEndpoints" | "intelligence">
|
|
393
|
+
> = {},
|
|
342
394
|
) {
|
|
343
|
-
const copilotkit = ref<{
|
|
344
|
-
runtimeUrl: string | undefined;
|
|
345
|
-
runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus;
|
|
346
|
-
headers: Record<string, string>;
|
|
347
|
-
intelligence: { wsUrl?: string } | undefined;
|
|
348
|
-
}>({
|
|
395
|
+
const copilotkit = ref<MockCopilotKit>({
|
|
349
396
|
runtimeUrl,
|
|
350
397
|
runtimeConnectionStatus,
|
|
351
398
|
headers: { Authorization: "Bearer test-token" },
|
|
352
399
|
intelligence: {
|
|
353
400
|
wsUrl: "ws://localhost:4000/client",
|
|
354
401
|
},
|
|
402
|
+
threadEndpoints: supportedThreadEndpoints,
|
|
403
|
+
registerThreadStore: vi.fn(),
|
|
404
|
+
unregisterThreadStore: vi.fn(),
|
|
405
|
+
...overrides,
|
|
355
406
|
});
|
|
356
407
|
mockUseCopilotKit.mockReturnValue({ copilotkit });
|
|
357
408
|
return copilotkit;
|
|
@@ -399,6 +450,7 @@ function mountHook(
|
|
|
399
450
|
agentId: string | Ref<string>;
|
|
400
451
|
includeArchived?: boolean | Ref<boolean | undefined>;
|
|
401
452
|
limit?: number | Ref<number | undefined>;
|
|
453
|
+
enabled?: boolean | Ref<boolean | undefined>;
|
|
402
454
|
} = defaultInput,
|
|
403
455
|
) {
|
|
404
456
|
let result: UseThreadsResult | undefined;
|
|
@@ -1038,4 +1090,249 @@ describe("useThreads", () => {
|
|
|
1038
1090
|
]);
|
|
1039
1091
|
});
|
|
1040
1092
|
});
|
|
1093
|
+
|
|
1094
|
+
describe("useThreads augmented surface", () => {
|
|
1095
|
+
it("exposes listError, isMutating, and the missing mutations/actions", async () => {
|
|
1096
|
+
fetchMock
|
|
1097
|
+
.mockReturnValueOnce(
|
|
1098
|
+
jsonResponse({ threads: sampleThreads, joinCode: "jc-1" }),
|
|
1099
|
+
)
|
|
1100
|
+
.mockReturnValueOnce(jsonResponse({ joinToken: "jt-1" }));
|
|
1101
|
+
|
|
1102
|
+
const { getResult } = mountHook();
|
|
1103
|
+
|
|
1104
|
+
await vi.waitFor(() => {
|
|
1105
|
+
expect(getResult().isLoading.value).toBe(false);
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
expect(getResult().listError).toBeDefined();
|
|
1109
|
+
expect(getResult().listError.value).toBeNull();
|
|
1110
|
+
expect(getResult().isMutating).toBeDefined();
|
|
1111
|
+
expect(getResult().isMutating.value).toBe(false);
|
|
1112
|
+
expect(typeof getResult().unarchiveThread).toBe("function");
|
|
1113
|
+
expect(typeof getResult().refetchThreads).toBe("function");
|
|
1114
|
+
expect(typeof getResult().startNewThread).toBe("function");
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
it("listError excludes the dev 'Runtime URL is not configured' error", async () => {
|
|
1118
|
+
// No runtimeUrl -> `error` is the dev string, but `listError` stays null.
|
|
1119
|
+
setupCopilotKit("");
|
|
1120
|
+
|
|
1121
|
+
const { getResult } = mountHook();
|
|
1122
|
+
|
|
1123
|
+
await nextTick();
|
|
1124
|
+
|
|
1125
|
+
expect(getResult().error.value).toBeInstanceOf(Error);
|
|
1126
|
+
expect(getResult().error.value?.message).toBe(
|
|
1127
|
+
"Runtime URL is not configured",
|
|
1128
|
+
);
|
|
1129
|
+
expect(getResult().listError.value).toBeNull();
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
it("unarchiveThread calls through to the store's unarchive contract", async () => {
|
|
1133
|
+
fetchMock
|
|
1134
|
+
.mockReturnValueOnce(
|
|
1135
|
+
jsonResponse({ threads: sampleThreads, joinCode: "jc-1" }),
|
|
1136
|
+
)
|
|
1137
|
+
.mockReturnValueOnce(jsonResponse({ joinToken: "jt-1" }))
|
|
1138
|
+
.mockReturnValueOnce(jsonResponse({}));
|
|
1139
|
+
|
|
1140
|
+
const { getResult } = mountHook();
|
|
1141
|
+
|
|
1142
|
+
await vi.waitFor(() => {
|
|
1143
|
+
expect(getResult().isLoading.value).toBe(false);
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
await getResult().unarchiveThread("t-1");
|
|
1147
|
+
|
|
1148
|
+
const [url, options] = fetchMock.mock.calls[2];
|
|
1149
|
+
expect(url).toContain("/threads/t-1/unarchive");
|
|
1150
|
+
expect(options.method).toBe("POST");
|
|
1151
|
+
expect(JSON.parse(options.body)).toMatchObject({
|
|
1152
|
+
agentId: "agent-1",
|
|
1153
|
+
});
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it("does not dispatch a context (or fetch) when enabled is false", async () => {
|
|
1157
|
+
const { getResult } = mountHook({ ...defaultInput, enabled: false });
|
|
1158
|
+
|
|
1159
|
+
await nextTick();
|
|
1160
|
+
|
|
1161
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
1162
|
+
expect(getResult().threads.value).toEqual([]);
|
|
1163
|
+
const lastDispatched = getDispatchedContexts().at(-1);
|
|
1164
|
+
expect(lastDispatched).toBeNull();
|
|
1165
|
+
// A disabled consumer must not be stuck in a permanent loading state:
|
|
1166
|
+
// preConnectLoading must factor in `enabled`, otherwise isLoading would
|
|
1167
|
+
// stay true forever since hasDispatchedContext never gets set.
|
|
1168
|
+
expect(getResult().isLoading.value).toBe(false);
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
it("re-arms the pre-connect loading indicator when enabled toggles false -> true", async () => {
|
|
1172
|
+
const enabled = ref(false);
|
|
1173
|
+
|
|
1174
|
+
fetchMock
|
|
1175
|
+
.mockReturnValueOnce(
|
|
1176
|
+
jsonResponse({ threads: sampleThreads, joinCode: "jc-1" }),
|
|
1177
|
+
)
|
|
1178
|
+
.mockReturnValueOnce(jsonResponse({ joinToken: "jt-1" }));
|
|
1179
|
+
|
|
1180
|
+
const { getResult } = mountHook({ ...defaultInput, enabled });
|
|
1181
|
+
|
|
1182
|
+
await nextTick();
|
|
1183
|
+
|
|
1184
|
+
// Disabled: inert, not loading, no fetch.
|
|
1185
|
+
expect(getResult().isLoading.value).toBe(false);
|
|
1186
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
1187
|
+
expect(getDispatchedContexts().at(-1)).toBeNull();
|
|
1188
|
+
|
|
1189
|
+
// Re-enable: the pre-connect loading indicator must re-arm (i.e.
|
|
1190
|
+
// hasDispatchedContext was reset to false on disable) so consumers see
|
|
1191
|
+
// isLoading=true again rather than a stale empty-list flash while the
|
|
1192
|
+
// new context dispatch/fetch is in flight.
|
|
1193
|
+
enabled.value = true;
|
|
1194
|
+
await nextTick();
|
|
1195
|
+
|
|
1196
|
+
expect(getResult().isLoading.value).toBe(true);
|
|
1197
|
+
|
|
1198
|
+
await vi.waitFor(() => {
|
|
1199
|
+
expect(getResult().isLoading.value).toBe(false);
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
1203
|
+
expect.stringContaining("/threads?agentId=agent-1"),
|
|
1204
|
+
expect.objectContaining({ method: "GET" }),
|
|
1205
|
+
);
|
|
1206
|
+
const nonNullContexts = getDispatchedContexts().filter(
|
|
1207
|
+
(context) => context !== null,
|
|
1208
|
+
);
|
|
1209
|
+
expect(nonNullContexts).toHaveLength(1);
|
|
1210
|
+
});
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
describe("thread store registration (core registry)", () => {
|
|
1214
|
+
it("registers the thread store with core when enabled", async () => {
|
|
1215
|
+
const copilotkit = setupCopilotKit();
|
|
1216
|
+
|
|
1217
|
+
fetchMock
|
|
1218
|
+
.mockReturnValueOnce(
|
|
1219
|
+
jsonResponse({ threads: sampleThreads, joinCode: "jc-1" }),
|
|
1220
|
+
)
|
|
1221
|
+
.mockReturnValueOnce(jsonResponse({ joinToken: "jt-1" }));
|
|
1222
|
+
|
|
1223
|
+
mountHook();
|
|
1224
|
+
|
|
1225
|
+
await vi.waitFor(() => {
|
|
1226
|
+
expect(copilotkit.value.registerThreadStore).toHaveBeenCalledWith(
|
|
1227
|
+
"agent-1",
|
|
1228
|
+
expect.objectContaining({ select: expect.any(Function) }),
|
|
1229
|
+
);
|
|
1230
|
+
});
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
it("does not register the thread store when disabled (avoids evicting a live store for the same agent)", async () => {
|
|
1234
|
+
const copilotkit = setupCopilotKit();
|
|
1235
|
+
|
|
1236
|
+
mountHook({ ...defaultInput, enabled: false });
|
|
1237
|
+
|
|
1238
|
+
await nextTick();
|
|
1239
|
+
|
|
1240
|
+
expect(copilotkit.value.registerThreadStore).not.toHaveBeenCalled();
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
it("unregisters the thread store with core on unmount", async () => {
|
|
1244
|
+
const copilotkit = setupCopilotKit();
|
|
1245
|
+
|
|
1246
|
+
fetchMock
|
|
1247
|
+
.mockReturnValueOnce(
|
|
1248
|
+
jsonResponse({ threads: sampleThreads, joinCode: "jc-1" }),
|
|
1249
|
+
)
|
|
1250
|
+
.mockReturnValueOnce(jsonResponse({ joinToken: "jt-1" }));
|
|
1251
|
+
|
|
1252
|
+
const { wrapper } = mountHook();
|
|
1253
|
+
|
|
1254
|
+
await vi.waitFor(() => {
|
|
1255
|
+
expect(copilotkit.value.registerThreadStore).toHaveBeenCalled();
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
wrapper.unmount();
|
|
1259
|
+
|
|
1260
|
+
expect(copilotkit.value.unregisterThreadStore).toHaveBeenCalledWith(
|
|
1261
|
+
"agent-1",
|
|
1262
|
+
);
|
|
1263
|
+
});
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
describe("threadEndpoints gating", () => {
|
|
1267
|
+
it("does not fetch when the runtime does not advertise the list endpoint and surfaces the endpoints-unavailable error", async () => {
|
|
1268
|
+
setupCopilotKit(
|
|
1269
|
+
"http://localhost:4000",
|
|
1270
|
+
CopilotKitCoreRuntimeConnectionStatus.Connected,
|
|
1271
|
+
{
|
|
1272
|
+
threadEndpoints: {
|
|
1273
|
+
list: false,
|
|
1274
|
+
inspect: false,
|
|
1275
|
+
mutations: false,
|
|
1276
|
+
realtimeMetadata: false,
|
|
1277
|
+
},
|
|
1278
|
+
intelligence: undefined,
|
|
1279
|
+
},
|
|
1280
|
+
);
|
|
1281
|
+
|
|
1282
|
+
const { getResult } = mountHook();
|
|
1283
|
+
|
|
1284
|
+
await vi.waitFor(() => {
|
|
1285
|
+
expect(getResult().isLoading.value).toBe(false);
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
1289
|
+
expect(getResult().threads.value).toEqual([]);
|
|
1290
|
+
const lastDispatched = getDispatchedContexts().at(-1);
|
|
1291
|
+
expect(lastDispatched).toBeNull();
|
|
1292
|
+
expect(getResult().error.value?.message).toBe(
|
|
1293
|
+
"Thread endpoints are not available on this CopilotKit runtime",
|
|
1294
|
+
);
|
|
1295
|
+
// The config error must NOT leak into the user-facing list error.
|
|
1296
|
+
expect(getResult().listError.value).toBeNull();
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
it("rejects mutations locally when the runtime reports mutations are unsupported", async () => {
|
|
1300
|
+
setupCopilotKit(
|
|
1301
|
+
"http://localhost:4000",
|
|
1302
|
+
CopilotKitCoreRuntimeConnectionStatus.Connected,
|
|
1303
|
+
{
|
|
1304
|
+
threadEndpoints: {
|
|
1305
|
+
list: true,
|
|
1306
|
+
inspect: true,
|
|
1307
|
+
mutations: false,
|
|
1308
|
+
realtimeMetadata: false,
|
|
1309
|
+
},
|
|
1310
|
+
intelligence: undefined,
|
|
1311
|
+
},
|
|
1312
|
+
);
|
|
1313
|
+
fetchMock.mockReturnValueOnce(jsonResponse({ threads: sampleThreads }));
|
|
1314
|
+
|
|
1315
|
+
const { getResult } = mountHook();
|
|
1316
|
+
|
|
1317
|
+
await vi.waitFor(() => {
|
|
1318
|
+
expect(getResult().isLoading.value).toBe(false);
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
fetchMock.mockClear();
|
|
1322
|
+
|
|
1323
|
+
await expect(getResult().renameThread("t-1", "Renamed")).rejects.toThrow(
|
|
1324
|
+
"Thread mutations are not available on this CopilotKit runtime",
|
|
1325
|
+
);
|
|
1326
|
+
await expect(getResult().archiveThread("t-1")).rejects.toThrow(
|
|
1327
|
+
"Thread mutations are not available on this CopilotKit runtime",
|
|
1328
|
+
);
|
|
1329
|
+
await expect(getResult().unarchiveThread("t-1")).rejects.toThrow(
|
|
1330
|
+
"Thread mutations are not available on this CopilotKit runtime",
|
|
1331
|
+
);
|
|
1332
|
+
await expect(getResult().deleteThread("t-1")).rejects.toThrow(
|
|
1333
|
+
"Thread mutations are not available on this CopilotKit runtime",
|
|
1334
|
+
);
|
|
1335
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
1336
|
+
});
|
|
1337
|
+
});
|
|
1041
1338
|
});
|
|
@@ -128,8 +128,11 @@ function getOrCreateThreadClone(
|
|
|
128
128
|
* ```
|
|
129
129
|
*/
|
|
130
130
|
export function useAgent(props: UseAgentProps = {}) {
|
|
131
|
-
const agentId = computed(() => toValue(props.agentId) ?? DEFAULT_AGENT_ID);
|
|
132
131
|
const chatConfig = useCopilotChatConfiguration();
|
|
132
|
+
const agentId = computed(
|
|
133
|
+
() =>
|
|
134
|
+
toValue(props.agentId) ?? chatConfig.value?.agentId ?? DEFAULT_AGENT_ID,
|
|
135
|
+
);
|
|
133
136
|
const threadId = computed(
|
|
134
137
|
() => toValue(props.threadId) ?? chatConfig.value?.threadId,
|
|
135
138
|
);
|