@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
@@ -5,23 +5,10 @@ import {
5
5
  ref,
6
6
  shallowRef,
7
7
  toValue,
8
- watch,
9
8
  } from "vue";
10
9
  import type { MaybeRefOrGetter, Ref } from "vue";
11
- import {
12
- randomUUID,
13
- getModalityFromMimeType,
14
- exceedsMaxSize,
15
- readFileAsBase64,
16
- generateVideoThumbnail,
17
- matchesAcceptFilter,
18
- formatFileSize,
19
- } from "@copilotkit/shared";
20
- import type {
21
- Attachment,
22
- AttachmentUploadResult,
23
- AttachmentsConfig,
24
- } from "@copilotkit/shared";
10
+ import { ɵAttachmentQueue } from "@copilotkit/core";
11
+ import type { Attachment, AttachmentsConfig } from "@copilotkit/shared";
25
12
 
26
13
  export interface UseAttachmentsProps {
27
14
  config?: MaybeRefOrGetter<AttachmentsConfig | undefined>;
@@ -33,6 +20,7 @@ export interface UseAttachmentsReturn {
33
20
  dragOver: Ref<boolean>;
34
21
  fileInputRef: Ref<HTMLInputElement | null>;
35
22
  containerRef: Ref<HTMLElement | null>;
23
+ /** Process files (validate, upload, add to state). No-op while attachments are disabled. */
36
24
  processFiles: (files: File[]) => Promise<void>;
37
25
  handleFileUpload: (event: Event) => Promise<void>;
38
26
  handleDragOver: (event: DragEvent) => void;
@@ -42,133 +30,43 @@ export interface UseAttachmentsReturn {
42
30
  consumeAttachments: () => Attachment[];
43
31
  }
44
32
 
33
+ /**
34
+ * Manages file attachment state — uploads, drag-and-drop, paste. The queue
35
+ * itself (validation, uploads, ready state) lives in `@copilotkit/core`'s
36
+ * `ɵAttachmentQueue`; this composable adds the DOM wiring and projects the
37
+ * queue into a `shallowRef`.
38
+ */
45
39
  export function useAttachments(
46
40
  props: UseAttachmentsProps,
47
41
  ): UseAttachmentsReturn {
48
- const attachments = shallowRef<Attachment[]>([]);
42
+ const queue = new ɵAttachmentQueue(() => toValue(props.config));
43
+ const attachments = shallowRef<Attachment[]>(queue.getSnapshot());
44
+ const unsubscribe = queue.subscribe(() => {
45
+ attachments.value = queue.getSnapshot();
46
+ });
49
47
  const dragOver = ref(false);
50
48
  const fileInputRef = ref<HTMLInputElement | null>(null);
51
49
  const containerRef = ref<HTMLElement | null>(null);
52
- const configRef = ref<AttachmentsConfig | undefined>(toValue(props.config));
53
- const attachmentsRef = shallowRef<Attachment[]>(attachments.value);
54
- const enabled = computed(() => configRef.value?.enabled ?? false);
55
-
56
- watch(
57
- () => toValue(props.config),
58
- (next) => {
59
- configRef.value = next;
60
- },
61
- { immediate: true },
62
- );
63
-
64
- const setAttachments = (next: Attachment[]) => {
65
- attachments.value = next;
66
- attachmentsRef.value = next;
67
- };
68
-
69
- const updateAttachments = (
70
- updater: (previous: Attachment[]) => Attachment[],
71
- ) => {
72
- setAttachments(updater(attachmentsRef.value));
73
- };
74
-
75
- async function processFiles(files: File[]) {
76
- const config = configRef.value;
77
- const accept = config?.accept ?? "*/*";
78
- const maxSize = config?.maxSize ?? 20 * 1024 * 1024;
79
-
80
- const rejectedFiles = files.filter(
81
- (file) => !matchesAcceptFilter(file, accept),
82
- );
83
- for (const file of rejectedFiles) {
84
- config?.onUploadFailed?.({
85
- reason: "invalid-type",
86
- file,
87
- message: `File "${file.name}" is not accepted. Supported types: ${accept}`,
88
- });
89
- }
90
-
91
- const validFiles = files.filter((file) =>
92
- matchesAcceptFilter(file, accept),
93
- );
94
- for (const file of validFiles) {
95
- if (exceedsMaxSize(file, maxSize)) {
96
- config?.onUploadFailed?.({
97
- reason: "file-too-large",
98
- file,
99
- message: `File "${file.name}" exceeds the maximum size of ${formatFileSize(maxSize)}`,
100
- });
101
- continue;
102
- }
103
-
104
- const modality = getModalityFromMimeType(file.type);
105
- const placeholderId = randomUUID();
106
- updateAttachments((previous) => [
107
- ...previous,
108
- {
109
- id: placeholderId,
110
- type: modality,
111
- source: { type: "data", value: "", mimeType: file.type },
112
- filename: file.name,
113
- size: file.size,
114
- status: "uploading",
115
- },
116
- ]);
50
+ const enabled = computed(() => toValue(props.config)?.enabled ?? false);
117
51
 
118
- try {
119
- let source: Attachment["source"];
120
- let uploadMetadata: Record<string, unknown> | undefined;
121
- if (config?.onUpload) {
122
- const uploadResult: AttachmentUploadResult =
123
- await config.onUpload(file);
124
- const { metadata, ...uploadSource } = uploadResult;
125
- source = uploadSource;
126
- uploadMetadata = metadata;
127
- } else {
128
- const base64 = await readFileAsBase64(file);
129
- source = { type: "data", value: base64, mimeType: file.type };
130
- }
131
-
132
- let thumbnail: string | undefined;
133
- if (modality === "video") {
134
- thumbnail = await generateVideoThumbnail(file);
135
- }
52
+ function processFiles(files: File[]) {
53
+ return queue.processFiles(files);
54
+ }
136
55
 
137
- updateAttachments((previous) =>
138
- previous.map((attachment) =>
139
- attachment.id === placeholderId
140
- ? {
141
- ...attachment,
142
- source,
143
- status: "ready",
144
- thumbnail,
145
- metadata: uploadMetadata,
146
- }
147
- : attachment,
148
- ),
149
- );
150
- } catch (error) {
151
- updateAttachments((previous) =>
152
- previous.filter((attachment) => attachment.id !== placeholderId),
153
- );
154
- console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
155
- config?.onUploadFailed?.({
156
- reason: "upload-failed",
157
- file,
158
- message:
159
- error instanceof Error
160
- ? error.message
161
- : `Failed to upload "${file.name}"`,
162
- });
163
- }
56
+ async function processSafely(files: File[], label: string) {
57
+ try {
58
+ await queue.processFiles(files);
59
+ } catch (error) {
60
+ console.error(`[CopilotKit] ${label}:`, error);
164
61
  }
165
62
  }
166
63
 
167
64
  async function handleFileUpload(event: Event) {
168
- const target = event.target as HTMLInputElement | null;
169
- if (!target?.files?.length) return;
170
- await processFiles(Array.from(target.files));
171
- if (target) target.value = "";
65
+ const target = event.target;
66
+ if (!(target instanceof HTMLInputElement) || !target.files?.length) return;
67
+ await processSafely(Array.from(target.files), "Upload error");
68
+ // Clear the picker so choosing the same file again fires `change`.
69
+ target.value = "";
172
70
  }
173
71
 
174
72
  function handleDragOver(event: DragEvent) {
@@ -188,56 +86,32 @@ export function useAttachments(
188
86
  event.preventDefault();
189
87
  event.stopPropagation();
190
88
  dragOver.value = false;
191
- if (!enabled.value) return;
192
-
193
89
  const files = Array.from(event.dataTransfer?.files ?? []);
194
- if (files.length > 0) {
195
- await processFiles(files);
196
- }
90
+ if (files.length > 0) await processSafely(files, "Drop error");
197
91
  }
198
92
 
199
93
  function removeAttachment(id: string) {
200
- updateAttachments((previous) =>
201
- previous.filter((attachment) => attachment.id !== id),
202
- );
94
+ queue.remove(id);
203
95
  }
204
96
 
205
97
  function consumeAttachments() {
206
- const ready = attachmentsRef.value.filter(
207
- (attachment) => attachment.status === "ready",
208
- );
209
- if (ready.length === 0) {
210
- return ready;
211
- }
212
- updateAttachments((previous) =>
213
- previous.filter((attachment) => attachment.status !== "ready"),
214
- );
215
- if (fileInputRef.value) {
98
+ const ready = queue.consumeReady();
99
+ if (ready.length > 0 && fileInputRef.value) {
216
100
  fileInputRef.value.value = "";
217
101
  }
218
102
  return ready;
219
103
  }
220
104
 
221
105
  async function handlePaste(event: ClipboardEvent) {
222
- if (!enabled.value) return;
223
-
224
- const target = event.target as HTMLElement | null;
225
- if (!target || !containerRef.value?.contains(target)) return;
226
-
227
- const accept = configRef.value?.accept ?? "*/*";
228
- const items = Array.from(event.clipboardData?.items ?? []);
229
- const fileItems = items.filter((item) => {
230
- if (item.kind !== "file") return false;
231
- const file = item.getAsFile();
232
- return file !== null && matchesAcceptFilter(file, accept);
233
- });
234
- if (fileItems.length === 0) return;
235
-
106
+ const target = event.target;
107
+ if (!(target instanceof Node) || !containerRef.value?.contains(target))
108
+ return;
109
+ const files = queue.filterPastedFiles(
110
+ Array.from(event.clipboardData?.items ?? []),
111
+ );
112
+ if (files.length === 0) return;
236
113
  event.preventDefault();
237
- const files = fileItems
238
- .map((item) => item.getAsFile())
239
- .filter((file): file is File => file !== null);
240
- await processFiles(files);
114
+ await processSafely(files, "Paste error");
241
115
  }
242
116
 
243
117
  function onPaste(event: ClipboardEvent) {
@@ -254,6 +128,8 @@ export function useAttachments(
254
128
  if (typeof document !== "undefined") {
255
129
  document.removeEventListener("paste", onPaste);
256
130
  }
131
+ unsubscribe();
132
+ queue.dispose();
257
133
  });
258
134
 
259
135
  return {
@@ -1,6 +1,7 @@
1
1
  import type { AgentCapabilities } from "@ag-ui/core";
2
2
  import { computed } from "vue";
3
3
  import type { ComputedRef } from "vue";
4
+ import { ɵgetAgentCapabilities } from "@copilotkit/core";
4
5
  import { useAgent } from "./use-agent";
5
6
 
6
7
  /**
@@ -20,11 +21,5 @@ export function useCapabilities(
20
21
  ): ComputedRef<AgentCapabilities | undefined> {
21
22
  const { agent } = useAgent({ agentId });
22
23
 
23
- return computed(() => {
24
- const a = agent.value;
25
- if (a && "capabilities" in a) {
26
- return (a as { capabilities?: AgentCapabilities }).capabilities;
27
- }
28
- return undefined;
29
- });
24
+ return computed(() => ɵgetAgentCapabilities(agent.value));
30
25
  }
@@ -2,6 +2,7 @@ import type { StandardSchemaV1, InferSchemaOutput } from "@copilotkit/shared";
2
2
  import type { Component } from "vue";
3
3
  import { h } from "vue";
4
4
  import type { WatchSource } from "vue";
5
+ import { ɵcomponentToolDescription } from "@copilotkit/core";
5
6
  import { useFrontendTool } from "./use-frontend-tool";
6
7
 
7
8
  type InferRenderProps<T> = T extends StandardSchemaV1
@@ -17,13 +18,14 @@ export function useComponent<
17
18
  parameters?: TSchema;
18
19
  render: Component<NoInfer<InferRenderProps<TSchema>>>;
19
20
  agentId?: string;
21
+ followUp?: boolean;
20
22
  },
21
23
  deps?: WatchSource<unknown>[],
22
24
  ): void {
23
- const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
24
- const fullDescription = config.description
25
- ? `${prefix}\n\n${config.description}`
26
- : prefix;
25
+ const fullDescription = ɵcomponentToolDescription(
26
+ config.name,
27
+ config.description,
28
+ );
27
29
 
28
30
  useFrontendTool(
29
31
  {
@@ -40,6 +42,7 @@ export function useComponent<
40
42
  );
41
43
  },
42
44
  agentId: config.agentId,
45
+ followUp: config.followUp,
43
46
  },
44
47
  deps,
45
48
  );
@@ -1,49 +1,17 @@
1
- import { computed, ref, watch } from "vue";
1
+ import { computed, watch } from "vue";
2
2
  import type { WatchSource } from "vue";
3
3
  import { useCopilotKit } from "../providers/useCopilotKit";
4
4
  import { useCopilotChatConfiguration } from "../providers/useCopilotChatConfiguration";
5
5
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
6
- import type {
7
- DynamicSuggestionsConfig,
8
- Suggestion,
9
- StaticSuggestionsConfig,
10
- SuggestionsConfig,
11
- } from "@copilotkit/core";
12
-
13
- type StaticSuggestionInput = Omit<Suggestion, "isLoading"> &
14
- Partial<Pick<Suggestion, "isLoading">>;
15
-
16
- type StaticSuggestionsConfigInput = Omit<
17
- StaticSuggestionsConfig,
18
- "suggestions"
19
- > & {
20
- suggestions: StaticSuggestionInput[];
21
- };
22
-
23
- type SuggestionsConfigInput =
24
- | DynamicSuggestionsConfig
25
- | StaticSuggestionsConfigInput;
26
-
27
- function isDynamicConfig(
28
- config: SuggestionsConfigInput,
29
- ): config is DynamicSuggestionsConfig {
30
- return "instructions" in config;
31
- }
32
-
33
- function normalizeStaticSuggestions(
34
- suggestions: StaticSuggestionInput[],
35
- ): Suggestion[] {
36
- return suggestions.map((s) => ({
37
- ...s,
38
- isLoading: s.isLoading ?? false,
39
- }));
40
- }
6
+ import { ɵbindSuggestionsConfig } from "@copilotkit/core";
7
+ import type { ɵSuggestionsConfigInput } from "@copilotkit/core";
41
8
 
42
9
  /**
43
10
  * Registers a suggestions configuration (dynamic or static) for the current
44
11
  * chat context.
45
12
  *
46
- * Configuration is kept in sync reactively and removed on scope cleanup.
13
+ * Configuration is kept in sync reactively and removed on scope cleanup. Pass
14
+ * `deps` to reload suggestions when values the config reads change.
47
15
  *
48
16
  * @example
49
17
  * ```ts
@@ -54,197 +22,39 @@ function normalizeStaticSuggestions(
54
22
  * ```
55
23
  */
56
24
  export function useConfigureSuggestions(
57
- config: SuggestionsConfigInput | null | undefined,
25
+ config: ɵSuggestionsConfigInput | null | undefined,
58
26
  deps?: WatchSource<unknown>[],
59
27
  ): void {
60
28
  const { copilotkit } = useCopilotKit();
61
29
  const chatConfig = useCopilotChatConfiguration();
62
- const extraDeps = deps ?? [];
63
-
64
- const resolvedConsumerAgentId = computed(
30
+ const chatAgentId = computed(
65
31
  () => chatConfig.value?.agentId ?? DEFAULT_AGENT_ID,
66
32
  );
67
- const rawConsumerAgentId = computed(() =>
68
- config ? (config as SuggestionsConfigInput).consumerAgentId : undefined,
69
- );
70
-
71
- const normalizedConfig = computed<SuggestionsConfig | null>(() => {
72
- if (
73
- !config ||
74
- (config as { available?: string }).available === "disabled"
75
- ) {
76
- return null;
77
- }
78
- if (isDynamicConfig(config)) {
79
- return { ...config };
80
- }
81
- const normalizedSuggestions = normalizeStaticSuggestions(
82
- config.suggestions,
83
- );
84
- return { ...config, suggestions: normalizedSuggestions };
85
- });
86
- const serializedConfig = computed(() =>
87
- normalizedConfig.value ? JSON.stringify(normalizedConfig.value) : null,
88
- );
89
-
90
- const targetAgentId = computed(() => {
91
- if (!normalizedConfig.value) return resolvedConsumerAgentId.value;
92
- const consumer = (
93
- normalizedConfig.value as
94
- | StaticSuggestionsConfig
95
- | DynamicSuggestionsConfig
96
- ).consumerAgentId;
97
- if (!consumer || consumer === "*") return resolvedConsumerAgentId.value;
98
- return consumer;
99
- });
100
-
101
- const isGlobalConfig = computed(
102
- () =>
103
- rawConsumerAgentId.value === undefined ||
104
- rawConsumerAgentId.value === "*",
105
- );
106
- const pendingGlobalReload = ref(false);
107
- const pendingReloadTimer = ref<ReturnType<typeof setTimeout> | null>(null);
108
- const previousSerializedConfig = ref<string | null>(null);
109
-
110
- const flushPendingGlobalReload = () => {
111
- if (!pendingGlobalReload.value || !isGlobalConfig.value) {
112
- return;
113
- }
114
- const hasRunning = Object.values(copilotkit.value.agents ?? {}).some(
115
- (entry) => !!entry.agentId && entry.isRunning,
116
- );
117
- if (hasRunning) {
118
- pendingReloadTimer.value = setTimeout(() => {
119
- pendingReloadTimer.value = null;
120
- flushPendingGlobalReload();
121
- }, 0);
122
- return;
123
- }
124
-
125
- pendingGlobalReload.value = false;
126
- requestReload();
127
- };
128
-
129
- const requestReload = () => {
130
- if (!normalizedConfig.value) {
131
- return;
132
- }
133
-
134
- if (isGlobalConfig.value) {
135
- let skippedBecauseRunning = false;
136
- const agents = Object.values(copilotkit.value.agents ?? {});
137
- for (const entry of agents) {
138
- const aid = entry.agentId;
139
- if (!aid) {
140
- continue;
141
- }
142
- if (!entry.isRunning) {
143
- copilotkit.value.reloadSuggestions(aid);
144
- } else {
145
- skippedBecauseRunning = true;
146
- }
147
- }
148
- pendingGlobalReload.value = skippedBecauseRunning;
149
- if (skippedBecauseRunning && pendingReloadTimer.value === null) {
150
- pendingReloadTimer.value = setTimeout(() => {
151
- pendingReloadTimer.value = null;
152
- flushPendingGlobalReload();
153
- }, 0);
154
- }
155
- return;
156
- }
157
-
158
- if (targetAgentId.value) {
159
- copilotkit.value.reloadSuggestions(targetAgentId.value);
160
- }
161
- };
162
-
163
- watch(
164
- [() => copilotkit.value, serializedConfig],
165
- ([_core, serialized], _old, onCleanup) => {
166
- if (!serialized || !normalizedConfig.value) {
167
- return;
168
- }
169
-
170
- const core = copilotkit.value;
171
- const cfg = normalizedConfig.value;
172
- const id = core.addSuggestionsConfig(cfg);
173
- requestReload();
174
-
175
- onCleanup(() => core.removeSuggestionsConfig(id));
176
- },
177
- { immediate: true },
178
- );
179
-
180
- watch(
181
- [normalizedConfig, serializedConfig],
182
- ([cfg, serialized]) => {
183
- if (!cfg) {
184
- previousSerializedConfig.value = null;
185
- return;
186
- }
187
-
188
- if (serialized && previousSerializedConfig.value === serialized) {
189
- return;
190
- }
191
-
192
- if (serialized) {
193
- previousSerializedConfig.value = serialized;
194
- }
195
-
196
- requestReload();
197
- },
198
- { immediate: false },
199
- );
200
-
201
- watch(
202
- [normalizedConfig, () => extraDeps.length, ...extraDeps],
203
- ([cfg]) => {
204
- if (!cfg || extraDeps.length === 0) {
205
- return;
206
- }
207
- requestReload();
208
- },
209
- { immediate: true },
210
- );
33
+ const extraDeps = deps ?? [];
211
34
 
212
35
  watch(
213
- [() => copilotkit.value, pendingGlobalReload],
214
- (_vals, _old, onCleanup) => {
215
- onCleanup(() => {
216
- if (pendingReloadTimer.value !== null) {
217
- clearTimeout(pendingReloadTimer.value);
218
- pendingReloadTimer.value = null;
219
- }
36
+ () => copilotkit.value,
37
+ (core, _old, onCleanup) => {
38
+ const binding = ɵbindSuggestionsConfig(core, config, {
39
+ chatAgentId: chatAgentId.value,
220
40
  });
221
- },
222
- { immediate: true },
223
- );
224
-
225
- watch(
226
- [() => copilotkit.value, isGlobalConfig],
227
- ([core, global], _old, onCleanup) => {
228
- if (!global) {
229
- return;
230
- }
231
-
232
- const agentSubscriptions = Object.values(core.agents ?? {})
233
- .filter((agent): agent is NonNullable<typeof agent> => !!agent)
234
- .map((agent) =>
235
- agent.subscribe({
236
- onRunStartedEvent: flushPendingGlobalReload,
237
- onRunFinishedEvent: flushPendingGlobalReload,
238
- onRunFinalized: flushPendingGlobalReload,
239
- onRunFailed: flushPendingGlobalReload,
240
- onRunErrorEvent: flushPendingGlobalReload,
241
- }),
242
- );
243
-
41
+ // Deep, so a `reactive()` config that changes in place is applied. The
42
+ // binding compares configs by content, so other re-runs are no-ops.
43
+ const stopConfig = watch(
44
+ [() => config, chatAgentId],
45
+ ([nextConfig, nextChatAgentId]) => {
46
+ binding.update(nextConfig, { chatAgentId: nextChatAgentId });
47
+ },
48
+ { deep: true },
49
+ );
50
+ const stopDeps =
51
+ extraDeps.length > 0
52
+ ? watch(extraDeps, () => binding.reload())
53
+ : undefined;
244
54
  onCleanup(() => {
245
- for (const sub of agentSubscriptions) {
246
- sub.unsubscribe();
247
- }
55
+ stopConfig();
56
+ stopDeps?.();
57
+ binding.dispose();
248
58
  });
249
59
  },
250
60
  { immediate: true },