@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.
- package/PARITY.md +93 -35
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +275 -158
- package/dist/index.mjs.map +1 -1
- package/dist/use-render-activity-message-JCCE1lgH.cjs +79 -0
- package/dist/use-render-activity-message-JCCE1lgH.cjs.map +1 -0
- package/dist/use-render-activity-message-bPlnHjQV.js +12499 -0
- package/dist/use-render-activity-message-bPlnHjQV.js.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/CopilotChat.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +8 -152
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.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/copilotChatToolCallItemRender.d.ts.map +1 -1
- package/dist/v2/components/chat/index.d.ts +3 -3
- package/dist/v2/hooks/use-agent.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.d.cts +2 -0
- package/dist/v2/index.d.mts +2 -0
- package/dist/v2/index.d.ts +2 -0
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/index.mjs +189 -72
- package/dist/v2/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/v2/__tests__/utils/test-helpers.ts +26 -6
- 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/CopilotChat.vue +100 -20
- package/src/v2/components/chat/CopilotPopupViewInternal.vue +1 -1
- package/src/v2/components/chat/__tests__/CopilotChat.clearOnFresh.test.ts +130 -2
- package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.ts +6 -1
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.ts +22 -40
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.ts +12 -24
- package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +20 -0
- package/src/v2/components/chat/copilotChatToolCallItemRender.ts +3 -2
- package/src/v2/hooks/__tests__/use-agent-headers.test.ts +83 -0
- package/src/v2/hooks/__tests__/use-agent-stability.test.ts +2 -0
- package/src/v2/hooks/__tests__/use-agent.e2e.test.ts +5 -3
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +2 -2
- package/src/v2/hooks/use-agent.ts +3 -3
- package/src/v2/index.ts +5 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.renderCustomMessages.e2e.test.ts +6 -4
- package/vite.config.ts +7 -0
- package/dist/use-render-activity-message-CGdS03Xj.cjs +0 -85
- package/dist/use-render-activity-message-CGdS03Xj.cjs.map +0 -1
- package/dist/use-render-activity-message-XmuT9fKy.js +0 -9799
- 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" }
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
177
|
+
const errorEvent: RunErrorEvent = {
|
|
178
178
|
type: EventType.RUN_ERROR,
|
|
179
|
-
|
|
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(
|
|
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(
|
|
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
|
|
396
|
-
|
|
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";
|