@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
@@ -1,20 +1,22 @@
1
- import { computed, defineComponent, h } from "vue";
2
- import type { PropType } from "vue";
3
- import * as z from "zod";
1
+ import { defineComponent, h, onBeforeUnmount, shallowRef, watch } from "vue";
2
+ import type { Component, PropType } from "vue";
3
+ import {
4
+ ɵA2UIActivityController,
5
+ ɵA2UI_SURFACE_ACTIVITY_TYPE,
6
+ ɵA2UI_SURFACE_CONTENT_SCHEMA,
7
+ } from "@copilotkit/core";
8
+ import type {
9
+ ɵA2UIActionAgent,
10
+ ɵA2UIActionInterceptor,
11
+ ɵA2UISurfaceContent,
12
+ } from "@copilotkit/core";
13
+ import type { ActivityMessage } from "@ag-ui/core";
4
14
  import type { VueActivityMessageRenderer } from "../types";
5
15
  import type { A2UITheme } from "../types";
6
16
  import A2UISurfaceActivityRenderer from "./A2UISurfaceActivityRenderer.vue";
7
- import {
8
- A2UIBuildingState,
9
- A2UILifecycleFields,
10
- A2UIRecoveryFailure,
11
- A2UIRetryingState,
12
- resolveDebugExposure,
13
- } from "./a2ui/A2UIRecoveryStates";
17
+ import { renderA2UILifecycleState } from "./a2ui/A2UIRecoveryStates";
14
18
  import type { A2UIRecoveryRendererOptions } from "./a2ui/A2UIRecoveryStates";
15
19
 
16
- const A2UI_OPERATIONS_KEY = "a2ui_operations";
17
-
18
20
  export type A2UIMessageRendererOptions = {
19
21
  theme: A2UITheme;
20
22
  catalog?: any;
@@ -25,88 +27,116 @@ export type A2UIMessageRendererOptions = {
25
27
  * sub-label appears, and how much retry/debug detail to surface.
26
28
  */
27
29
  recovery?: A2UIRecoveryRendererOptions;
30
+ /**
31
+ * Intercept actions before they reach the agent: return `null` to handle an
32
+ * action client-side, return an action to forward that one instead, or
33
+ * return nothing to forward it unchanged.
34
+ */
35
+ onAction?: ɵA2UIActionInterceptor;
28
36
  };
29
37
 
38
+ function isComponent(value: unknown): value is Component {
39
+ return (
40
+ typeof value === "function" || (typeof value === "object" && value !== null)
41
+ );
42
+ }
43
+
30
44
  /**
31
45
  * The `a2ui-surface` activity carries the WHOLE generative-UI lifecycle on one
32
46
  * stable messageId: pre-paint `status` ("building" | "retrying" | "failed")
33
- * with recovery detail, then `a2ui_operations` on paint. The states swap in
34
- * place, so the painted surface replaces the skeleton. `.loose()` keeps
35
- * the operations and any future fields.
47
+ * with recovery detail, then `a2ui_operations` on paint. Core's
48
+ * `ɵA2UIActivityController` decides what shows; this renderer draws it and
49
+ * keeps the surface mounted, hidden under the loader, until it has painted.
36
50
  */
37
- const A2UISurfaceContentSchema = z
38
- .object({
39
- a2ui_operations: z.array(z.any()).optional(),
40
- ...A2UILifecycleFields,
41
- })
42
- .loose();
43
-
44
51
  export function createA2UIMessageRenderer(
45
52
  options: A2UIMessageRendererOptions,
46
53
  ): VueActivityMessageRenderer<any> {
47
- const showAfterMs = options.recovery?.showAfterMs ?? 2000;
48
- const showAfterAttempts = options.recovery?.showAfterAttempts ?? 2;
49
- const optionDebugExposure = options.recovery?.debugExposure ?? "collapsed";
50
-
51
- // Renders the pre-paint lifecycle state for a content snapshot.
52
- const renderLifecycle = (content: any) => {
53
- const debugExposure = resolveDebugExposure(content, optionDebugExposure);
54
- if (content?.status === "failed") {
55
- return h(A2UIRecoveryFailure, { content, debugExposure });
56
- }
57
- if (content?.status === "retrying") {
58
- return h(A2UIRetryingState, {
59
- content,
60
- showAfterMs,
61
- showAfterAttempts,
62
- debugExposure,
63
- });
64
- }
65
- // "building" / default: a host-supplied loader wins; else the skeleton.
66
- if (options.loadingComponent) {
67
- return h(options.loadingComponent as any);
68
- }
69
- return h(A2UIBuildingState, { content });
70
- };
54
+ const loadingComponent = isComponent(options.loadingComponent)
55
+ ? options.loadingComponent
56
+ : undefined;
71
57
 
72
58
  return {
73
- activityType: "a2ui-surface",
74
- content: A2UISurfaceContentSchema,
59
+ activityType: ɵA2UI_SURFACE_ACTIVITY_TYPE,
60
+ content: ɵA2UI_SURFACE_CONTENT_SCHEMA,
75
61
  render: defineComponent({
76
62
  name: "A2UIMessageRendererHost",
77
63
  props: {
78
64
  activityType: { type: String, required: true },
79
- content: { type: Object as PropType<any>, required: true },
80
- message: { type: Object as PropType<any>, required: true },
65
+ content: {
66
+ type: Object as PropType<ɵA2UISurfaceContent>,
67
+ required: true,
68
+ },
69
+ message: { type: Object as PropType<ActivityMessage>, required: true },
81
70
  agent: {
82
- type: Object as PropType<any>,
71
+ type: Object as PropType<ɵA2UIActionAgent>,
83
72
  required: false,
84
73
  default: undefined,
85
74
  },
86
75
  },
87
76
  setup(props) {
88
- const operations = computed(() =>
89
- Array.isArray(props.content?.[A2UI_OPERATIONS_KEY])
90
- ? props.content[A2UI_OPERATIONS_KEY]
91
- : [],
77
+ const controller = new ɵA2UIActivityController({
78
+ recovery: options.recovery,
79
+ warn:
80
+ process.env.NODE_ENV !== "production"
81
+ ? (message) => console.warn(message)
82
+ : undefined,
83
+ });
84
+ const snapshot = shallowRef(controller.getSnapshot());
85
+ const unsubscribe = controller.subscribe(() => {
86
+ snapshot.value = controller.getSnapshot();
87
+ });
88
+ watch(
89
+ () => props.content,
90
+ (content) => controller.setContent(content),
91
+ { immediate: true },
92
92
  );
93
+ onBeforeUnmount(() => {
94
+ unsubscribe();
95
+ controller.dispose();
96
+ });
97
+ const markRendered = () => controller.markRendered();
93
98
 
94
99
  return () => {
95
- // No painted surface yet → the pre-paint lifecycle state. It shares
96
- // this activity's messageId, so the painted surface below replaces
97
- // it in place once operations arrive.
98
- if (operations.value.length === 0) {
99
- return renderLifecycle(props.content);
100
+ const current = snapshot.value;
101
+ if (current.view === "lifecycle") {
102
+ return renderA2UILifecycleState(current, loadingComponent);
100
103
  }
101
104
 
102
- return h(A2UISurfaceActivityRenderer, {
103
- activityType: "a2ui-surface",
104
- content: { operations: operations.value },
105
- message: props.message,
106
- agent: props.agent,
107
- theme: options.theme,
108
- catalog: options.catalog,
109
- });
105
+ const handingOff = current.view === "handoff";
106
+ return h("div", { style: { position: "relative" } }, [
107
+ // The surface stays mounted in the same place across the
108
+ // hand-off; only its wrapper styling changes, so what painted
109
+ // while hidden is kept.
110
+ h(
111
+ "div",
112
+ {
113
+ "aria-hidden": handingOff ? "true" : undefined,
114
+ style: handingOff
115
+ ? {
116
+ position: "absolute",
117
+ inset: "0",
118
+ opacity: 0,
119
+ pointerEvents: "none",
120
+ }
121
+ : undefined,
122
+ },
123
+ [
124
+ h(A2UISurfaceActivityRenderer, {
125
+ activityType: ɵA2UI_SURFACE_ACTIVITY_TYPE,
126
+ content: { operations: current.operations },
127
+ message: props.message,
128
+ agent: props.agent,
129
+ theme: options.theme,
130
+ catalog: options.catalog,
131
+ onAction: options.onAction,
132
+ onRendered: markRendered,
133
+ }),
134
+ ],
135
+ ),
136
+ handingOff
137
+ ? renderA2UILifecycleState(current, loadingComponent)
138
+ : null,
139
+ ]);
110
140
  };
111
141
  },
112
142
  }),
@@ -1,147 +1,112 @@
1
1
  <script setup lang="ts">
2
- import { computed, h, onBeforeUnmount, shallowRef, ref, watch } from "vue";
3
- import type { VNode } from "vue";
2
+ import { computed, onBeforeUnmount, shallowRef, watch } from "vue";
4
3
  import type { ActivityMessage } from "@ag-ui/core";
5
4
  import type { A2UITheme } from "../types";
6
5
  import type { A2UIOperation } from "./a2ui";
7
- import { getOperationSurfaceId } from "./a2ui";
8
6
  import { useCopilotKit } from "../providers";
9
7
  import { MessageProcessor } from "@a2ui/web_core/v0_9";
10
8
  import type { SurfaceModel } from "@a2ui/web_core/v0_9";
9
+ import { A2UIOperationApplier } from "@copilotkit/a2ui-renderer/engine";
10
+ import { A2UISurfaceDiagnostics } from "@copilotkit/a2ui-renderer/engine-core";
11
+ import { ɵrunA2UIAction, ɵtoA2UIClientEvent } from "@copilotkit/core";
12
+ import type {
13
+ ɵA2UIActionAgent,
14
+ ɵA2UIActionInterceptor,
15
+ } from "@copilotkit/core";
11
16
  import { vueBasicCatalog, A2uiSurface } from "./a2ui/index";
12
17
  import type { VueComponentImplementation } from "./a2ui/index";
13
18
 
14
- const DEFAULT_SURFACE_ID = "default";
15
-
16
19
  const props = defineProps<{
17
20
  activityType: string;
18
- content: { operations: A2UIOperation[] };
21
+ content: { operations: readonly A2UIOperation[] };
19
22
  message: ActivityMessage;
20
- agent?: object;
23
+ agent?: ɵA2UIActionAgent;
21
24
  theme?: A2UITheme;
22
25
  catalog?: any;
26
+ /** Intercepts a user action before it reaches the agent. */
27
+ onAction?: ɵA2UIActionInterceptor;
23
28
  }>();
24
29
 
25
- const { copilotkit } = useCopilotKit();
30
+ const emit = defineEmits<{
31
+ /** After each batch of operations was applied. */
32
+ rendered: [];
33
+ }>();
26
34
 
27
- // MessageProcessor from @a2ui/web_core — framework-agnostic
28
- // Use shallowRef to avoid Vue's deep UnwrapRef which strips private class members
29
- const processorRef =
30
- shallowRef<MessageProcessor<VueComponentImplementation> | null>(null);
31
- // Version counter to trigger Vue reactivity on processor state changes
32
- const version = ref(0);
33
- // Error state
34
- const error = ref<string | null>(null);
35
- // Track last processed operations hash to avoid re-processing
36
- let lastOpsHash = "";
35
+ const { copilotkit } = useCopilotKit();
37
36
 
38
- function getOrCreateProcessor(): MessageProcessor<VueComponentImplementation> {
39
- if (!processorRef.value) {
37
+ type Surface = SurfaceModel<VueComponentImplementation>;
38
+
39
+ // One processor for all surfaces of the activity; the core-shared applier
40
+ // validates the operations and applies only what is new in each snapshot.
41
+ // shallowRef avoids Vue's deep UnwrapRef, which strips private class members.
42
+ let processor: MessageProcessor<VueComponentImplementation> | null = null;
43
+ let applier: A2UIOperationApplier<VueComponentImplementation> | null = null;
44
+ const surfaces = shallowRef<readonly Surface[]>([]);
45
+ const error = shallowRef<string | null>(null);
46
+ const diagnostics =
47
+ process.env.NODE_ENV !== "production"
48
+ ? new A2UISurfaceDiagnostics({ warn: (message) => console.warn(message) })
49
+ : null;
50
+
51
+ function getApplier() {
52
+ if (!processor || !applier) {
40
53
  const catalog = props.catalog ?? vueBasicCatalog;
41
- processorRef.value = new MessageProcessor<VueComponentImplementation>(
54
+ const created = new MessageProcessor<VueComponentImplementation>(
42
55
  [catalog],
43
- (action: unknown) => {
44
- handleAction(action);
45
- },
56
+ (action) =>
57
+ ɵrunA2UIAction({
58
+ message: ɵtoA2UIClientEvent(action),
59
+ agent: props.agent,
60
+ core: copilotkit.value,
61
+ onAction: props.onAction,
62
+ }),
46
63
  );
64
+ processor = created;
65
+ applier = new A2UIOperationApplier(created, catalog.id);
47
66
  }
48
- return processorRef.value;
49
- }
50
-
51
- async function handleAction(message: unknown) {
52
- if (!props.agent) return;
53
- try {
54
- copilotkit.value.setProperties({
55
- ...copilotkit.value.properties,
56
- a2uiAction: message,
57
- });
58
- await copilotkit.value.runAgent({ agent: props.agent as any });
59
- } finally {
60
- const { a2uiAction, ...rest } = copilotkit.value.properties ?? {};
61
- copilotkit.value.setProperties(rest);
62
- }
67
+ return applier;
63
68
  }
64
69
 
65
- function processOperations(operations: A2UIOperation[]) {
66
- if (!operations?.length) return;
67
-
68
- const hash = JSON.stringify(operations);
69
- if (hash === lastOpsHash) return;
70
- lastOpsHash = hash;
71
-
72
- const processor = getOrCreateProcessor();
70
+ function processOperations(operations: readonly A2UIOperation[]) {
71
+ const current = getApplier();
73
72
  try {
74
- // Group operations by surface ID
75
- const grouped = new Map<string, A2UIOperation[]>();
76
- for (const op of operations) {
77
- const surfaceId = getOperationSurfaceId(op) ?? DEFAULT_SURFACE_ID;
78
- if (!grouped.has(surfaceId)) grouped.set(surfaceId, []);
79
- grouped.get(surfaceId)!.push(op);
80
- }
81
-
82
- // For each surface, skip createSurface if the surface already exists
83
- for (const [surfaceId, ops] of grouped) {
84
- const existing = processor.model.getSurface(surfaceId);
85
- const filtered = existing
86
- ? ops.filter((op) => !(op as any)?.createSurface)
87
- : ops;
88
- processor.processMessages(filtered as any);
89
- }
73
+ surfaces.value = current.apply(operations);
90
74
  error.value = null;
91
75
  } catch (err) {
92
76
  console.warn("[A2UI Vue] processMessages error:", err);
93
77
  error.value = err instanceof Error ? err.message : String(err);
78
+ surfaces.value = [];
79
+ return;
94
80
  }
95
- version.value++;
81
+ if (processor) diagnostics?.check(operations, processor.model);
82
+ emit("rendered");
96
83
  }
97
84
 
98
- // Process operations on mount and when they change
85
+ // A new catalog needs a new processor, rebuilt from the current operations.
99
86
  watch(
100
- () => [props.content.operations, props.theme, props.catalog, props.agent],
87
+ () => props.catalog,
101
88
  () => {
102
- processOperations(props.content.operations);
89
+ processor = null;
90
+ applier = null;
91
+ processOperations(props.content.operations ?? []);
103
92
  },
93
+ );
94
+
95
+ watch(
96
+ () => props.content.operations,
97
+ (operations) => processOperations(operations ?? []),
104
98
  { deep: true, immediate: true },
105
99
  );
106
100
 
107
101
  onBeforeUnmount(() => {
108
- processorRef.value = null;
109
- lastOpsHash = "";
102
+ diagnostics?.dispose();
103
+ processor = null;
104
+ applier = null;
110
105
  });
111
106
 
112
107
  const hasOperations = computed(
113
108
  () => (props.content.operations ?? []).length > 0,
114
109
  );
115
-
116
- // Compute the list of surfaces to render
117
- const surfaceEntries = computed(() => {
118
- // Touch version to ensure reactivity
119
- void version.value;
120
-
121
- if (!processorRef.value) return [];
122
-
123
- const entries: Array<{
124
- surfaceId: string;
125
- surface: SurfaceModel<VueComponentImplementation>;
126
- }> = [];
127
-
128
- // Group operations by surface to know which surfaces we expect
129
- const grouped = new Map<string, A2UIOperation[]>();
130
- for (const op of props.content.operations ?? []) {
131
- const surfaceId = getOperationSurfaceId(op) ?? DEFAULT_SURFACE_ID;
132
- if (!grouped.has(surfaceId)) grouped.set(surfaceId, []);
133
- grouped.get(surfaceId)!.push(op);
134
- }
135
-
136
- for (const [surfaceId] of grouped) {
137
- const surface = processorRef.value.model.getSurface(surfaceId);
138
- if (surface) {
139
- entries.push({ surfaceId, surface });
140
- }
141
- }
142
-
143
- return entries;
144
- });
145
110
  </script>
146
111
 
147
112
  <template>
@@ -160,13 +125,13 @@ const surfaceEntries = computed(() => {
160
125
  data-testid="a2ui-activity-renderer"
161
126
  >
162
127
  <div
163
- v-for="entry in surfaceEntries"
164
- :key="entry.surfaceId"
128
+ v-for="surface in surfaces"
129
+ :key="surface.id"
165
130
  class="cpk-a2ui-message-surface-host"
166
- :data-surface-id="entry.surfaceId"
131
+ :data-surface-id="surface.id"
167
132
  >
168
133
  <div class="a2ui-surface cpk-a2ui-message-surface">
169
- <A2uiSurface :surface="entry.surface" />
134
+ <A2uiSurface :surface="surface" />
170
135
  </div>
171
136
  </div>
172
137
  </div>
@@ -4,9 +4,9 @@ defineOptions({
4
4
  inheritAttrs: false,
5
5
  });
6
6
 
7
- import { onMounted, onUnmounted, shallowRef, useAttrs, watch } from "vue";
8
- import type { ObjectDirective } from "vue";
9
- import type { WebInspectorElement } from "@copilotkit/web-inspector";
7
+ import { onBeforeUnmount, onMounted, ref, useAttrs, watch } from "vue";
8
+ import { ɵmountInspector } from "@copilotkit/web-inspector/mount";
9
+ import type { ɵInspectorMount } from "@copilotkit/web-inspector/mount";
10
10
  import type { CopilotKitCoreVue } from "../lib/vue-core";
11
11
  import type { VueInspectorOpenRequest } from "../providers/keys";
12
12
 
@@ -15,72 +15,125 @@ const props = defineProps<{
15
15
  openRequest?: VueInspectorOpenRequest | null;
16
16
  }>();
17
17
 
18
+ const emit = defineEmits<{
19
+ "visibility-change": [visible: boolean];
20
+ }>();
21
+
18
22
  const attrs = useAttrs();
19
- const inspectorTag = shallowRef<string | null>(null);
20
- const inspector = shallowRef<WebInspectorElement | null>(null);
21
-
22
- let isMounted = true;
23
- let configureInspector:
24
- | ((
25
- element: WebInspectorElement,
26
- core: CopilotKitCoreVue | null,
27
- ) => WebInspectorElement)
28
- | null = null;
29
-
30
- const vConfigureInspector: ObjectDirective<WebInspectorElement, undefined> = {
31
- created(element) {
32
- configureInspector?.(element, props.core ?? null);
33
- inspector.value = element;
34
- if (props.openRequest) {
35
- element.openInspector?.("message_toolbar", props.openRequest);
23
+ const host = ref<HTMLSpanElement | null>(null);
24
+ let mount: ɵInspectorMount | null = null;
25
+ let appliedAttributes: Record<string, string> = {};
26
+ const boundListeners = new Map<
27
+ string,
28
+ { eventName: string; listener: (event: Event) => void }
29
+ >();
30
+
31
+ function isListener(name: string, value: unknown) {
32
+ const isHandler = typeof value === "function" || Array.isArray(value);
33
+ return /^on[A-Z]/.test(name) && isHandler;
34
+ }
35
+
36
+ /** `onCpkInspectorVisibilityChange` listens to `cpk-inspector-visibility-change`, as in Vue. */
37
+ function toEventName(name: string) {
38
+ return name
39
+ .slice(2)
40
+ .replace(/\B([A-Z])/g, "-$1")
41
+ .toLowerCase();
42
+ }
43
+
44
+ // Text attributes and listeners go to the Inspector element (the host is only
45
+ // a mount point), so `data-testid` or `@cpk-inspector-visibility-change`
46
+ // land where callers expect them.
47
+ function toAttributes(source: Record<string, unknown>) {
48
+ const attributes: Record<string, string> = {};
49
+ for (const [name, value] of Object.entries(source)) {
50
+ if (typeof value === "string" || typeof value === "number") {
51
+ attributes[name] = String(value);
52
+ } else if (value === true) {
53
+ attributes[name] = "";
36
54
  }
37
- },
38
- };
55
+ }
56
+ return attributes;
57
+ }
58
+
59
+ function callListener(name: string, event: Event) {
60
+ const handler = attrs[name];
61
+ const handlers = Array.isArray(handler) ? handler : [handler];
62
+ for (const candidate of handlers) {
63
+ if (typeof candidate === "function") candidate(event);
64
+ }
65
+ }
66
+
67
+ function syncListeners(element: Element) {
68
+ const names = Object.keys(attrs).filter((name) =>
69
+ isListener(name, attrs[name]),
70
+ );
71
+ for (const [name, bound] of boundListeners) {
72
+ if (names.includes(name)) continue;
73
+ element.removeEventListener(bound.eventName, bound.listener);
74
+ boundListeners.delete(name);
75
+ }
76
+ for (const name of names) {
77
+ if (boundListeners.has(name)) continue;
78
+ const bound = {
79
+ eventName: toEventName(name),
80
+ listener: (event: Event) => callListener(name, event),
81
+ };
82
+ element.addEventListener(bound.eventName, bound.listener);
83
+ boundListeners.set(name, bound);
84
+ }
85
+ }
86
+
87
+ function syncAttributes() {
88
+ const element = mount?.element;
89
+ if (!element) return;
90
+ const next = toAttributes(attrs);
91
+ for (const name of Object.keys(appliedAttributes)) {
92
+ if (!(name in next)) element.removeAttribute(name);
93
+ }
94
+ for (const [name, value] of Object.entries(next)) {
95
+ element.setAttribute(name, value);
96
+ }
97
+ appliedAttributes = next;
98
+ syncListeners(element);
99
+ }
39
100
 
40
101
  onMounted(() => {
41
- void import("@copilotkit/web-inspector")
42
- .then((mod) => {
43
- if (!isMounted) return;
44
-
45
- mod.defineWebInspector?.();
46
- configureInspector = (element, core) =>
47
- mod.configureWebInspectorElement(element, core, {
48
- development: process.env.NODE_ENV === "development",
49
- framework: "vue",
50
- sdkVersion,
51
- });
52
- inspectorTag.value = mod.WEB_INSPECTOR_TAG;
53
- })
54
- .catch((error: unknown) => {
55
- console.error("Failed to load CopilotKit inspector:", error);
56
- });
102
+ if (!host.value) return;
103
+ mount = ɵmountInspector(host.value, props.core ?? null, {
104
+ load: () => import("@copilotkit/web-inspector"),
105
+ notificationContext: {
106
+ development: process.env.NODE_ENV === "development",
107
+ framework: "vue",
108
+ sdkVersion,
109
+ },
110
+ });
111
+ mount.onVisibility((visible) => emit("visibility-change", visible));
112
+ mount.onElement(syncAttributes);
113
+ if (props.openRequest) mount.open(props.openRequest);
57
114
  });
58
115
 
59
- onUnmounted(() => {
60
- isMounted = false;
61
- inspector.value = null;
116
+ onBeforeUnmount(() => {
117
+ boundListeners.clear();
118
+ mount?.dispose();
119
+ mount = null;
62
120
  });
63
121
 
64
122
  watch(
65
123
  () => props.core,
66
- (core) => {
67
- if (inspector.value) inspector.value.core = core ?? null;
68
- },
124
+ (core) => mount?.setCore(core ?? null),
69
125
  );
70
126
 
71
127
  watch(
72
128
  () => props.openRequest,
73
129
  (request) => {
74
- if (request) inspector.value?.openInspector?.("message_toolbar", request);
130
+ if (request) mount?.open(request);
75
131
  },
76
132
  );
133
+
134
+ watch(() => ({ ...attrs }), syncAttributes);
77
135
  </script>
78
136
 
79
137
  <template>
80
- <component
81
- :is="inspectorTag"
82
- v-if="inspectorTag"
83
- v-bind="attrs"
84
- v-configure-inspector
85
- />
138
+ <span ref="host" style="display: contents" />
86
139
  </template>