@copilotkit/vue 2.0.0-rc.20 → 2.0.0-rc.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.mjs +13 -13
  3. package/dist/use-render-activity-message-BAhbpSDh.js +16200 -0
  4. package/dist/use-render-activity-message-BAhbpSDh.js.map +1 -0
  5. package/dist/use-render-activity-message-DJ6q1qHV.cjs +114 -0
  6. package/dist/use-render-activity-message-DJ6q1qHV.cjs.map +1 -0
  7. package/dist/v2/components/A2UIMessageRenderer.d.ts +14 -0
  8. package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
  9. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts +10 -3
  10. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
  11. package/dist/v2/components/CopilotKitInspector.vue.d.ts +5 -1
  12. package/dist/v2/components/CopilotKitInspector.vue.d.ts.map +1 -1
  13. package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -1
  14. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts +7 -21
  15. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts.map +1 -1
  16. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +0 -5
  17. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
  18. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts +10 -15
  19. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts.map +1 -1
  20. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +14 -138
  21. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -1
  22. package/dist/v2/components/a2ui/catalog.d.ts.map +1 -1
  23. package/dist/v2/components/a2ui/utils.d.ts +3 -15
  24. package/dist/v2/components/a2ui/utils.d.ts.map +1 -1
  25. package/dist/v2/components/a2ui.d.ts +6 -1
  26. package/dist/v2/components/a2ui.d.ts.map +1 -1
  27. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  28. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  29. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts +4 -8
  30. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
  31. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  32. package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -1
  33. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  34. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
  35. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  36. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  37. package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -1
  38. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  39. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  40. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +1 -1
  41. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +1 -1
  42. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
  43. package/dist/v2/components/chat/CopilotThreadsDrawer.vue.d.ts.map +1 -1
  44. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts +1 -0
  45. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
  46. package/dist/v2/components/chat/audioRecorder.d.ts +3 -4
  47. package/dist/v2/components/chat/audioRecorder.d.ts.map +1 -1
  48. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +3 -2
  49. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  50. package/dist/v2/components/chat/index.d.ts +6 -6
  51. package/dist/v2/components/chat/tool-call-display-context.d.ts +3 -1
  52. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
  53. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
  54. package/dist/v2/components/chat/types.d.ts +1 -1
  55. package/dist/v2/components/chat/types.d.ts.map +1 -1
  56. package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -1
  57. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +1 -1
  58. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -1
  59. package/dist/v2/hooks/use-agent-context.d.ts +2 -3
  60. package/dist/v2/hooks/use-agent-context.d.ts.map +1 -1
  61. package/dist/v2/hooks/use-agent.d.ts +4 -1
  62. package/dist/v2/hooks/use-agent.d.ts.map +1 -1
  63. package/dist/v2/hooks/use-attachments.d.ts +7 -0
  64. package/dist/v2/hooks/use-attachments.d.ts.map +1 -1
  65. package/dist/v2/hooks/use-capabilities.d.ts.map +1 -1
  66. package/dist/v2/hooks/use-component.d.ts +1 -0
  67. package/dist/v2/hooks/use-component.d.ts.map +1 -1
  68. package/dist/v2/hooks/use-configure-suggestions.d.ts +4 -9
  69. package/dist/v2/hooks/use-configure-suggestions.d.ts.map +1 -1
  70. package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
  71. package/dist/v2/hooks/use-human-in-the-loop.d.ts +2 -1
  72. package/dist/v2/hooks/use-human-in-the-loop.d.ts.map +1 -1
  73. package/dist/v2/hooks/use-interrupt.d.ts +0 -1
  74. package/dist/v2/hooks/use-interrupt.d.ts.map +1 -1
  75. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  76. package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
  77. package/dist/v2/hooks/use-suggestions.d.ts.map +1 -1
  78. package/dist/v2/hooks/use-threads.d.ts +0 -8
  79. package/dist/v2/hooks/use-threads.d.ts.map +1 -1
  80. package/dist/v2/index.cjs +1 -1
  81. package/dist/v2/index.mjs +1 -1
  82. package/dist/v2/lib/vue-core.d.ts +12 -11
  83. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  84. package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
  85. package/dist/v2/providers/CopilotKitProvider.types.d.ts +9 -124
  86. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  87. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +9 -10
  88. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  89. package/dist/v2/providers/chat-configuration-store-key.d.ts +8 -0
  90. package/dist/v2/providers/chat-configuration-store-key.d.ts.map +1 -0
  91. package/dist/v2/providers/markdown-renderer.d.ts +3 -1
  92. package/dist/v2/providers/markdown-renderer.d.ts.map +1 -1
  93. package/dist/v2/providers/use-inspector-thread-override.d.ts +5 -0
  94. package/dist/v2/providers/use-inspector-thread-override.d.ts.map +1 -1
  95. package/package.json +8 -8
  96. package/src/v2/__tests__/exports.test.ts +10 -0
  97. package/src/v2/__tests__/mocks/web-inspector.ts +6 -0
  98. package/src/v2/__tests__/utils/test-helpers.ts +34 -4
  99. package/src/v2/components/A2UIMessageRenderer.ts +98 -68
  100. package/src/v2/components/A2UISurfaceActivityRenderer.vue +68 -103
  101. package/src/v2/components/CopilotKitInspector.vue +104 -51
  102. package/src/v2/components/MCPAppsActivityRenderer.ts +75 -201
  103. package/src/v2/components/OpenGenerativeUIRenderer.ts +78 -80
  104. package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +171 -8
  105. package/src/v2/components/__tests__/CopilotKitInspector.test.ts +42 -0
  106. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.ts +9 -2
  107. package/src/v2/components/__tests__/OpenGenerativeUIToolRenderer.test.ts +38 -0
  108. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +1 -6
  109. package/src/v2/components/a2ui/A2UICatalogContext.ts +27 -93
  110. package/src/v2/components/a2ui/A2UIRecoveryStates.ts +127 -327
  111. package/src/v2/components/a2ui/catalog.ts +2 -3
  112. package/src/v2/components/a2ui/utils.ts +13 -67
  113. package/src/v2/components/a2ui.ts +14 -21
  114. package/src/v2/components/chat/CopilotChat.vue +136 -324
  115. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +22 -45
  116. package/src/v2/components/chat/CopilotChatAudioRecorder.vue +19 -246
  117. package/src/v2/components/chat/CopilotChatInput.vue +69 -407
  118. package/src/v2/components/chat/CopilotChatLightbox.vue +10 -31
  119. package/src/v2/components/chat/CopilotChatMessageView.vue +52 -112
  120. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +14 -45
  121. package/src/v2/components/chat/CopilotChatToolCallItem.vue +6 -4
  122. package/src/v2/components/chat/CopilotChatToolCallsView.vue +16 -19
  123. package/src/v2/components/chat/CopilotChatView.vue +28 -23
  124. package/src/v2/components/chat/CopilotPopupViewInternal.vue +21 -97
  125. package/src/v2/components/chat/CopilotSidebarViewInternal.vue +24 -25
  126. package/src/v2/components/chat/CopilotThreadsDrawer.vue +37 -102
  127. package/src/v2/components/chat/StreamingMarkdownDefault.vue +13 -26
  128. package/src/v2/components/chat/__tests__/CopilotChat.test.ts +12 -10
  129. package/src/v2/components/chat/__tests__/CopilotChatAttachmentQueue.test.ts +28 -0
  130. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +56 -0
  131. package/src/v2/components/chat/__tests__/CopilotPopupView.test.ts +19 -0
  132. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +32 -8
  133. package/src/v2/components/chat/__tests__/CopilotSidebarView.test.ts +2 -1
  134. package/src/v2/components/chat/__tests__/StreamingMarkdownDefault.test.ts +54 -0
  135. package/src/v2/components/chat/audioRecorder.ts +4 -7
  136. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +22 -71
  137. package/src/v2/components/chat/tool-call-display-context.ts +6 -1
  138. package/src/v2/components/chat/tool-call-group-parts.ts +13 -33
  139. package/src/v2/components/chat/types.ts +1 -1
  140. package/src/v2/components/chat/use-lightbox.ts +15 -33
  141. package/src/v2/hooks/__tests__/use-agent-error-state.test.ts +5 -3
  142. package/src/v2/hooks/__tests__/use-agent-stability.test.ts +25 -4
  143. package/src/v2/hooks/__tests__/use-agent-throttle.test.ts +5 -1
  144. package/src/v2/hooks/__tests__/use-agent.test.ts +6 -3
  145. package/src/v2/hooks/__tests__/use-component.test.ts +21 -0
  146. package/src/v2/hooks/__tests__/use-configure-suggestions.test.ts +30 -1
  147. package/src/v2/hooks/__tests__/use-default-render-tool.test.ts +10 -10
  148. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +4 -0
  149. package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.ts +6 -1
  150. package/src/v2/hooks/__tests__/use-human-in-the-loop.test.ts +187 -12
  151. package/src/v2/hooks/__tests__/use-interrupt.test.ts +97 -0
  152. package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +2 -1
  153. package/src/v2/hooks/__tests__/use-threads.test.ts +48 -6
  154. package/src/v2/hooks/use-agent-context.ts +4 -10
  155. package/src/v2/hooks/use-agent.ts +86 -290
  156. package/src/v2/hooks/use-attachments.ts +42 -166
  157. package/src/v2/hooks/use-capabilities.ts +2 -7
  158. package/src/v2/hooks/use-component.ts +7 -4
  159. package/src/v2/hooks/use-configure-suggestions.ts +28 -218
  160. package/src/v2/hooks/use-default-render-tool.ts +34 -94
  161. package/src/v2/hooks/use-human-in-the-loop.ts +11 -80
  162. package/src/v2/hooks/use-interrupt.ts +29 -308
  163. package/src/v2/hooks/use-render-activity-message.ts +17 -20
  164. package/src/v2/hooks/use-render-custom-messages.ts +20 -63
  165. package/src/v2/hooks/use-suggestions.ts +9 -33
  166. package/src/v2/hooks/use-threads.ts +43 -273
  167. package/src/v2/lib/__tests__/vue-core-renderers.test.ts +42 -0
  168. package/src/v2/lib/vue-core.ts +67 -72
  169. package/src/v2/providers/CopilotChatConfigurationProvider.vue +48 -186
  170. package/src/v2/providers/CopilotKitProvider.types.ts +12 -127
  171. package/src/v2/providers/CopilotKitProvider.vue +188 -402
  172. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.ts +22 -2
  173. package/src/v2/providers/__tests__/CopilotKitProvider.a2ui.test.ts +19 -1
  174. package/src/v2/providers/__tests__/CopilotKitProvider.config.test.ts +124 -0
  175. package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.ts +4 -1
  176. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +45 -2
  177. package/src/v2/providers/chat-configuration-store-key.ts +9 -0
  178. package/src/v2/providers/markdown-renderer.ts +7 -1
  179. package/src/v2/providers/use-inspector-thread-override.ts +22 -80
  180. package/vite.config.ts +5 -1
  181. package/vitest.config.ts +12 -7
  182. package/dist/use-render-activity-message-CsWYyd6r.cjs +0 -91
  183. package/dist/use-render-activity-message-CsWYyd6r.cjs.map +0 -1
  184. package/dist/use-render-activity-message-DntKKNdd.js +0 -17418
  185. package/dist/use-render-activity-message-DntKKNdd.js.map +0 -1
  186. package/dist/v2/components/chat/normalize-auto-scroll.d.ts +0 -3
  187. package/dist/v2/components/chat/normalize-auto-scroll.d.ts.map +0 -1
  188. package/dist/v2/lib/is-mobile-viewport.d.ts +0 -2
  189. package/dist/v2/lib/is-mobile-viewport.d.ts.map +0 -1
  190. package/src/v2/components/__tests__/A2UICatalogContext.test.ts +0 -63
  191. package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +0 -175
  192. package/src/v2/components/chat/normalize-auto-scroll.ts +0 -3
  193. package/src/v2/lib/is-mobile-viewport.ts +0 -5
@@ -115,6 +115,25 @@ describe("CopilotPopupView", () => {
115
115
  expect(reopened.find("[data-copilot-popup]").exists()).toBe(false);
116
116
  });
117
117
 
118
+ it("stays open for a pointer down in a chat popover outside the panel", async () => {
119
+ const wrapper = mountPopupView({ clickOutsideToClose: true });
120
+ // Popovers, such as a reply rating's comment, render in a portal.
121
+ const popover = document.createElement("div");
122
+ popover.setAttribute("data-copilotkit-popover", "");
123
+ const field = document.createElement("textarea");
124
+ popover.append(field);
125
+ document.body.append(popover);
126
+
127
+ field.dispatchEvent(new Event("pointerdown", { bubbles: true }));
128
+ await nextTick();
129
+ vi.advanceTimersByTime(200);
130
+ await nextTick();
131
+
132
+ expect(wrapper.find("[data-copilot-popup]").exists()).toBe(true);
133
+ popover.remove();
134
+ wrapper.unmount();
135
+ });
136
+
118
137
  it("ignores outside-close detection when the toggle button is clicked and supports custom slots", async () => {
119
138
  const wrapper = mountPopupView(
120
139
  {
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { afterEach, describe, expect, it } from "vitest";
2
2
  import { h } from "vue";
3
3
  import { mount } from "@vue/test-utils";
4
4
  import type { Message } from "@ag-ui/core";
@@ -22,21 +22,31 @@ function cssRule(selector: string): string {
22
22
  return stylesheet.slice(body, stylesheet.indexOf("}", body));
23
23
  }
24
24
 
25
+ /** Mounted sidebars. Only one docked sidebar may own the page margin. */
26
+ const mounted: { unmount: () => void }[] = [];
27
+ afterEach(() => {
28
+ for (const wrapper of mounted.splice(0)) wrapper.unmount();
29
+ });
30
+
25
31
  function mountSidebarView(props: Record<string, unknown> = {}) {
26
- return mount(CopilotKitProvider, {
32
+ const wrapper = mount(CopilotKitProvider, {
27
33
  props: { runtimeUrl: "/api/copilotkit" },
28
34
  slots: {
29
35
  default: () =>
30
36
  h(CopilotSidebarView, { messages: sampleMessages, ...props }),
31
37
  },
32
38
  });
39
+ mounted.push(wrapper);
40
+ return wrapper;
33
41
  }
34
42
 
35
43
  function mountSidebar(props: Record<string, unknown> = {}) {
36
- return mount(CopilotKitProvider, {
44
+ const wrapper = mount(CopilotKitProvider, {
37
45
  props: { agents__unsafe_dev_only: { default: new StateCapturingAgent() } },
38
46
  slots: { default: () => h(CopilotSidebar, props) },
39
47
  });
48
+ mounted.push(wrapper);
49
+ return wrapper;
40
50
  }
41
51
 
42
52
  describe("CopilotSidebarView position prop", () => {
@@ -87,12 +97,26 @@ describe("CopilotSidebarView position prop", () => {
87
97
  });
88
98
 
89
99
  it("makes room for the sidebar on the edge it docks to", () => {
90
- const left = mountSidebarView({ position: "left", width: 400 });
91
- expect(left.html()).toContain("margin-inline-start: 400px");
92
- expect(left.html()).not.toContain("margin-inline-end");
100
+ const body = document.body.style;
101
+ mountSidebarView({ position: "left", width: 400 });
102
+ expect(body.marginInlineStart).toBe("400px");
103
+ expect(body.marginInlineEnd).toBe("");
104
+ mounted.pop()?.unmount();
105
+
106
+ mountSidebarView({ width: 400 });
107
+ expect(body.marginInlineEnd).toBe("400px");
108
+ });
109
+
110
+ it("gives the page its own margins back on unmount", () => {
111
+ const body = document.body.style;
112
+ body.marginInlineEnd = "16px";
113
+ mountSidebarView({ width: 400 });
114
+ expect(body.marginInlineEnd).toBe("400px");
115
+
116
+ mounted.pop()?.unmount();
93
117
 
94
- const right = mountSidebarView({ width: 400 });
95
- expect(right.html()).toContain("margin-inline-end: 400px");
118
+ expect(body.marginInlineEnd).toBe("16px");
119
+ body.marginInlineEnd = "";
96
120
  });
97
121
  });
98
122
 
@@ -91,7 +91,8 @@ describe("CopilotSidebarView", () => {
91
91
  const sidebar = wrapper.get("[data-copilot-sidebar]");
92
92
  expect(sidebar.attributes("aria-hidden")).toBe("false");
93
93
  expect(sidebar.attributes("style")).toContain("--sidebar-width: 612px");
94
- expect(wrapper.html()).toContain("margin-inline-end: 612px");
94
+ expect(document.body.style.marginInlineEnd).toBe("612px");
95
+ wrapper.unmount();
95
96
  });
96
97
 
97
98
  it("applies explicit width and renders default header and toggle button", () => {
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render, fireEvent } from "@testing-library/vue";
3
+ import StreamingMarkdownDefault from "../StreamingMarkdownDefault.vue";
4
+
5
+ function renderDefault(props: Record<string, unknown>) {
6
+ return render(StreamingMarkdownDefault, { props }).container;
7
+ }
8
+
9
+ describe("StreamingMarkdownDefault (vue)", () => {
10
+ it("renders nothing for empty content", () => {
11
+ expect(renderDefault({ content: "" }).innerHTML).toBe("<!--v-if-->");
12
+ });
13
+
14
+ it("renders fenced code with the shared code block class", () => {
15
+ const pre = renderDefault({ content: "```ts\nx\n```" }).querySelector(
16
+ "pre",
17
+ );
18
+ expect(pre?.classList.contains("cpk-markdown-code-block")).toBe(true);
19
+ expect(pre?.querySelector("code")?.getAttribute("data-code-info")).toBe(
20
+ "ts",
21
+ );
22
+ });
23
+
24
+ it("shows the caret only while streaming", () => {
25
+ const caret = "[data-streaming-markdown-caret]";
26
+ expect(
27
+ renderDefault({ content: "Hi", isStreaming: true }).querySelector(caret),
28
+ ).not.toBeNull();
29
+ expect(renderDefault({ content: "Hi" }).querySelector(caret)).toBeNull();
30
+ });
31
+
32
+ it("passes link and citation clicks to the callbacks", async () => {
33
+ const calls: string[] = [];
34
+ const container = renderDefault({
35
+ content:
36
+ "[link](https://example.com) cite [^ref]\n\n[^ref]: Ref https://example.org",
37
+ onLinkClick: (event: MouseEvent, url: string) => {
38
+ event.preventDefault();
39
+ calls.push(`link:${url}`);
40
+ },
41
+ onCitationClick: (event: MouseEvent, citation: { id: string }) => {
42
+ event.preventDefault();
43
+ calls.push(`cite:${citation.id}`);
44
+ },
45
+ });
46
+ await fireEvent.click(
47
+ container.querySelector('a[href="https://example.com"]')!,
48
+ );
49
+ await fireEvent.click(
50
+ container.querySelector('sup a[role="doc-noteref"]')!,
51
+ );
52
+ expect(calls).toEqual(["link:https://example.com", "cite:ref"]);
53
+ });
54
+ });
@@ -1,11 +1,8 @@
1
- export type AudioRecorderState = "idle" | "recording" | "processing";
1
+ import type { ɵAudioRecorderState } from "@copilotkit/core";
2
2
 
3
- export class AudioRecorderError extends Error {
4
- constructor(message: string) {
5
- super(message);
6
- this.name = "AudioRecorderError";
7
- }
8
- }
3
+ export type AudioRecorderState = ɵAudioRecorderState;
4
+
5
+ export { ɵAudioRecorderError as AudioRecorderError } from "@copilotkit/core";
9
6
 
10
7
  export interface CopilotChatAudioRecorderRef {
11
8
  state: AudioRecorderState;
@@ -1,95 +1,46 @@
1
- import type { Message, ToolCall, ToolMessage } from "@ag-ui/core";
2
- import { contentToText } from "@ag-ui/core";
3
- import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
4
- import { ToolCallStatus, ɵresolveToolCallRenderer } from "@copilotkit/core";
5
- import { partialJSONParse } from "@copilotkit/shared";
1
+ import type { Message, ToolCall } from "@ag-ui/core";
2
+ import {
3
+ ɵbuildToolCallRenderProps,
4
+ ɵfindToolMessage,
5
+ ɵresolveToolCallAgentId,
6
+ ɵresolveToolCallRenderer,
7
+ } from "@copilotkit/core";
6
8
  import type { CopilotChatToolCallRenderSlotProps } from "./types";
7
9
  import type {
8
10
  VueToolCallRenderer,
9
11
  VueToolCallRendererRenderProps,
10
12
  } from "../../types";
11
13
 
12
- export function findToolMessage(
13
- messages: Message[],
14
- toolCallId: string,
15
- ): ToolMessage | undefined {
16
- return messages.find(
17
- (message) =>
18
- message.role === "tool" &&
19
- (message as ToolMessage).toolCallId === toolCallId,
20
- ) as ToolMessage | undefined;
21
- }
22
-
23
14
  export function getToolCallSlotName(toolName: string): `tool-call-${string}` {
24
15
  return `tool-call-${toolName}`;
25
16
  }
26
17
 
18
+ /** Props for a `tool-call` / `tool-call-<name>` slot. */
27
19
  export function getToolCallRenderProps(
28
20
  toolCall: ToolCall,
29
21
  messages: Message[],
30
22
  executingToolCallIds: ReadonlySet<string>,
31
23
  ): CopilotChatToolCallRenderSlotProps {
32
- const toolMessage = findToolMessage(messages, toolCall.id);
33
- const parsedArgs = partialJSONParse(toolCall.function.arguments);
34
-
35
- if (toolMessage) {
36
- return {
37
- name: toolCall.function.name,
38
- args: parsedArgs,
39
- status: ToolCallStatus.Complete,
40
- result: contentToText(toolMessage.content),
41
- toolCall,
42
- toolMessage,
43
- };
44
- }
45
-
46
- const isExecuting = executingToolCallIds.has(toolCall.id);
47
- return {
48
- name: toolCall.function.name,
49
- args: parsedArgs,
50
- status: isExecuting ? ToolCallStatus.Executing : ToolCallStatus.InProgress,
51
- result: undefined,
24
+ const toolMessage = ɵfindToolMessage(messages, toolCall.id);
25
+ const { name, args, status, result } = ɵbuildToolCallRenderProps(
52
26
  toolCall,
53
- toolMessage: undefined,
54
- };
27
+ toolMessage,
28
+ { isExecuting: executingToolCallIds.has(toolCall.id) },
29
+ );
30
+ return { name, args, status, result, toolCall, toolMessage };
55
31
  }
56
32
 
33
+ /** Props for a registered renderer's `render`. */
57
34
  export function getCoreRenderProps(
58
35
  toolCall: ToolCall,
59
36
  messages: Message[],
60
37
  executingToolCallIds: ReadonlySet<string>,
61
38
  ): VueToolCallRendererRenderProps<unknown> {
62
- const toolMessage = findToolMessage(messages, toolCall.id);
63
- const parsedArgs = partialJSONParse(toolCall.function.arguments);
64
-
65
- if (toolMessage) {
66
- return {
67
- name: toolCall.function.name,
68
- toolCallId: toolCall.id,
69
- args: parsedArgs,
70
- status: ToolCallStatus.Complete,
71
- result: contentToText(toolMessage.content),
72
- };
73
- }
74
-
75
- const isExecuting = executingToolCallIds.has(toolCall.id);
76
- if (isExecuting) {
77
- return {
78
- name: toolCall.function.name,
79
- toolCallId: toolCall.id,
80
- args: parsedArgs,
81
- status: ToolCallStatus.Executing,
82
- result: undefined,
83
- };
84
- }
85
-
86
- return {
87
- name: toolCall.function.name,
88
- toolCallId: toolCall.id,
89
- args: parsedArgs && typeof parsedArgs === "object" ? parsedArgs : {},
90
- status: ToolCallStatus.InProgress,
91
- result: undefined,
92
- };
39
+ return ɵbuildToolCallRenderProps(
40
+ toolCall,
41
+ ɵfindToolMessage(messages, toolCall.id),
42
+ { isExecuting: executingToolCallIds.has(toolCall.id) },
43
+ );
93
44
  }
94
45
 
95
46
  export function getToolCallMemoDeps(
@@ -97,13 +48,13 @@ export function getToolCallMemoDeps(
97
48
  messages: Message[],
98
49
  executingToolCallIds: ReadonlySet<string>,
99
50
  renderToolCalls: readonly VueToolCallRenderer<unknown>[],
100
- agentId: string = DEFAULT_AGENT_ID,
51
+ agentId: string = ɵresolveToolCallAgentId(undefined, undefined),
101
52
  ): unknown[] {
102
53
  return [
103
54
  toolCall.id,
104
55
  toolCall.function.name,
105
56
  toolCall.function.arguments,
106
- findToolMessage(messages, toolCall.id)?.content,
57
+ ɵfindToolMessage(messages, toolCall.id)?.content,
107
58
  executingToolCallIds.has(toolCall.id),
108
59
  ɵresolveToolCallRenderer(renderToolCalls, toolCall.function.name, agentId)
109
60
  ?.render,
@@ -1,5 +1,8 @@
1
1
  import type { ComputedRef, InjectionKey } from "vue";
2
- import type { ɵToolCallDisplayContextValue } from "@copilotkit/core";
2
+ import type {
3
+ ɵToolCallDisplayContextValue,
4
+ ɵToolCallRowStatus,
5
+ } from "@copilotkit/core";
3
6
 
4
7
  /**
5
8
  * Provided by the message view so each message's tool calls follow the view's
@@ -18,6 +21,8 @@ export const ToolCallDisplayContextKey: InjectionKey<
18
21
  export interface ToolCallGroupContextValue {
19
22
  appearance: "card" | "line" | "stack";
20
23
  failedIds: ReadonlySet<string>;
24
+ /** Each call's row status, by call id. */
25
+ statusById: ReadonlyMap<string, ɵToolCallRowStatus>;
21
26
  /** The agent may still finish the group's calls; see `isActive`. */
22
27
  active: boolean;
23
28
  }
@@ -1,6 +1,8 @@
1
1
  import { defineComponent, h, onMounted, ref, watch } from "vue";
2
2
  import type { PropType } from "vue";
3
3
  import {
4
+ ɵTOOL_CALL_STATUS_ICONS,
5
+ ɵTOOL_CALL_STATUS_LABEL_KEYS,
4
6
  ɵcountFailedToolCalls,
5
7
  ɵformatLabel,
6
8
  ɵgetArgumentPreview,
@@ -11,13 +13,7 @@ import type {
11
13
  ToolCallEntry,
12
14
  ɵToolCallRowStatus,
13
15
  } from "@copilotkit/core";
14
- import {
15
- IconCheck,
16
- IconChevronRight,
17
- IconCircle,
18
- IconLoader2,
19
- IconX,
20
- } from "../icons";
16
+ import { CopilotIcon, IconChevronRight } from "../icons";
21
17
  import { useCopilotId } from "../ui/use-id";
22
18
  import { useChatLabels } from "./use-chat-labels";
23
19
 
@@ -29,16 +25,7 @@ export function toolCallStatusLabel(
29
25
  labels: Readonly<CopilotChatLabels>,
30
26
  status: ɵToolCallRowStatus,
31
27
  ) {
32
- switch (status) {
33
- case "running":
34
- return labels.toolCallStatusRunningLabel;
35
- case "done":
36
- return labels.toolCallStatusDoneLabel;
37
- case "failed":
38
- return labels.toolCallStatusFailedLabel;
39
- case "stopped":
40
- return labels.toolCallStatusStoppedLabel;
41
- }
28
+ return labels[ɵTOOL_CALL_STATUS_LABEL_KEYS[status]];
42
29
  }
43
30
 
44
31
  /**
@@ -83,22 +70,15 @@ export const ToolCallStatusIcon = defineComponent({
83
70
  },
84
71
  setup(props) {
85
72
  return () =>
86
- h(
87
- {
88
- running: IconLoader2,
89
- done: IconCheck,
90
- failed: IconX,
91
- stopped: IconCircle,
92
- }[props.status],
93
- {
94
- "aria-hidden": "true",
95
- class: [
96
- "cpk-tool-call-status-icon",
97
- props.status === "running" && "cpk-tool-call-status-icon-running",
98
- props.status === "failed" && "cpk-tool-call-status-icon-failed",
99
- ],
100
- },
101
- );
73
+ h(CopilotIcon, {
74
+ name: ɵTOOL_CALL_STATUS_ICONS[props.status],
75
+ "aria-hidden": "true",
76
+ class: [
77
+ "cpk-tool-call-status-icon",
78
+ props.status === "running" && "cpk-tool-call-status-icon-running",
79
+ props.status === "failed" && "cpk-tool-call-status-icon-failed",
80
+ ],
81
+ });
102
82
  },
103
83
  });
104
84
 
@@ -21,7 +21,7 @@ import type {
21
21
  ToolCallEntry,
22
22
  } from "@copilotkit/core";
23
23
  import type { InterruptEvent } from "../../types";
24
- import type { AutoScrollMode } from "./normalize-auto-scroll";
24
+ import type { ɵAutoScrollMode as AutoScrollMode } from "@copilotkit/core";
25
25
 
26
26
  export type CopilotChatInputMode = "input" | "transcribe" | "processing";
27
27
  export type { Attachment, AttachmentsConfig, AttachmentModality };
@@ -1,12 +1,7 @@
1
1
  import { nextTick, ref } from "vue";
2
+ import { ɵmorphViewTransition } from "@copilotkit/core";
2
3
  import { useCopilotId } from "../ui/use-id";
3
4
 
4
- type DocWithVT = Document & {
5
- startViewTransition?: (cb: () => void | Promise<void>) => {
6
- finished: Promise<void>;
7
- };
8
- };
9
-
10
5
  /**
11
6
  * Opens and closes a lightbox, using the View Transition API to morph the
12
7
  * thumbnail into the full-screen content, as React's `useLightbox`.
@@ -24,40 +19,27 @@ export function useLightbox() {
24
19
  const vtName = useCopilotId("cpk-lightbox").replace(/[^\w-]/g, "-");
25
20
 
26
21
  const openLightbox = () => {
27
- const thumb = thumbnailRef.value;
28
- const doc =
29
- typeof document === "undefined" ? undefined : (document as DocWithVT);
30
-
31
- if (doc?.startViewTransition && thumb) {
32
- thumb.style.viewTransitionName = vtName;
33
- doc.startViewTransition(async () => {
34
- thumb.style.viewTransitionName = "";
22
+ ɵmorphViewTransition(
23
+ thumbnailRef.value,
24
+ vtName,
25
+ async () => {
35
26
  open.value = true;
36
27
  await nextTick();
37
- });
38
- } else {
39
- open.value = true;
40
- }
28
+ },
29
+ "open",
30
+ );
41
31
  };
42
32
 
43
33
  const closeLightbox = () => {
44
- const thumb = thumbnailRef.value;
45
- const doc =
46
- typeof document === "undefined" ? undefined : (document as DocWithVT);
47
-
48
- if (doc?.startViewTransition && thumb) {
49
- const transition = doc.startViewTransition(async () => {
34
+ ɵmorphViewTransition(
35
+ thumbnailRef.value,
36
+ vtName,
37
+ async () => {
50
38
  open.value = false;
51
39
  await nextTick();
52
- thumb.style.viewTransitionName = vtName;
53
- });
54
- const clear = () => {
55
- thumb.style.viewTransitionName = "";
56
- };
57
- transition.finished.then(clear, clear);
58
- } else {
59
- open.value = false;
60
- }
40
+ },
41
+ "close",
42
+ );
61
43
  };
62
44
 
63
45
  return { thumbnailRef, vtName, open, openLightbox, closeLightbox };
@@ -31,10 +31,10 @@ describe("useAgent error state", () => {
31
31
 
32
32
  const TestComponent = defineComponent({
33
33
  setup() {
34
- const { agent } = useAgent({ agentId: "nonexistent" });
35
- return { agent };
34
+ const { agent, isReady } = useAgent({ agentId: "nonexistent" });
35
+ return { agent, isReady };
36
36
  },
37
- template: `<div data-testid="agent-id">{{ agent.agentId }}</div>`,
37
+ template: `<div data-testid="agent-id">{{ agent.agentId }}</div><div data-testid="ready">{{ isReady }}</div>`,
38
38
  });
39
39
 
40
40
  const Host = defineComponent({
@@ -59,6 +59,8 @@ describe("useAgent error state", () => {
59
59
  // Should render without crashing — agent is provisional
60
60
  const el = await screen.findByTestId("agent-id");
61
61
  expect(el.textContent).toBe("nonexistent");
62
+ // A provisional stand-in is not the real agent.
63
+ expect(screen.getByTestId("ready").textContent).toBe("false");
62
64
  expect(capturedErrors).toEqual([]);
63
65
  });
64
66
  });
@@ -6,7 +6,23 @@ import type { AbstractAgent } from "@ag-ui/client";
6
6
  import { useCopilotKit } from "../../providers/useCopilotKit";
7
7
  import { MockStepwiseAgent } from "../../__tests__/utils/test-helpers";
8
8
  import { useAgent } from "../use-agent";
9
- import { CopilotKitCoreRuntimeConnectionStatus } from "@copilotkit/core";
9
+ import {
10
+ CopilotKitCoreRuntimeConnectionStatus,
11
+ ProxiedCopilotRuntimeAgent,
12
+ } from "@copilotkit/core";
13
+
14
+ /** Core's `ɵruntimeInfo` snapshot, built from a fake core's runtime fields. */
15
+ function runtimeInfoOf(core: {
16
+ runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus;
17
+ threadEndpoints?: unknown;
18
+ intelligence?: unknown;
19
+ }) {
20
+ return {
21
+ status: core.runtimeConnectionStatus,
22
+ threadEndpoints: core.threadEndpoints,
23
+ intelligence: core.intelligence,
24
+ };
25
+ }
10
26
 
11
27
  // Mock the CopilotKitProvider to control copilotkit state directly
12
28
  vi.mock("../../providers/useCopilotKit", () => ({
@@ -26,6 +42,7 @@ describe("useAgent stability during runtime connection", () => {
26
42
  type MockCopilotkit = {
27
43
  getAgent: ReturnType<typeof vi.fn>;
28
44
  runtimeUrl: string | undefined;
45
+ readonly ɵruntimeInfo: unknown;
29
46
  runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus;
30
47
  runtimeTransport: string;
31
48
  headers: Record<string, string>;
@@ -45,6 +62,9 @@ describe("useAgent stability during runtime connection", () => {
45
62
  copilotkitRef = ref({
46
63
  getAgent: vi.fn(() => undefined),
47
64
  runtimeUrl: "http://localhost:3000/api/copilotkit",
65
+ get ɵruntimeInfo() {
66
+ return runtimeInfoOf(this);
67
+ },
48
68
  runtimeConnectionStatus:
49
69
  CopilotKitCoreRuntimeConnectionStatus.Disconnected,
50
70
  runtimeTransport: "rest",
@@ -198,7 +218,6 @@ describe("useAgent stability during runtime connection", () => {
198
218
  // Phase 1: Disconnected — provisional created
199
219
  const { rerender } = render(AgentTracker, { props: { tick: 0 } });
200
220
  await nextTick();
201
- const provisionalThreadId = agentInstances[0]!.threadId;
202
221
 
203
222
  // Phase 2: Connecting
204
223
  mockCopilotkit.runtimeConnectionStatus =
@@ -232,8 +251,10 @@ describe("useAgent stability during runtime connection", () => {
232
251
  // CopilotChat's connect guard ensures connect fires only once (when Connected),
233
252
  // so the agent reference change doesn't cause a duplicate connect.
234
253
  expect(agentInstances.length).toBe(2);
235
- // First instance was the provisional
236
- expect(agentInstances[0]!.threadId).toBe(provisionalThreadId);
254
+ // First instance was the provisional. The hand-off then syncs it with
255
+ // the registered agent, so its threadId follows the registered one.
256
+ expect(agentInstances[0]).toBeInstanceOf(ProxiedCopilotRuntimeAgent);
257
+ expect(agentInstances[0]!.threadId).toBe(registeredAgent.threadId);
237
258
  // Second instance is the registered agent
238
259
  expect(agentInstances[1]).toBe(registeredAgent);
239
260
  });
@@ -541,6 +541,8 @@ describe("useAgent throttleMs", () => {
541
541
  it("single notification within window does not trigger a trailing re-render", async () => {
542
542
  const renderCount = { current: 0 };
543
543
  mountHookComponent({ agent, throttleMs: 100, renderCount });
544
+ // The provider connects on mount, and the status change re-renders once.
545
+ await flushVue();
544
546
  const afterMount = renderCount.current;
545
547
  agent.setMessages([userMsg("1", "a")]);
546
548
  agent.notifyMessagesChanged();
@@ -692,8 +694,10 @@ describe("useAgent defaultThrottleMs from provider", () => {
692
694
  agent,
693
695
  defaultThrottleMs: value,
694
696
  });
697
+ // Core validates the value (the provider no longer logs its own copy).
695
698
  expect(errorSpy).toHaveBeenCalledWith(
696
- expect.stringContaining("CopilotKitProvider: defaultThrottleMs"),
699
+ expect.stringContaining("CopilotKitCore.setDefaultThrottleMs"),
700
+ expect.any(Error),
697
701
  );
698
702
  agent.setMessages([userMsg("1", "a")]);
699
703
  agent.notifyMessagesChanged();
@@ -21,13 +21,15 @@ describe("useAgent", () => {
21
21
 
22
22
  const Child = defineComponent({
23
23
  setup() {
24
- const { agent } = useAgent({ agentId: "test-agent" });
25
- return () =>
24
+ const { agent, isReady } = useAgent({ agentId: "test-agent" });
25
+ return () => [
26
26
  h(
27
27
  "span",
28
28
  { "data-testid": "agent-id" },
29
29
  agent.value?.agentId ?? "none",
30
- );
30
+ ),
31
+ h("span", { "data-testid": "ready" }, String(isReady.value)),
32
+ ];
31
33
  },
32
34
  });
33
35
 
@@ -38,6 +40,7 @@ describe("useAgent", () => {
38
40
  });
39
41
 
40
42
  expect(wrapper.find("[data-testid=agent-id]").text()).toBe("test-agent");
43
+ expect(wrapper.find("[data-testid=ready]").text()).toBe("true");
41
44
  });
42
45
 
43
46
  it("uses agentId from chat configuration when no agentId prop is provided", () => {
@@ -106,6 +106,27 @@ describe("useComponent", () => {
106
106
  expect(forwardedDeps).toBe(deps);
107
107
  });
108
108
 
109
+ it("forwards followUp, as React and Angular do", () => {
110
+ const Harness = defineComponent({
111
+ setup() {
112
+ useComponent({
113
+ name: "showWeatherCard",
114
+ render: defineComponent({ template: `<div />` }),
115
+ followUp: false,
116
+ });
117
+ return {};
118
+ },
119
+ template: `<div />`,
120
+ });
121
+
122
+ render(Harness);
123
+
124
+ const [toolConfig] = mockUseFrontendTool.mock.calls[0] as [
125
+ { followUp?: boolean },
126
+ ];
127
+ expect(toolConfig.followUp).toBe(false);
128
+ });
129
+
109
130
  it("creates a render function that passes args into the component", () => {
110
131
  const DemoComponent = defineComponent({
111
132
  props: {