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

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-CyrhHFhA.js +16200 -0
  4. package/dist/use-render-activity-message-CyrhHFhA.js.map +1 -0
  5. package/dist/use-render-activity-message-EwHHrwQB.cjs +114 -0
  6. package/dist/use-render-activity-message-EwHHrwQB.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,17 +1,6 @@
1
- import { computed, onScopeDispose, ref, toValue, watch } from "vue";
1
+ import { computed, onScopeDispose, shallowRef, toValue, watch } from "vue";
2
2
  import type { MaybeRefOrGetter, Ref } from "vue";
3
- import {
4
- CopilotKitCoreRuntimeConnectionStatus,
5
- ɵcreateThreadStore,
6
- ɵselectHasNextPage,
7
- ɵselectIsFetchingNextPage,
8
- ɵselectIsMutating,
9
- ɵselectThreads,
10
- ɵselectThreadsError,
11
- ɵselectFetchMoreError,
12
- ɵselectThreadsIsLoading,
13
- } from "@copilotkit/core";
14
- import type { ɵThreadRuntimeContext, ɵThreadStore } from "@copilotkit/core";
3
+ import { ɵThreadsController, ɵcreateThreadStore } from "@copilotkit/core";
15
4
  import { useCopilotKit } from "../providers/useCopilotKit";
16
5
 
17
6
  export interface Thread {
@@ -67,283 +56,64 @@ export interface UseThreadsResult {
67
56
  deleteThread: (threadId: string) => Promise<void>;
68
57
  }
69
58
 
70
- function bindThreadStoreSelector<T>(
71
- store: ɵThreadStore,
72
- selector: (state: ReturnType<ɵThreadStore["getState"]>) => T,
73
- target: Ref<T>,
74
- ): void {
75
- target.value = selector(store.getState());
76
- const subscription = store.select(selector).subscribe(() => {
77
- target.value = selector(store.getState());
78
- });
79
- onScopeDispose(() => subscription.unsubscribe());
80
- }
81
-
82
- /**
83
- * Vue composable for listing and managing CopilotKit Intelligence threads.
84
- *
85
- * The hook fetches threads for the runtime-authenticated user and the given
86
- * `agentId`, then keeps the result in sync via the core thread store's realtime
87
- * channel when available. Inputs accept refs/computeds to make thread context
88
- * changes reactive.
89
- */
90
59
  export function useThreads(input: UseThreadsInput): UseThreadsResult {
91
60
  const { copilotkit } = useCopilotKit();
92
- const store = ɵcreateThreadStore({
93
- fetch: copilotkit.value.ɵruntimeFetch,
61
+ const readInput = () => ({
62
+ core: copilotkit.value,
63
+ agentId: toValue(input.agentId),
64
+ includeArchived: toValue(input.includeArchived),
65
+ limit: toValue(input.limit),
66
+ enabled: toValue(input.enabled) ?? true,
94
67
  });
95
68
 
96
- const resolvedAgentId = computed(() => toValue(input.agentId));
97
- const resolvedIncludeArchived = computed(() =>
98
- toValue(input.includeArchived),
99
- );
100
- const resolvedLimit = computed(() => toValue(input.limit));
101
- const resolvedEnabled = computed(() => toValue(input.enabled) ?? true);
102
-
103
- // Thread-endpoint capability, derived from the runtime's `/info` payload.
104
- // `threadEndpoints` is `undefined` on legacy runtimes that don't advertise
105
- // the capability, so we treat "not explicitly false" as supported (matching
106
- // React's `!== false` semantics) to preserve legacy thread behavior.
107
- const threadListEndpointSupported = computed(
108
- () => copilotkit.value.threadEndpoints?.list !== false,
109
- );
110
- const threadMutationsSupported = computed(
111
- () => copilotkit.value.threadEndpoints?.mutations !== false,
112
- );
113
- // Connected, but the runtime explicitly does not serve the list endpoint.
114
- const threadEndpointsUnavailable = computed(
115
- () =>
116
- !!copilotkit.value.runtimeUrl &&
117
- copilotkit.value.runtimeConnectionStatus ===
118
- CopilotKitCoreRuntimeConnectionStatus.Connected &&
119
- !threadListEndpointSupported.value,
120
- );
121
- const threadEndpointsError = computed<Error | null>(() =>
122
- threadEndpointsUnavailable.value
123
- ? new Error(
124
- "Thread endpoints are not available on this CopilotKit runtime",
125
- )
126
- : null,
127
- );
128
- const threadMutationsError = computed<Error | null>(() =>
129
- threadMutationsSupported.value
130
- ? null
131
- : new Error(
132
- "Thread mutations are not available on this CopilotKit runtime",
133
- ),
69
+ // Core's ɵThreadsController owns the view model: thread projection, the
70
+ // configuration errors, the gated and de-duplicated context dispatch, the
71
+ // registry claim and the mutation guard. This composable feeds it and
72
+ // projects its snapshot.
73
+ const controller = new ɵThreadsController(
74
+ ɵcreateThreadStore({ fetch: copilotkit.value.ɵruntimeFetch }),
75
+ readInput(),
134
76
  );
135
-
136
- // Keyed on the source's generation, not on header VALUES: a builder's
137
- // returned token can change on every resolution without a new source ever
138
- // being set, and this must not re-dispatch the thread context on that (see
139
- // #1937).
140
- const headersKey = computed(() => copilotkit.value.ɵheadersGeneration);
141
-
142
- const threads = ref<Thread[]>([]);
143
- const storeIsLoading = ref(false);
144
- const storeError = ref<Error | null>(null);
145
- const fetchMoreError = ref<Error | null>(null);
146
- const hasMoreThreads = ref(false);
147
- const isFetchingMoreThreads = ref(false);
148
- const isMutating = ref(false);
149
-
150
- bindThreadStoreSelector(store, ɵselectThreads, threads as Ref<Thread[]>);
151
- bindThreadStoreSelector(store, ɵselectThreadsIsLoading, storeIsLoading);
152
- bindThreadStoreSelector(store, ɵselectThreadsError, storeError);
153
- bindThreadStoreSelector(store, ɵselectFetchMoreError, fetchMoreError);
154
- bindThreadStoreSelector(store, ɵselectHasNextPage, hasMoreThreads);
155
- bindThreadStoreSelector(
156
- store,
157
- ɵselectIsFetchingNextPage,
158
- isFetchingMoreThreads,
159
- );
160
- bindThreadStoreSelector(store, ɵselectIsMutating, isMutating);
161
-
162
- store.start();
77
+ const snapshot = shallowRef(controller.getSnapshot());
78
+ const unsubscribe = controller.subscribe(() => {
79
+ snapshot.value = controller.getSnapshot();
80
+ });
81
+ const stop = controller.start();
163
82
  onScopeDispose(() => {
164
- store.stop();
83
+ stop();
84
+ unsubscribe();
85
+ controller.dispose();
165
86
  });
166
87
 
167
- // Register this store in core's single-slot thread-store registry so other
168
- // surfaces (e.g. the chat view) can resolve the same store for the agent.
169
- //
170
- // Gated on `resolvedEnabled`: a disabled (e.g. unlicensed) drawer must not
171
- // claim the agentId slot. The registry is single-slot/last-writer-wins, so
172
- // registering an inert store would evict — and on unmount tear down — a
173
- // co-mounted live store for the same agent. Staying unregistered while
174
- // disabled leaves the live store's registration intact.
175
- watch(
176
- [resolvedEnabled, resolvedAgentId],
177
- ([enabled, agentId], _prev, onCleanup) => {
178
- if (!enabled) return;
179
- copilotkit.value.registerThreadStore(agentId, store);
180
- onCleanup(() => {
181
- copilotkit.value.unregisterThreadStore(agentId);
182
- });
183
- },
184
- { immediate: true },
185
- );
186
-
187
- // Tracks whether we've dispatched the first real context to the store.
188
- // The store itself starts with `isLoading: false`, so before we dispatch
189
- // consumers would otherwise see an empty, non-loading state (empty-list
190
- // flash). While runtimeUrl is set and we haven't dispatched yet, we
191
- // synthesize `isLoading: true` so the UI keeps its loading indicator until
192
- // the first fetch is in flight (at which point the store's own
193
- // isLoading takes over).
194
- const hasDispatchedContext = ref(false);
195
-
196
- // Defer setting the context until the runtime reports Connected. Before
197
- // `/info` resolves we don't know `intelligence.wsUrl`, so dispatching the
198
- // context early would issue a list fetch with `wsUrl: undefined`, then a
199
- // second list fetch (and a `/threads/subscribe`) once the flag lands.
200
- // Waiting lets the hook issue just one `/threads?…` + one `/threads/subscribe`.
201
- //
202
- // When `runtimeUrl` is absent we dispatch `null` to clear the store. For
203
- // transient states (Disconnected/Connecting/Error with a URL still set) we
204
- // leave the previously-dispatched context in place — any in-flight
205
- // realtime subscription or cached thread list stays usable while the
206
- // runtime recovers, and we don't re-trigger a fetch storm on transitions.
207
88
  watch(
208
89
  [
90
+ readInput,
209
91
  () => copilotkit.value.runtimeUrl,
210
92
  () => copilotkit.value.runtimeConnectionStatus,
211
- headersKey,
93
+ () => copilotkit.value.ɵheadersGeneration,
212
94
  () => copilotkit.value.intelligence?.wsUrl,
213
- resolvedAgentId,
214
- resolvedIncludeArchived,
215
- resolvedLimit,
216
- resolvedEnabled,
217
- threadListEndpointSupported,
95
+ () => copilotkit.value.threadEndpoints?.list,
96
+ () => copilotkit.value.threadEndpoints?.mutations,
218
97
  ],
219
- ([
220
- runtimeUrl,
221
- runtimeStatus,
222
- ,
223
- wsUrl,
224
- agentId,
225
- includeArchived,
226
- limit,
227
- enabled,
228
- listEndpointSupported,
229
- ]) => {
230
- if (!runtimeUrl || !enabled) {
231
- store.setContext(null);
232
- hasDispatchedContext.value = false;
233
- return;
234
- }
235
-
236
- if (runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) {
237
- return;
238
- }
239
-
240
- // Connected, but the runtime explicitly does not serve the list
241
- // endpoint: stay inert so no `/threads` fetch fires. The
242
- // `threadEndpointsError` surfaces via `error` instead.
243
- if (!listEndpointSupported) {
244
- store.setContext(null);
245
- hasDispatchedContext.value = false;
246
- return;
247
- }
248
-
249
- // No `headers` here: the store's `fetch` is `copilotkit.value.ɵruntimeFetch`,
250
- // which already resolves and overlays the current core headers at send
251
- // time (#1937) — a snapshot here would go stale the moment the builder
252
- // returns something new.
253
- const context: ɵThreadRuntimeContext = {
254
- runtimeUrl,
255
- wsUrl,
256
- agentId,
257
- includeArchived,
258
- limit,
259
- };
260
-
261
- store.setContext(context);
262
- hasDispatchedContext.value = true;
263
- },
98
+ ([next]) => controller.update(next),
264
99
  { immediate: true },
265
100
  );
266
101
 
267
- const runtimeError = computed<Error | null>(() =>
268
- copilotkit.value.runtimeUrl
269
- ? null
270
- : new Error("Runtime URL is not configured"),
271
- );
272
-
273
- const preConnectLoading = computed(
274
- () =>
275
- !!copilotkit.value.runtimeUrl &&
276
- resolvedEnabled.value &&
277
- !threadEndpointsUnavailable.value &&
278
- !hasDispatchedContext.value,
279
- );
280
-
281
- const isLoading = computed(() =>
282
- runtimeError.value || threadEndpointsError.value
283
- ? false
284
- : preConnectLoading.value || storeIsLoading.value,
285
- );
286
-
287
- // Folds developer/config errors (missing runtime URL, runtime without
288
- // thread endpoints) together with genuine store errors. `listError` below
289
- // deliberately excludes the config errors.
290
- const error = computed<Error | null>(
291
- () => runtimeError.value ?? threadEndpointsError.value ?? storeError.value,
292
- );
293
-
294
- // Genuine store errors only. Unlike `error`, this omits the dev/config
295
- // `runtimeError`/`threadEndpointsError` strings so they are never shown in
296
- // user-facing error UI.
297
- const listError = computed<Error | null>(() => storeError.value);
298
-
299
- // Reject mutations locally (before touching the network) when the runtime
300
- // reports mutations are unsupported, matching React's `guardMutation`.
301
- function guardMutation<TArgs extends unknown[]>(
302
- mutation: (...args: TArgs) => Promise<void>,
303
- ): (...args: TArgs) => Promise<void> {
304
- return (...args: TArgs) => {
305
- if (threadMutationsError.value) {
306
- return Promise.reject(threadMutationsError.value);
307
- }
308
- return mutation(...args);
309
- };
310
- }
311
-
312
102
  return {
313
- threads: computed(() =>
314
- threads.value.map(
315
- ({ id, agentId, name, archived, createdAt, updatedAt, lastRunAt }) => ({
316
- id,
317
- agentId,
318
- name,
319
- archived,
320
- createdAt,
321
- updatedAt,
322
- ...(lastRunAt !== undefined ? { lastRunAt } : {}),
323
- }),
324
- ),
325
- ),
326
- isLoading,
327
- error,
328
- listError,
329
- fetchMoreError,
330
- hasMoreThreads,
331
- isFetchingMoreThreads,
332
- isMutating,
333
- fetchMoreThreads: () => store.fetchNextPage(),
334
- refetchThreads: () => store.refetchThreads(),
335
- startNewThread: () => store.startNewThread(),
336
- renameThread: guardMutation((threadId: string, name: string) =>
337
- store.renameThread(threadId, name),
338
- ),
339
- archiveThread: guardMutation((threadId: string) =>
340
- store.archiveThread(threadId),
341
- ),
342
- unarchiveThread: guardMutation((threadId: string) =>
343
- store.unarchiveThread(threadId),
344
- ),
345
- deleteThread: guardMutation((threadId: string) =>
346
- store.deleteThread(threadId),
347
- ),
103
+ threads: computed(() => snapshot.value.threads),
104
+ isLoading: computed(() => snapshot.value.isLoading),
105
+ error: computed(() => snapshot.value.error),
106
+ listError: computed(() => snapshot.value.listError),
107
+ fetchMoreError: computed(() => snapshot.value.fetchMoreError),
108
+ hasMoreThreads: computed(() => snapshot.value.hasMoreThreads),
109
+ isFetchingMoreThreads: computed(() => snapshot.value.isFetchingMoreThreads),
110
+ isMutating: computed(() => snapshot.value.isMutating),
111
+ fetchMoreThreads: controller.fetchMoreThreads,
112
+ refetchThreads: controller.refetchThreads,
113
+ startNewThread: controller.startNewThread,
114
+ renameThread: controller.renameThread,
115
+ archiveThread: controller.archiveThread,
116
+ unarchiveThread: controller.unarchiveThread,
117
+ deleteThread: controller.deleteThread,
348
118
  };
349
119
  }
@@ -0,0 +1,42 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { ref } from "vue";
3
+ import { CopilotKitCoreVue } from "../vue-core";
4
+ import type { VueActivityMessageRenderer } from "../../types";
5
+
6
+ const planRenderer: VueActivityMessageRenderer<unknown> = {
7
+ activityType: "plan",
8
+ content: {
9
+ "~standard": {
10
+ version: 1,
11
+ vendor: "test",
12
+ validate: (value) => ({ value }),
13
+ },
14
+ },
15
+ render: () => null,
16
+ };
17
+
18
+ describe("CopilotKitCoreVue renderer lists", () => {
19
+ it("notifies subscribers when activity renderers change", () => {
20
+ const core = new CopilotKitCoreVue({});
21
+ const seen: string[][] = [];
22
+ core.subscribe({
23
+ onRenderActivityMessagesChanged: ({ renderActivityMessages }) => {
24
+ seen.push(renderActivityMessages.map((r) => r.activityType));
25
+ },
26
+ });
27
+
28
+ core.setRenderActivityMessages([planRenderer]);
29
+
30
+ expect(seen).toEqual([["plan"]]);
31
+ });
32
+
33
+ it("keeps working behind a Vue reactive proxy", () => {
34
+ const wrapped = ref(new CopilotKitCoreVue({}));
35
+
36
+ wrapped.value.addHookRenderToolCall({ name: "search", render: () => null });
37
+
38
+ expect(wrapped.value.renderToolCalls.map((r) => r.name)).toEqual([
39
+ "search",
40
+ ]);
41
+ });
42
+ });
@@ -5,11 +5,12 @@ import type {
5
5
  VueCustomMessageRenderer,
6
6
  } from "../types";
7
7
  import type { InterruptRenderProps } from "../types/interrupt";
8
- import { CopilotKitCore } from "@copilotkit/core";
8
+ import { CopilotKitCore, ɵRendererRegistry } from "@copilotkit/core";
9
9
  import type {
10
10
  CopilotKitCoreConfig,
11
11
  CopilotKitCoreSubscriber,
12
12
  CopilotKitCoreSubscription,
13
+ ɵRendererKind,
13
14
  } from "@copilotkit/core";
14
15
 
15
16
  export interface CopilotKitCoreVueConfig extends CopilotKitCoreConfig {
@@ -27,6 +28,10 @@ export interface CopilotKitCoreVueSubscriber extends CopilotKitCoreSubscriber {
27
28
  copilotkit: CopilotKitCoreVue;
28
29
  renderCustomMessages: VueCustomMessageRenderer[];
29
30
  }) => void | Promise<void>;
31
+ onRenderActivityMessagesChanged?: (event: {
32
+ copilotkit: CopilotKitCoreVue;
33
+ renderActivityMessages: VueActivityMessageRenderer<unknown>[];
34
+ }) => void | Promise<void>;
30
35
  onInterruptStateChanged?: (event: {
31
36
  copilotkit: CopilotKitCoreVue;
32
37
  interruptState: InterruptRenderProps<unknown, unknown> | null;
@@ -34,119 +39,109 @@ export interface CopilotKitCoreVueSubscriber extends CopilotKitCoreSubscriber {
34
39
  }
35
40
 
36
41
  export class CopilotKitCoreVue extends CopilotKitCore {
37
- private _renderToolCalls: VueToolCallRenderer<unknown>[] = [];
38
- private _hookRenderToolCalls: Map<string, VueToolCallRenderer<unknown>> =
39
- new Map();
40
- private _cachedMergedRenderToolCalls: VueToolCallRenderer<unknown>[] | null =
41
- null;
42
- private _renderCustomMessages: VueCustomMessageRenderer[] = [];
43
- private _renderActivityMessages: VueActivityMessageRenderer<unknown>[] = [];
44
- private _interruptState: InterruptRenderProps<unknown, unknown> | null = null;
42
+ private readonly _renderers: ɵRendererRegistry<
43
+ VueToolCallRenderer<unknown>,
44
+ VueActivityMessageRenderer<unknown>,
45
+ VueCustomMessageRenderer
46
+ >;
45
47
 
46
48
  constructor(config: CopilotKitCoreVueConfig) {
47
49
  // Tool handlers can change reactive state. `nextTick` lets Vue flush those
48
50
  // updates before core starts the follow-up run.
49
51
  super({ ...config, ɵwaitForFrameworkUpdates: () => nextTick() });
50
- this._renderToolCalls = config.renderToolCalls ?? [];
51
- this._renderCustomMessages = config.renderCustomMessages ?? [];
52
- this._renderActivityMessages = config.renderActivityMessages ?? [];
52
+ this._renderers = new ɵRendererRegistry({
53
+ toolCalls: config.renderToolCalls,
54
+ activity: config.renderActivityMessages,
55
+ custom: config.renderCustomMessages,
56
+ });
57
+ this._renderers.subscribe((kind) => this.notifyRenderersChanged(kind));
58
+ // The props live in core's generic interrupt slot; keep the Vue
59
+ // subscriber event for existing listeners.
60
+ this.ɵsubscribeToInterruptPayload(() => {
61
+ void this.notifySubscribers((subscriber) => {
62
+ const vueSubscriber = subscriber as CopilotKitCoreVueSubscriber;
63
+ return vueSubscriber.onInterruptStateChanged?.({
64
+ copilotkit: this,
65
+ interruptState: this.interruptState,
66
+ });
67
+ }, "Subscriber onInterruptStateChanged error:");
68
+ });
53
69
  }
54
70
 
55
- get renderCustomMessages(): Readonly<VueCustomMessageRenderer[]> {
56
- return this._renderCustomMessages;
71
+ get renderCustomMessages() {
72
+ return this._renderers.custom;
57
73
  }
58
74
 
59
- get renderActivityMessages(): Readonly<
60
- VueActivityMessageRenderer<unknown>[]
61
- > {
62
- return this._renderActivityMessages;
75
+ get renderActivityMessages() {
76
+ return this._renderers.activity;
63
77
  }
64
78
 
65
79
  setRenderActivityMessages(
66
80
  renderers: VueActivityMessageRenderer<unknown>[],
67
81
  ): void {
68
- this._renderActivityMessages = [...renderers];
82
+ this._renderers.setActivity(renderers);
69
83
  }
70
84
 
71
85
  setRenderCustomMessages(renderers: VueCustomMessageRenderer[]): void {
72
- this._renderCustomMessages = [...renderers];
73
- void this.notifySubscribers((subscriber) => {
74
- const vueSubscriber = subscriber as CopilotKitCoreVueSubscriber;
75
- return vueSubscriber.onRenderCustomMessagesChanged?.({
76
- copilotkit: this,
77
- renderCustomMessages: [...this.renderCustomMessages],
78
- });
79
- }, "Subscriber onRenderCustomMessagesChanged error:");
86
+ this._renderers.setCustom(renderers);
80
87
  }
81
88
 
82
- get propRenderToolCalls(): Readonly<VueToolCallRenderer<unknown>[]> {
83
- return this._renderToolCalls;
89
+ /** The renderers the provider was given, without hook renderers. */
90
+ get propRenderToolCalls() {
91
+ return this._renderers.propToolCalls;
84
92
  }
85
93
 
86
- get renderToolCalls(): Readonly<VueToolCallRenderer<unknown>[]> {
87
- if (this._hookRenderToolCalls.size === 0) {
88
- return this._renderToolCalls;
89
- }
90
- if (this._cachedMergedRenderToolCalls) {
91
- return this._cachedMergedRenderToolCalls;
92
- }
93
- const merged = new Map<string, VueToolCallRenderer<unknown>>();
94
- for (const rc of this._renderToolCalls) {
95
- merged.set(`${rc.agentId ?? ""}:${rc.name}`, rc);
96
- }
97
- for (const [key, rc] of this._hookRenderToolCalls) {
98
- merged.set(key, rc);
99
- }
100
- this._cachedMergedRenderToolCalls = Array.from(merged.values());
101
- return this._cachedMergedRenderToolCalls;
94
+ /** Prop and hook renderers merged; a hook renderer wins over a prop one. */
95
+ get renderToolCalls() {
96
+ return this._renderers.toolCalls;
102
97
  }
103
98
 
104
99
  get interruptState(): InterruptRenderProps<unknown, unknown> | null {
105
- return this._interruptState;
100
+ return this.ɵinterruptPayload as InterruptRenderProps<
101
+ unknown,
102
+ unknown
103
+ > | null;
106
104
  }
107
105
 
108
106
  setRenderToolCalls(renderToolCalls: VueToolCallRenderer<unknown>[]): void {
109
- this._renderToolCalls = [...renderToolCalls];
110
- this._cachedMergedRenderToolCalls = null;
111
- this._notifyRenderToolCallsChanged();
107
+ this._renderers.setPropToolCalls(renderToolCalls);
112
108
  }
113
109
 
114
110
  addHookRenderToolCall(entry: VueToolCallRenderer<unknown>): void {
115
- const key = `${entry.agentId ?? ""}:${entry.name}`;
116
- this._hookRenderToolCalls.set(key, entry);
117
- this._cachedMergedRenderToolCalls = null;
118
- this._notifyRenderToolCallsChanged();
111
+ this._renderers.addHookToolCall(entry);
119
112
  }
120
113
 
121
114
  removeHookRenderToolCall(name: string, agentId?: string): void {
122
- const key = `${agentId ?? ""}:${name}`;
123
- if (this._hookRenderToolCalls.delete(key)) {
124
- this._cachedMergedRenderToolCalls = null;
125
- this._notifyRenderToolCallsChanged();
126
- }
115
+ this._renderers.removeHookToolCall(name, agentId);
127
116
  }
128
117
 
129
- private _notifyRenderToolCallsChanged(): void {
118
+ private notifyRenderersChanged(kind: ɵRendererKind): void {
130
119
  void this.notifySubscribers((subscriber) => {
131
120
  const vueSubscriber = subscriber as CopilotKitCoreVueSubscriber;
132
- return vueSubscriber.onRenderToolCallsChanged?.({
133
- copilotkit: this,
134
- renderToolCalls: [...this.renderToolCalls],
135
- });
136
- }, "Subscriber onRenderToolCallsChanged error:");
121
+ switch (kind) {
122
+ case "toolCalls":
123
+ return vueSubscriber.onRenderToolCallsChanged?.({
124
+ copilotkit: this,
125
+ renderToolCalls: [...this.renderToolCalls],
126
+ });
127
+ case "activity":
128
+ return vueSubscriber.onRenderActivityMessagesChanged?.({
129
+ copilotkit: this,
130
+ renderActivityMessages: [...this.renderActivityMessages],
131
+ });
132
+ case "custom":
133
+ return vueSubscriber.onRenderCustomMessagesChanged?.({
134
+ copilotkit: this,
135
+ renderCustomMessages: [...this.renderCustomMessages],
136
+ });
137
+ }
138
+ }, `Subscriber renderer change (${kind}) error:`);
137
139
  }
138
140
 
139
141
  setInterruptState(
140
142
  interruptState: InterruptRenderProps<unknown, unknown> | null,
141
143
  ): void {
142
- this._interruptState = interruptState;
143
- void this.notifySubscribers((subscriber) => {
144
- const vueSubscriber = subscriber as CopilotKitCoreVueSubscriber;
145
- return vueSubscriber.onInterruptStateChanged?.({
146
- copilotkit: this,
147
- interruptState: this._interruptState,
148
- });
149
- }, "Subscriber onInterruptStateChanged error:");
144
+ this.ɵsetInterruptPayload(interruptState);
150
145
  }
151
146
 
152
147
  subscribe(