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

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 (241) hide show
  1. package/PARITY.md +3 -3
  2. package/dist/hooks/use-copilot-action.d.ts.map +1 -1
  3. package/dist/hooks/use-frontend-tool.d.ts +1 -1
  4. package/dist/hooks/use-frontend-tool.d.ts.map +1 -1
  5. package/dist/hooks/zod-parameters.d.ts +14 -0
  6. package/dist/hooks/zod-parameters.d.ts.map +1 -0
  7. package/dist/index.cjs +1 -73
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.mjs +353 -16908
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/use-render-activity-message-CsWYyd6r.cjs +91 -0
  12. package/dist/use-render-activity-message-CsWYyd6r.cjs.map +1 -0
  13. package/dist/use-render-activity-message-DntKKNdd.js +17418 -0
  14. package/dist/use-render-activity-message-DntKKNdd.js.map +1 -0
  15. package/dist/v2/components/InlineFeatureWarning.vue.d.ts.map +1 -1
  16. package/dist/v2/components/LicenseWarningBanner.vue.d.ts +2 -2
  17. package/dist/v2/components/LicenseWarningBanner.vue.d.ts.map +1 -1
  18. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts +13 -45
  19. package/dist/v2/components/OpenGenerativeUIRenderer.d.ts.map +1 -1
  20. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts +1 -1
  21. package/dist/v2/components/a2ui/A2UICatalogContext.d.ts.map +1 -1
  22. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +13 -5
  23. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -1
  24. package/dist/v2/components/a2ui/catalog.d.ts.map +1 -1
  25. package/dist/v2/components/a2ui/utils.d.ts.map +1 -1
  26. package/dist/v2/components/a2ui.d.ts +3 -7
  27. package/dist/v2/components/a2ui.d.ts.map +1 -1
  28. package/dist/v2/components/chat/CopilotChat.vue.d.ts +2 -2
  29. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  30. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
  31. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  32. package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
  33. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts +1 -1
  34. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts.map +1 -1
  35. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +6 -270
  36. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  37. package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts.map +1 -1
  38. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +1 -1
  39. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  40. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  41. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +1 -1
  42. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
  43. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  44. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +1 -1
  45. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  46. package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts.map +1 -1
  47. package/dist/v2/components/chat/CopilotChatView.vue.d.ts +2 -2
  48. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  49. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -1
  50. package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -1
  51. package/dist/v2/components/chat/CopilotPopup.vue.d.ts +8 -3
  52. package/dist/v2/components/chat/CopilotPopup.vue.d.ts.map +1 -1
  53. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts +5 -3
  54. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts.map +1 -1
  55. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts +5 -3
  56. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  57. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts +8 -3
  58. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts.map +1 -1
  59. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +5 -3
  60. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts.map +1 -1
  61. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +5 -3
  62. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
  63. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +0 -1
  64. package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
  65. package/dist/v2/components/chat/forwarded-slots.d.ts +12 -0
  66. package/dist/v2/components/chat/forwarded-slots.d.ts.map +1 -0
  67. package/dist/v2/components/chat/index.d.ts +28 -25
  68. package/dist/v2/components/chat/index.d.ts.map +1 -1
  69. package/dist/v2/components/chat/inspector-shortcuts.d.ts +2 -1
  70. package/dist/v2/components/chat/inspector-shortcuts.d.ts.map +1 -1
  71. package/dist/v2/components/chat/message-feedback.d.ts +18 -49
  72. package/dist/v2/components/chat/message-feedback.d.ts.map +1 -1
  73. package/dist/v2/components/chat/normalize-auto-scroll.d.ts +2 -2
  74. package/dist/v2/components/chat/normalize-auto-scroll.d.ts.map +1 -1
  75. package/dist/v2/components/chat/tool-call-display-context.d.ts +2 -19
  76. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
  77. package/dist/v2/components/chat/tool-call-group-parts.d.ts +7 -20
  78. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
  79. package/dist/v2/components/chat/turn-work.d.ts +0 -2
  80. package/dist/v2/components/chat/turn-work.d.ts.map +1 -1
  81. package/dist/v2/components/chat/types.d.ts +8 -3
  82. package/dist/v2/components/chat/types.d.ts.map +1 -1
  83. package/dist/v2/components/chat/use-chat-labels.d.ts +8 -0
  84. package/dist/v2/components/chat/use-chat-labels.d.ts.map +1 -0
  85. package/dist/v2/components/chat/use-copy-feedback.d.ts +11 -0
  86. package/dist/v2/components/chat/use-copy-feedback.d.ts.map +1 -0
  87. package/dist/v2/components/icons/index.d.ts +968 -1
  88. package/dist/v2/components/icons/index.d.ts.map +1 -1
  89. package/dist/v2/hooks/use-attachments.d.ts.map +1 -1
  90. package/dist/v2/hooks/use-frontend-tool.d.ts +5 -0
  91. package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -1
  92. package/dist/v2/hooks/use-keyboard-height.d.ts +1 -1
  93. package/dist/v2/hooks/use-keyboard-height.d.ts.map +1 -1
  94. package/dist/v2/hooks/use-pin-to-send.d.ts +4 -12
  95. package/dist/v2/hooks/use-pin-to-send.d.ts.map +1 -1
  96. package/dist/v2/hooks/use-render-activity-message.d.ts +13 -9
  97. package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
  98. package/dist/v2/hooks/use-render-custom-messages.d.ts +5 -7
  99. package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
  100. package/dist/v2/index.cjs +1 -1
  101. package/dist/v2/index.mjs +197 -200
  102. package/dist/v2/index.mjs.map +1 -1
  103. package/dist/v2/lib/is-mobile-viewport.d.ts +1 -14
  104. package/dist/v2/lib/is-mobile-viewport.d.ts.map +1 -1
  105. package/dist/v2/lib/processPartialHtml.d.ts +1 -2
  106. package/dist/v2/lib/processPartialHtml.d.ts.map +1 -1
  107. package/dist/v2/lib/shallow-stable.d.ts.map +1 -1
  108. package/dist/v2/lib/vue-core.d.ts +0 -1
  109. package/dist/v2/lib/vue-core.d.ts.map +1 -1
  110. package/dist/v2/providers/CopilotKitProvider.types.d.ts +0 -5
  111. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  112. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +1 -1
  113. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  114. package/dist/v2/providers/markdown-renderer.d.ts +2 -2
  115. package/dist/v2/providers/markdown-renderer.d.ts.map +1 -1
  116. package/dist/v2/providers/types.d.ts +3 -46
  117. package/dist/v2/providers/types.d.ts.map +1 -1
  118. package/dist/v2/types/human-in-the-loop.d.ts +8 -29
  119. package/dist/v2/types/human-in-the-loop.d.ts.map +1 -1
  120. package/dist/v2/types/interrupt.d.ts +1 -22
  121. package/dist/v2/types/interrupt.d.ts.map +1 -1
  122. package/dist/v2/types/vue-activity-message-renderer.d.ts +16 -15
  123. package/dist/v2/types/vue-activity-message-renderer.d.ts.map +1 -1
  124. package/dist/v2/types/vue-custom-message-renderer.d.ts +7 -16
  125. package/dist/v2/types/vue-custom-message-renderer.d.ts.map +1 -1
  126. package/dist/v2/types/vue-tool-call-renderer.d.ts +16 -35
  127. package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
  128. package/package.json +10 -11
  129. package/src/__tests__/build-entries.test.ts +27 -0
  130. package/src/hooks/__tests__/zod-parameters.test.ts +520 -0
  131. package/src/hooks/use-copilot-action.ts +2 -1
  132. package/src/hooks/use-frontend-tool.ts +3 -6
  133. package/src/hooks/zod-parameters.ts +162 -0
  134. package/src/v2/components/A2UIMessageRenderer.ts +3 -3
  135. package/src/v2/components/InlineFeatureWarning.vue +20 -21
  136. package/src/v2/components/LicenseWarningBanner.vue +27 -135
  137. package/src/v2/components/OpenGenerativeUIRenderer.ts +51 -373
  138. package/src/v2/components/__tests__/A2UICatalogContext.test.ts +1 -1
  139. package/src/v2/components/__tests__/LicenseWarningBanner.test.ts +60 -0
  140. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.ts +55 -6
  141. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +1 -1
  142. package/src/v2/components/a2ui/A2UICatalogContext.ts +10 -102
  143. package/src/v2/components/a2ui/A2UIRecoveryStates.ts +1 -1
  144. package/src/v2/components/a2ui/catalog.ts +1 -0
  145. package/src/v2/components/a2ui/utils.ts +2 -0
  146. package/src/v2/components/a2ui.ts +1 -1
  147. package/src/v2/components/chat/CopilotChat.vue +19 -40
  148. package/src/v2/components/chat/CopilotChatAssistantMessage.vue +19 -43
  149. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +14 -10
  150. package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +14 -6
  151. package/src/v2/components/chat/CopilotChatFeedbackCommentPopover.vue +2 -2
  152. package/src/v2/components/chat/CopilotChatInput.vue +40 -30
  153. package/src/v2/components/chat/CopilotChatInspectorButton.vue +45 -108
  154. package/src/v2/components/chat/CopilotChatMessageView.vue +72 -140
  155. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +7 -15
  156. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +60 -37
  157. package/src/v2/components/chat/CopilotChatToolCallItem.vue +3 -3
  158. package/src/v2/components/chat/CopilotChatToolCallsView.vue +15 -13
  159. package/src/v2/components/chat/CopilotChatUserMessage.vue +24 -112
  160. package/src/v2/components/chat/CopilotChatView.vue +5 -5
  161. package/src/v2/components/chat/CopilotChatWorkSummary.vue +26 -36
  162. package/src/v2/components/chat/CopilotModalHeader.vue +11 -23
  163. package/src/v2/components/chat/CopilotPopup.vue +23 -25
  164. package/src/v2/components/chat/CopilotPopupView.vue +10 -39
  165. package/src/v2/components/chat/CopilotPopupViewInternal.vue +14 -34
  166. package/src/v2/components/chat/CopilotSidebar.vue +23 -25
  167. package/src/v2/components/chat/CopilotSidebarView.vue +10 -39
  168. package/src/v2/components/chat/CopilotSidebarViewInternal.vue +14 -34
  169. package/src/v2/components/chat/StreamingMarkdownDefault.vue +4 -4
  170. package/src/v2/components/chat/__tests__/CopilotChat.labels.test.ts +19 -4
  171. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.ts +26 -9
  172. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.learningFeedback.test.ts +5 -5
  173. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +33 -1
  174. package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.ts +247 -403
  175. package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +1 -99
  176. package/src/v2/components/chat/__tests__/CopilotPopup.test.ts +90 -1
  177. package/src/v2/components/chat/__tests__/CopilotSidebar.test.ts +90 -1
  178. package/src/v2/components/chat/copilotChatToolCallItemRender.ts +3 -19
  179. package/src/v2/components/chat/forwarded-slots.ts +17 -0
  180. package/src/v2/components/chat/index.ts +0 -1
  181. package/src/v2/components/chat/inspector-shortcuts.ts +10 -16
  182. package/src/v2/components/chat/message-feedback.ts +62 -197
  183. package/src/v2/components/chat/normalize-auto-scroll.ts +3 -17
  184. package/src/v2/components/chat/tool-call-display-context.ts +2 -23
  185. package/src/v2/components/chat/tool-call-group-parts.ts +44 -85
  186. package/src/v2/components/chat/turn-work.ts +0 -9
  187. package/src/v2/components/chat/types.ts +12 -4
  188. package/src/v2/components/chat/use-chat-labels.ts +16 -0
  189. package/src/v2/components/chat/use-copy-feedback.ts +21 -0
  190. package/src/v2/components/icons/__tests__/icons.test.ts +72 -68
  191. package/src/v2/components/icons/index.ts +109 -27
  192. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +62 -1
  193. package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +105 -0
  194. package/src/v2/hooks/__tests__/use-render-tool.test.ts +3 -6
  195. package/src/v2/hooks/__tests__/zod-regression.test.ts +5 -4
  196. package/src/v2/hooks/use-attachments.ts +6 -2
  197. package/src/v2/hooks/use-frontend-tool.ts +10 -10
  198. package/src/v2/hooks/use-keyboard-height.ts +17 -40
  199. package/src/v2/hooks/use-pin-to-send.ts +15 -99
  200. package/src/v2/hooks/use-render-activity-message.ts +27 -48
  201. package/src/v2/hooks/use-render-custom-messages.ts +14 -35
  202. package/src/v2/lib/__tests__/shallow-stable.test.ts +54 -0
  203. package/src/v2/lib/is-mobile-viewport.ts +5 -23
  204. package/src/v2/lib/processPartialHtml.ts +6 -21
  205. package/src/v2/lib/shallow-stable.ts +2 -16
  206. package/src/v2/lib/vue-core.ts +9 -13
  207. package/src/v2/providers/CopilotKitProvider.types.ts +0 -5
  208. package/src/v2/providers/CopilotKitProvider.vue +53 -112
  209. package/src/v2/providers/__tests__/CopilotKitProvider.a2ui.test.ts +1 -1
  210. package/src/v2/providers/__tests__/CopilotKitProvider.headersAtSendTime.test.ts +3 -3
  211. package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +3 -1
  212. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +17 -33
  213. package/src/v2/providers/markdown-renderer.ts +2 -2
  214. package/src/v2/providers/types.ts +3 -49
  215. package/src/v2/types/__tests__/defineToolCallRenderer.test.ts +28 -33
  216. package/src/v2/types/defineToolCallRenderer.ts +5 -4
  217. package/src/v2/types/human-in-the-loop.ts +8 -32
  218. package/src/v2/types/interrupt.ts +7 -31
  219. package/src/v2/types/vue-activity-message-renderer.ts +24 -15
  220. package/src/v2/types/vue-custom-message-renderer.ts +12 -16
  221. package/src/v2/types/vue-tool-call-renderer.ts +23 -38
  222. package/tsconfig.json +0 -1
  223. package/vite.config.ts +3 -4
  224. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts +0 -9
  225. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +0 -1
  226. package/dist/v2/components/chat/tool-call-layout.d.ts +0 -115
  227. package/dist/v2/components/chat/tool-call-layout.d.ts.map +0 -1
  228. package/dist/v2/lib/message-feedback.d.ts +0 -33
  229. package/dist/v2/lib/message-feedback.d.ts.map +0 -1
  230. package/dist/v2/lib/transcription-client.d.ts +0 -19
  231. package/dist/v2/lib/transcription-client.d.ts.map +0 -1
  232. package/src/v2/components/chat/BasicMarkdown.vue +0 -160
  233. package/src/v2/components/chat/__tests__/BasicMarkdown.test.ts +0 -69
  234. package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +0 -39
  235. package/src/v2/components/chat/tool-call-layout.ts +0 -304
  236. package/src/v2/lib/__tests__/is-mobile-viewport.test.ts +0 -48
  237. package/src/v2/lib/__tests__/message-feedback.test.ts +0 -152
  238. package/src/v2/lib/__tests__/processPartialHtml.test.ts +0 -84
  239. package/src/v2/lib/__tests__/transcription-client.test.ts +0 -67
  240. package/src/v2/lib/message-feedback.ts +0 -120
  241. package/src/v2/lib/transcription-client.ts +0 -151
@@ -0,0 +1,21 @@
1
+ import { onBeforeUnmount, readonly, ref } from "vue";
2
+ import { ɵCopyFeedback } from "@copilotkit/core";
3
+
4
+ /**
5
+ * The "copied" check mark of a copy button, from core's `ɵCopyFeedback`:
6
+ * `copy(text)` writes to the clipboard and shows the check mark for two
7
+ * seconds; a second copy restarts the wait.
8
+ * @internal
9
+ */
10
+ export function useCopyFeedback() {
11
+ const feedback = new ɵCopyFeedback();
12
+ const copied = ref(feedback.getSnapshot());
13
+ const unsubscribe = feedback.subscribe(() => {
14
+ copied.value = feedback.getSnapshot();
15
+ });
16
+ onBeforeUnmount(() => {
17
+ unsubscribe();
18
+ feedback.dispose();
19
+ });
20
+ return { copied: readonly(copied), copy: feedback.copy };
21
+ }
@@ -1,86 +1,90 @@
1
1
  import { describe, expect, it } from "vitest";
2
+ import { h } from "vue";
2
3
  import { mount } from "@vue/test-utils";
3
- import {
4
- IconArrowUp,
5
- IconCheck,
6
- IconCheckCircle,
7
- IconChevronDown,
8
- IconChevronLeft,
9
- IconChevronRight,
10
- IconCircle,
11
- IconCopy,
12
- IconEdit,
13
- IconLoader2,
14
- IconMessageCircle,
15
- IconMic,
16
- IconPlus,
17
- IconRefreshCw,
18
- IconSquare,
19
- IconThumbsDown,
20
- IconThumbsUp,
21
- IconVolume2,
22
- IconX,
23
- } from "../index";
4
+ import { CopilotIcon, IconCircle, IconPlay, IconX } from "../index";
24
5
 
25
- describe("icons adapter", () => {
26
- it("exports all expected icon aliases", () => {
27
- const icons = [
28
- IconMessageCircle,
29
- IconX,
30
- IconChevronDown,
31
- IconPlus,
32
- IconMic,
33
- IconArrowUp,
34
- IconCheck,
35
- IconSquare,
36
- IconLoader2,
37
- IconCopy,
38
- IconEdit,
39
- IconChevronLeft,
40
- IconChevronRight,
41
- IconThumbsUp,
42
- IconThumbsDown,
43
- IconVolume2,
44
- IconRefreshCw,
45
- IconCheckCircle,
46
- IconCircle,
47
- ];
6
+ describe("CopilotIcon", () => {
7
+ it("draws the named icon with Lucide's classes and default attributes", () => {
8
+ const svg = mount(CopilotIcon, { props: { name: "X" } }).find("svg");
48
9
 
49
- for (const icon of icons) {
50
- expect(icon).toBeDefined();
51
- }
10
+ expect(svg.classes()).toEqual(["lucide", "lucide-x"]);
11
+ expect(svg.attributes()).toMatchObject({
12
+ xmlns: "http://www.w3.org/2000/svg",
13
+ width: "24",
14
+ height: "24",
15
+ viewBox: "0 0 24 24",
16
+ fill: "none",
17
+ stroke: "currentColor",
18
+ "stroke-width": "2",
19
+ "stroke-linecap": "round",
20
+ "stroke-linejoin": "round",
21
+ });
22
+ expect(svg.findAll("path").map((path) => path.attributes("d"))).toEqual([
23
+ "M18 6 6 18",
24
+ "m6 6 12 12",
25
+ ]);
52
26
  });
53
27
 
54
- it("renders svg icons from aliases", () => {
55
- const xWrapper = mount(IconX);
56
- const loaderWrapper = mount(IconLoader2);
28
+ it("draws circle and polygon nodes", () => {
29
+ const circle = mount(IconCircle).find("svg").find("circle");
30
+ const polygon = mount(IconPlay).find("svg").find("polygon");
57
31
 
58
- expect(xWrapper.find("svg").exists()).toBe(true);
59
- expect(loaderWrapper.find("svg").exists()).toBe(true);
32
+ expect(circle.attributes()).toMatchObject({ cx: "12", cy: "12", r: "10" });
33
+ expect(polygon.attributes("points")).toBe("6 3 20 12 6 21 6 3");
60
34
  });
61
35
 
62
- it("passes through classes, size, style, and accessibility attributes", () => {
63
- const wrapper = mount(IconX, {
36
+ it("applies size, color and stroke-width", () => {
37
+ const svg = mount(IconX, {
38
+ props: { size: 18, color: "red", strokeWidth: 1.75 },
39
+ }).find("svg");
40
+
41
+ expect(svg.attributes()).toMatchObject({
42
+ width: "18",
43
+ height: "18",
44
+ stroke: "red",
45
+ "stroke-width": "1.75",
46
+ });
47
+ });
48
+
49
+ it("passes other attributes to the svg after the Lucide classes", () => {
50
+ const svg = mount(IconX, {
64
51
  attrs: {
65
52
  class: "icon-class",
66
- width: "18",
67
- height: "20",
68
- "stroke-width": "1.5",
69
- role: "img",
70
- "aria-hidden": "true",
53
+ fill: "currentColor",
54
+ "data-testid": "close-icon",
71
55
  style: "color: rgb(255, 0, 0);",
72
56
  },
73
- });
57
+ }).find("svg");
58
+
59
+ expect(svg.classes()).toEqual(["lucide", "lucide-x", "icon-class"]);
60
+ expect(svg.attributes("fill")).toBe("currentColor");
61
+ expect(svg.attributes("data-testid")).toBe("close-icon");
62
+ expect(svg.attributes("style")).toContain("color: rgb(255, 0, 0)");
63
+ });
74
64
 
75
- const svg = wrapper.find("svg");
65
+ it("hides an unnamed icon from screen readers", () => {
66
+ const svg = mount(IconX).find("svg");
76
67
 
77
- expect(svg.exists()).toBe(true);
78
- expect(svg.classes()).toContain("icon-class");
79
- expect(svg.attributes("width")).toBe("18");
80
- expect(svg.attributes("height")).toBe("20");
81
- expect(svg.attributes("stroke-width")).toBe("1.5");
82
- expect(svg.attributes("role")).toBe("img");
83
68
  expect(svg.attributes("aria-hidden")).toBe("true");
84
- expect(svg.attributes("style")).toContain("color");
69
+ });
70
+
71
+ it("keeps a named icon visible to screen readers", () => {
72
+ const labelled = mount(IconX, { attrs: { "aria-label": "Close" } }).find(
73
+ "svg",
74
+ );
75
+ const withRole = mount(IconX, { attrs: { role: "img" } }).find("svg");
76
+
77
+ expect(labelled.attributes("aria-hidden")).toBeUndefined();
78
+ expect(labelled.attributes("aria-label")).toBe("Close");
79
+ expect(withRole.attributes("aria-hidden")).toBeUndefined();
80
+ });
81
+
82
+ it("keeps an icon with slot content visible to screen readers", () => {
83
+ const svg = mount(IconX, {
84
+ slots: { default: () => h("title", "Close") },
85
+ }).find("svg");
86
+
87
+ expect(svg.attributes("aria-hidden")).toBeUndefined();
88
+ expect(svg.find("title").text()).toBe("Close");
85
89
  });
86
90
  });
@@ -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
  */
@@ -134,7 +134,7 @@ describe("useRenderTool Zod regression", () => {
134
134
  const schema = z.object({
135
135
  action: z.discriminatedUnion("type", [
136
136
  z.object({ type: z.literal("search"), query: z.string() }),
137
- z.object({ type: z.literal("navigate"), url: z.string().url() }),
137
+ z.object({ type: z.literal("navigate"), url: z.url() }),
138
138
  ]),
139
139
  });
140
140
 
@@ -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", () => {
@@ -240,15 +240,19 @@ export function useAttachments(
240
240
  await processFiles(files);
241
241
  }
242
242
 
243
+ function onPaste(event: ClipboardEvent) {
244
+ void handlePaste(event);
245
+ }
246
+
243
247
  onMounted(() => {
244
248
  if (typeof document !== "undefined") {
245
- document.addEventListener("paste", handlePaste);
249
+ document.addEventListener("paste", onPaste);
246
250
  }
247
251
  });
248
252
 
249
253
  onBeforeUnmount(() => {
250
254
  if (typeof document !== "undefined") {
251
- document.removeEventListener("paste", handlePaste);
255
+ document.removeEventListener("paste", onPaste);
252
256
  }
253
257
  });
254
258
 
@@ -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
  );