@copilotkit/vue 1.75.2 → 1.77.0

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.
Files changed (52) hide show
  1. package/PARITY.md +93 -35
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.mjs +275 -158
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/use-render-activity-message-JCCE1lgH.cjs +79 -0
  7. package/dist/use-render-activity-message-JCCE1lgH.cjs.map +1 -0
  8. package/dist/use-render-activity-message-bPlnHjQV.js +12499 -0
  9. package/dist/use-render-activity-message-bPlnHjQV.js.map +1 -0
  10. package/dist/v2/components/MCPAppsActivityRenderer.d.ts +10 -42
  11. package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -1
  12. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  13. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +8 -152
  14. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
  15. package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -1
  16. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  17. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  18. package/dist/v2/components/chat/index.d.ts +3 -3
  19. package/dist/v2/hooks/use-agent.d.ts.map +1 -1
  20. package/dist/v2/index.cjs +1 -1
  21. package/dist/v2/index.d.cts +2 -0
  22. package/dist/v2/index.d.mts +2 -0
  23. package/dist/v2/index.d.ts +2 -0
  24. package/dist/v2/index.d.ts.map +1 -1
  25. package/dist/v2/index.mjs +189 -72
  26. package/dist/v2/index.mjs.map +1 -1
  27. package/package.json +8 -7
  28. package/src/v2/__tests__/utils/test-helpers.ts +26 -6
  29. package/src/v2/components/MCPAppsActivityRenderer.ts +175 -658
  30. package/src/v2/components/__tests__/MCPAppsActivityRenderer.test.ts +39 -20
  31. package/src/v2/components/__tests__/MCPAppsActivityRendererContentError.test.ts +122 -0
  32. package/src/v2/components/__tests__/MCPAppsActivityRendererSeed.test.ts +109 -0
  33. package/src/v2/components/chat/CopilotChat.vue +100 -20
  34. package/src/v2/components/chat/CopilotPopupViewInternal.vue +1 -1
  35. package/src/v2/components/chat/__tests__/CopilotChat.clearOnFresh.test.ts +130 -2
  36. package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.ts +6 -1
  37. package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.ts +22 -40
  38. package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.ts +12 -24
  39. package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +20 -0
  40. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +3 -2
  41. package/src/v2/hooks/__tests__/use-agent-headers.test.ts +83 -0
  42. package/src/v2/hooks/__tests__/use-agent-stability.test.ts +2 -0
  43. package/src/v2/hooks/__tests__/use-agent.e2e.test.ts +5 -3
  44. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +2 -2
  45. package/src/v2/hooks/use-agent.ts +3 -3
  46. package/src/v2/index.ts +5 -0
  47. package/src/v2/providers/__tests__/CopilotKitProvider.renderCustomMessages.e2e.test.ts +6 -4
  48. package/vite.config.ts +7 -0
  49. package/dist/use-render-activity-message-CGdS03Xj.cjs +0 -85
  50. package/dist/use-render-activity-message-CGdS03Xj.cjs.map +0 -1
  51. package/dist/use-render-activity-message-XmuT9fKy.js +0 -9799
  52. package/dist/use-render-activity-message-XmuT9fKy.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
- import { defineComponent, h, nextTick } from "vue";
2
+ import { defineComponent, h, nextTick, ref } from "vue";
3
3
  import { flushPromises, mount } from "@vue/test-utils";
4
4
  import CopilotKitProvider from "../../../providers/CopilotKitProvider.vue";
5
5
  import CopilotChatConfigurationProvider from "../../../providers/CopilotChatConfigurationProvider.vue";
@@ -14,6 +14,134 @@ import CopilotChat from "../CopilotChat.vue";
14
14
  // configuration transitions to a fresh, non-explicit thread via
15
15
  // startNewThread()
16
16
  describe("CopilotChat clear-on-fresh", () => {
17
+ it.each(["clearReplayCursor", "clearReconnectCursor"] as const)(
18
+ "clears state and the captured %s cursor on a fresh selection",
19
+ async (method) => {
20
+ const agent = new StateCapturingAgent();
21
+ const cursors = new Map<string, string>();
22
+ Object.defineProperty(agent, method, {
23
+ value: (threadId: string) => cursors.delete(threadId),
24
+ });
25
+ const threadId = ref("saved");
26
+ const explicit = ref(true);
27
+ const wrapper = mount(CopilotKitProvider, {
28
+ props: { agents__unsafe_dev_only: { default: agent } },
29
+ slots: {
30
+ default: () =>
31
+ h(
32
+ CopilotChatConfigurationProvider,
33
+ { threadId: threadId.value, hasExplicitThreadId: explicit.value },
34
+ { default: () => h(CopilotChat, { welcomeScreen: false }) },
35
+ ),
36
+ },
37
+ });
38
+ await flushPromises();
39
+ agent.setState({ saved: true });
40
+ agent.pendingInterrupts = [{ id: "approval-A", reason: "confirmation" }];
41
+ cursors.set("saved", "last-event");
42
+ cursors.set("other", "keep-other");
43
+ threadId.value = "fresh";
44
+ explicit.value = false;
45
+ await flushPromises();
46
+ expect(agent.state).toEqual({});
47
+ expect(agent.pendingInterrupts).toEqual([]);
48
+ expect(cursors.get("saved")).toBeUndefined();
49
+ expect(cursors.get("other")).toBe("keep-other");
50
+ wrapper.unmount();
51
+ },
52
+ );
53
+ it.each(["fresh", "saved", "other", "via-fresh", "swap"])(
54
+ "protects %s from an abandoned connect that completes later",
55
+ async (destination) => {
56
+ let releaseDetach = () => {};
57
+ let releaseConnect = () => {};
58
+ let pendingDetach: Promise<void> | undefined;
59
+ const oldConnect = new Promise<void>((resolve) => {
60
+ releaseConnect = resolve;
61
+ });
62
+ const cursors = new Map<string, string>();
63
+ const requests: (string | null)[] = [];
64
+ class ReplayAgent extends StateCapturingAgent {
65
+ clearReplayCursor(threadId: string) {
66
+ cursors.delete(threadId);
67
+ }
68
+ async detachActiveRun() {
69
+ await pendingDetach;
70
+ await super.detachActiveRun();
71
+ }
72
+ async connectAgent(
73
+ ...args: Parameters<StateCapturingAgent["connectAgent"]>
74
+ ) {
75
+ requests.push(cursors.get(this.threadId) ?? null);
76
+ const first = requests.length === 1;
77
+ const result = await super.connectAgent(...args);
78
+ if (first) await oldConnect;
79
+ return result;
80
+ }
81
+ }
82
+ const agent = new ReplayAgent();
83
+ let currentAgent = agent;
84
+ const threadId = ref("saved");
85
+ const explicit = ref(true);
86
+ const wrapper = mount(CopilotKitProvider, {
87
+ props: { agents__unsafe_dev_only: { default: agent } },
88
+ slots: {
89
+ default: () =>
90
+ h(
91
+ CopilotChatConfigurationProvider,
92
+ { threadId: threadId.value, hasExplicitThreadId: explicit.value },
93
+ { default: () => h(CopilotChat, { welcomeScreen: false }) },
94
+ ),
95
+ },
96
+ });
97
+ await flushPromises();
98
+ expect(requests).toEqual([null]);
99
+ pendingDetach = new Promise<void>((resolve) => {
100
+ releaseDetach = resolve;
101
+ });
102
+ cursors.set("saved", "old-event");
103
+ threadId.value = "fresh";
104
+ explicit.value = false;
105
+ await nextTick();
106
+ if (destination === "via-fresh") {
107
+ threadId.value = "fresh-again";
108
+ await nextTick();
109
+ }
110
+ if (destination === "swap") {
111
+ currentAgent = new ReplayAgent();
112
+ await wrapper.setProps({
113
+ agents__unsafe_dev_only: { default: currentAgent },
114
+ });
115
+ }
116
+ const target =
117
+ destination === "via-fresh" || destination === "swap"
118
+ ? "saved"
119
+ : destination;
120
+ if (target !== "fresh") {
121
+ threadId.value = target;
122
+ explicit.value = true;
123
+ await nextTick();
124
+ }
125
+ expect(requests).toEqual([null]);
126
+ cursors.set("saved", "late-event");
127
+ releaseDetach();
128
+ await flushPromises();
129
+ expect(requests).toEqual(target === "fresh" ? [null] : [null, null]);
130
+ currentAgent.setState({ current: true });
131
+ currentAgent.pendingInterrupts = [
132
+ { id: "approval-new", reason: "confirmation" },
133
+ ];
134
+ cursors.set(target, "new-event");
135
+ releaseConnect();
136
+ await flushPromises();
137
+ expect(currentAgent.state).toEqual({ current: true });
138
+ expect(currentAgent.pendingInterrupts).toEqual([
139
+ { id: "approval-new", reason: "confirmation" },
140
+ ]);
141
+ expect(cursors.get(target)).toBe("new-event");
142
+ wrapper.unmount();
143
+ },
144
+ );
17
145
  it("does not clear messages on initial mount", async () => {
18
146
  const agent = new StateCapturingAgent();
19
147
  // Spy before mount: attaching afterwards can never observe a mount-time
@@ -133,7 +261,7 @@ describe("CopilotChat clear-on-fresh", () => {
133
261
  await nextTick();
134
262
  expect(currentThreadId).toBe("elsewhere");
135
263
 
136
- newAgent!.setMessages([{ id: "m1", role: "user", content: "hi" } as never]);
264
+ newAgent!.setMessages([{ id: "m1", role: "user", content: "hi" }]);
137
265
  expect(newAgent!.messages.length).toBe(1);
138
266
 
139
267
  // Switch back non-explicitly. Any `setMessages([])` observed here must come
@@ -3,6 +3,7 @@ import { computed, defineComponent, onMounted } from "vue";
3
3
  import type { PropType } from "vue";
4
4
  import { afterEach, describe, expect, it, vi } from "vitest";
5
5
  import { EventType } from "@ag-ui/client";
6
+ import type { RunErrorEvent } from "@ag-ui/client";
6
7
  import { z } from "zod";
7
8
  import { useConfigureSuggestions } from "../../../hooks/use-configure-suggestions";
8
9
  import {
@@ -189,7 +190,11 @@ describe("CopilotChat E2E - Chat Basics and Streaming Patterns", () => {
189
190
  );
190
191
  });
191
192
 
192
- await agent.emit({ type: EventType.RUN_ERROR } as any);
193
+ const errorEvent: RunErrorEvent = {
194
+ type: EventType.RUN_ERROR,
195
+ message: "Test run failed",
196
+ };
197
+ await agent.emit(errorEvent);
193
198
  await agent.complete();
194
199
 
195
200
  await waitFor(() => {
@@ -21,6 +21,10 @@ import CopilotChatToolCallsView from "../CopilotChatToolCallsView.vue";
21
21
  import { useFrontendTool } from "../../../hooks/use-frontend-tool";
22
22
  import type { VueFrontendTool } from "../../../types";
23
23
  import type { VueToolCallRendererRenderProps } from "../../../types/vue-tool-call-renderer";
24
+ import {
25
+ runStartedEvent,
26
+ runFinishedEvent,
27
+ } from "../../../__tests__/utils/test-helpers";
24
28
 
25
29
  afterEach(() => {
26
30
  cleanup();
@@ -43,7 +47,7 @@ class MockStreamingAgent extends AbstractAgent {
43
47
  const messageId = `m_${Date.now()}`;
44
48
  const toolCallId = `tc_${Date.now()}`;
45
49
 
46
- observer.next({ type: EventType.RUN_STARTED } as BaseEvent);
50
+ observer.next(runStartedEvent(_input));
47
51
  observer.next({
48
52
  type: EventType.TEXT_MESSAGE_CHUNK,
49
53
  messageId,
@@ -68,7 +72,7 @@ class MockStreamingAgent extends AbstractAgent {
68
72
  messageId: `${messageId}_result`,
69
73
  content: JSON.stringify({ temperature: 21, unit: "celsius" }),
70
74
  } as BaseEvent);
71
- observer.next({ type: EventType.RUN_FINISHED } as BaseEvent);
75
+ observer.next(runFinishedEvent(_input));
72
76
  observer.complete();
73
77
  });
74
78
  }
@@ -492,9 +496,7 @@ describe("Streaming in-progress without timers", () => {
492
496
  const messageId = "m_step";
493
497
  const toolCallId = "tc_step";
494
498
 
495
- await agent.emit({
496
- type: EventType.RUN_STARTED,
497
- } as BaseEvent);
499
+ await agent.emit(runStartedEvent());
498
500
  await agent.emit({
499
501
  type: EventType.TEXT_MESSAGE_CHUNK,
500
502
  messageId,
@@ -543,9 +545,7 @@ describe("Streaming in-progress without timers", () => {
543
545
  expect(el.textContent).toContain("21");
544
546
  });
545
547
 
546
- await agent.emit({
547
- type: EventType.RUN_FINISHED,
548
- } as BaseEvent);
548
+ await agent.emit(runFinishedEvent());
549
549
  await agent.complete();
550
550
  });
551
551
  });
@@ -586,9 +586,7 @@ describe("Executing State Transitions", () => {
586
586
  const messageId = "m_exec";
587
587
  const toolCallId = "tc_exec";
588
588
 
589
- await agent.emit({
590
- type: EventType.RUN_STARTED,
591
- } as BaseEvent);
589
+ await agent.emit(runStartedEvent());
592
590
  await agent.emit({
593
591
  type: EventType.TOOL_CALL_CHUNK,
594
592
  toolCallId,
@@ -603,9 +601,7 @@ describe("Executing State Transitions", () => {
603
601
  expect(status.textContent).toMatch(/Value: test/);
604
602
  });
605
603
 
606
- await agent.emit({
607
- type: EventType.RUN_FINISHED,
608
- } as BaseEvent);
604
+ await agent.emit(runFinishedEvent());
609
605
  await agent.complete();
610
606
 
611
607
  await waitFor(() => {
@@ -653,9 +649,7 @@ describe("Multiple Tool Calls in Same Message", () => {
653
649
  const toolCallId2 = "tc_2";
654
650
  const toolCallId3 = "tc_3";
655
651
 
656
- await agent.emit({
657
- type: EventType.RUN_STARTED,
658
- } as BaseEvent);
652
+ await agent.emit(runStartedEvent());
659
653
  await agent.emit({
660
654
  type: EventType.TOOL_CALL_CHUNK,
661
655
  toolCallId: toolCallId1,
@@ -714,9 +708,7 @@ describe("Multiple Tool Calls in Same Message", () => {
714
708
  expect(screen.getByTestId("tool1-third").textContent).toContain("C");
715
709
  });
716
710
 
717
- await agent.emit({
718
- type: EventType.RUN_FINISHED,
719
- } as BaseEvent);
711
+ await agent.emit(runFinishedEvent());
720
712
  await agent.complete();
721
713
  });
722
714
  });
@@ -747,9 +739,7 @@ describe("Partial Args Accumulation", () => {
747
739
  const messageId = "m_partial";
748
740
  const toolCallId = "tc_partial";
749
741
 
750
- await agent.emit({
751
- type: EventType.RUN_STARTED,
752
- } as BaseEvent);
742
+ await agent.emit(runStartedEvent());
753
743
  await agent.emit({
754
744
  type: EventType.TOOL_CALL_CHUNK,
755
745
  toolCallId,
@@ -801,9 +791,7 @@ describe("Partial Args Accumulation", () => {
801
791
  expect(tool.textContent).toMatch(/Status: (complete|inProgress)/i);
802
792
  });
803
793
 
804
- await agent.emit({
805
- type: EventType.RUN_FINISHED,
806
- } as BaseEvent);
794
+ await agent.emit(runFinishedEvent());
807
795
  await agent.complete();
808
796
  });
809
797
  });
@@ -885,7 +873,7 @@ describe("toolCallId parity for registered renderers", () => {
885
873
  const messageId = "m_id";
886
874
  const toolCallId = "tc_id_parity";
887
875
 
888
- await agent.emit({ type: EventType.RUN_STARTED } as BaseEvent);
876
+ await agent.emit(runStartedEvent());
889
877
  await agent.emit({
890
878
  type: EventType.TOOL_CALL_CHUNK,
891
879
  toolCallId,
@@ -900,7 +888,7 @@ describe("toolCallId parity for registered renderers", () => {
900
888
  expect(el.textContent).toContain(`id:${toolCallId}`);
901
889
  });
902
890
 
903
- await agent.emit({ type: EventType.RUN_FINISHED } as BaseEvent);
891
+ await agent.emit(runFinishedEvent());
904
892
  await agent.complete();
905
893
 
906
894
  await waitFor(() => {
@@ -948,6 +936,8 @@ describe("toolCallId parity for registered renderers", () => {
948
936
  describe("Status Persistence After Agent Stops", () => {
949
937
  it("should remain in InProgress status after agent stops if no result", async () => {
950
938
  const agent = new MockStepwiseAgent();
939
+ const firstRun = { threadId: agent.threadId, runId: "first-run" };
940
+ const secondRun = { threadId: agent.threadId, runId: "second-run" };
951
941
  renderChatHarness({
952
942
  agent,
953
943
  frontendTools: [
@@ -967,9 +957,7 @@ describe("Status Persistence After Agent Stops", () => {
967
957
  const messageId = "msg_status";
968
958
  const toolCallId = "tc_status";
969
959
 
970
- await agent.emit({
971
- type: EventType.RUN_STARTED,
972
- } as BaseEvent);
960
+ await agent.emit(runStartedEvent(firstRun));
973
961
  await agent.emit({
974
962
  type: EventType.TOOL_CALL_CHUNK,
975
963
  toolCallId,
@@ -983,9 +971,7 @@ describe("Status Persistence After Agent Stops", () => {
983
971
  expect(statusElement.textContent).toBe("inProgress");
984
972
  });
985
973
 
986
- await agent.emit({
987
- type: EventType.RUN_FINISHED,
988
- } as BaseEvent);
974
+ await agent.emit(runFinishedEvent(firstRun));
989
975
 
990
976
  await waitFor(() => {
991
977
  const statusElement = screen.getByTestId("status");
@@ -996,9 +982,7 @@ describe("Status Persistence After Agent Stops", () => {
996
982
  expect(statusElement.textContent).toBe("inProgress");
997
983
  expect(statusElement.textContent).not.toBe("complete");
998
984
 
999
- await agent.emit({
1000
- type: EventType.RUN_STARTED,
1001
- } as BaseEvent);
985
+ await agent.emit(runStartedEvent(secondRun));
1002
986
  await agent.emit({
1003
987
  type: EventType.TOOL_CALL_RESULT,
1004
988
  toolCallId,
@@ -1011,9 +995,7 @@ describe("Status Persistence After Agent Stops", () => {
1011
995
  expect(statusEl.textContent).toBe("complete");
1012
996
  });
1013
997
 
1014
- await agent.emit({
1015
- type: EventType.RUN_FINISHED,
1016
- } as BaseEvent);
998
+ await agent.emit(runFinishedEvent(secondRun));
1017
999
  await agent.complete();
1018
1000
  });
1019
1001
  });
@@ -17,6 +17,10 @@ import CopilotKitProvider from "../../../providers/CopilotKitProvider.vue";
17
17
  import CopilotChatConfigurationProvider from "../../../providers/CopilotChatConfigurationProvider.vue";
18
18
  import CopilotChat from "../CopilotChat.vue";
19
19
  import type { VueFrontendTool } from "../../../types";
20
+ import {
21
+ runStartedEvent,
22
+ runFinishedEvent,
23
+ } from "../../../__tests__/utils/test-helpers";
20
24
 
21
25
  afterEach(() => {
22
26
  cleanup();
@@ -183,9 +187,7 @@ describe("Tool Call Re-render Prevention", () => {
183
187
  const messageId = "m_rerender_test";
184
188
  const toolCallId = "tc_rerender_test";
185
189
 
186
- await agent.emit({
187
- type: EventType.RUN_STARTED,
188
- } as BaseEvent);
190
+ await agent.emit(runStartedEvent());
189
191
  await agent.emit({
190
192
  type: EventType.TOOL_CALL_CHUNK,
191
193
  toolCallId,
@@ -252,9 +254,7 @@ describe("Tool Call Re-render Prevention", () => {
252
254
  expect(screen.getByTestId("location").textContent).toBe("Paris");
253
255
  expect(screen.getByTestId("result").textContent).toContain("temperature");
254
256
 
255
- await agent.emit({
256
- type: EventType.RUN_FINISHED,
257
- } as BaseEvent);
257
+ await agent.emit(runFinishedEvent());
258
258
  await agent.complete();
259
259
  });
260
260
 
@@ -302,9 +302,7 @@ describe("Tool Call Re-render Prevention", () => {
302
302
  const messageId = "m_search";
303
303
  const toolCallId = "tc_search";
304
304
 
305
- await agent.emit({
306
- type: EventType.RUN_STARTED,
307
- } as BaseEvent);
305
+ await agent.emit(runStartedEvent());
308
306
  await agent.emit({
309
307
  type: EventType.TOOL_CALL_CHUNK,
310
308
  toolCallId,
@@ -334,9 +332,7 @@ describe("Tool Call Re-render Prevention", () => {
334
332
  const renderCountAfterText = toolRenderCount;
335
333
  expect(renderCountAfterText).toBe(renderCountAfterToolCall);
336
334
 
337
- await agent.emit({
338
- type: EventType.RUN_FINISHED,
339
- } as BaseEvent);
335
+ await agent.emit(runFinishedEvent());
340
336
  await agent.complete();
341
337
  });
342
338
 
@@ -384,9 +380,7 @@ describe("Tool Call Re-render Prevention", () => {
384
380
  const messageId = "m_search_update";
385
381
  const toolCallId = "tc_search_update";
386
382
 
387
- await agent.emit({
388
- type: EventType.RUN_STARTED,
389
- } as BaseEvent);
383
+ await agent.emit(runStartedEvent());
390
384
  await agent.emit({
391
385
  type: EventType.TOOL_CALL_CHUNK,
392
386
  toolCallId,
@@ -422,9 +416,7 @@ describe("Tool Call Re-render Prevention", () => {
422
416
  expect(capturedArgs).toContain("Rea");
423
417
  expect(capturedArgs).toContain("React hooks");
424
418
 
425
- await agent.emit({
426
- type: EventType.RUN_FINISHED,
427
- } as BaseEvent);
419
+ await agent.emit(runFinishedEvent());
428
420
  await agent.complete();
429
421
  });
430
422
 
@@ -471,9 +463,7 @@ describe("Tool Call Re-render Prevention", () => {
471
463
  const messageId = "m_data";
472
464
  const toolCallId = "tc_data";
473
465
 
474
- await agent.emit({
475
- type: EventType.RUN_STARTED,
476
- } as BaseEvent);
466
+ await agent.emit(runStartedEvent());
477
467
  await agent.emit({
478
468
  type: EventType.TOOL_CALL_CHUNK,
479
469
  toolCallId,
@@ -508,9 +498,7 @@ describe("Tool Call Re-render Prevention", () => {
508
498
  expect(capturedStatuses).toContain(ToolCallStatus.InProgress);
509
499
  expect(capturedStatuses).toContain(ToolCallStatus.Complete);
510
500
 
511
- await agent.emit({
512
- type: EventType.RUN_FINISHED,
513
- } as BaseEvent);
501
+ await agent.emit(runFinishedEvent());
514
502
  await agent.complete();
515
503
  });
516
504
  });
@@ -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
  import type { Message, ToolCall, ToolMessage } from "@ag-ui/core";
2
+ import { contentToText } from "@ag-ui/core";
2
3
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
3
4
  import { ToolCallStatus } from "@copilotkit/core";
4
5
  import { partialJSONParse } from "@copilotkit/shared";
@@ -53,7 +54,7 @@ export function getToolCallRenderProps(
53
54
  name: toolCall.function.name,
54
55
  args: parsedArgs,
55
56
  status: ToolCallStatus.Complete,
56
- result: toolMessage.content,
57
+ result: contentToText(toolMessage.content),
57
58
  toolCall,
58
59
  toolMessage,
59
60
  };
@@ -84,7 +85,7 @@ export function getCoreRenderProps(
84
85
  toolCallId: toolCall.id,
85
86
  args: parsedArgs,
86
87
  status: ToolCallStatus.Complete,
87
- result: toolMessage.content,
88
+ result: contentToText(toolMessage.content),
88
89
  };
89
90
  }
90
91
 
@@ -0,0 +1,83 @@
1
+ import { AbstractAgent, HttpAgent } from "@ag-ui/client";
2
+ import { defineComponent, h, nextTick } from "vue";
3
+ import { describe, expect, it } from "vitest";
4
+ import { mountWithProvider } from "../../__tests__/utils/mount";
5
+ import { useAgent } from "../use-agent";
6
+
7
+ // Models an HttpAgent from another installed copy of @ag-ui/client.
8
+ class ForeignHttpAgent extends AbstractAgent {
9
+ url = "https://agent.example";
10
+ headers: Record<string, string> = {
11
+ Authorization: "Bearer agent-token",
12
+ "X-Agent": "agent",
13
+ };
14
+ abortController = new AbortController();
15
+
16
+ run(): never {
17
+ throw new Error("This test does not run the agent");
18
+ }
19
+ }
20
+
21
+ describe("useAgent headers (Vue-specific semantics)", () => {
22
+ it.each([
23
+ [
24
+ "HttpAgent",
25
+ () =>
26
+ new HttpAgent({
27
+ url: "https://agent.example",
28
+ headers: { Authorization: "Bearer agent-token", "X-Agent": "agent" },
29
+ }),
30
+ ],
31
+ ["foreign HttpAgent", () => new ForeignHttpAgent()],
32
+ ] as const)(
33
+ "preserves %s headers when the provider changes headers",
34
+ async (_name, createAgent) => {
35
+ const agent = createAgent();
36
+ let headersAfterHook: Record<string, string> | undefined;
37
+ const Child = defineComponent({
38
+ setup() {
39
+ useAgent();
40
+ headersAfterHook = { ...agent.headers };
41
+ return () => null;
42
+ },
43
+ });
44
+ const { wrapper } = mountWithProvider(() => h(Child), {
45
+ runtimeUrl: undefined,
46
+ agents__unsafe_dev_only: { default: agent },
47
+ headers: { "X-Core": "initial" },
48
+ });
49
+
50
+ try {
51
+ expect(headersAfterHook).toEqual({
52
+ Authorization: "Bearer agent-token",
53
+ "X-Agent": "agent",
54
+ "X-Core": "initial",
55
+ });
56
+ expect(agent.headers).toEqual({
57
+ Authorization: "Bearer agent-token",
58
+ "X-Agent": "agent",
59
+ "X-Core": "initial",
60
+ });
61
+
62
+ await wrapper.setProps({
63
+ headers: { Authorization: "Bearer core-token", "X-Core": "updated" },
64
+ });
65
+ await nextTick();
66
+ expect(agent.headers).toEqual({
67
+ Authorization: "Bearer core-token",
68
+ "X-Agent": "agent",
69
+ "X-Core": "updated",
70
+ });
71
+
72
+ await wrapper.setProps({ headers: {} });
73
+ await nextTick();
74
+ expect(agent.headers).toEqual({
75
+ Authorization: "Bearer agent-token",
76
+ "X-Agent": "agent",
77
+ });
78
+ } finally {
79
+ wrapper.unmount();
80
+ }
81
+ },
82
+ );
83
+ });
@@ -34,6 +34,7 @@ describe("useAgent stability during runtime connection", () => {
34
34
  // need a no-op subscription object here; stability/ref-identity behavior
35
35
  // is orthogonal to subscribe internals.
36
36
  subscribeToAgentWithOptions: ReturnType<typeof vi.fn>;
37
+ applyHeadersToAgent: ReturnType<typeof vi.fn>;
37
38
  };
38
39
 
39
40
  let mockCopilotkit: MockCopilotkit;
@@ -49,6 +50,7 @@ describe("useAgent stability during runtime connection", () => {
49
50
  headers: {},
50
51
  agents: {},
51
52
  subscribeToAgentWithOptions: vi.fn(() => ({ unsubscribe: vi.fn() })),
53
+ applyHeadersToAgent: vi.fn(),
52
54
  });
53
55
  mockCopilotkit = copilotkitRef.value;
54
56
 
@@ -2,7 +2,7 @@ import { defineComponent } from "vue";
2
2
  import { screen, fireEvent, waitFor, cleanup } from "@testing-library/vue";
3
3
  import { afterEach, describe, expect, it } from "vitest";
4
4
  import { EventType } from "@ag-ui/client";
5
- import type { BaseEvent, RunAgentInput } from "@ag-ui/client";
5
+ import type { BaseEvent, RunAgentInput, RunErrorEvent } from "@ag-ui/client";
6
6
  import type { Observable } from "rxjs";
7
7
  import {
8
8
  MockStepwiseAgent,
@@ -174,9 +174,11 @@ describe("useAgent e2e", () => {
174
174
  expect(screen.getByTestId("status").textContent).toBe("running");
175
175
  });
176
176
 
177
- await agent.emit({
177
+ const errorEvent: RunErrorEvent = {
178
178
  type: EventType.RUN_ERROR,
179
- } as BaseEvent);
179
+ message: "The agent run failed",
180
+ };
181
+ await agent.emit(errorEvent);
180
182
  await agent.complete();
181
183
 
182
184
  await waitFor(() => {
@@ -665,7 +665,7 @@ describe("useFrontendTool E2E - Dynamic Registration", () => {
665
665
  const messageId = testId(`m-${this.runCount}`);
666
666
  const toolCallId = testId(`tc-${this.runCount}`);
667
667
  const valueArg = this.runCount === 1 ? "first call" : "second call";
668
- observer.next({ type: EventType.RUN_STARTED } as BaseEvent);
668
+ observer.next(runStartedEvent(_input));
669
669
  observer.next({
670
670
  type: EventType.TOOL_CALL_CHUNK,
671
671
  toolCallId,
@@ -673,7 +673,7 @@ describe("useFrontendTool E2E - Dynamic Registration", () => {
673
673
  parentMessageId: messageId,
674
674
  delta: JSON.stringify({ value: valueArg }),
675
675
  } as BaseEvent);
676
- observer.next({ type: EventType.RUN_FINISHED } as BaseEvent);
676
+ observer.next(runFinishedEvent(_input));
677
677
  observer.complete();
678
678
  return () => {};
679
679
  });
@@ -1,11 +1,11 @@
1
1
  import { computed, shallowRef, toValue, triggerRef, watch } from "vue";
2
2
  import type { MaybeRefOrGetter } from "vue";
3
3
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
4
- import { HttpAgent } from "@ag-ui/client";
5
4
  import type { AbstractAgent } from "@ag-ui/client";
6
5
  import {
7
6
  ProxiedCopilotRuntimeAgent,
8
7
  CopilotKitCoreRuntimeConnectionStatus,
8
+ ɵisHttpAgent,
9
9
  } from "@copilotkit/core";
10
10
  import type {
11
11
  CopilotRuntimeTransport,
@@ -392,8 +392,8 @@ export function useAgent(props: UseAgentProps = {}) {
392
392
  ),
393
393
  ],
394
394
  ([currentAgent]) => {
395
- if (currentAgent instanceof HttpAgent) {
396
- currentAgent.headers = { ...copilotkit.value.headers };
395
+ if (ɵisHttpAgent(currentAgent)) {
396
+ copilotkit.value.applyHeadersToAgent(currentAgent);
397
397
  }
398
398
  },
399
399
  { immediate: true },
package/src/v2/index.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  // Re-export core (framework-agnostic)
2
2
  export * from "@copilotkit/core";
3
3
  export * from "@ag-ui/client";
4
+ // AG-UI 1.0 moved the zod validators from `@ag-ui/client` to this subpath.
5
+ // Re-export them so existing imports keep working. Both modules export
6
+ // PROTOCOL_VERSION, so name it once to keep it from being ambiguous.
7
+ export * from "@ag-ui/core/schemas";
8
+ export { PROTOCOL_VERSION } from "@ag-ui/client";
4
9
 
5
10
  // Local V2 vue code
6
11
  export * from "./components";