@copilotkit/vue 2.0.0-rc.6 → 2.0.0-rc.8

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 (128) hide show
  1. package/PARITY.md +1 -1
  2. package/dist/index.cjs +1 -73
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.mjs +292 -16949
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/use-render-activity-message-C5LCG9a9.js +16451 -0
  7. package/dist/use-render-activity-message-C5LCG9a9.js.map +1 -0
  8. package/dist/use-render-activity-message-CPzguixR.cjs +74 -0
  9. package/dist/use-render-activity-message-CPzguixR.cjs.map +1 -0
  10. package/dist/v2/components/LicenseWarningBanner.vue.d.ts.map +1 -1
  11. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  12. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  13. package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
  14. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +6 -270
  15. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  16. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +1 -1
  17. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  18. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  19. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +1 -1
  20. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
  21. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  22. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +1 -1
  23. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  24. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  25. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -1
  26. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +0 -1
  27. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  28. package/dist/v2/components/chat/index.d.ts +0 -1
  29. package/dist/v2/components/chat/index.d.ts.map +1 -1
  30. package/dist/v2/components/chat/tool-call-display-context.d.ts +2 -19
  31. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
  32. package/dist/v2/components/chat/tool-call-group-parts.d.ts +7 -20
  33. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
  34. package/dist/v2/components/chat/turn-work.d.ts +0 -2
  35. package/dist/v2/components/chat/turn-work.d.ts.map +1 -1
  36. package/dist/v2/components/chat/types.d.ts +1 -2
  37. package/dist/v2/components/chat/types.d.ts.map +1 -1
  38. package/dist/v2/components/chat/use-chat-labels.d.ts +8 -0
  39. package/dist/v2/components/chat/use-chat-labels.d.ts.map +1 -0
  40. package/dist/v2/components/icons/index.d.ts +968 -1
  41. package/dist/v2/components/icons/index.d.ts.map +1 -1
  42. package/dist/v2/hooks/use-frontend-tool.d.ts +5 -0
  43. package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -1
  44. package/dist/v2/hooks/use-render-activity-message.d.ts +13 -9
  45. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  46. package/dist/v2/hooks/use-render-custom-messages.d.ts +5 -7
  47. package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
  48. package/dist/v2/index.cjs +1 -1
  49. package/dist/v2/index.mjs +196 -199
  50. package/dist/v2/index.mjs.map +1 -1
  51. package/dist/v2/lib/vue-core.d.ts +0 -1
  52. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  53. package/dist/v2/providers/CopilotKitProvider.types.d.ts +0 -5
  54. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  55. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  56. package/dist/v2/providers/types.d.ts +3 -46
  57. package/dist/v2/providers/types.d.ts.map +1 -1
  58. package/dist/v2/types/human-in-the-loop.d.ts +8 -29
  59. package/dist/v2/types/human-in-the-loop.d.ts.map +1 -1
  60. package/dist/v2/types/interrupt.d.ts +1 -22
  61. package/dist/v2/types/interrupt.d.ts.map +1 -1
  62. package/dist/v2/types/vue-activity-message-renderer.d.ts +16 -15
  63. package/dist/v2/types/vue-activity-message-renderer.d.ts.map +1 -1
  64. package/dist/v2/types/vue-custom-message-renderer.d.ts +7 -16
  65. package/dist/v2/types/vue-custom-message-renderer.d.ts.map +1 -1
  66. package/dist/v2/types/vue-tool-call-renderer.d.ts +16 -35
  67. package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
  68. package/package.json +7 -8
  69. package/src/__tests__/build-entries.test.ts +27 -0
  70. package/src/v2/components/LicenseWarningBanner.vue +31 -15
  71. package/src/v2/components/__tests__/LicenseWarningBanner.test.ts +20 -0
  72. package/src/v2/components/chat/CopilotChat.vue +19 -40
  73. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +14 -10
  74. package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +14 -6
  75. package/src/v2/components/chat/CopilotChatInput.vue +40 -30
  76. package/src/v2/components/chat/CopilotChatMessageView.vue +72 -140
  77. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +31 -13
  78. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +60 -37
  79. package/src/v2/components/chat/CopilotChatToolCallItem.vue +3 -3
  80. package/src/v2/components/chat/CopilotChatToolCallsView.vue +15 -13
  81. package/src/v2/components/chat/CopilotChatView.vue +5 -5
  82. package/src/v2/components/chat/CopilotChatWorkSummary.vue +44 -18
  83. package/src/v2/components/chat/__tests__/CopilotChat.labels.test.ts +19 -4
  84. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +33 -1
  85. package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.ts +247 -403
  86. package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +1 -99
  87. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +3 -19
  88. package/src/v2/components/chat/index.ts +0 -1
  89. package/src/v2/components/chat/tool-call-display-context.ts +2 -23
  90. package/src/v2/components/chat/tool-call-group-parts.ts +44 -85
  91. package/src/v2/components/chat/turn-work.ts +0 -9
  92. package/src/v2/components/chat/types.ts +4 -4
  93. package/src/v2/components/chat/use-chat-labels.ts +16 -0
  94. package/src/v2/components/icons/__tests__/icons.test.ts +72 -68
  95. package/src/v2/components/icons/index.ts +109 -27
  96. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +62 -1
  97. package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +105 -0
  98. package/src/v2/hooks/__tests__/use-render-tool.test.ts +3 -6
  99. package/src/v2/hooks/__tests__/zod-regression.test.ts +4 -3
  100. package/src/v2/hooks/use-frontend-tool.ts +10 -10
  101. package/src/v2/hooks/use-render-activity-message.ts +27 -48
  102. package/src/v2/hooks/use-render-custom-messages.ts +14 -35
  103. package/src/v2/lib/vue-core.ts +3 -5
  104. package/src/v2/providers/CopilotKitProvider.types.ts +0 -5
  105. package/src/v2/providers/CopilotKitProvider.vue +29 -80
  106. package/src/v2/providers/__tests__/CopilotKitProvider.headersAtSendTime.test.ts +3 -3
  107. package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +3 -1
  108. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +17 -33
  109. package/src/v2/providers/types.ts +3 -49
  110. package/src/v2/types/__tests__/defineToolCallRenderer.test.ts +28 -33
  111. package/src/v2/types/defineToolCallRenderer.ts +5 -4
  112. package/src/v2/types/human-in-the-loop.ts +8 -32
  113. package/src/v2/types/interrupt.ts +7 -31
  114. package/src/v2/types/vue-activity-message-renderer.ts +24 -15
  115. package/src/v2/types/vue-custom-message-renderer.ts +12 -16
  116. package/src/v2/types/vue-tool-call-renderer.ts +23 -38
  117. package/vite.config.ts +1 -2
  118. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts +0 -9
  119. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +0 -1
  120. package/dist/v2/components/chat/tool-call-layout.d.ts +0 -115
  121. package/dist/v2/components/chat/tool-call-layout.d.ts.map +0 -1
  122. package/dist/v2/lib/transcription-client.d.ts +0 -19
  123. package/dist/v2/lib/transcription-client.d.ts.map +0 -1
  124. package/src/v2/components/chat/BasicMarkdown.vue +0 -160
  125. package/src/v2/components/chat/__tests__/BasicMarkdown.test.ts +0 -69
  126. package/src/v2/components/chat/tool-call-layout.ts +0 -304
  127. package/src/v2/lib/__tests__/transcription-client.test.ts +0 -67
  128. package/src/v2/lib/transcription-client.ts +0 -151
@@ -1,27 +1,109 @@
1
- export {
2
- MessageCircle as IconMessageCircle,
3
- X as IconX,
4
- ChevronDown as IconChevronDown,
5
- Plus as IconPlus,
6
- Mic as IconMic,
7
- ArrowUp as IconArrowUp,
8
- Check as IconCheck,
9
- Square as IconSquare,
10
- Loader2 as IconLoader2,
11
- Copy as IconCopy,
12
- Download as IconDownload,
13
- Edit as IconEdit,
14
- ChevronLeft as IconChevronLeft,
15
- ChevronRight as IconChevronRight,
16
- ThumbsUp as IconThumbsUp,
17
- ThumbsDown as IconThumbsDown,
18
- Volume2 as IconVolume2,
19
- RefreshCw as IconRefreshCw,
20
- CheckCircle as IconCheckCircle,
21
- Circle as IconCircle,
22
- PanelLeftOpen as IconPanelLeftOpen,
23
- Play as IconPlay,
24
- Upload as IconUpload,
25
- Wrench as IconWrench,
26
- EyeOff as IconEyeOff,
27
- } from "lucide-vue-next";
1
+ import { defineComponent, h } from "vue";
2
+ import type { PropType } from "vue";
3
+ import { ɵcopilotIcons, ɵcopilotIconSvgAttributes } from "@copilotkit/core";
4
+ import type { CopilotIconName } from "@copilotkit/core";
5
+
6
+ /** True when the attributes give the icon an accessible name or role. */
7
+ function hasAccessibleAttribute(attributes: object) {
8
+ return Object.keys(attributes).some(
9
+ (key) => key.startsWith("aria-") || key === "role" || key === "title",
10
+ );
11
+ }
12
+
13
+ /** Props that change how an icon is drawn. Other attributes go on the `<svg>`. */
14
+ const iconDrawProps = {
15
+ /** Width and height of the icon. Defaults to `24`. */
16
+ size: { type: [Number, String], default: 24 },
17
+ /** Line color. Defaults to `currentColor`. */
18
+ color: { type: String, default: "currentColor" },
19
+ /** Line width. Defaults to `2`. */
20
+ strokeWidth: { type: [Number, String], default: 2 },
21
+ };
22
+
23
+ /**
24
+ * Draws one of the chat icons as an inline `<svg>`.
25
+ *
26
+ * The `<svg>` has the classes `lucide lucide-<icon name>`, so CSS that selects
27
+ * them (for example `svg.lucide-arrow-up`) keeps working. The icon is hidden
28
+ * from screen readers unless you give it an `aria-*` attribute, a `role` or a
29
+ * `title`. Other attributes, such as `class` or `fill`, go on the `<svg>`.
30
+ *
31
+ * Props:
32
+ * - `name`: the chat icon, for example `"ArrowUp"` or `"X"`.
33
+ * - `size`: width and height. Defaults to `24`.
34
+ * - `stroke-width`: line width. Defaults to `2`.
35
+ * - `color`: line color. Defaults to `currentColor`.
36
+ *
37
+ * @example
38
+ * ```vue
39
+ * <CopilotIcon name="ArrowUp" class="cpk-chat-input-send-icon" />
40
+ * <CopilotIcon name="X" aria-label="Close" />
41
+ * ```
42
+ */
43
+ export const CopilotIcon = defineComponent({
44
+ name: "CopilotIcon",
45
+ props: {
46
+ /** The chat icon to draw, for example `"ArrowUp"`. */
47
+ name: { type: String as PropType<CopilotIconName>, required: true },
48
+ ...iconDrawProps,
49
+ },
50
+ setup(props, { attrs, slots }) {
51
+ return () => {
52
+ const icon = ɵcopilotIcons[props.name];
53
+ const content = slots.default?.() ?? [];
54
+ const isDecorative =
55
+ content.length === 0 && !hasAccessibleAttribute(attrs);
56
+ return h(
57
+ "svg",
58
+ {
59
+ ...ɵcopilotIconSvgAttributes,
60
+ width: props.size,
61
+ height: props.size,
62
+ stroke: props.color,
63
+ "stroke-width": props.strokeWidth,
64
+ class: ["lucide", `lucide-${icon.name}`],
65
+ "aria-hidden": isDecorative ? "true" : undefined,
66
+ },
67
+ [...icon.nodes.map((node) => h(node.tag, node.attributes)), ...content],
68
+ );
69
+ };
70
+ },
71
+ });
72
+
73
+ /**
74
+ * Internal. Makes a component that draws one chat icon, such as `IconArrowUp`.
75
+ * It takes the same `size`, `color` and `stroke-width` props as `CopilotIcon`.
76
+ */
77
+ function defineIcon(name: CopilotIconName) {
78
+ return defineComponent({
79
+ name: `Icon${name}`,
80
+ props: iconDrawProps,
81
+ setup(props, { slots }) {
82
+ return () => h(CopilotIcon, { name, ...props }, slots);
83
+ },
84
+ });
85
+ }
86
+
87
+ export const IconArrowUp = defineIcon("ArrowUp");
88
+ export const IconCheck = defineIcon("Check");
89
+ export const IconChevronDown = defineIcon("ChevronDown");
90
+ export const IconChevronLeft = defineIcon("ChevronLeft");
91
+ export const IconChevronRight = defineIcon("ChevronRight");
92
+ export const IconCircle = defineIcon("Circle");
93
+ export const IconCopy = defineIcon("Copy");
94
+ export const IconEdit = defineIcon("Edit");
95
+ export const IconEyeOff = defineIcon("EyeOff");
96
+ export const IconLoader2 = defineIcon("Loader2");
97
+ export const IconMessageCircle = defineIcon("MessageCircle");
98
+ export const IconMic = defineIcon("Mic");
99
+ export const IconPanelLeftOpen = defineIcon("PanelLeftOpen");
100
+ export const IconPlay = defineIcon("Play");
101
+ export const IconPlus = defineIcon("Plus");
102
+ export const IconRefreshCw = defineIcon("RefreshCw");
103
+ export const IconSquare = defineIcon("Square");
104
+ export const IconThumbsDown = defineIcon("ThumbsDown");
105
+ export const IconThumbsUp = defineIcon("ThumbsUp");
106
+ export const IconUpload = defineIcon("Upload");
107
+ export const IconVolume2 = defineIcon("Volume2");
108
+ export const IconWrench = defineIcon("Wrench");
109
+ export const IconX = defineIcon("X");
@@ -1,4 +1,12 @@
1
- import { computed, defineComponent, onMounted, ref, watch } from "vue";
1
+ import {
2
+ computed,
3
+ defineComponent,
4
+ h,
5
+ nextTick,
6
+ onMounted,
7
+ ref,
8
+ watch,
9
+ } from "vue";
2
10
  import type { PropType } from "vue";
3
11
  import { screen, fireEvent, waitFor, cleanup } from "@testing-library/vue";
4
12
  import { afterEach, describe, expect, it } from "vitest";
@@ -27,6 +35,11 @@ import {
27
35
  textChunkEvent,
28
36
  testId,
29
37
  } from "../../__tests__/utils/test-helpers";
38
+ import { mountWithProvider } from "../../__tests__/utils/mount";
39
+ import {
40
+ StateCapturingAgent,
41
+ toolCallMessage,
42
+ } from "../../__tests__/utils/agents";
30
43
 
31
44
  afterEach(() => {
32
45
  cleanup();
@@ -912,6 +925,54 @@ describe("useFrontendTool E2E - Dynamic Registration", () => {
912
925
  });
913
926
  });
914
927
 
928
+ describe("Overriding a provider tool", () => {
929
+ it("brings the provider tool back after the overriding component unmounts", async () => {
930
+ const calls: string[] = [];
931
+ const lookupTool = (owner: string): VueFrontendTool => ({
932
+ name: "lookup",
933
+ parameters: z.object({}),
934
+ followUp: false,
935
+ handler: async () => {
936
+ calls.push(owner);
937
+ return owner;
938
+ },
939
+ });
940
+
941
+ const OverridingTool = defineComponent({
942
+ setup() {
943
+ useFrontendTool(lookupTool("component"));
944
+ return () => null;
945
+ },
946
+ });
947
+
948
+ const showOverride = ref(true);
949
+ const agent = new StateCapturingAgent(
950
+ [
951
+ { newMessages: [toolCallMessage("lookup")] },
952
+ { newMessages: [toolCallMessage("lookup")] },
953
+ ],
954
+ "test-agent",
955
+ );
956
+ const { getCore, wrapper } = mountWithProvider(
957
+ () => (showOverride.value ? h(OverridingTool) : h("div")),
958
+ {
959
+ frontendTools: [lookupTool("provider")],
960
+ agents__unsafe_dev_only: { "test-agent": agent },
961
+ },
962
+ );
963
+
964
+ await nextTick();
965
+ await getCore().runAgent({ agent });
966
+
967
+ showOverride.value = false;
968
+ await nextTick();
969
+ await getCore().runAgent({ agent });
970
+
971
+ expect(calls).toEqual(["component", "provider"]);
972
+ wrapper.unmount();
973
+ });
974
+ });
975
+
915
976
  describe("Integration with Chat UI", () => {
916
977
  it("should render tool output correctly in chat interface", async () => {
917
978
  const agent = new MockStepwiseAgent();
@@ -0,0 +1,105 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ expectTypeOf,
7
+ it,
8
+ vi,
9
+ } from "vitest";
10
+ import { defineComponent, h, toRaw } from "vue";
11
+ import { z } from "zod";
12
+ import type { ActivityMessage } from "@ag-ui/core";
13
+ import { useRenderActivityMessage } from "../use-render-activity-message";
14
+ import type {
15
+ VueActivityMessageRenderer,
16
+ VueActivityMessageRendererProps,
17
+ } from "../../types";
18
+ import { renderWithCopilotKit } from "../../__tests__/utils/mount";
19
+
20
+ const ProgressRenderer = defineComponent({
21
+ props: { content: { type: Object, required: true } },
22
+ setup: () => () => null,
23
+ });
24
+
25
+ const progressSchema = z.object({
26
+ label: z.string(),
27
+ percent: z.number().default(0),
28
+ });
29
+
30
+ // A zod schema is a Standard Schema, so it fits the `content` field, and the
31
+ // renderer's content type is the schema's output type.
32
+ const progressRenderer = {
33
+ activityType: "progress",
34
+ content: progressSchema,
35
+ render: ProgressRenderer,
36
+ } satisfies VueActivityMessageRenderer<z.output<typeof progressSchema>>;
37
+
38
+ function activityMessage(content: Record<string, unknown>): ActivityMessage {
39
+ return {
40
+ id: "activity-1",
41
+ role: "activity",
42
+ activityType: "progress",
43
+ content,
44
+ };
45
+ }
46
+
47
+ function renderWithProgressRenderer() {
48
+ let helpers: ReturnType<typeof useRenderActivityMessage> | undefined;
49
+ const Probe = defineComponent({
50
+ setup() {
51
+ helpers = useRenderActivityMessage();
52
+ return () => null;
53
+ },
54
+ });
55
+
56
+ renderWithCopilotKit(() => h(Probe), {
57
+ providerProps: { renderActivityMessages: [progressRenderer] },
58
+ });
59
+
60
+ if (!helpers) throw new Error("useRenderActivityMessage did not run");
61
+ return helpers;
62
+ }
63
+
64
+ describe("useRenderActivityMessage", () => {
65
+ let consoleWarnSpy: ReturnType<typeof vi.spyOn>;
66
+
67
+ beforeEach(() => {
68
+ consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
69
+ });
70
+
71
+ afterEach(() => {
72
+ consoleWarnSpy.mockRestore();
73
+ });
74
+
75
+ it("types a zod content schema's output as the render props content", () => {
76
+ type Props = VueActivityMessageRendererProps<
77
+ z.output<typeof progressSchema>
78
+ >;
79
+ expectTypeOf<Props["content"]>().toEqualTypeOf<{
80
+ label: string;
81
+ percent: number;
82
+ }>();
83
+ });
84
+
85
+ it("passes the zod schema's parsed output to the renderer", () => {
86
+ const { renderActivityMessage } = renderWithProgressRenderer();
87
+
88
+ const result = renderActivityMessage(activityMessage({ label: "Upload" }));
89
+
90
+ // The provider stores renderers in reactive state, so compare the raw value.
91
+ expect(toRaw(result?.renderer)).toBe(ProgressRenderer);
92
+ expect(result?.props.content).toEqual({ label: "Upload", percent: 0 });
93
+ });
94
+
95
+ it("returns null and warns when the content fails the schema", () => {
96
+ const { renderActivityMessage } = renderWithProgressRenderer();
97
+
98
+ const result = renderActivityMessage(activityMessage({ label: 42 }));
99
+
100
+ expect(result).toBeNull();
101
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
102
+ "Failed to validate content for activity message 'progress'.",
103
+ );
104
+ });
105
+ });
@@ -93,7 +93,7 @@ describe("useRenderTool", () => {
93
93
  expect(typeof renderer?.render).toBe("function");
94
94
  });
95
95
 
96
- it("registers wildcard renderer and defaults parameters schema to z.any", () => {
96
+ it("registers wildcard renderer and defaults parameters to a schema that accepts any value", () => {
97
97
  const core = createMockCore();
98
98
  mockUseCopilotKit.mockReturnValue({ copilotkit: { value: core } });
99
99
 
@@ -118,11 +118,8 @@ describe("useRenderTool", () => {
118
118
  const renderer = core.renderToolCalls.find((item) => item.name === "*");
119
119
  expect(renderer).toBeDefined();
120
120
  expect(typeof renderer?.render).toBe("function");
121
- const wildcardArgs = renderer?.args as
122
- | { safeParse: (v: unknown) => { success: boolean } }
123
- | undefined;
124
- expect(wildcardArgs).toBeDefined();
125
- expect(wildcardArgs!.safeParse({ arbitrary: true }).success).toBe(true);
121
+ const result = renderer?.args["~standard"].validate({ arbitrary: true });
122
+ expect(result).toEqual({ value: { arbitrary: true } });
126
123
  });
127
124
 
128
125
  it("accepts a Vue component renderer", () => {
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Covers:
6
6
  * 1. useRenderTool with complex Zod schemas
7
- * 2. defineToolCallRenderer with Zod (named + wildcard default z.any())
7
+ * 2. defineToolCallRenderer with Zod (named + wildcard default schema)
8
8
  * 3. useComponent with Zod schemas
9
9
  * 4. The registered schema object is the original Zod instance (identity check)
10
10
  */
@@ -196,14 +196,15 @@ describe("useRenderTool Zod regression", () => {
196
196
  });
197
197
 
198
198
  describe("defineToolCallRenderer Zod regression", () => {
199
- it("wildcard tool defaults to z.any() for args", () => {
199
+ it("wildcard tool defaults to a schema that accepts any value", () => {
200
200
  const renderer = defineToolCallRenderer({
201
201
  name: "*",
202
202
  render: () => "wildcard",
203
203
  });
204
204
 
205
205
  expect(renderer.name).toBe("*");
206
- expect((renderer.args as any)["~standard"].vendor).toBe("zod");
206
+ const result = renderer.args["~standard"].validate({ city: "Paris" });
207
+ expect(result).toEqual({ value: { city: "Paris" } });
207
208
  });
208
209
 
209
210
  it("named tool with Zod args preserves schema identity", () => {
@@ -12,6 +12,11 @@ const EMPTY_DEPS: WatchSource<unknown>[] = [];
12
12
  * The tool registration is reactive to provided dependencies and is cleaned up
13
13
  * automatically when the current scope is disposed.
14
14
  *
15
+ * A tool with the same name and `agentId` as a tool that is already
16
+ * registered (for example a provider `frontendTools` entry) takes its place
17
+ * while this scope is active. When the scope is disposed, the earlier tool is
18
+ * active again.
19
+ *
15
20
  * @example
16
21
  * ```ts
17
22
  * useFrontendTool({
@@ -42,13 +47,10 @@ export function useFrontendTool<T extends Record<string, unknown>>(
42
47
  const core = copilotkit.value;
43
48
  const name = tool.name;
44
49
 
45
- if (core.getTool({ toolName: name, agentId: tool.agentId })) {
46
- console.warn(
47
- `Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`,
48
- );
49
- core.removeTool(name, tool.agentId);
50
- }
51
- core.addTool(tool);
50
+ // Registrations stack in core: this one shadows an earlier tool with the
51
+ // same name and agentId (a provider tool, or another component's), and
52
+ // unregistering it brings that tool back.
53
+ const unregister = core.addTool(tool);
52
54
 
53
55
  // The render function is registered even when tool.parameters is
54
56
  // undefined — tools like HITL confirm dialogs have no parameters
@@ -62,9 +64,7 @@ export function useFrontendTool<T extends Record<string, unknown>>(
62
64
  } as VueToolCallRenderer<unknown>);
63
65
  }
64
66
 
65
- onCleanup(() => {
66
- core.removeTool(name, tool.agentId);
67
- });
67
+ onCleanup(unregister);
68
68
  },
69
69
  { immediate: true },
70
70
  );
@@ -1,19 +1,11 @@
1
1
  import { computed } from "vue";
2
2
  import type { Component } from "vue";
3
3
  import type { ActivityMessage } from "@ag-ui/core";
4
- import type { AbstractAgent } from "@ag-ui/client";
4
+ import { ɵresolveActivityRenderer } from "@copilotkit/core";
5
5
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
6
6
  import { useCopilotKit } from "../providers/useCopilotKit";
7
7
  import { useCopilotChatConfiguration } from "../providers/useCopilotChatConfiguration";
8
- import type {
9
- VueActivityMessageRenderer,
10
- VueActivityMessageRendererProps,
11
- } from "../types";
12
-
13
- interface ActivityRendererResult {
14
- renderer: Component<VueActivityMessageRendererProps<unknown>>;
15
- props: VueActivityMessageRendererProps<unknown>;
16
- }
8
+ import type { VueActivityMessageRendererProps } from "../types";
17
9
 
18
10
  /**
19
11
  * Returns helpers for rendering activity messages.
@@ -21,70 +13,57 @@ interface ActivityRendererResult {
21
13
  * Matches the React `useRenderActivityMessage` API: `findRenderer` locates a
22
14
  * registered renderer by activity type (preferring agent-scoped over global,
23
15
  * with `"*"` as a wildcard fallback), and `renderActivityMessage` resolves the
24
- * renderer and validates the content schema.
16
+ * renderer and validates the content with the renderer's `content` schema
17
+ * (any Standard Schema, such as zod).
25
18
  */
26
19
  export function useRenderActivityMessage() {
27
20
  const { copilotkit } = useCopilotKit();
28
21
  const config = useCopilotChatConfiguration();
29
22
  const agentId = computed(() => config.value?.agentId ?? DEFAULT_AGENT_ID);
30
23
 
31
- const renderers = computed(() => [
32
- ...copilotkit.value.renderActivityMessages,
33
- ]);
34
-
35
- function findRenderer(
36
- activityType: string,
37
- ): VueActivityMessageRenderer<unknown> | null {
38
- const list = renderers.value;
39
- if (!list.length) {
40
- return null;
41
- }
42
-
43
- const matches = list.filter(
24
+ function findRenderer(activityType: string) {
25
+ const renderers = copilotkit.value.renderActivityMessages;
26
+ const matches = renderers.filter(
44
27
  (renderer) => renderer.activityType === activityType,
45
28
  );
46
29
 
47
30
  return (
48
31
  matches.find((candidate) => candidate.agentId === agentId.value) ??
49
32
  matches.find((candidate) => candidate.agentId === undefined) ??
50
- list.find((candidate) => candidate.activityType === "*") ??
33
+ renderers.find((candidate) => candidate.activityType === "*") ??
51
34
  null
52
35
  );
53
36
  }
54
37
 
55
- function renderActivityMessage(
56
- message: ActivityMessage,
57
- ): ActivityRendererResult | null {
58
- const renderer = findRenderer(message.activityType);
59
-
60
- if (!renderer) {
61
- return null;
62
- }
63
-
64
- const parseResult = renderer.content.safeParse(message.content);
38
+ function renderActivityMessage(message: ActivityMessage) {
39
+ const match = ɵresolveActivityRenderer(
40
+ copilotkit.value.renderActivityMessages,
41
+ {
42
+ activityType: message.activityType,
43
+ agentId: agentId.value,
44
+ content: message.content,
45
+ },
46
+ );
65
47
 
66
- if (!parseResult.success) {
67
- console.warn(
68
- `Failed to parse content for activity message ` +
69
- `'${message.activityType}':`,
70
- parseResult.error,
71
- );
48
+ if (!match) {
49
+ if (findRenderer(message.activityType)) {
50
+ console.warn(
51
+ `Failed to validate content for activity message ` +
52
+ `'${message.activityType}'.`,
53
+ );
54
+ }
72
55
  return null;
73
56
  }
74
57
 
75
- const agent: AbstractAgent | undefined = copilotkit.value.getAgent(
76
- agentId.value,
77
- );
78
-
79
58
  return {
80
- renderer: renderer.render as Component<
59
+ renderer: match.renderer.render as Component<
81
60
  VueActivityMessageRendererProps<unknown>
82
61
  >,
83
62
  props: {
84
63
  activityType: message.activityType,
85
- content: parseResult.data,
64
+ content: match.content,
86
65
  message,
87
- agent,
66
+ agent: copilotkit.value.getAgent(agentId.value),
88
67
  },
89
68
  };
90
69
  }
@@ -1,5 +1,5 @@
1
- import type { Component, VNodeChild } from "vue";
2
1
  import type { Message } from "@ag-ui/core";
2
+ import { ɵresolveCustomMessageRenderers } from "@copilotkit/core";
3
3
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
4
4
  import { useCopilotKit } from "../providers/useCopilotKit";
5
5
  import { useCopilotChatConfiguration } from "../providers/useCopilotChatConfiguration";
@@ -13,13 +13,6 @@ interface UseRenderCustomMessagesParams {
13
13
  position: VueCustomMessageRendererPosition;
14
14
  }
15
15
 
16
- interface CustomMessageRendererResult {
17
- renderer:
18
- | Component<VueCustomMessageRendererProps>
19
- | ((props: VueCustomMessageRendererProps) => VNodeChild);
20
- props: VueCustomMessageRendererProps;
21
- }
22
-
23
16
  /**
24
17
  * Returns a function that resolves the appropriate custom message renderer for
25
18
  * a given message and position.
@@ -37,9 +30,7 @@ export function useRenderCustomMessages() {
37
30
  const { copilotkit } = useCopilotKit();
38
31
  const config = useCopilotChatConfiguration();
39
32
 
40
- return function renderCustomMessage(
41
- params: UseRenderCustomMessagesParams,
42
- ): CustomMessageRendererResult | null {
33
+ return function renderCustomMessage(params: UseRenderCustomMessagesParams) {
43
34
  const configValue = config.value;
44
35
  if (!configValue) return null;
45
36
 
@@ -47,17 +38,10 @@ export function useRenderCustomMessages() {
47
38
  const { threadId } = configValue;
48
39
  const core = copilotkit.value;
49
40
 
50
- const customMessageRenderers = [...core.renderCustomMessages]
51
- .filter(
52
- (renderer) =>
53
- renderer.agentId === undefined || renderer.agentId === agentId,
54
- )
55
- .sort((a, b) => {
56
- const aHasAgent = a.agentId !== undefined;
57
- const bHasAgent = b.agentId !== undefined;
58
- if (aHasAgent === bHasAgent) return 0;
59
- return aHasAgent ? -1 : 1;
60
- });
41
+ const customMessageRenderers = ɵresolveCustomMessageRenderers(
42
+ core.renderCustomMessages,
43
+ agentId,
44
+ );
61
45
 
62
46
  if (!customMessageRenderers.length) {
63
47
  return null;
@@ -101,7 +85,7 @@ export function useRenderCustomMessages() {
101
85
  ? core.getStateByRun(agentId, threadId, resolvedRunId)
102
86
  : undefined;
103
87
 
104
- const props: CustomMessageRendererResult["props"] = {
88
+ const props: VueCustomMessageRendererProps = {
105
89
  message,
106
90
  position,
107
91
  runId,
@@ -112,18 +96,13 @@ export function useRenderCustomMessages() {
112
96
  stateSnapshot,
113
97
  };
114
98
 
115
- // Iterate renderers like React: try each one and use the first that
116
- // returns a non-null render function.
117
- for (const candidate of customMessageRenderers) {
118
- if (!candidate.render) {
119
- continue;
120
- }
121
- return {
122
- renderer: candidate.render as CustomMessageRendererResult["renderer"],
123
- props,
124
- };
125
- }
99
+ // Agent-scoped renderers come first. Use the first one that has a
100
+ // render function.
101
+ const selected = customMessageRenderers.find(
102
+ (candidate) => candidate.render,
103
+ );
104
+ if (!selected) return null;
126
105
 
127
- return null;
106
+ return { renderer: selected.render, props };
128
107
  };
129
108
  }
@@ -44,7 +44,9 @@ export class CopilotKitCoreVue extends CopilotKitCore {
44
44
  private _interruptState: InterruptRenderProps<unknown, unknown> | null = null;
45
45
 
46
46
  constructor(config: CopilotKitCoreVueConfig) {
47
- super(config);
47
+ // Tool handlers can change reactive state. `nextTick` lets Vue flush those
48
+ // updates before core starts the follow-up run.
49
+ super({ ...config, ɵwaitForFrameworkUpdates: () => nextTick() });
48
50
  this._renderToolCalls = config.renderToolCalls ?? [];
49
51
  this._renderCustomMessages = config.renderCustomMessages ?? [];
50
52
  this._renderActivityMessages = config.renderActivityMessages ?? [];
@@ -154,8 +156,4 @@ export class CopilotKitCoreVue extends CopilotKitCore {
154
156
  ): CopilotKitCoreSubscription {
155
157
  return super.subscribe(subscriber);
156
158
  }
157
-
158
- async waitForPendingFrameworkUpdates(): Promise<void> {
159
- await nextTick();
160
- }
161
159
  }
@@ -84,11 +84,6 @@ export interface CopilotKitProviderProps {
84
84
  sandboxFunctions?: SandboxFunction[];
85
85
  designSkill?: string;
86
86
  };
87
- /**
88
- * @deprecated This prop no longer controls the Inspector. Use
89
- * `enableInspector` instead.
90
- */
91
- showDevConsole?: boolean | "auto";
92
87
  /**
93
88
  * Disable the CopilotKit Inspector in development.
94
89
  * The Inspector is enabled by default in development browser builds and is