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

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 (239) hide show
  1. package/PARITY.md +13 -12
  2. package/README.md +1 -2
  3. package/dist/components/copilot-provider/CopilotKit.vue.d.ts +3 -1
  4. package/dist/components/copilot-provider/CopilotKit.vue.d.ts.map +1 -1
  5. package/dist/index.cjs +43 -46
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.mjs +10170 -7506
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/v2/components/A2UIMessageRenderer.d.ts +7 -0
  10. package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
  11. package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
  12. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +11 -2
  13. package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
  14. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +136 -0
  15. package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -0
  16. package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +1 -1
  17. package/dist/v2/components/chat/CopilotChat.vue.d.ts +1 -0
  18. package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
  19. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +3 -1
  20. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
  21. package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts +16 -0
  22. package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts.map +1 -0
  23. package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
  24. package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
  25. package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
  26. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts +22 -0
  27. package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts.map +1 -0
  28. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +69 -16
  29. package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
  30. package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts +12 -0
  31. package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts.map +1 -0
  32. package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts +22 -0
  33. package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -0
  34. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +20 -0
  35. package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
  36. package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
  37. package/dist/v2/components/chat/CopilotChatSuggestionPill.vue.d.ts.map +1 -1
  38. package/dist/v2/components/chat/CopilotChatSuggestionView.vue.d.ts.map +1 -1
  39. package/dist/v2/components/chat/CopilotChatToggleButton.vue.d.ts.map +1 -1
  40. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +32 -0
  41. package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -0
  42. package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
  43. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +11 -1
  44. package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
  45. package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts.map +1 -1
  46. package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -0
  47. package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
  48. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts +5 -0
  49. package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -0
  50. package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -1
  51. package/dist/v2/components/chat/CopilotModalHeaderCloseButton.d.ts.map +1 -1
  52. package/dist/v2/components/chat/CopilotModalHeaderTitle.d.ts.map +1 -1
  53. package/dist/v2/components/chat/CopilotPopup.vue.d.ts +1 -0
  54. package/dist/v2/components/chat/CopilotPopup.vue.d.ts.map +1 -1
  55. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts +1 -0
  56. package/dist/v2/components/chat/CopilotPopupView.vue.d.ts.map +1 -1
  57. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts +1 -0
  58. package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
  59. package/dist/v2/components/chat/CopilotPopupWelcomeScreen.vue.d.ts.map +1 -1
  60. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts +2 -0
  61. package/dist/v2/components/chat/CopilotSidebar.vue.d.ts.map +1 -1
  62. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +2 -0
  63. package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts.map +1 -1
  64. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +2 -0
  65. package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
  66. package/dist/v2/components/chat/CopilotSidebarWelcomeScreen.vue.d.ts.map +1 -1
  67. package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
  68. package/dist/v2/components/chat/index.d.ts +17 -0
  69. package/dist/v2/components/chat/index.d.ts.map +1 -1
  70. package/dist/v2/components/chat/inspector-shortcuts.d.ts +12 -0
  71. package/dist/v2/components/chat/inspector-shortcuts.d.ts.map +1 -0
  72. package/dist/v2/components/chat/message-feedback.d.ts +89 -0
  73. package/dist/v2/components/chat/message-feedback.d.ts.map +1 -0
  74. package/dist/v2/components/chat/tool-call-capture.d.ts +39 -0
  75. package/dist/v2/components/chat/tool-call-capture.d.ts.map +1 -0
  76. package/dist/v2/components/chat/tool-call-display-context.d.ts +39 -0
  77. package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -0
  78. package/dist/v2/components/chat/tool-call-group-parts.d.ts +144 -0
  79. package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -0
  80. package/dist/v2/components/chat/tool-call-layout.d.ts +115 -0
  81. package/dist/v2/components/chat/tool-call-layout.d.ts.map +1 -0
  82. package/dist/v2/components/chat/turn-work.d.ts +35 -0
  83. package/dist/v2/components/chat/turn-work.d.ts.map +1 -0
  84. package/dist/v2/components/chat/types.d.ts +104 -4
  85. package/dist/v2/components/chat/types.d.ts.map +1 -1
  86. package/dist/v2/components/chat/use-lightbox.d.ts +18 -0
  87. package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -0
  88. package/dist/v2/components/icons/index.d.ts +1 -1
  89. package/dist/v2/components/icons/index.d.ts.map +1 -1
  90. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +55 -0
  91. package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -0
  92. package/dist/v2/components/ui/CopilotPopover.vue.d.ts +40 -0
  93. package/dist/v2/components/ui/CopilotPopover.vue.d.ts.map +1 -0
  94. package/dist/v2/components/ui/CopilotTooltip.vue.d.ts +37 -0
  95. package/dist/v2/components/ui/CopilotTooltip.vue.d.ts.map +1 -0
  96. package/dist/v2/components/ui/dropdown-menu.d.ts +14 -0
  97. package/dist/v2/components/ui/dropdown-menu.d.ts.map +1 -0
  98. package/dist/v2/components/ui/floating.d.ts +84 -0
  99. package/dist/v2/components/ui/floating.d.ts.map +1 -0
  100. package/dist/v2/components/ui/presence.d.ts +9 -0
  101. package/dist/v2/components/ui/presence.d.ts.map +1 -0
  102. package/dist/v2/components/ui/use-id.d.ts +9 -0
  103. package/dist/v2/components/ui/use-id.d.ts.map +1 -0
  104. package/dist/v2/hooks/use-default-render-tool.d.ts +6 -0
  105. package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
  106. package/dist/v2/hooks/use-render-tool.d.ts +4 -0
  107. package/dist/v2/hooks/use-render-tool.d.ts.map +1 -1
  108. package/dist/v2/index.cjs +1 -1
  109. package/dist/v2/index.d.cts +3 -0
  110. package/dist/v2/index.d.mts +3 -0
  111. package/dist/v2/index.d.ts +3 -0
  112. package/dist/v2/index.d.ts.map +1 -1
  113. package/dist/v2/index.mjs +165 -162
  114. package/dist/v2/lib/message-feedback.d.ts +33 -0
  115. package/dist/v2/lib/message-feedback.d.ts.map +1 -0
  116. package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
  117. package/dist/v2/providers/CopilotKitProvider.types.d.ts +56 -1
  118. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  119. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +5 -1
  120. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  121. package/dist/v2/providers/index.d.ts +1 -1
  122. package/dist/v2/providers/index.d.ts.map +1 -1
  123. package/dist/v2/providers/keys.d.ts +8 -0
  124. package/dist/v2/providers/keys.d.ts.map +1 -1
  125. package/dist/v2/providers/types.d.ts +9 -0
  126. package/dist/v2/providers/types.d.ts.map +1 -1
  127. package/dist/v2/styles/CopilotKitStyles.d.ts +18 -0
  128. package/dist/v2/styles/CopilotKitStyles.d.ts.map +1 -0
  129. package/dist/v2/types/defineToolCallRenderer.d.ts +2 -0
  130. package/dist/v2/types/defineToolCallRenderer.d.ts.map +1 -1
  131. package/dist/v2/types/vue-tool-call-renderer.d.ts +7 -0
  132. package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
  133. package/package.json +11 -23
  134. package/src/components/copilot-provider/CopilotKit.vue +4 -1
  135. package/src/v2/__tests__/exports.test.ts +4 -2
  136. package/src/v2/components/A2UIMessageRenderer.ts +55 -67
  137. package/src/v2/components/A2UISurfaceActivityRenderer.vue +11 -23
  138. package/src/v2/components/__tests__/A2UIBasicCatalogColors.test.ts +8 -9
  139. package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +63 -36
  140. package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +14 -257
  141. package/src/v2/components/a2ui/A2UIRecoveryStates.ts +444 -0
  142. package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +175 -0
  143. package/src/v2/components/chat/BasicMarkdown.vue +8 -6
  144. package/src/v2/components/chat/CopilotChat.vue +7 -0
  145. package/src/v2/components/chat/CopilotChatAssistantMessage.vue +267 -67
  146. package/src/v2/components/chat/CopilotChatAssistantToolbarButton.vue +46 -0
  147. package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +141 -168
  148. package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +67 -18
  149. package/src/v2/components/chat/CopilotChatAudioRecorder.vue +2 -2
  150. package/src/v2/components/chat/CopilotChatFeedbackCommentPopover.vue +240 -0
  151. package/src/v2/components/chat/CopilotChatInput.vue +210 -161
  152. package/src/v2/components/chat/CopilotChatInspectorButton.vue +331 -0
  153. package/src/v2/components/chat/CopilotChatLightbox.vue +105 -0
  154. package/src/v2/components/chat/CopilotChatMessageView.vue +261 -18
  155. package/src/v2/components/chat/CopilotChatReasoningMessage.vue +21 -22
  156. package/src/v2/components/chat/CopilotChatSuggestionPill.vue +5 -14
  157. package/src/v2/components/chat/CopilotChatSuggestionView.vue +1 -4
  158. package/src/v2/components/chat/CopilotChatToggleButton.vue +3 -9
  159. package/src/v2/components/chat/CopilotChatToggleButtonCloseIcon.ts +1 -1
  160. package/src/v2/components/chat/CopilotChatToggleButtonOpenIcon.ts +1 -1
  161. package/src/v2/components/chat/CopilotChatToolCallGroup.vue +330 -0
  162. package/src/v2/components/chat/CopilotChatToolCallItem.vue +21 -11
  163. package/src/v2/components/chat/CopilotChatToolCallsView.vue +192 -21
  164. package/src/v2/components/chat/CopilotChatUserMessage.vue +105 -42
  165. package/src/v2/components/chat/CopilotChatView.vue +33 -38
  166. package/src/v2/components/chat/CopilotChatWorkSummary.vue +85 -0
  167. package/src/v2/components/chat/CopilotModalHeader.vue +10 -8
  168. package/src/v2/components/chat/CopilotModalHeaderCloseButton.ts +7 -6
  169. package/src/v2/components/chat/CopilotModalHeaderTitle.ts +1 -4
  170. package/src/v2/components/chat/CopilotPopup.vue +3 -0
  171. package/src/v2/components/chat/CopilotPopupView.vue +1 -0
  172. package/src/v2/components/chat/CopilotPopupViewInternal.vue +9 -19
  173. package/src/v2/components/chat/CopilotPopupWelcomeScreen.vue +5 -8
  174. package/src/v2/components/chat/CopilotSidebar.vue +5 -0
  175. package/src/v2/components/chat/CopilotSidebarView.vue +2 -0
  176. package/src/v2/components/chat/CopilotSidebarViewInternal.vue +24 -17
  177. package/src/v2/components/chat/CopilotSidebarWelcomeScreen.vue +7 -10
  178. package/src/v2/components/chat/StreamingMarkdownDefault.vue +3 -5
  179. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.inspector.test.ts +269 -0
  180. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.learningFeedback.test.ts +760 -0
  181. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.ts +3 -5
  182. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.ts +2 -2
  183. package/src/v2/components/chat/__tests__/CopilotChatInput.addMenu.test.ts +328 -0
  184. package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.ts +6 -3
  185. package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +22 -13
  186. package/src/v2/components/chat/__tests__/CopilotChatLightbox.test.ts +160 -0
  187. package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +735 -0
  188. package/src/v2/components/chat/__tests__/CopilotChatSuggestionPill.test.ts +1 -1
  189. package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.ts +3 -1
  190. package/src/v2/components/chat/__tests__/CopilotChatToolCallsView.learning.test.ts +75 -0
  191. package/src/v2/components/chat/__tests__/CopilotChatUserMessage.attachments.test.ts +143 -0
  192. package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.ts +5 -4
  193. package/src/v2/components/chat/__tests__/CopilotChatView.rootWidth.test.ts +7 -33
  194. package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.ts +1 -1
  195. package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +3 -3
  196. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +126 -0
  197. package/src/v2/components/chat/__tests__/messageFeedbackDefaults.test.ts +33 -0
  198. package/src/v2/components/chat/__tests__/modalChatDisplayProps.test.ts +44 -0
  199. package/src/v2/components/chat/index.ts +2 -0
  200. package/src/v2/components/chat/inspector-shortcuts.ts +34 -0
  201. package/src/v2/components/chat/message-feedback.ts +291 -0
  202. package/src/v2/components/chat/tool-call-capture.ts +55 -0
  203. package/src/v2/components/chat/tool-call-display-context.ts +49 -0
  204. package/src/v2/components/chat/tool-call-group-parts.ts +292 -0
  205. package/src/v2/components/chat/tool-call-layout.ts +304 -0
  206. package/src/v2/components/chat/turn-work.ts +61 -0
  207. package/src/v2/components/chat/types.ts +113 -3
  208. package/src/v2/components/chat/use-lightbox.ts +64 -0
  209. package/src/v2/components/icons/index.ts +4 -0
  210. package/src/v2/components/ui/CopilotDropdownMenuContent.vue +465 -0
  211. package/src/v2/components/ui/CopilotPopover.vue +138 -0
  212. package/src/v2/components/ui/CopilotTooltip.vue +237 -0
  213. package/src/v2/components/ui/__tests__/CopilotTooltip.test.ts +168 -0
  214. package/src/v2/components/ui/dropdown-menu.ts +15 -0
  215. package/src/v2/components/ui/floating.ts +321 -0
  216. package/src/v2/components/ui/presence.ts +54 -0
  217. package/src/v2/components/ui/use-id.ts +16 -0
  218. package/src/v2/hooks/use-default-render-tool.ts +123 -62
  219. package/src/v2/hooks/use-render-tool.ts +8 -0
  220. package/src/v2/index.ts +3 -0
  221. package/src/v2/lib/__tests__/message-feedback.test.ts +152 -0
  222. package/src/v2/lib/message-feedback.ts +120 -0
  223. package/src/v2/providers/CopilotChatConfigurationProvider.vue +29 -2
  224. package/src/v2/providers/CopilotKitProvider.types.ts +57 -0
  225. package/src/v2/providers/CopilotKitProvider.vue +125 -3
  226. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.openThread.test.ts +69 -0
  227. package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +534 -0
  228. package/src/v2/providers/index.ts +4 -1
  229. package/src/v2/providers/keys.ts +12 -0
  230. package/src/v2/providers/types.ts +11 -0
  231. package/src/v2/styles/CopilotKitStyles.ts +95 -0
  232. package/src/v2/styles/__tests__/CopilotKitStyles.client.test.ts +149 -0
  233. package/src/v2/styles/__tests__/CopilotKitStyles.ssr.test.ts +71 -0
  234. package/src/v2/types/defineToolCallRenderer.ts +4 -0
  235. package/src/v2/types/vue-tool-call-renderer.ts +7 -0
  236. package/vite.config.ts +2 -1
  237. package/dist/styles.css +0 -2
  238. package/scripts/scope-preflight.mjs +0 -100
  239. package/src/styles/globals.css +0 -233
@@ -0,0 +1,534 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { defineComponent, h, nextTick, ref } from "vue";
3
+ import { cleanup, render } from "@testing-library/vue";
4
+ import type { CopilotKitCore, LegacyLearningConfig } from "@copilotkit/core";
5
+ import CopilotKitProvider from "../CopilotKitProvider.vue";
6
+ import type { CopilotKitLearningOptions } from "../CopilotKitProvider.types";
7
+ import { useCopilotKit } from "../useCopilotKit";
8
+ import { CopilotKitCoreVue } from "../../lib/vue-core";
9
+
10
+ // Ported from React's learning-wiring and trajectory-wiring tests. Core and
11
+ // browser capture are real. Authenticated capture is checked at its connect
12
+ // request; the realtime channel after it is Core's, and React's tests cover it.
13
+
14
+ type Learning = boolean | CopilotKitLearningOptions;
15
+ type LearningSink = LegacyLearningConfig["sink"];
16
+ type LearningBatch = Parameters<LearningSink>[0];
17
+
18
+ const FIRST_ID = "10000000-0000-4000-8000-000000000001";
19
+ const SECOND_ID = "10000000-0000-4000-8000-000000000002";
20
+ const UUID =
21
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
22
+ const nativePushState = History.prototype.pushState;
23
+
24
+ let core: CopilotKitCore;
25
+ const CoreProbe = defineComponent({
26
+ setup() {
27
+ core = useCopilotKit().copilotkit.value;
28
+ return () => null;
29
+ },
30
+ });
31
+
32
+ /**
33
+ * Renders the provider. `learning` is left off the provider when the ref
34
+ * holds `"missing"`, to check the absent prop rather than `undefined`.
35
+ */
36
+ function renderApp(initial: Learning | undefined | "missing") {
37
+ const learning = ref<Learning | undefined | "missing">(initial);
38
+ const show = ref(true);
39
+ const App = defineComponent({
40
+ setup() {
41
+ return () =>
42
+ show.value
43
+ ? h(
44
+ CopilotKitProvider,
45
+ {
46
+ runtimeUrl: "/api/copilotkit",
47
+ // Runtime discovery is mocked out, so pin the transport: with
48
+ // "auto", capture waits for discovery before connecting.
49
+ useSingleEndpoint: true,
50
+ ...(learning.value === "missing"
51
+ ? {}
52
+ : { learning: learning.value }),
53
+ },
54
+ { default: () => h(CoreProbe) },
55
+ )
56
+ : null;
57
+ },
58
+ });
59
+ const view = render(App);
60
+ const rerender = async (next: Learning | undefined | "missing") => {
61
+ learning.value = next;
62
+ await nextTick();
63
+ };
64
+ const unmount = async () => {
65
+ show.value = false;
66
+ await nextTick();
67
+ };
68
+ return { view, rerender, unmount };
69
+ }
70
+
71
+ function deferred<T>() {
72
+ let resolve!: (value: T) => void;
73
+ let reject!: (error: Error) => void;
74
+ const promise = new Promise<T>((done, fail) => {
75
+ resolve = done;
76
+ reject = fail;
77
+ });
78
+ return { promise, resolve, reject };
79
+ }
80
+
81
+ let fetchStub: ReturnType<typeof vi.fn>;
82
+
83
+ /** The bodies of the Trajectory connect requests sent so far. */
84
+ function connectRequests(): {
85
+ method: string;
86
+ params: { trajectoryId: string };
87
+ }[] {
88
+ return fetchStub.mock.calls
89
+ .map(([, init]) => JSON.parse(String(init?.body ?? "{}")))
90
+ .filter((body) => body.method === "trajectory/connect");
91
+ }
92
+ const connectedIds = () =>
93
+ connectRequests().map((body) => body.params.trajectoryId);
94
+
95
+ beforeEach(() => {
96
+ history.replaceState(null, "", "/deals");
97
+ // Keep runtime discovery out of these tests; the app pins the transport.
98
+ vi.spyOn(CopilotKitCoreVue.prototype, "connect").mockImplementation(() => {});
99
+ // Requests stay pending: these tests stop at the connect request.
100
+ fetchStub = vi.fn(() => new Promise<Response>(() => {}));
101
+ vi.stubGlobal("fetch", fetchStub);
102
+ });
103
+
104
+ afterEach(() => {
105
+ cleanup();
106
+ vi.restoreAllMocks();
107
+ vi.unstubAllGlobals();
108
+ });
109
+
110
+ describe("CopilotKitProvider learning prop", () => {
111
+ describe("with a custom sink", () => {
112
+ let batches: LearningBatch[] = [];
113
+ const sink: LearningSink = (batch) => {
114
+ batches.push(batch);
115
+ };
116
+ const withSink = (
117
+ options: Partial<CopilotKitLearningOptions> = {},
118
+ ): CopilotKitLearningOptions =>
119
+ ({
120
+ sink,
121
+ trajectoryId: "traj-1",
122
+ capture: { network: false },
123
+ ...options,
124
+ }) as CopilotKitLearningOptions;
125
+
126
+ function drain() {
127
+ window.dispatchEvent(new Event("pagehide"));
128
+ return batches.flatMap((batch) =>
129
+ batch.events.map((event) => ({
130
+ name: event.name,
131
+ value: event.value,
132
+ })),
133
+ );
134
+ }
135
+
136
+ beforeEach(() => {
137
+ batches = [];
138
+ });
139
+
140
+ it("starts capture when learning becomes available after mount", async () => {
141
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
142
+ const app = renderApp(false);
143
+
144
+ await app.rerender(withSink());
145
+ history.pushState(null, "", "/deals/42");
146
+ const events = drain();
147
+ await app.unmount();
148
+
149
+ expect(events.map((event) => event.name)).toEqual(["page", "navigation"]);
150
+ expect(warn).not.toHaveBeenCalledWith(
151
+ expect.stringContaining("startTrajectory()"),
152
+ );
153
+ });
154
+
155
+ it("starts a custom sink without a trajectoryId using a generated ID", async () => {
156
+ const app = renderApp({
157
+ sink,
158
+ capture: { network: false },
159
+ } as CopilotKitLearningOptions);
160
+ const events = drain();
161
+ await app.unmount();
162
+
163
+ expect(events.map((event) => event.name)).toEqual(["page"]);
164
+ expect(batches[0]?.trajectoryId).toMatch(UUID);
165
+ });
166
+
167
+ it("uses the latest config and container IDs for the next Trajectory", async () => {
168
+ const nextBatches: LearningBatch[] = [];
169
+ const nextSink: LearningSink = (batch) => {
170
+ nextBatches.push(batch);
171
+ };
172
+ const app = renderApp(withSink({ learningContainerIds: ["old"] }));
173
+ drain();
174
+ batches = [];
175
+
176
+ await app.rerender(
177
+ withSink({ learningContainerIds: ["new"], sink: nextSink }),
178
+ );
179
+ history.pushState(null, "", "/deals/42");
180
+ expect(drain().map((event) => event.name)).toEqual(["navigation"]);
181
+ expect(batches[0]?.learningContainerIds).toEqual(["old"]);
182
+ expect(nextBatches).toEqual([]);
183
+
184
+ await app.rerender(
185
+ withSink({
186
+ trajectoryId: "traj-2",
187
+ learningContainerIds: ["new"],
188
+ sink: nextSink,
189
+ }),
190
+ );
191
+ drain();
192
+ await app.unmount();
193
+
194
+ expect(nextBatches).toHaveLength(1);
195
+ expect(nextBatches[0]).toMatchObject({
196
+ trajectoryId: "traj-2",
197
+ learningContainerIds: ["new"],
198
+ });
199
+ expect(nextBatches[0]?.events.map((event) => event.name)).toEqual([
200
+ "page",
201
+ ]);
202
+ });
203
+
204
+ it("flushes to the previous sink and stops when learning is removed", async () => {
205
+ const app = renderApp(withSink());
206
+ drain();
207
+ batches = [];
208
+ history.pushState(null, "", "/deals/42");
209
+
210
+ await app.rerender(false);
211
+ expect(
212
+ batches.flatMap((batch) => batch.events.map((event) => event.name)),
213
+ ).toEqual(["navigation"]);
214
+ batches = [];
215
+ history.pushState(null, "", "/deals/43");
216
+ expect(drain()).toEqual([]);
217
+ expect(History.prototype.pushState).toBe(nativePushState);
218
+ });
219
+
220
+ it("stops capture on unmount", async () => {
221
+ const app = renderApp(withSink());
222
+ drain();
223
+ batches = [];
224
+
225
+ await app.unmount();
226
+ history.pushState(null, "", "/deals/7");
227
+
228
+ expect(drain()).toEqual([]);
229
+ expect(History.prototype.pushState).toBe(nativePushState);
230
+ });
231
+
232
+ it("installs nothing without the learning prop", async () => {
233
+ const app = renderApp("missing");
234
+
235
+ history.pushState(null, "", "/deals/8");
236
+ expect(drain()).toEqual([]);
237
+ expect(History.prototype.pushState).toBe(nativePushState);
238
+ await app.unmount();
239
+ });
240
+ });
241
+
242
+ describe("authenticated capture", () => {
243
+ const CONTAINER_WARNING =
244
+ "learningContainerIds is supported only with a custom sink. Authenticated Trajectory capture ignores it; assign Learning Spaces on the server with getTrajectoryLearningContainerIds";
245
+ const containerWarnings = (warn: ReturnType<typeof vi.spyOn>) =>
246
+ warn.mock.calls.filter(([message]) =>
247
+ String(message).includes(CONTAINER_WARNING),
248
+ ).length;
249
+
250
+ // Exactly one warning per mount: Core warns when the provider starts with
251
+ // the options, and the provider warns only when it does not start.
252
+ it.each<{
253
+ name: string;
254
+ learning: CopilotKitLearningOptions;
255
+ manualStartId?: string;
256
+ connectedId?: string;
257
+ }>([
258
+ {
259
+ name: "auto start with a supplied ID, warned by Core",
260
+ learning: {
261
+ trajectoryId: FIRST_ID,
262
+ autoStart: true,
263
+ learningContainerIds: ["private-container"],
264
+ },
265
+ connectedId: FIRST_ID,
266
+ },
267
+ {
268
+ name: "default auto start with a generated ID, warned by Core",
269
+ learning: { learningContainerIds: ["private-container"] },
270
+ },
271
+ {
272
+ name: "manual start, warned by the provider",
273
+ learning: {
274
+ autoStart: false,
275
+ learningContainerIds: ["private-container"],
276
+ },
277
+ manualStartId: FIRST_ID,
278
+ connectedId: FIRST_ID,
279
+ },
280
+ {
281
+ name: "manual start with an ignored trajectoryId, warned by the provider",
282
+ learning: {
283
+ autoStart: false,
284
+ trajectoryId: SECOND_ID,
285
+ learningContainerIds: ["private-container"],
286
+ },
287
+ manualStartId: FIRST_ID,
288
+ connectedId: FIRST_ID,
289
+ },
290
+ ])(
291
+ "warns once about provider container options without sending them ($name)",
292
+ ({ learning, manualStartId, connectedId }) => {
293
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
294
+ renderApp(learning);
295
+
296
+ expect(containerWarnings(warn)).toBe(1);
297
+ expect(JSON.stringify(warn.mock.calls)).not.toContain(
298
+ "private-container",
299
+ );
300
+ if (manualStartId !== undefined) {
301
+ void core.startTrajectory({ trajectoryId: manualStartId });
302
+ }
303
+ expect(connectRequests()).toEqual([
304
+ {
305
+ method: "trajectory/connect",
306
+ params: {
307
+ trajectoryId: connectedId ?? expect.stringMatching(UUID),
308
+ },
309
+ body: {},
310
+ },
311
+ ]);
312
+ expect(containerWarnings(warn)).toBe(1);
313
+ },
314
+ );
315
+
316
+ it("cancels the previous authentication when the supplied ID changes", async () => {
317
+ const stop = vi.spyOn(CopilotKitCoreVue.prototype, "stopTrajectory");
318
+ const app = renderApp({ trajectoryId: FIRST_ID });
319
+ await app.rerender({ trajectoryId: SECOND_ID });
320
+
321
+ expect(connectedIds()).toEqual([FIRST_ID, SECOND_ID]);
322
+ expect(stop).toHaveBeenCalledTimes(1);
323
+ });
324
+
325
+ it("cancels a manual start on unmount when autoStart is false", async () => {
326
+ const app = renderApp({ autoStart: false });
327
+ expect(connectedIds()).toEqual([]);
328
+
329
+ const start = core.startTrajectory();
330
+ await app.unmount();
331
+ await expect(start).resolves.toEqual({
332
+ status: "error",
333
+ code: "CANCELLED",
334
+ });
335
+ });
336
+
337
+ it("consumes unexpected startup rejections without exposing the underlying error", async () => {
338
+ vi.spyOn(
339
+ CopilotKitCoreVue.prototype,
340
+ "startTrajectory",
341
+ ).mockRejectedValue(new Error("private token"));
342
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
343
+ renderApp({ trajectoryId: FIRST_ID });
344
+ await nextTick();
345
+
346
+ expect(warn).toHaveBeenCalledWith(
347
+ "[CopilotKit] Failed to start interaction capture.",
348
+ );
349
+ expect(JSON.stringify(warn.mock.calls)).not.toContain("private token");
350
+ });
351
+
352
+ it("silences a previous start rejection without stopping the replacement", async () => {
353
+ const previous =
354
+ deferred<Awaited<ReturnType<CopilotKitCore["startTrajectory"]>>>();
355
+ const start = vi
356
+ .spyOn(CopilotKitCoreVue.prototype, "startTrajectory")
357
+ .mockImplementationOnce(() => previous.promise)
358
+ .mockResolvedValue({ status: "started", trajectoryId: SECOND_ID });
359
+ const stop = vi.spyOn(CopilotKitCoreVue.prototype, "stopTrajectory");
360
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
361
+ const app = renderApp({ trajectoryId: FIRST_ID });
362
+ await app.rerender({ trajectoryId: SECOND_ID });
363
+ expect(start).toHaveBeenCalledTimes(2);
364
+ expect(stop).toHaveBeenCalledTimes(1);
365
+
366
+ previous.reject(new Error("late failure"));
367
+ await new Promise((done) => setTimeout(done, 0));
368
+ expect(warn).not.toHaveBeenCalled();
369
+ expect(stop).toHaveBeenCalledTimes(1);
370
+ });
371
+
372
+ describe("generated Trajectory IDs", () => {
373
+ it("generates a Trajectory ID and starts capture after mount", () => {
374
+ renderApp(true);
375
+
376
+ expect(connectedIds()).toEqual([expect.stringMatching(UUID)]);
377
+ expect(core.ɵlearningConfigured).toBe(true);
378
+ });
379
+
380
+ it("treats the bare learning attribute as true", () => {
381
+ // `<CopilotKitProvider learning>` passes an empty string.
382
+ renderApp("" as unknown as Learning);
383
+
384
+ expect(connectedIds()).toEqual([expect.stringMatching(UUID)]);
385
+ expect(core.ɵlearningConfigured).toBe(true);
386
+ });
387
+
388
+ it.each([false, undefined, "missing" as const])(
389
+ "keeps capture off for learning=%s",
390
+ (learning) => {
391
+ renderApp(learning);
392
+
393
+ expect(connectedIds()).toEqual([]);
394
+ expect(core.ɵlearningConfigured).toBe(false);
395
+ },
396
+ );
397
+
398
+ it("keeps the generated ID across rerenders", async () => {
399
+ const app = renderApp(true);
400
+ await app.rerender(true);
401
+ await app.rerender({});
402
+ await app.rerender({});
403
+
404
+ expect(connectedIds()).toEqual([expect.stringMatching(UUID)]);
405
+ });
406
+
407
+ it("stops on disable and starts a new Trajectory with a new ID when enabled again", async () => {
408
+ const app = renderApp(true);
409
+ const [first] = connectedIds();
410
+
411
+ await app.rerender(false);
412
+ expect(core.trajectoryId).toBeNull();
413
+ expect(core.ɵlearningConfigured).toBe(false);
414
+
415
+ await app.rerender(true);
416
+ const [, second] = connectedIds();
417
+ expect(second).toMatch(UUID);
418
+ expect(second).not.toBe(first);
419
+ });
420
+
421
+ it("switches between the shorthand and a supplied ID", async () => {
422
+ const app = renderApp(true);
423
+ await app.rerender({ trajectoryId: SECOND_ID });
424
+ await app.rerender({});
425
+ const [first, second, third] = connectedIds();
426
+ expect(second).toBe(SECOND_ID);
427
+ expect(third).toMatch(UUID);
428
+ expect(third).not.toBe(first);
429
+
430
+ await app.rerender({ autoStart: false });
431
+ expect(core.trajectoryId).toBeNull();
432
+ expect(connectedIds()).toEqual([first, SECOND_ID, third]);
433
+ });
434
+
435
+ it("keeps one Trajectory when an inline object rerenders", async () => {
436
+ const onError = vi.fn();
437
+ const stop = vi.spyOn(CopilotKitCoreVue.prototype, "stopTrajectory");
438
+ const app = renderApp({ onError });
439
+ await app.rerender({ onError });
440
+ await app.rerender({ onError });
441
+
442
+ expect(connectedIds()).toEqual([expect.stringMatching(UUID)]);
443
+ expect(stop).not.toHaveBeenCalled();
444
+ });
445
+
446
+ it("keeps the generated ID when switching between generating forms", async () => {
447
+ const onError = vi.fn();
448
+ const stop = vi.spyOn(CopilotKitCoreVue.prototype, "stopTrajectory");
449
+ const app = renderApp(true);
450
+ await app.rerender({ onError });
451
+ await app.rerender({
452
+ onError,
453
+ ignoreUrls: ["https://private.invalid"],
454
+ });
455
+
456
+ expect(connectedIds()).toEqual([expect.stringMatching(UUID)]);
457
+ expect(stop).not.toHaveBeenCalled();
458
+ });
459
+
460
+ it("stops when autoStart turns off and generates a new ID when it turns on again", async () => {
461
+ const onError = vi.fn();
462
+ const stop = vi.spyOn(CopilotKitCoreVue.prototype, "stopTrajectory");
463
+ const app = renderApp({ onError });
464
+ const [first] = connectedIds();
465
+
466
+ await app.rerender({ onError, autoStart: false });
467
+ expect(stop).toHaveBeenCalledTimes(1);
468
+ expect(core.ɵlearningConfigured).toBe(true);
469
+
470
+ await app.rerender({ onError });
471
+ const [, second] = connectedIds();
472
+ expect(second).toMatch(UUID);
473
+ expect(second).not.toBe(first);
474
+ });
475
+
476
+ it("stops capture on unmount", async () => {
477
+ const stop = vi.spyOn(CopilotKitCoreVue.prototype, "stopTrajectory");
478
+ const app = renderApp(true);
479
+
480
+ await app.unmount();
481
+ expect(stop).toHaveBeenCalledTimes(1);
482
+ expect(History.prototype.pushState).toBe(nativePushState);
483
+ });
484
+
485
+ it("lets a manual start join the generated Trajectory instead of starting another", () => {
486
+ renderApp(true);
487
+ void core.startTrajectory();
488
+
489
+ expect(connectedIds()).toEqual([expect.stringMatching(UUID)]);
490
+ });
491
+ });
492
+
493
+ describe("autoStart: false", () => {
494
+ it("waits for a manual start", () => {
495
+ renderApp({ autoStart: false });
496
+ expect(connectedIds()).toEqual([]);
497
+ expect(core.ɵlearningConfigured).toBe(true);
498
+
499
+ void core.startTrajectory({ trajectoryId: FIRST_ID });
500
+ expect(connectedIds()).toEqual([FIRST_ID]);
501
+ });
502
+
503
+ it("ignores a supplied trajectoryId and warns once without the ID", async () => {
504
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
505
+ // A new inline object on every render must not repeat the warning.
506
+ const app = renderApp({ autoStart: false, trajectoryId: FIRST_ID });
507
+ await app.rerender({ autoStart: false, trajectoryId: FIRST_ID });
508
+ await app.rerender({ autoStart: false, trajectoryId: FIRST_ID });
509
+
510
+ expect(connectedIds()).toEqual([]);
511
+ expect(core.trajectoryId).toBeNull();
512
+ expect(warn).toHaveBeenCalledTimes(1);
513
+ expect(warn.mock.calls[0]![0]).toContain(
514
+ "[CopilotKit] learning.trajectoryId is ignored when autoStart is false",
515
+ );
516
+ expect(JSON.stringify(warn.mock.calls)).not.toContain(FIRST_ID);
517
+ });
518
+
519
+ it("does not pass autoStart to Core", async () => {
520
+ const setLearningConfig = vi.spyOn(
521
+ CopilotKitCoreVue.prototype,
522
+ "setLearningConfig",
523
+ );
524
+ const app = renderApp({ autoStart: false, routes: ["/deals"] });
525
+ await app.rerender({ autoStart: true, routes: ["/deals"] });
526
+
527
+ expect(setLearningConfig).toHaveBeenCalled();
528
+ for (const [config] of setLearningConfig.mock.calls) {
529
+ expect(config).toEqual({ routes: ["/deals"] });
530
+ }
531
+ });
532
+ });
533
+ });
534
+ });
@@ -1,5 +1,8 @@
1
1
  export { default as CopilotKitProvider } from "./CopilotKitProvider.vue";
2
- export type { CopilotKitProviderProps } from "./CopilotKitProvider.types";
2
+ export type {
3
+ CopilotKitLearningOptions,
4
+ CopilotKitProviderProps,
5
+ } from "./CopilotKitProvider.types";
3
6
  export type { DebugConfig } from "@copilotkit/shared";
4
7
  export { default as CopilotChatConfigurationProvider } from "./CopilotChatConfigurationProvider.vue";
5
8
  export type { CopilotChatConfigurationProviderProps } from "./CopilotChatConfigurationProvider.types";
@@ -38,3 +38,15 @@ export type VueInspectorContextValue = {
38
38
  export const InspectorKey: InjectionKey<VueInspectorContextValue> = Symbol(
39
39
  "CopilotKitInspector",
40
40
  );
41
+
42
+ export interface CopilotStyleOptions {
43
+ /** CSP nonce for the `<style>` tag. */
44
+ nonce?: string;
45
+ /** `false` when the app loads `@copilotkit/core/styles.css` itself. */
46
+ inject: boolean;
47
+ }
48
+
49
+ /** Passes the provider's style options to `<CopilotKitStyles>`. */
50
+ export const CopilotStyleOptionsKey: InjectionKey<
51
+ ComputedRef<CopilotStyleOptions>
52
+ > = Symbol("CopilotStyleOptions");
@@ -10,8 +10,19 @@ export const CopilotChatDefaultLabels = {
10
10
  assistantMessageToolbarCopyCodeLabel: "Copy",
11
11
  assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
12
12
  assistantMessageToolbarCopyMessageLabel: "Copy",
13
+ assistantMessageToolbarInspectorLabel: "View in Inspector",
14
+ assistantMessageToolbarInspectorDescription:
15
+ "Open this message in the Inspector",
16
+ assistantMessageToolbarInspectorLocalOnlyLabel: "Local only",
17
+ assistantMessageToolbarInspectorLocalOnlyDescription:
18
+ "Only visible on localhost or loopback hosts in development. Never shown in production.",
19
+ assistantMessageToolbarInspectorTitle: "CopilotKit Inspector",
20
+ assistantMessageToolbarInspectorHideLabel: "Hide this icon",
21
+ assistantMessageToolbarInspectorHideDescription: "Until you reload this page",
13
22
  assistantMessageToolbarThumbsUpLabel: "Good response",
14
23
  assistantMessageToolbarThumbsDownLabel: "Bad response",
24
+ assistantMessageFeedbackCommentPlaceholder: "Add a comment (optional)",
25
+ assistantMessageFeedbackCommentSubmitLabel: "Send comment",
15
26
  assistantMessageToolbarReadAloudLabel: "Read aloud",
16
27
  assistantMessageToolbarRegenerateLabel: "Regenerate",
17
28
  userMessageToolbarCopyMessageLabel: "Copy",
@@ -0,0 +1,95 @@
1
+ import {
2
+ computed,
3
+ defineComponent,
4
+ getCurrentInstance,
5
+ h,
6
+ inject,
7
+ onActivated,
8
+ onDeactivated,
9
+ onUnmounted,
10
+ shallowRef,
11
+ } from "vue";
12
+ import type { AppContext, ShallowRef } from "vue";
13
+ import { stylesheet } from "@copilotkit/core/styles";
14
+ import { CopilotStyleOptionsKey } from "../providers/keys";
15
+ import type { CopilotStyleOptions } from "../providers/keys";
16
+
17
+ interface StylesInstance {
18
+ enabled: () => boolean;
19
+ }
20
+
21
+ // The mounted instances of each app, in setup order. Keyed by app, so on the
22
+ // server each request's app has its own list.
23
+ const instancesByApp = new WeakMap<
24
+ AppContext,
25
+ ShallowRef<readonly StylesInstance[]>
26
+ >();
27
+
28
+ /**
29
+ * Renders the CSS for CopilotKit's UI, so apps never import a stylesheet.
30
+ * `CopilotChat` (and so `CopilotPopup` and `CopilotSidebar`) renders it for
31
+ * you. Render it yourself only when you build a layout from the chat parts
32
+ * without `CopilotChat`.
33
+ *
34
+ * The `<style>` tag renders in place, so it is in the server HTML and the page
35
+ * is styled before it hydrates. Only the first instance in the app renders the
36
+ * tag, so a page with many chats has one copy. When that instance unmounts, the
37
+ * next one renders the tag in the same update. The server and the client set
38
+ * up instances in the same order, so hydration matches.
39
+ *
40
+ * Renders nothing when `CopilotKitProvider` has `:inject-styles="false"`.
41
+ */
42
+ export const CopilotKitStyles = defineComponent({
43
+ name: "CopilotKitStyles",
44
+ setup() {
45
+ const options = inject(
46
+ CopilotStyleOptionsKey,
47
+ () => computed<CopilotStyleOptions>(() => ({ inject: true })),
48
+ true,
49
+ );
50
+ const appContext = getCurrentInstance()!.appContext;
51
+ let instances = instancesByApp.get(appContext);
52
+ if (!instances) {
53
+ instances = shallowRef([]);
54
+ instancesByApp.set(appContext, instances);
55
+ }
56
+ const list = instances;
57
+ const self: StylesInstance = { enabled: () => options.value.inject };
58
+
59
+ const add = () => {
60
+ if (!list.value.includes(self)) {
61
+ list.value = [...list.value, self];
62
+ }
63
+ };
64
+ const remove = () => {
65
+ list.value = list.value.filter((instance) => instance !== self);
66
+ };
67
+ add();
68
+ onUnmounted(remove);
69
+ // A deactivated <KeepAlive> child is out of the document, so it can't
70
+ // hold the tag.
71
+ onDeactivated(remove);
72
+ onActivated(add);
73
+
74
+ const rendersTag = computed(
75
+ () => list.value.find((instance) => instance.enabled()) === self,
76
+ );
77
+
78
+ return () => {
79
+ if (!rendersTag.value) {
80
+ return null;
81
+ }
82
+ const { nonce } = options.value;
83
+ return h("style", {
84
+ "data-copilotkit-styles": "",
85
+ nonce,
86
+ // Browsers hide a nonce from the DOM, so hydration would see a
87
+ // mismatch that isn't one.
88
+ "data-allow-mismatch": nonce ? "attribute" : undefined,
89
+ // innerHTML, not a text child: the server would escape `>` in a text
90
+ // child, and inside <style> that breaks the CSS.
91
+ innerHTML: stylesheet,
92
+ });
93
+ };
94
+ },
95
+ });