@copilotkit/vue 2.0.0-rc.20 → 2.0.0-rc.21
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/index.cjs +1 -1
- package/dist/index.mjs +13 -13
- package/dist/use-render-activity-message-BAhbpSDh.js +16200 -0
- package/dist/use-render-activity-message-BAhbpSDh.js.map +1 -0
- package/dist/use-render-activity-message-DJ6q1qHV.cjs +114 -0
- package/dist/use-render-activity-message-DJ6q1qHV.cjs.map +1 -0
- package/dist/v2/components/A2UIMessageRenderer.d.ts +14 -0
- package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
- package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts +10 -3
- package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
- package/dist/v2/components/CopilotKitInspector.vue.d.ts +5 -1
- package/dist/v2/components/CopilotKitInspector.vue.d.ts.map +1 -1
- package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -1
- package/dist/v2/components/OpenGenerativeUIRenderer.d.ts +7 -21
- package/dist/v2/components/OpenGenerativeUIRenderer.d.ts.map +1 -1
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +0 -5
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
- package/dist/v2/components/a2ui/A2UICatalogContext.d.ts +10 -15
- package/dist/v2/components/a2ui/A2UICatalogContext.d.ts.map +1 -1
- package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +14 -138
- package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -1
- package/dist/v2/components/a2ui/catalog.d.ts.map +1 -1
- package/dist/v2/components/a2ui/utils.d.ts +3 -15
- package/dist/v2/components/a2ui/utils.d.ts.map +1 -1
- package/dist/v2/components/a2ui.d.ts +6 -1
- package/dist/v2/components/a2ui.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts +4 -8
- package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -1
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +1 -1
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +1 -1
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotThreadsDrawer.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts +1 -0
- package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/audioRecorder.d.ts +3 -4
- package/dist/v2/components/chat/audioRecorder.d.ts.map +1 -1
- package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +3 -2
- package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
- package/dist/v2/components/chat/index.d.ts +6 -6
- package/dist/v2/components/chat/tool-call-display-context.d.ts +3 -1
- package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
- package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
- package/dist/v2/components/chat/types.d.ts +1 -1
- package/dist/v2/components/chat/types.d.ts.map +1 -1
- package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -1
- package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +1 -1
- package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -1
- package/dist/v2/hooks/use-agent-context.d.ts +2 -3
- package/dist/v2/hooks/use-agent-context.d.ts.map +1 -1
- package/dist/v2/hooks/use-agent.d.ts +4 -1
- package/dist/v2/hooks/use-agent.d.ts.map +1 -1
- package/dist/v2/hooks/use-attachments.d.ts +7 -0
- package/dist/v2/hooks/use-attachments.d.ts.map +1 -1
- package/dist/v2/hooks/use-capabilities.d.ts.map +1 -1
- package/dist/v2/hooks/use-component.d.ts +1 -0
- package/dist/v2/hooks/use-component.d.ts.map +1 -1
- package/dist/v2/hooks/use-configure-suggestions.d.ts +4 -9
- package/dist/v2/hooks/use-configure-suggestions.d.ts.map +1 -1
- package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
- package/dist/v2/hooks/use-human-in-the-loop.d.ts +2 -1
- package/dist/v2/hooks/use-human-in-the-loop.d.ts.map +1 -1
- package/dist/v2/hooks/use-interrupt.d.ts +0 -1
- package/dist/v2/hooks/use-interrupt.d.ts.map +1 -1
- package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
- package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
- package/dist/v2/hooks/use-suggestions.d.ts.map +1 -1
- package/dist/v2/hooks/use-threads.d.ts +0 -8
- package/dist/v2/hooks/use-threads.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/lib/vue-core.d.ts +12 -11
- package/dist/v2/lib/vue-core.d.ts.map +1 -1
- package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts +9 -124
- package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts +9 -10
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/chat-configuration-store-key.d.ts +8 -0
- package/dist/v2/providers/chat-configuration-store-key.d.ts.map +1 -0
- package/dist/v2/providers/markdown-renderer.d.ts +3 -1
- package/dist/v2/providers/markdown-renderer.d.ts.map +1 -1
- package/dist/v2/providers/use-inspector-thread-override.d.ts +5 -0
- package/dist/v2/providers/use-inspector-thread-override.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/v2/__tests__/exports.test.ts +10 -0
- package/src/v2/__tests__/mocks/web-inspector.ts +6 -0
- package/src/v2/__tests__/utils/test-helpers.ts +34 -4
- package/src/v2/components/A2UIMessageRenderer.ts +98 -68
- package/src/v2/components/A2UISurfaceActivityRenderer.vue +68 -103
- package/src/v2/components/CopilotKitInspector.vue +104 -51
- package/src/v2/components/MCPAppsActivityRenderer.ts +75 -201
- package/src/v2/components/OpenGenerativeUIRenderer.ts +78 -80
- package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +171 -8
- package/src/v2/components/__tests__/CopilotKitInspector.test.ts +42 -0
- package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.ts +9 -2
- package/src/v2/components/__tests__/OpenGenerativeUIToolRenderer.test.ts +38 -0
- package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +1 -6
- package/src/v2/components/a2ui/A2UICatalogContext.ts +27 -93
- package/src/v2/components/a2ui/A2UIRecoveryStates.ts +127 -327
- package/src/v2/components/a2ui/catalog.ts +2 -3
- package/src/v2/components/a2ui/utils.ts +13 -67
- package/src/v2/components/a2ui.ts +14 -21
- package/src/v2/components/chat/CopilotChat.vue +136 -324
- package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +22 -45
- package/src/v2/components/chat/CopilotChatAudioRecorder.vue +19 -246
- package/src/v2/components/chat/CopilotChatInput.vue +69 -407
- package/src/v2/components/chat/CopilotChatLightbox.vue +10 -31
- package/src/v2/components/chat/CopilotChatMessageView.vue +52 -112
- package/src/v2/components/chat/CopilotChatToolCallGroup.vue +14 -45
- package/src/v2/components/chat/CopilotChatToolCallItem.vue +6 -4
- package/src/v2/components/chat/CopilotChatToolCallsView.vue +16 -19
- package/src/v2/components/chat/CopilotChatView.vue +28 -23
- package/src/v2/components/chat/CopilotPopupViewInternal.vue +21 -97
- package/src/v2/components/chat/CopilotSidebarViewInternal.vue +24 -25
- package/src/v2/components/chat/CopilotThreadsDrawer.vue +37 -102
- package/src/v2/components/chat/StreamingMarkdownDefault.vue +13 -26
- package/src/v2/components/chat/__tests__/CopilotChat.test.ts +12 -10
- package/src/v2/components/chat/__tests__/CopilotChatAttachmentQueue.test.ts +28 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +56 -0
- package/src/v2/components/chat/__tests__/CopilotPopupView.test.ts +19 -0
- package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +32 -8
- package/src/v2/components/chat/__tests__/CopilotSidebarView.test.ts +2 -1
- package/src/v2/components/chat/__tests__/StreamingMarkdownDefault.test.ts +54 -0
- package/src/v2/components/chat/audioRecorder.ts +4 -7
- package/src/v2/components/chat/copilotChatToolCallItemRender.ts +22 -71
- package/src/v2/components/chat/tool-call-display-context.ts +6 -1
- package/src/v2/components/chat/tool-call-group-parts.ts +13 -33
- package/src/v2/components/chat/types.ts +1 -1
- package/src/v2/components/chat/use-lightbox.ts +15 -33
- package/src/v2/hooks/__tests__/use-agent-error-state.test.ts +5 -3
- package/src/v2/hooks/__tests__/use-agent-stability.test.ts +25 -4
- package/src/v2/hooks/__tests__/use-agent-throttle.test.ts +5 -1
- package/src/v2/hooks/__tests__/use-agent.test.ts +6 -3
- package/src/v2/hooks/__tests__/use-component.test.ts +21 -0
- package/src/v2/hooks/__tests__/use-configure-suggestions.test.ts +30 -1
- package/src/v2/hooks/__tests__/use-default-render-tool.test.ts +10 -10
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +4 -0
- package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.ts +6 -1
- package/src/v2/hooks/__tests__/use-human-in-the-loop.test.ts +187 -12
- package/src/v2/hooks/__tests__/use-interrupt.test.ts +97 -0
- package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +2 -1
- package/src/v2/hooks/__tests__/use-threads.test.ts +48 -6
- package/src/v2/hooks/use-agent-context.ts +4 -10
- package/src/v2/hooks/use-agent.ts +86 -290
- package/src/v2/hooks/use-attachments.ts +42 -166
- package/src/v2/hooks/use-capabilities.ts +2 -7
- package/src/v2/hooks/use-component.ts +7 -4
- package/src/v2/hooks/use-configure-suggestions.ts +28 -218
- package/src/v2/hooks/use-default-render-tool.ts +34 -94
- package/src/v2/hooks/use-human-in-the-loop.ts +11 -80
- package/src/v2/hooks/use-interrupt.ts +29 -308
- package/src/v2/hooks/use-render-activity-message.ts +17 -20
- package/src/v2/hooks/use-render-custom-messages.ts +20 -63
- package/src/v2/hooks/use-suggestions.ts +9 -33
- package/src/v2/hooks/use-threads.ts +43 -273
- package/src/v2/lib/__tests__/vue-core-renderers.test.ts +42 -0
- package/src/v2/lib/vue-core.ts +67 -72
- package/src/v2/providers/CopilotChatConfigurationProvider.vue +48 -186
- package/src/v2/providers/CopilotKitProvider.types.ts +12 -127
- package/src/v2/providers/CopilotKitProvider.vue +188 -402
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.ts +22 -2
- package/src/v2/providers/__tests__/CopilotKitProvider.a2ui.test.ts +19 -1
- package/src/v2/providers/__tests__/CopilotKitProvider.config.test.ts +124 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.ts +4 -1
- package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +45 -2
- package/src/v2/providers/chat-configuration-store-key.ts +9 -0
- package/src/v2/providers/markdown-renderer.ts +7 -1
- package/src/v2/providers/use-inspector-thread-override.ts +22 -80
- package/vite.config.ts +5 -1
- package/vitest.config.ts +12 -7
- package/dist/use-render-activity-message-CsWYyd6r.cjs +0 -91
- package/dist/use-render-activity-message-CsWYyd6r.cjs.map +0 -1
- package/dist/use-render-activity-message-DntKKNdd.js +0 -17418
- package/dist/use-render-activity-message-DntKKNdd.js.map +0 -1
- package/dist/v2/components/chat/normalize-auto-scroll.d.ts +0 -3
- package/dist/v2/components/chat/normalize-auto-scroll.d.ts.map +0 -1
- package/dist/v2/lib/is-mobile-viewport.d.ts +0 -2
- package/dist/v2/lib/is-mobile-viewport.d.ts.map +0 -1
- package/src/v2/components/__tests__/A2UICatalogContext.test.ts +0 -63
- package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +0 -175
- package/src/v2/components/chat/normalize-auto-scroll.ts +0 -3
- package/src/v2/lib/is-mobile-viewport.ts +0 -5
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { defineComponent, h, ref } from "vue";
|
|
3
|
-
import { render, waitFor } from "@testing-library/vue";
|
|
3
|
+
import { fireEvent, render, waitFor } from "@testing-library/vue";
|
|
4
|
+
import type { ɵA2UIActionInterceptor } from "@copilotkit/core";
|
|
4
5
|
import { createA2UIMessageRenderer } from "../A2UIMessageRenderer";
|
|
5
6
|
import { CopilotKitKey } from "../../providers/keys";
|
|
6
7
|
|
|
7
|
-
function copilotKitProvide(
|
|
8
|
+
function copilotKitProvide(
|
|
9
|
+
onRunAgent: (properties: Record<string, unknown>) => void = () => undefined,
|
|
10
|
+
) {
|
|
11
|
+
const copilotkit = {
|
|
12
|
+
properties: {} as Record<string, unknown>,
|
|
13
|
+
setProperties(properties: Record<string, unknown>) {
|
|
14
|
+
copilotkit.properties = properties;
|
|
15
|
+
},
|
|
16
|
+
runAgent: async () => {
|
|
17
|
+
onRunAgent(copilotkit.properties);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
8
20
|
return {
|
|
9
21
|
[CopilotKitKey as symbol]: {
|
|
10
|
-
copilotkit: ref(
|
|
11
|
-
properties: {},
|
|
12
|
-
setProperties: () => undefined,
|
|
13
|
-
runAgent: async () => undefined,
|
|
14
|
-
}),
|
|
22
|
+
copilotkit: ref(copilotkit),
|
|
15
23
|
executingToolCallIds: ref(new Set()),
|
|
16
24
|
a2uiTheme: ref({}),
|
|
17
25
|
a2uiCatalog: ref(undefined),
|
|
@@ -22,6 +30,10 @@ function copilotKitProvide() {
|
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
describe("createA2UIMessageRenderer", () => {
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
vi.useRealTimers();
|
|
35
|
+
});
|
|
36
|
+
|
|
25
37
|
const renderLifecycle = (
|
|
26
38
|
content: Record<string, unknown>,
|
|
27
39
|
options: Partial<Parameters<typeof createA2UIMessageRenderer>[0]> = {},
|
|
@@ -46,6 +58,43 @@ describe("createA2UIMessageRenderer", () => {
|
|
|
46
58
|
expect(container.textContent).toContain("~97 tokens");
|
|
47
59
|
});
|
|
48
60
|
|
|
61
|
+
it("keeps an early retry looking like building until showAfterMs passes", async () => {
|
|
62
|
+
vi.useFakeTimers();
|
|
63
|
+
const { container } = renderLifecycle(
|
|
64
|
+
{ status: "retrying", attempt: 1, maxAttempts: 3 },
|
|
65
|
+
{ recovery: { showAfterMs: 2000 } },
|
|
66
|
+
);
|
|
67
|
+
expect(container.textContent).toContain("Building interface");
|
|
68
|
+
await vi.advanceTimersByTimeAsync(2000);
|
|
69
|
+
expect(container.textContent).toContain(
|
|
70
|
+
"Retrying generation… (1/3 attempts)",
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("shows retry validation issues unless debugExposure is hidden", () => {
|
|
75
|
+
const retry = {
|
|
76
|
+
status: "retrying",
|
|
77
|
+
attempt: 2,
|
|
78
|
+
errors: [{ code: "missing_required_prop" }],
|
|
79
|
+
};
|
|
80
|
+
const shown = renderLifecycle(retry);
|
|
81
|
+
expect(shown.container.querySelector("details summary")?.textContent).toBe(
|
|
82
|
+
"validation issues",
|
|
83
|
+
);
|
|
84
|
+
expect(shown.container.textContent).toContain("missing_required_prop");
|
|
85
|
+
const hidden = renderLifecycle(retry, {
|
|
86
|
+
recovery: { debugExposure: "hidden" },
|
|
87
|
+
});
|
|
88
|
+
expect(hidden.container.querySelector("details")).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("shows collapsed developer details on failure by default", () => {
|
|
92
|
+
const { container } = renderLifecycle({ status: "failed", error: "boom" });
|
|
93
|
+
const details = container.querySelector("details");
|
|
94
|
+
expect(details?.hasAttribute("open")).toBe(false);
|
|
95
|
+
expect(details?.textContent).toContain("boom");
|
|
96
|
+
});
|
|
97
|
+
|
|
49
98
|
it("shows the retry state for status 'retrying'", () => {
|
|
50
99
|
const { container } = renderLifecycle({
|
|
51
100
|
status: "retrying",
|
|
@@ -295,4 +344,118 @@ describe("createA2UIMessageRenderer", () => {
|
|
|
295
344
|
expect(container.querySelector("[data-surface-id='s2']")).not.toBeNull();
|
|
296
345
|
});
|
|
297
346
|
});
|
|
347
|
+
|
|
348
|
+
const buttonContent = {
|
|
349
|
+
a2ui_operations: [
|
|
350
|
+
{
|
|
351
|
+
version: "v0.9",
|
|
352
|
+
createSurface: {
|
|
353
|
+
surfaceId: "form",
|
|
354
|
+
catalogId: "https://a2ui.org/specification/v0_9/basic_catalog.json",
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
version: "v0.9",
|
|
359
|
+
updateComponents: {
|
|
360
|
+
surfaceId: "form",
|
|
361
|
+
components: [
|
|
362
|
+
{
|
|
363
|
+
id: "root",
|
|
364
|
+
component: "Button",
|
|
365
|
+
child: "label",
|
|
366
|
+
action: { event: { name: "confirm", context: { n: 1 } } },
|
|
367
|
+
},
|
|
368
|
+
{ id: "label", component: "Text", text: "Confirm" },
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
/** Renders the button surface, clicks it, and returns what reached the agent. */
|
|
376
|
+
async function clickButton(onAction?: ɵA2UIActionInterceptor) {
|
|
377
|
+
const forwarded: Record<string, unknown>[] = [];
|
|
378
|
+
const renderer = createA2UIMessageRenderer({ theme: {}, onAction });
|
|
379
|
+
const { findByRole } = render(renderer.render, {
|
|
380
|
+
props: {
|
|
381
|
+
activityType: "a2ui-surface",
|
|
382
|
+
content: buttonContent,
|
|
383
|
+
message: {},
|
|
384
|
+
agent: { agentId: "a" },
|
|
385
|
+
},
|
|
386
|
+
global: {
|
|
387
|
+
provide: copilotKitProvide((properties) =>
|
|
388
|
+
forwarded.push({ ...properties }),
|
|
389
|
+
),
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
await fireEvent.click(await findByRole("button", { hidden: true }));
|
|
393
|
+
return forwarded;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
it("forwards a button click to the agent wrapped as an A2UI userAction envelope", async () => {
|
|
397
|
+
const forwarded = await clickButton();
|
|
398
|
+
await waitFor(() => expect(forwarded).toHaveLength(1));
|
|
399
|
+
expect(forwarded[0]).toEqual({
|
|
400
|
+
a2uiAction: {
|
|
401
|
+
userAction: {
|
|
402
|
+
name: "confirm",
|
|
403
|
+
surfaceId: "form",
|
|
404
|
+
sourceComponentId: "root",
|
|
405
|
+
context: { n: 1 },
|
|
406
|
+
timestamp: expect.any(String),
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("does not run the agent when onAction handles the action", async () => {
|
|
413
|
+
const onAction = vi.fn<ɵA2UIActionInterceptor>(() => null);
|
|
414
|
+
const forwarded = await clickButton(onAction);
|
|
415
|
+
await waitFor(() => expect(onAction).toHaveBeenCalledOnce());
|
|
416
|
+
expect(forwarded).toEqual([]);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("keeps the loader over the hidden surface until the surface can paint", async () => {
|
|
420
|
+
const renderer = createA2UIMessageRenderer({ theme: {} });
|
|
421
|
+
const createOnly = {
|
|
422
|
+
a2ui_operations: [buttonContent.a2ui_operations[0]],
|
|
423
|
+
};
|
|
424
|
+
const mounted = render(renderer.render, {
|
|
425
|
+
props: {
|
|
426
|
+
activityType: "a2ui-surface",
|
|
427
|
+
content: { status: "building", progressTokens: 120 },
|
|
428
|
+
message: {},
|
|
429
|
+
agent: {},
|
|
430
|
+
},
|
|
431
|
+
global: { provide: copilotKitProvide() },
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
await mounted.rerender({
|
|
435
|
+
activityType: "a2ui-surface",
|
|
436
|
+
content: createOnly,
|
|
437
|
+
message: {},
|
|
438
|
+
agent: {},
|
|
439
|
+
});
|
|
440
|
+
// A surface without components cannot paint: the loader stays on top.
|
|
441
|
+
expect(mounted.container.textContent).toContain("~120 tokens");
|
|
442
|
+
expect(
|
|
443
|
+
mounted.container.querySelector(
|
|
444
|
+
"[aria-hidden='true'] [data-testid='a2ui-activity-renderer']",
|
|
445
|
+
),
|
|
446
|
+
).not.toBeNull();
|
|
447
|
+
|
|
448
|
+
await mounted.rerender({
|
|
449
|
+
activityType: "a2ui-surface",
|
|
450
|
+
content: buttonContent,
|
|
451
|
+
message: {},
|
|
452
|
+
agent: {},
|
|
453
|
+
});
|
|
454
|
+
await waitFor(() => {
|
|
455
|
+
expect(mounted.container.textContent).not.toContain("Building interface");
|
|
456
|
+
expect(
|
|
457
|
+
mounted.container.querySelector("[aria-hidden='true']"),
|
|
458
|
+
).toBeNull();
|
|
459
|
+
});
|
|
460
|
+
});
|
|
298
461
|
});
|
|
@@ -62,6 +62,48 @@ describe("CopilotKitInspector", () => {
|
|
|
62
62
|
);
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
+
it("forwards event listeners to the web inspector element", async () => {
|
|
66
|
+
const onVisibility = vi.fn();
|
|
67
|
+
const wrapper = mount(CopilotKitInspector, {
|
|
68
|
+
attrs: { onCpkInspectorVisibilityChange: onVisibility },
|
|
69
|
+
});
|
|
70
|
+
await settleInspectorLoad();
|
|
71
|
+
|
|
72
|
+
const event = new CustomEvent("cpk-inspector-visibility-change", {
|
|
73
|
+
detail: { visible: false },
|
|
74
|
+
});
|
|
75
|
+
wrapper.get("cpk-web-inspector").element.dispatchEvent(event);
|
|
76
|
+
|
|
77
|
+
expect(onVisibility).toHaveBeenCalledWith(event);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("opens a request made before the web inspector module resolves", async () => {
|
|
81
|
+
const wrapper = mount(CopilotKitInspector);
|
|
82
|
+
const openRequest = { messageId: "message-early" };
|
|
83
|
+
await wrapper.setProps({ openRequest });
|
|
84
|
+
|
|
85
|
+
await settleInspectorLoad();
|
|
86
|
+
|
|
87
|
+
const inspector = wrapper.get("cpk-web-inspector")
|
|
88
|
+
.element as HTMLElement & {
|
|
89
|
+
openInspector: ReturnType<typeof vi.fn>;
|
|
90
|
+
};
|
|
91
|
+
expect(inspector.openInspector).toHaveBeenCalledExactlyOnceWith(
|
|
92
|
+
"message_toolbar",
|
|
93
|
+
openRequest,
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("emits Inspector visibility and removes the element on unmount", async () => {
|
|
98
|
+
const wrapper = mount(CopilotKitInspector, { attachTo: document.body });
|
|
99
|
+
await settleInspectorLoad();
|
|
100
|
+
expect(wrapper.emitted("visibility-change")).toEqual([[true]]);
|
|
101
|
+
|
|
102
|
+
wrapper.unmount();
|
|
103
|
+
|
|
104
|
+
expect(document.querySelector("cpk-web-inspector")).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
|
|
65
107
|
it("forwards Inspector open requests to the mounted element", async () => {
|
|
66
108
|
const openRequest = { messageId: "message-1" };
|
|
67
109
|
const wrapper = mount(CopilotKitInspector, {
|
|
@@ -100,16 +100,23 @@ describe("OpenGenerativeUIRenderer", () => {
|
|
|
100
100
|
vi.restoreAllMocks();
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
it("
|
|
103
|
+
it("shows the progress overlay with a spinner while generating", async () => {
|
|
104
104
|
const { getByTestId, queryByTestId } = renderRenderer({
|
|
105
105
|
initialHeight: 300,
|
|
106
106
|
});
|
|
107
107
|
await flushImport();
|
|
108
|
-
expect(getByTestId("open-generative-ui-
|
|
108
|
+
expect(getByTestId("open-generative-ui-spinner")).not.toBeNull();
|
|
109
109
|
expect(queryByTestId("open-generative-ui-final-sandbox")).toBeNull();
|
|
110
110
|
expect(mockCreate).not.toHaveBeenCalled();
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
+
it("shows no spinner or overlay once generation is done", async () => {
|
|
114
|
+
const { queryByTestId } = renderRenderer({ generating: false });
|
|
115
|
+
await flushImport();
|
|
116
|
+
expect(queryByTestId("open-generative-ui-progress-overlay")).toBeNull();
|
|
117
|
+
expect(queryByTestId("open-generative-ui-spinner")).toBeNull();
|
|
118
|
+
});
|
|
119
|
+
|
|
113
120
|
it("creates final sandbox when html is complete", async () => {
|
|
114
121
|
renderRenderer({
|
|
115
122
|
html: ["<head></head><body><p>Hello</p></body>"],
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { render } from "@testing-library/vue";
|
|
3
|
+
import { ToolCallStatus } from "@copilotkit/core";
|
|
4
|
+
import { OpenGenerativeUIToolRenderer } from "../OpenGenerativeUIRenderer";
|
|
5
|
+
|
|
6
|
+
function toolProps(messages: string[], status = ToolCallStatus.InProgress) {
|
|
7
|
+
return {
|
|
8
|
+
name: "generateSandboxedUi",
|
|
9
|
+
args: { placeholderMessages: [...messages] },
|
|
10
|
+
status,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("OpenGenerativeUIToolRenderer", () => {
|
|
15
|
+
beforeEach(() => vi.useFakeTimers());
|
|
16
|
+
afterEach(() => vi.useRealTimers());
|
|
17
|
+
|
|
18
|
+
it("keeps rotating while streamed snapshots repeat the same messages", async () => {
|
|
19
|
+
const view = render(OpenGenerativeUIToolRenderer, {
|
|
20
|
+
props: toolProps(["Sketching", "Styling"]),
|
|
21
|
+
});
|
|
22
|
+
expect(view.container.textContent).toBe("Styling");
|
|
23
|
+
|
|
24
|
+
await vi.advanceTimersByTimeAsync(4000);
|
|
25
|
+
await view.rerender(toolProps(["Sketching", "Styling"]));
|
|
26
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
27
|
+
|
|
28
|
+
expect(view.container.textContent).toBe("Sketching");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("renders nothing once the tool call is complete", async () => {
|
|
32
|
+
const view = render(OpenGenerativeUIToolRenderer, {
|
|
33
|
+
props: toolProps(["Sketching"]),
|
|
34
|
+
});
|
|
35
|
+
await view.rerender(toolProps(["Sketching"], ToolCallStatus.Complete));
|
|
36
|
+
expect(view.container.textContent).toBe("");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { watch } from "vue";
|
|
2
2
|
import type { ShallowRef } from "vue";
|
|
3
3
|
import * as z from "zod";
|
|
4
|
+
import { ɵRENDER_A2UI_TOOL_NAME as RENDER_A2UI_TOOL_NAME } from "@copilotkit/core";
|
|
4
5
|
import type { CopilotKitCoreVue } from "../../lib/vue-core";
|
|
5
6
|
import { defineToolCallRenderer } from "../../types/defineToolCallRenderer";
|
|
6
7
|
|
|
7
|
-
/**
|
|
8
|
-
* Tool name used by the dynamic A2UI generation secondary LLM.
|
|
9
|
-
* This renderer is auto-registered when A2UI is enabled.
|
|
10
|
-
*/
|
|
11
|
-
export const RENDER_A2UI_TOOL_NAME = "render_a2ui";
|
|
12
|
-
|
|
13
8
|
/**
|
|
14
9
|
* Registers a no-op renderer for the `render_a2ui` tool call so its raw
|
|
15
10
|
* streamed args are never surfaced in the transcript, as React does.
|
|
@@ -1,116 +1,50 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { watch } from "vue";
|
|
2
2
|
import type { ShallowRef } from "vue";
|
|
3
3
|
import type { Catalog, ComponentApi } from "@a2ui/web_core/v0_9";
|
|
4
|
-
import {
|
|
5
|
-
A2UI_SCHEMA_CONTEXT_DESCRIPTION,
|
|
6
|
-
buildCatalogContextValue as buildContextValue,
|
|
7
|
-
extractCatalogComponentSchemas,
|
|
8
|
-
} from "@copilotkit/a2ui-renderer/catalog-schema";
|
|
9
|
-
import {
|
|
10
|
-
A2UI_DEFAULT_GENERATION_GUIDELINES,
|
|
11
|
-
A2UI_DEFAULT_DESIGN_GUIDELINES,
|
|
12
|
-
} from "@copilotkit/shared";
|
|
4
|
+
import { A2UICatalogController } from "@copilotkit/a2ui-renderer/catalog-schema";
|
|
13
5
|
import type { CopilotKitCoreVue } from "../../lib/vue-core";
|
|
14
6
|
import { vueBasicCatalog } from "./catalog";
|
|
15
7
|
|
|
16
8
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
9
|
+
* Keeps the provider's A2UI catalog in sync with core through the shared
|
|
10
|
+
* `A2UICatalogController`: registers its components (so the Inspector can
|
|
11
|
+
* toggle them), reports the catalog with disabled components removed, and
|
|
12
|
+
* registers the agent context entries scoped to `core.a2uiAgents` while A2UI
|
|
13
|
+
* is on. Call from the provider's setup function.
|
|
19
14
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* @param copilotkit - The provider's core.
|
|
16
|
+
* @param options.onFilteredCatalog - Receives the filtered catalog each time
|
|
17
|
+
* it changes; the provider renders surfaces with it.
|
|
22
18
|
*/
|
|
23
|
-
export function
|
|
24
|
-
catalog?: Catalog<ComponentApi>,
|
|
25
|
-
options?: { includeSchema?: boolean },
|
|
26
|
-
): string {
|
|
27
|
-
return buildContextValue(catalog ?? vueBasicCatalog, options);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Registers agent context describing the available A2UI catalog, component
|
|
32
|
-
* schemas, and generation/design guidelines.
|
|
33
|
-
*
|
|
34
|
-
* Vue-native equivalent of React's `<A2UICatalogContext>` component.
|
|
35
|
-
* Call from the provider's setup function.
|
|
36
|
-
*/
|
|
37
|
-
export function registerA2UICatalogContext(
|
|
19
|
+
export function registerA2UICatalog(
|
|
38
20
|
copilotkit: ShallowRef<CopilotKitCoreVue>,
|
|
39
21
|
options: {
|
|
40
22
|
enabled: () => boolean;
|
|
41
23
|
catalog: () => Catalog<ComponentApi> | undefined;
|
|
42
24
|
includeSchema: () => boolean;
|
|
25
|
+
onFilteredCatalog: (catalog: Catalog<ComponentApi> | undefined) => void;
|
|
43
26
|
},
|
|
44
27
|
): void {
|
|
45
|
-
const contextValue = computed(() =>
|
|
46
|
-
buildCatalogContextValue(options.catalog(), {
|
|
47
|
-
includeSchema: options.includeSchema(),
|
|
48
|
-
}),
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// Register catalog capabilities context
|
|
52
|
-
watch(
|
|
53
|
-
[() => copilotkit.value, options.enabled, contextValue],
|
|
54
|
-
([core, isEnabled, value], _prev, onCleanup) => {
|
|
55
|
-
if (!isEnabled) return;
|
|
56
|
-
// Scope to the agents the runtime applies A2UI to (#5369), so other
|
|
57
|
-
// agents on the endpoint don't receive the catalog payload.
|
|
58
|
-
const agentIds = core.a2uiAgents;
|
|
59
|
-
const id = core.addContext({
|
|
60
|
-
description:
|
|
61
|
-
"A2UI catalog capabilities: available catalog IDs and " +
|
|
62
|
-
"custom component definitions the client can render.",
|
|
63
|
-
value,
|
|
64
|
-
...(agentIds ? { agentIds } : {}),
|
|
65
|
-
});
|
|
66
|
-
onCleanup(() => core.removeContext(id));
|
|
67
|
-
},
|
|
68
|
-
{ immediate: true },
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
// Register schema + generation/design guidelines
|
|
72
|
-
const schemaValue = computed(() => {
|
|
73
|
-
if (!options.includeSchema()) return null;
|
|
74
|
-
return JSON.stringify(
|
|
75
|
-
extractCatalogComponentSchemas(options.catalog() ?? vueBasicCatalog),
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
28
|
watch(
|
|
80
|
-
|
|
81
|
-
(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const ids: string[] = [];
|
|
86
|
-
ids.push(
|
|
87
|
-
core.addContext({
|
|
88
|
-
description: A2UI_SCHEMA_CONTEXT_DESCRIPTION,
|
|
89
|
-
value: schema,
|
|
90
|
-
...scope,
|
|
91
|
-
}),
|
|
29
|
+
() => copilotkit.value,
|
|
30
|
+
(core, _previous, onCleanup) => {
|
|
31
|
+
const controller = new A2UICatalogController<Catalog<ComponentApi>>(
|
|
32
|
+
core,
|
|
33
|
+
vueBasicCatalog,
|
|
92
34
|
);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
description:
|
|
96
|
-
"A2UI generation guidelines — protocol rules, tool arguments, " +
|
|
97
|
-
"path rules, data model format, and " +
|
|
98
|
-
"form/two-way-binding instructions.",
|
|
99
|
-
value: A2UI_DEFAULT_GENERATION_GUIDELINES,
|
|
100
|
-
...scope,
|
|
101
|
-
}),
|
|
35
|
+
const unsubscribe = controller.subscribe(() =>
|
|
36
|
+
options.onFilteredCatalog(controller.getSnapshot()),
|
|
102
37
|
);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
value: A2UI_DEFAULT_DESIGN_GUIDELINES,
|
|
109
|
-
...scope,
|
|
110
|
-
}),
|
|
38
|
+
const stop = watch(
|
|
39
|
+
[options.enabled, options.catalog, options.includeSchema],
|
|
40
|
+
([enabled, catalog, includeSchema]) =>
|
|
41
|
+
controller.update({ enabled, catalog, includeSchema }),
|
|
42
|
+
{ immediate: true },
|
|
111
43
|
);
|
|
112
44
|
onCleanup(() => {
|
|
113
|
-
|
|
45
|
+
stop();
|
|
46
|
+
unsubscribe();
|
|
47
|
+
controller.dispose();
|
|
114
48
|
});
|
|
115
49
|
},
|
|
116
50
|
{ immediate: true },
|