@copilotkit/vue 1.76.0 → 1.77.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PARITY.md +93 -35
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +124 -122
- package/dist/index.mjs.map +1 -1
- package/dist/{use-render-activity-message-Eu6R2vGT.js → use-render-activity-message-C5XGgWI-.js} +4317 -4628
- package/dist/use-render-activity-message-C5XGgWI-.js.map +1 -0
- package/dist/use-render-activity-message-N-zuj24U.cjs +70 -0
- package/dist/use-render-activity-message-N-zuj24U.cjs.map +1 -0
- package/dist/v2/components/MCPAppsActivityRenderer.d.ts +10 -42
- package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -1
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/index.d.ts +3 -3
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +125 -123
- package/dist/v2/index.mjs.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/v2/components/MCPAppsActivityRenderer.ts +175 -658
- package/src/v2/components/__tests__/MCPAppsActivityRenderer.test.ts +39 -20
- package/src/v2/components/__tests__/MCPAppsActivityRendererContentError.test.ts +122 -0
- package/src/v2/components/__tests__/MCPAppsActivityRendererSeed.test.ts +109 -0
- package/src/v2/components/chat/CopilotPopupViewInternal.vue +1 -1
- package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +20 -0
- package/src/v2/providers/CopilotKitProvider.vue +1 -14
- package/vite.config.ts +7 -0
- package/dist/use-render-activity-message-BTKmd3f3.cjs +0 -110
- package/dist/use-render-activity-message-BTKmd3f3.cjs.map +0 -1
- package/dist/use-render-activity-message-Eu6R2vGT.js.map +0 -1
|
@@ -48,6 +48,32 @@ async function flushAsync() {
|
|
|
48
48
|
await nextTick();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Wait for the sandbox iframe to be configured by the shared session. The host
|
|
53
|
+
* mounts the iframe synchronously, but the bridge package is loaded through a
|
|
54
|
+
* dynamic import and the resource is fetched through the agent, so `srcdoc` (and
|
|
55
|
+
* the sandbox contract attributes) land a few macrotasks later.
|
|
56
|
+
*/
|
|
57
|
+
async function waitForSandboxIframe(wrapper: {
|
|
58
|
+
find: (selector: string) => { exists: () => boolean; element: Element };
|
|
59
|
+
}): Promise<HTMLIFrameElement> {
|
|
60
|
+
let iframe: HTMLIFrameElement | undefined;
|
|
61
|
+
await vi.waitFor(
|
|
62
|
+
() => {
|
|
63
|
+
const found = wrapper.find("iframe");
|
|
64
|
+
expect(found.exists()).toBe(true);
|
|
65
|
+
const element = found.element as HTMLIFrameElement;
|
|
66
|
+
expect(element.srcdoc).toBeTruthy();
|
|
67
|
+
iframe = element;
|
|
68
|
+
},
|
|
69
|
+
// The bridge arrives through a dynamic import, so this waits on real module
|
|
70
|
+
// loading rather than a microtask. `vi.waitFor`'s 1s default is enough in
|
|
71
|
+
// isolation but not when the whole monorepo's suites run in parallel.
|
|
72
|
+
{ timeout: 10_000, interval: 50 },
|
|
73
|
+
);
|
|
74
|
+
return iframe!;
|
|
75
|
+
}
|
|
76
|
+
|
|
51
77
|
describe("MCPAppsActivityRenderer", () => {
|
|
52
78
|
it("exports the expected activity type and schema", () => {
|
|
53
79
|
expect(MCPAppsActivityType).toBe("mcp-apps");
|
|
@@ -80,8 +106,8 @@ describe("MCPAppsActivityRenderer", () => {
|
|
|
80
106
|
},
|
|
81
107
|
message: {
|
|
82
108
|
id: "activity-1",
|
|
83
|
-
role: "
|
|
84
|
-
content:
|
|
109
|
+
role: "activity",
|
|
110
|
+
content: {},
|
|
85
111
|
activityType: MCPAppsActivityType,
|
|
86
112
|
},
|
|
87
113
|
agent: undefined,
|
|
@@ -123,8 +149,8 @@ describe("MCPAppsActivityRenderer", () => {
|
|
|
123
149
|
},
|
|
124
150
|
message: {
|
|
125
151
|
id: "activity-no-border",
|
|
126
|
-
role: "
|
|
127
|
-
content:
|
|
152
|
+
role: "activity",
|
|
153
|
+
content: {},
|
|
128
154
|
activityType: MCPAppsActivityType,
|
|
129
155
|
},
|
|
130
156
|
agent,
|
|
@@ -174,18 +200,15 @@ describe("MCPAppsActivityRenderer", () => {
|
|
|
174
200
|
},
|
|
175
201
|
message: {
|
|
176
202
|
id: "activity-csp",
|
|
177
|
-
role: "
|
|
178
|
-
content:
|
|
203
|
+
role: "activity",
|
|
204
|
+
content: {},
|
|
179
205
|
activityType: MCPAppsActivityType,
|
|
180
206
|
},
|
|
181
207
|
agent,
|
|
182
208
|
},
|
|
183
209
|
});
|
|
184
210
|
|
|
185
|
-
await
|
|
186
|
-
await flushAsync();
|
|
187
|
-
|
|
188
|
-
const iframe = wrapper.find("iframe").element as HTMLIFrameElement;
|
|
211
|
+
const iframe = await waitForSandboxIframe(wrapper);
|
|
189
212
|
expect(iframe.srcdoc).toContain("script-src");
|
|
190
213
|
expect(iframe.srcdoc).toContain("frame-src");
|
|
191
214
|
expect(iframe.srcdoc).toContain("https://widgets.example.com");
|
|
@@ -214,18 +237,15 @@ describe("MCPAppsActivityRenderer", () => {
|
|
|
214
237
|
},
|
|
215
238
|
message: {
|
|
216
239
|
id: "activity-default-csp",
|
|
217
|
-
role: "
|
|
218
|
-
content:
|
|
240
|
+
role: "activity",
|
|
241
|
+
content: {},
|
|
219
242
|
activityType: MCPAppsActivityType,
|
|
220
243
|
},
|
|
221
244
|
agent,
|
|
222
245
|
},
|
|
223
246
|
});
|
|
224
247
|
|
|
225
|
-
await
|
|
226
|
-
await flushAsync();
|
|
227
|
-
|
|
228
|
-
const iframe = wrapper.find("iframe").element as HTMLIFrameElement;
|
|
248
|
+
const iframe = await waitForSandboxIframe(wrapper);
|
|
229
249
|
expect(iframe.srcdoc).toContain(
|
|
230
250
|
"script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob: data: http://localhost:* https://localhost:*;",
|
|
231
251
|
);
|
|
@@ -260,16 +280,15 @@ describe("MCPAppsActivityRenderer", () => {
|
|
|
260
280
|
},
|
|
261
281
|
message: {
|
|
262
282
|
id: "activity-testid",
|
|
263
|
-
role: "
|
|
264
|
-
content:
|
|
283
|
+
role: "activity",
|
|
284
|
+
content: {},
|
|
265
285
|
activityType: MCPAppsActivityType,
|
|
266
286
|
},
|
|
267
287
|
agent,
|
|
268
288
|
},
|
|
269
289
|
});
|
|
270
290
|
|
|
271
|
-
await
|
|
272
|
-
await flushAsync();
|
|
291
|
+
await waitForSandboxIframe(wrapper);
|
|
273
292
|
|
|
274
293
|
const iframe = wrapper.find('iframe[data-testid="mcp-app-iframe"]');
|
|
275
294
|
expect(iframe.exists()).toBe(true);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A content rejection is RECOVERABLE: the session reports it through
|
|
3
|
+
* `onContentError` and, once valid content resumes, calls it again with `null`.
|
|
4
|
+
* The adapter must clear the message then - otherwise the widget starts working
|
|
5
|
+
* again while the host still shows a stale error until the next bind.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
8
|
+
import { nextTick } from "vue";
|
|
9
|
+
import { mount } from "@vue/test-utils";
|
|
10
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
11
|
+
|
|
12
|
+
// Capture the hooks the adapter hands to the session, so the test can drive the
|
|
13
|
+
// recoverable/fatal channels directly.
|
|
14
|
+
const { bindMcpAppSpy, sessionSpy } = vi.hoisted(() => {
|
|
15
|
+
const session = {
|
|
16
|
+
sendToolInput: vi.fn(),
|
|
17
|
+
sendToolResult: vi.fn(),
|
|
18
|
+
syncContent: vi.fn(),
|
|
19
|
+
teardown: vi.fn(),
|
|
20
|
+
};
|
|
21
|
+
return { bindMcpAppSpy: vi.fn(() => session), sessionSpy: session };
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
vi.mock("@copilotkit/mcp-apps-renderer", () => ({
|
|
25
|
+
bindMcpApp: bindMcpAppSpy,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock("../../providers/useCopilotKit", () => ({
|
|
29
|
+
useCopilotKit: () => ({
|
|
30
|
+
copilotkit: { value: { runAgent: vi.fn() } },
|
|
31
|
+
}),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
MCPAppsActivityRenderer,
|
|
36
|
+
MCPAppsActivityType,
|
|
37
|
+
} from "../MCPAppsActivityRenderer";
|
|
38
|
+
|
|
39
|
+
function createAgentMock(): AbstractAgent {
|
|
40
|
+
return {
|
|
41
|
+
threadId: "content-error-thread",
|
|
42
|
+
isRunning: false,
|
|
43
|
+
messages: [],
|
|
44
|
+
runAgent: vi.fn().mockResolvedValue({ result: { contents: [] } }),
|
|
45
|
+
addMessage: vi.fn(),
|
|
46
|
+
subscribe: vi.fn(() => ({ unsubscribe: vi.fn() })),
|
|
47
|
+
} as unknown as AbstractAgent;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mountRenderer() {
|
|
51
|
+
return mount(MCPAppsActivityRenderer, {
|
|
52
|
+
props: {
|
|
53
|
+
activityType: MCPAppsActivityType,
|
|
54
|
+
content: {
|
|
55
|
+
resourceUri: "ui://server/app",
|
|
56
|
+
serverHash: "hash",
|
|
57
|
+
result: { content: [] },
|
|
58
|
+
},
|
|
59
|
+
message: {
|
|
60
|
+
id: "activity-1",
|
|
61
|
+
role: "activity",
|
|
62
|
+
content: {},
|
|
63
|
+
activityType: MCPAppsActivityType,
|
|
64
|
+
},
|
|
65
|
+
agent: createAgentMock(),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The hooks object passed to the most recent bindMcpApp call. */
|
|
71
|
+
async function capturedHooks() {
|
|
72
|
+
await vi.waitFor(() => expect(bindMcpAppSpy).toHaveBeenCalled());
|
|
73
|
+
const options = bindMcpAppSpy.mock.calls.at(-1)![0] as {
|
|
74
|
+
hooks?: {
|
|
75
|
+
onContentError?: (err: Error | null) => void;
|
|
76
|
+
onError?: (err: Error) => void;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
return options.hooks!;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe("MCPAppsActivityRenderer content error recovery", () => {
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
vi.clearAllMocks();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("shows a rejected-content error and clears it when valid content resumes", async () => {
|
|
88
|
+
const wrapper = mountRenderer();
|
|
89
|
+
const hooks = await capturedHooks();
|
|
90
|
+
|
|
91
|
+
// valid -> nothing shown
|
|
92
|
+
expect(wrapper.text()).not.toContain("Error:");
|
|
93
|
+
|
|
94
|
+
// invalid -> the rejection is visible
|
|
95
|
+
hooks.onContentError!(new Error("content does not match the schema"));
|
|
96
|
+
await nextTick();
|
|
97
|
+
expect(wrapper.text()).toContain("content does not match the schema");
|
|
98
|
+
|
|
99
|
+
// valid again -> the message disappears
|
|
100
|
+
hooks.onContentError!(null);
|
|
101
|
+
await nextTick();
|
|
102
|
+
expect(wrapper.text()).not.toContain("content does not match the schema");
|
|
103
|
+
expect(wrapper.text()).not.toContain("Error:");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("keeps a fatal session error on screen", async () => {
|
|
107
|
+
const wrapper = mountRenderer();
|
|
108
|
+
const hooks = await capturedHooks();
|
|
109
|
+
|
|
110
|
+
hooks.onError!(new Error("No resource content in response"));
|
|
111
|
+
await nextTick();
|
|
112
|
+
expect(wrapper.text()).toContain("No resource content in response");
|
|
113
|
+
|
|
114
|
+
// Clearing the recoverable channel must not clear a fatal error.
|
|
115
|
+
hooks.onContentError!(null);
|
|
116
|
+
await nextTick();
|
|
117
|
+
expect(wrapper.text()).toContain("No resource content in response");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Referenced so the session stub is not tree-shaken out of the mock factory.
|
|
122
|
+
void sessionSpy;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue counterpart of react-core's `MCPAppsActivityRendererSeed.test.tsx`.
|
|
3
|
+
*
|
|
4
|
+
* The session is self-driving: given a `messageId`, it subscribes to the agent's
|
|
5
|
+
* message store and pushes tool input/result to the widget itself. That store is
|
|
6
|
+
* authoritative only for activities that live in it - a host can render an
|
|
7
|
+
* activity from an EXTERNAL messages list (absent from `agent.messages`), whose
|
|
8
|
+
* props may never change after mount. For those, the adapter must seed the
|
|
9
|
+
* widget by calling `session.syncContent(props.content)` immediately after
|
|
10
|
+
* binding, otherwise the widget would never receive its initial content.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
13
|
+
import { mount } from "@vue/test-utils";
|
|
14
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
15
|
+
|
|
16
|
+
// Capture the bindMcpApp options and the returned session's calls. Hoisted so
|
|
17
|
+
// the vi.mock factory (which is itself hoisted above imports) can reference them.
|
|
18
|
+
const { bindMcpAppSpy, sessionSpy } = vi.hoisted(() => {
|
|
19
|
+
const session = {
|
|
20
|
+
sendToolInput: vi.fn(),
|
|
21
|
+
sendToolResult: vi.fn(),
|
|
22
|
+
syncContent: vi.fn(),
|
|
23
|
+
teardown: vi.fn(),
|
|
24
|
+
};
|
|
25
|
+
const bind = vi.fn(() => session);
|
|
26
|
+
return { bindMcpAppSpy: bind, sessionSpy: session };
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Only the bridge-carrying root entry is mocked; the bridge-free `/activity`
|
|
30
|
+
// entry (activity type + content schema) stays real, as in the React test.
|
|
31
|
+
vi.mock("@copilotkit/mcp-apps-renderer", () => ({
|
|
32
|
+
bindMcpApp: bindMcpAppSpy,
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
vi.mock("../../providers/useCopilotKit", () => ({
|
|
36
|
+
useCopilotKit: () => ({
|
|
37
|
+
copilotkit: { value: { runAgent: vi.fn() } },
|
|
38
|
+
}),
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
import {
|
|
42
|
+
MCPAppsActivityRenderer,
|
|
43
|
+
MCPAppsActivityType,
|
|
44
|
+
} from "../MCPAppsActivityRenderer";
|
|
45
|
+
import type { MCPAppsActivityContent } from "../MCPAppsActivityRenderer";
|
|
46
|
+
|
|
47
|
+
function createAgentMock(): AbstractAgent {
|
|
48
|
+
return {
|
|
49
|
+
threadId: "seed-thread",
|
|
50
|
+
isRunning: false,
|
|
51
|
+
// The activity under test is NOT in the store: this is the external
|
|
52
|
+
// messages-list case the seed exists for.
|
|
53
|
+
messages: [],
|
|
54
|
+
runAgent: vi.fn().mockResolvedValue({ result: { contents: [] } }),
|
|
55
|
+
addMessage: vi.fn(),
|
|
56
|
+
subscribe: vi.fn(() => ({ unsubscribe: vi.fn() })),
|
|
57
|
+
} as unknown as AbstractAgent;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const content: MCPAppsActivityContent = {
|
|
61
|
+
resourceUri: "ui://server/external",
|
|
62
|
+
serverHash: "hash-external",
|
|
63
|
+
toolInput: { city: "Paris" },
|
|
64
|
+
result: { content: [{ type: "text", text: "Forecast" }], isError: false },
|
|
65
|
+
} as MCPAppsActivityContent;
|
|
66
|
+
|
|
67
|
+
function mountRenderer() {
|
|
68
|
+
return mount(MCPAppsActivityRenderer, {
|
|
69
|
+
props: {
|
|
70
|
+
activityType: MCPAppsActivityType,
|
|
71
|
+
content,
|
|
72
|
+
message: {
|
|
73
|
+
id: "external-activity-1",
|
|
74
|
+
role: "activity",
|
|
75
|
+
content: {},
|
|
76
|
+
activityType: MCPAppsActivityType,
|
|
77
|
+
},
|
|
78
|
+
agent: createAgentMock(),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
describe("MCPAppsActivityRenderer external-activity seed", () => {
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
vi.clearAllMocks();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("passes the activity messageId to the bridge for self-subscription", async () => {
|
|
89
|
+
mountRenderer();
|
|
90
|
+
|
|
91
|
+
await vi.waitFor(() => expect(bindMcpAppSpy).toHaveBeenCalledTimes(1));
|
|
92
|
+
const opts = bindMcpAppSpy.mock.calls[0]![0];
|
|
93
|
+
expect(opts.messageId).toBe("external-activity-1");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("seeds the widget with the initial tool input/result right after binding, even when props never change", async () => {
|
|
97
|
+
mountRenderer();
|
|
98
|
+
|
|
99
|
+
// The content watcher does not re-run for unchanged props, so without the
|
|
100
|
+
// explicit seed the widget would never receive this initial content.
|
|
101
|
+
await vi.waitFor(() => expect(sessionSpy.syncContent).toHaveBeenCalled());
|
|
102
|
+
expect(sessionSpy.syncContent).toHaveBeenCalledWith(
|
|
103
|
+
expect.objectContaining({
|
|
104
|
+
toolInput: { city: "Paris" },
|
|
105
|
+
resourceUri: "ui://server/external",
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -352,7 +352,7 @@ onBeforeUnmount(() => {
|
|
|
352
352
|
<div
|
|
353
353
|
v-if="isRendered"
|
|
354
354
|
data-copilotkit
|
|
355
|
-
class="cpk:fixed cpk:inset-0 cpk:z-[1200] cpk:flex cpk:max-w-full cpk:flex-col cpk:items-stretch cpk:md:inset-auto cpk:md:bottom-24 cpk:md:right-6 cpk:md:items-end cpk:md:gap-4"
|
|
355
|
+
class="cpk:fixed cpk:inset-0 cpk:z-[1200] cpk:flex cpk:max-w-full cpk:flex-col cpk:items-stretch cpk:bg-transparent cpk:md:inset-auto cpk:md:bottom-24 cpk:md:right-6 cpk:md:items-end cpk:md:gap-4"
|
|
356
356
|
>
|
|
357
357
|
<div
|
|
358
358
|
ref="containerRef"
|
|
@@ -540,6 +540,26 @@ describe("CopilotPopupView Slot System E2E Tests", () => {
|
|
|
540
540
|
});
|
|
541
541
|
|
|
542
542
|
describe("8. Integration Tests", () => {
|
|
543
|
+
it("keeps the transparent-background utility on the positioning wrapper", () => {
|
|
544
|
+
const Host = defineComponent({
|
|
545
|
+
components: { CopilotPopupView },
|
|
546
|
+
setup() {
|
|
547
|
+
return { sampleMessages };
|
|
548
|
+
},
|
|
549
|
+
template: `<CopilotPopupView :messages="sampleMessages" :default-open="true" />`,
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
const { container } = renderInWrapper(Host);
|
|
553
|
+
const popup = container.querySelector("[data-copilot-popup]");
|
|
554
|
+
const positioningWrapper = popup?.parentElement;
|
|
555
|
+
|
|
556
|
+
expect(positioningWrapper?.hasAttribute("data-copilotkit")).toBe(true);
|
|
557
|
+
// jsdom does not load the generated stylesheet; this guards the utility-class contract only.
|
|
558
|
+
expect(positioningWrapper?.classList.contains("cpk:bg-transparent")).toBe(
|
|
559
|
+
true,
|
|
560
|
+
);
|
|
561
|
+
});
|
|
562
|
+
|
|
543
563
|
it("should render popup with all default components when open", () => {
|
|
544
564
|
const Host = defineComponent({
|
|
545
565
|
components: { CopilotPopupView },
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { GENERATE_SANDBOXED_UI_DESCRIPTION } from "@copilotkit/shared";
|
|
2
3
|
import {
|
|
3
4
|
computed,
|
|
4
5
|
h,
|
|
@@ -88,20 +89,6 @@ const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follo
|
|
|
88
89
|
- Minimal transitions (150ms) for hover/focus states only. No decorative animations.
|
|
89
90
|
- Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
|
|
90
91
|
|
|
91
|
-
const GENERATE_SANDBOXED_UI_DESCRIPTION =
|
|
92
|
-
"Generate sandboxed UI. " +
|
|
93
|
-
"IMPORTANT: The generated code runs in a sandboxed iframe WITHOUT same-origin access. " +
|
|
94
|
-
"Do NOT use localStorage, sessionStorage, document.cookie, IndexedDB, or fetch/XMLHttpRequest to same-origin URLs. " +
|
|
95
|
-
"To communicate with the host application, use Websandbox.connection.remote.<functionName>(args) which returns a Promise.\n\n" +
|
|
96
|
-
"You CAN use external libraries from CDNs by including <script> or <link> tags in the HTML <head> (e.g., Chart.js, D3, Three.js, x-data-spreadsheet, etc.). " +
|
|
97
|
-
"CDN resources load normally inside the sandbox.\n\n" +
|
|
98
|
-
"PARAMETER ORDER IS CRITICAL — generate parameters in exactly this order:\n" +
|
|
99
|
-
"1. initialHeight + placeholderMessages (shown to user while generating)\n" +
|
|
100
|
-
"2. css (all styles FIRST — the user sees a placeholder until CSS is complete)\n" +
|
|
101
|
-
"3. html (streams in live — the user watches the UI build as HTML is generated)\n" +
|
|
102
|
-
"4. jsFunctions (reusable helper functions)\n" +
|
|
103
|
-
"5. jsExpressions (applied one-by-one — the user sees each expression take effect)";
|
|
104
|
-
|
|
105
92
|
const props = withDefaults(defineProps<CopilotKitProviderProps>(), {
|
|
106
93
|
headers: () => ({}),
|
|
107
94
|
properties: () => ({}),
|
package/vite.config.ts
CHANGED
|
@@ -29,6 +29,13 @@ export default defineConfig({
|
|
|
29
29
|
"@ag-ui/client",
|
|
30
30
|
"@ag-ui/core",
|
|
31
31
|
"@copilotkit/core",
|
|
32
|
+
// Keep the shared MCP Apps host + its subpaths external. The root entry
|
|
33
|
+
// carries the ext-apps AppBridge and is loaded through a dynamic
|
|
34
|
+
// import() in the renderer, so it must stay a real runtime import (not
|
|
35
|
+
// be inlined, which would pull the bridge into the eager chunk and undo
|
|
36
|
+
// the lazy-loading); `/activity` is the bridge-free surface imported
|
|
37
|
+
// statically for activity registration.
|
|
38
|
+
/^@copilotkit\/mcp-apps-renderer(\/.*)?$/,
|
|
32
39
|
"@copilotkit/shared",
|
|
33
40
|
"@copilotkit/web-inspector",
|
|
34
41
|
// Keep @copilotkit/web-components (the Lit drawer element) + its subpaths
|