@copilotkit/vue 1.63.1 → 1.63.2-canary.1784918757

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.
@@ -1 +1 @@
1
- {"version":3,"file":"use-attachments.d.ts","sourceRoot":"","sources":["../../../src/v2/hooks/use-attachments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAUjD,OAAO,KAAK,EACV,UAAU,EAEV,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC/B,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,YAAY,EAAE,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC3C,YAAY,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtC,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,cAAc,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC3C,eAAe,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC5C,UAAU,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,UAAU,EAAE,CAAC;CACxC;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,GACzB,oBAAoB,CA8NtB"}
1
+ {"version":3,"file":"use-attachments.d.ts","sourceRoot":"","sources":["../../../src/v2/hooks/use-attachments.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAUjD,OAAO,KAAK,EACV,UAAU,EAEV,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC/B,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,YAAY,EAAE,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC3C,YAAY,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtC,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,cAAc,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC3C,eAAe,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC5C,UAAU,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,UAAU,EAAE,CAAC;CACxC;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,GACzB,oBAAoB,CA8NtB"}
package/dist/v2/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@copilotkit/core"),i=require("@ag-ui/client"),e=require("../use-render-activity-message-CIvEoQzA.cjs"),n=require("@copilotkit/shared");exports.A2UIActivityContentSchema=e.A2UIActivityContentSchema;exports.A2UISurfaceActivityRenderer=e._sfc_main;exports.A2UISurfaceActivityType=e.A2UISurfaceActivityType;exports.CopilotChat=e.CopilotChat;exports.CopilotChatAssistantMessage=e._sfc_main$1;exports.CopilotChatAttachmentQueue=e._sfc_main$2;exports.CopilotChatAttachmentRenderer=e._sfc_main$3;exports.CopilotChatAudioRecorder=e._sfc_main$4;exports.CopilotChatConfigurationProvider=e._sfc_main$5;exports.CopilotChatDefaultLabels=e.CopilotChatDefaultLabels;exports.CopilotChatInput=e._sfc_main$6;exports.CopilotChatMessageView=e._sfc_main$7;exports.CopilotChatReasoningMessage=e._sfc_main$8;exports.CopilotChatSuggestionPill=e._sfc_main$9;exports.CopilotChatSuggestionView=e._sfc_main$10;exports.CopilotChatToggleButton=e.CopilotChatToggleButton;exports.CopilotChatToggleButtonCloseIcon=e.CopilotChatToggleButtonCloseIcon;exports.CopilotChatToggleButtonOpenIcon=e.CopilotChatToggleButtonOpenIcon;exports.CopilotChatToolCallsView=e._sfc_main$11;exports.CopilotChatUserMessage=e._sfc_main$12;exports.CopilotChatView=e._sfc_main$13;exports.CopilotKitCoreVue=e.CopilotKitCoreVue;exports.CopilotKitInspector=e._sfc_main$14;exports.CopilotKitProvider=e._sfc_main$15;exports.CopilotModalHeader=e.CopilotModalHeader;exports.CopilotPopup=e._sfc_main$16;exports.CopilotPopupView=e.CopilotPopupView;exports.CopilotSidebar=e._sfc_main$17;exports.CopilotSidebarView=e.CopilotSidebarView;exports.CopilotThreadsDrawer=e._sfc_main$18;exports.GenerateSandboxedUiArgsSchema=e.GenerateSandboxedUiArgsSchema;exports.LicenseContextKey=e.LicenseContextKey;exports.MCPAppsActivityContentSchema=e.MCPAppsActivityContentSchema;exports.MCPAppsActivityRenderer=e.MCPAppsActivityRenderer;exports.MCPAppsActivityType=e.MCPAppsActivityType;exports.OpenGenerativeUIActivityRenderer=e.OpenGenerativeUIActivityRenderer;exports.OpenGenerativeUIActivityType=e.OpenGenerativeUIActivityType;exports.OpenGenerativeUIContentSchema=e.OpenGenerativeUIContentSchema;exports.OpenGenerativeUIRenderer=e.OpenGenerativeUIRenderer;exports.OpenGenerativeUIToolRenderer=e.OpenGenerativeUIToolRenderer;exports.UseAgentUpdate=e.UseAgentUpdate;exports.createA2UIMessageRenderer=e.createA2UIMessageRenderer;exports.createDefaultLicenseRef=e.createDefaultLicenseRef;exports.defineToolCallRenderer=e.defineToolCallRenderer;exports.extractCompleteStyles=e.extractCompleteStyles;exports.getOperationSurfaceId=e.getOperationSurfaceId;exports.processPartialHtml=e.processPartialHtml;exports.useAgent=e.useAgent;exports.useAgentContext=e.useAgentContext;exports.useAttachments=e.useAttachments;exports.useCapabilities=e.useCapabilities;exports.useComponent=e.useComponent;exports.useConfigureSuggestions=e.useConfigureSuggestions;exports.useCopilotChatConfiguration=e.useCopilotChatConfiguration;exports.useCopilotKit=e.useCopilotKit;exports.useDefaultRenderTool=e.useDefaultRenderTool;exports.useFrontendTool=e.useFrontendTool;exports.useHumanInTheLoop=e.useHumanInTheLoop;exports.useInterrupt=e.useInterrupt;exports.useKatexStyles=e.useKatexStyles;exports.useKeyboardHeight=e.useKeyboardHeight;exports.useLicenseContext=e.useLicenseContext;exports.useRenderActivityMessage=e.useRenderActivityMessage;exports.useRenderCustomMessages=e.useRenderCustomMessages;exports.useRenderTool=e.useRenderTool;exports.useSandboxFunctions=e.useSandboxFunctions;exports.useSuggestions=e.useSuggestions;exports.useThreads=e.useThreads;exports.vueBasicCatalog=e.vueBasicCatalog;Object.defineProperty(exports,"createLicenseContextValue",{enumerable:!0,get:()=>n.createLicenseContextValue});Object.keys(o).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>o[t]})});Object.keys(i).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>i[t]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@copilotkit/core"),i=require("@ag-ui/client"),e=require("../use-render-activity-message-DzgJkxe2.cjs"),n=require("@copilotkit/shared");exports.A2UIActivityContentSchema=e.A2UIActivityContentSchema;exports.A2UISurfaceActivityRenderer=e._sfc_main;exports.A2UISurfaceActivityType=e.A2UISurfaceActivityType;exports.CopilotChat=e.CopilotChat;exports.CopilotChatAssistantMessage=e._sfc_main$1;exports.CopilotChatAttachmentQueue=e._sfc_main$2;exports.CopilotChatAttachmentRenderer=e._sfc_main$3;exports.CopilotChatAudioRecorder=e._sfc_main$4;exports.CopilotChatConfigurationProvider=e._sfc_main$5;exports.CopilotChatDefaultLabels=e.CopilotChatDefaultLabels;exports.CopilotChatInput=e._sfc_main$6;exports.CopilotChatMessageView=e._sfc_main$7;exports.CopilotChatReasoningMessage=e._sfc_main$8;exports.CopilotChatSuggestionPill=e._sfc_main$9;exports.CopilotChatSuggestionView=e._sfc_main$10;exports.CopilotChatToggleButton=e.CopilotChatToggleButton;exports.CopilotChatToggleButtonCloseIcon=e.CopilotChatToggleButtonCloseIcon;exports.CopilotChatToggleButtonOpenIcon=e.CopilotChatToggleButtonOpenIcon;exports.CopilotChatToolCallsView=e._sfc_main$11;exports.CopilotChatUserMessage=e._sfc_main$12;exports.CopilotChatView=e._sfc_main$13;exports.CopilotKitCoreVue=e.CopilotKitCoreVue;exports.CopilotKitInspector=e._sfc_main$14;exports.CopilotKitProvider=e._sfc_main$15;exports.CopilotModalHeader=e.CopilotModalHeader;exports.CopilotPopup=e._sfc_main$16;exports.CopilotPopupView=e.CopilotPopupView;exports.CopilotSidebar=e._sfc_main$17;exports.CopilotSidebarView=e.CopilotSidebarView;exports.CopilotThreadsDrawer=e._sfc_main$18;exports.GenerateSandboxedUiArgsSchema=e.GenerateSandboxedUiArgsSchema;exports.LicenseContextKey=e.LicenseContextKey;exports.MCPAppsActivityContentSchema=e.MCPAppsActivityContentSchema;exports.MCPAppsActivityRenderer=e.MCPAppsActivityRenderer;exports.MCPAppsActivityType=e.MCPAppsActivityType;exports.OpenGenerativeUIActivityRenderer=e.OpenGenerativeUIActivityRenderer;exports.OpenGenerativeUIActivityType=e.OpenGenerativeUIActivityType;exports.OpenGenerativeUIContentSchema=e.OpenGenerativeUIContentSchema;exports.OpenGenerativeUIRenderer=e.OpenGenerativeUIRenderer;exports.OpenGenerativeUIToolRenderer=e.OpenGenerativeUIToolRenderer;exports.UseAgentUpdate=e.UseAgentUpdate;exports.createA2UIMessageRenderer=e.createA2UIMessageRenderer;exports.createDefaultLicenseRef=e.createDefaultLicenseRef;exports.defineToolCallRenderer=e.defineToolCallRenderer;exports.extractCompleteStyles=e.extractCompleteStyles;exports.getOperationSurfaceId=e.getOperationSurfaceId;exports.processPartialHtml=e.processPartialHtml;exports.useAgent=e.useAgent;exports.useAgentContext=e.useAgentContext;exports.useAttachments=e.useAttachments;exports.useCapabilities=e.useCapabilities;exports.useComponent=e.useComponent;exports.useConfigureSuggestions=e.useConfigureSuggestions;exports.useCopilotChatConfiguration=e.useCopilotChatConfiguration;exports.useCopilotKit=e.useCopilotKit;exports.useDefaultRenderTool=e.useDefaultRenderTool;exports.useFrontendTool=e.useFrontendTool;exports.useHumanInTheLoop=e.useHumanInTheLoop;exports.useInterrupt=e.useInterrupt;exports.useKatexStyles=e.useKatexStyles;exports.useKeyboardHeight=e.useKeyboardHeight;exports.useLicenseContext=e.useLicenseContext;exports.useRenderActivityMessage=e.useRenderActivityMessage;exports.useRenderCustomMessages=e.useRenderCustomMessages;exports.useRenderTool=e.useRenderTool;exports.useSandboxFunctions=e.useSandboxFunctions;exports.useSuggestions=e.useSuggestions;exports.useThreads=e.useThreads;exports.vueBasicCatalog=e.vueBasicCatalog;Object.defineProperty(exports,"createLicenseContextValue",{enumerable:!0,get:()=>n.createLicenseContextValue});Object.keys(o).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>o[t]})});Object.keys(i).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>i[t]})});
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/v2/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from "@copilotkit/core";
2
2
  export * from "@ag-ui/client";
3
- import { A as o, _ as i, a as n, C, b as r, c as p, d as l, e as u, f as c, g, h as d, i as h, j as A, k as v, l as S, m, n as y, o as I, p as f, q as R, r as T, s as U, t as x, u as M, v as P, w as b, x as V, y as w, z as K, B as O, G, L, M as B, D, E as H, O as F, F as Q, H as j, I as k, J as q, U as z, K as E, N as J, P as N, Q as W, R as X, S as Y, T as Z, V as _, W as $, X as ee, Y as ae, Z as te, $ as se, a0 as oe, a1 as ie, a2 as ne, a3 as Ce, a4 as re, a5 as pe, a6 as le, a7 as ue, a8 as ce, a9 as ge, aa as de, ab as he, ac as Ae, ad as ve, ae as Se } from "../use-render-activity-message-CbKeqosY.js";
3
+ import { A as o, _ as i, a as n, C, b as r, c as p, d as l, e as u, f as c, g, h as d, i as h, j as A, k as v, l as S, m, n as y, o as I, p as f, q as R, r as T, s as U, t as x, u as M, v as P, w as b, x as V, y as w, z as K, B as O, G, L, M as B, D, E as H, O as F, F as Q, H as j, I as k, J as q, U as z, K as E, N as J, P as N, Q as W, R as X, S as Y, T as Z, V as _, W as $, X as ee, Y as ae, Z as te, $ as se, a0 as oe, a1 as ie, a2 as ne, a3 as Ce, a4 as re, a5 as pe, a6 as le, a7 as ue, a8 as ce, a9 as ge, aa as de, ab as he, ac as Ae, ad as ve, ae as Se } from "../use-render-activity-message-CszKQeFs.js";
4
4
  import { createLicenseContextValue as ye } from "@copilotkit/shared";
5
5
  export {
6
6
  o as A2UIActivityContentSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/vue",
3
- "version": "1.63.1",
3
+ "version": "1.63.2-canary.1784918757",
4
4
  "private": false,
5
5
  "description": "Vue 3 components and composables for CopilotKit",
6
6
  "keywords": [
@@ -70,10 +70,10 @@
70
70
  "streamdown-vue": "^1.0.29",
71
71
  "zod": "^3.25.75",
72
72
  "zod-to-json-schema": "^3.24.5",
73
- "@copilotkit/core": "1.63.1",
74
- "@copilotkit/shared": "1.63.1",
75
- "@copilotkit/web-inspector": "1.63.1",
76
- "@copilotkit/web-components": "1.63.1"
73
+ "@copilotkit/shared": "1.63.2-canary.1784918757",
74
+ "@copilotkit/web-components": "1.63.2-canary.1784918757",
75
+ "@copilotkit/web-inspector": "1.63.2-canary.1784918757",
76
+ "@copilotkit/core": "1.63.2-canary.1784918757"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@eslint/js": "^9.39.2",
@@ -1,24 +1,89 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
- import { fireEvent, waitFor } from "@testing-library/vue";
3
- import type { BaseEvent, RunAgentInput } from "@ag-ui/client";
4
- import { Observable, EMPTY } from "rxjs";
2
+ import { fireEvent, screen, waitFor } from "@testing-library/vue";
3
+ import type {
4
+ AgentSubscriber,
5
+ BaseEvent,
6
+ Message,
7
+ RunAgentInput,
8
+ RunAgentParameters,
9
+ RunAgentResult,
10
+ } from "@ag-ui/client";
11
+ import type { Observable } from "rxjs";
12
+ import { EMPTY } from "rxjs";
5
13
  import CopilotChat from "../CopilotChat.vue";
6
14
  import {
7
15
  MockStepwiseAgent,
8
16
  renderWithCopilotKit,
9
17
  } from "../../../__tests__/utils/test-helpers";
10
- import type { AttachmentUploadError } from "@copilotkit/shared";
18
+ import type { AttachmentUploadError, InputContent } from "@copilotkit/shared";
11
19
 
12
20
  class NoopAgent extends MockStepwiseAgent {
13
- run(_input: RunAgentInput): Observable<BaseEvent> {
21
+ run(): Observable<BaseEvent> {
14
22
  return EMPTY;
15
23
  }
16
24
 
17
- connect(_input: RunAgentInput): Observable<BaseEvent> {
25
+ connect(): Observable<BaseEvent> {
18
26
  return EMPTY;
19
27
  }
20
28
  }
21
29
 
30
+ class CloneCapturingAgent extends NoopAgent {
31
+ constructor(
32
+ private readonly inputs: RunAgentInput[] = [],
33
+ public readonly addedMessages: Message[] = [],
34
+ public readonly runErrors: unknown[] = [],
35
+ ) {
36
+ super();
37
+ }
38
+
39
+ get capturedInputs() {
40
+ return this.inputs;
41
+ }
42
+
43
+ override clone(): CloneCapturingAgent {
44
+ const clone = new CloneCapturingAgent(
45
+ this.inputs,
46
+ this.addedMessages,
47
+ this.runErrors,
48
+ );
49
+ clone.agentId = this.agentId;
50
+ clone.threadId = this.threadId;
51
+ return clone;
52
+ }
53
+
54
+ override addMessage(message: Message): void {
55
+ this.addedMessages.push(message);
56
+ super.addMessage(message);
57
+ }
58
+
59
+ override async runAgent(
60
+ parameters: RunAgentParameters = {},
61
+ subscriber?: AgentSubscriber,
62
+ ): Promise<RunAgentResult> {
63
+ let input: RunAgentInput;
64
+ try {
65
+ input = this.prepareRunAgentInput(parameters);
66
+ } catch (error) {
67
+ this.runErrors.push(error);
68
+ throw error;
69
+ }
70
+ this.inputs.push(input);
71
+ await subscriber?.onRunInitialized?.({
72
+ agent: this,
73
+ messages: this.messages,
74
+ state: this.state,
75
+ input,
76
+ });
77
+ await subscriber?.onRunFinalized?.({
78
+ agent: this,
79
+ messages: this.messages,
80
+ state: this.state,
81
+ input,
82
+ });
83
+ return { newMessages: [] };
84
+ }
85
+ }
86
+
22
87
  function createFile(name: string, size: number, type: string): File {
23
88
  const content = new Uint8Array(size);
24
89
  return new File([content], name, { type });
@@ -29,9 +94,10 @@ function renderChat(props: {
29
94
  accept?: string;
30
95
  maxSize?: number;
31
96
  onUpload?: (file: File) => unknown;
97
+ agent?: NoopAgent;
32
98
  }) {
33
- const agent = new NoopAgent();
34
- return renderWithCopilotKit({
99
+ const agent = props.agent ?? new NoopAgent();
100
+ const result = renderWithCopilotKit({
35
101
  agent,
36
102
  children: {
37
103
  components: { CopilotChat },
@@ -54,14 +120,17 @@ function renderChat(props: {
54
120
  `,
55
121
  },
56
122
  });
123
+ return { ...result, agent };
57
124
  }
58
125
 
59
- async function dropFiles(container: HTMLElement, files: File[]) {
60
- const dropTarget = container.querySelector(
61
- '[data-testid="copilot-chat-view"]',
62
- );
126
+ async function dropFiles(
127
+ container: HTMLElement,
128
+ files: File[],
129
+ selector = '[data-testid="copilot-chat-view"]',
130
+ ) {
131
+ const dropTarget = container.querySelector(selector);
63
132
  if (!dropTarget) {
64
- throw new Error("Could not find copilot-chat drop target");
133
+ throw new Error(`Could not find drop target: ${selector}`);
65
134
  }
66
135
 
67
136
  await fireEvent.dragOver(dropTarget, {
@@ -234,4 +303,106 @@ describe("CopilotChat attachments", () => {
234
303
  expect(onUploadFailed).not.toHaveBeenCalled();
235
304
  });
236
305
  });
306
+
307
+ describe("Vue-specific reactive semantics", () => {
308
+ it("submits custom uploaded attachments through clone-safe agent input", async () => {
309
+ const agent = new CloneCapturingAgent();
310
+ const props = {
311
+ accept: "application/pdf",
312
+ onUpload: async () => ({
313
+ type: "url" as const,
314
+ value: "https://example.com/report.pdf",
315
+ mimeType: "application/pdf",
316
+ metadata: { source: "test-upload" },
317
+ }),
318
+ };
319
+ const { container } = renderWithCopilotKit({
320
+ agent,
321
+ children: {
322
+ components: { CopilotChat },
323
+ data() {
324
+ return { props };
325
+ },
326
+ template: `
327
+ <div style="height: 400px;">
328
+ <CopilotChat
329
+ :welcome-screen="false"
330
+ :attachments="{
331
+ enabled: true,
332
+ accept: props.accept,
333
+ onUpload: props.onUpload
334
+ }"
335
+ >
336
+ <template #chat-view="{ attachments, onDragOver, onDrop, onSubmitMessage }">
337
+ <div
338
+ data-testid="attachment-submit-harness"
339
+ @dragover="onDragOver?.($event)"
340
+ @drop="onDrop?.($event)"
341
+ >
342
+ <span
343
+ v-for="attachment in attachments"
344
+ :key="attachment.id"
345
+ >
346
+ {{ attachment.filename }}
347
+ </span>
348
+ <button
349
+ data-testid="submit-attachment-message"
350
+ @click="onSubmitMessage('Review this report')"
351
+ >
352
+ submit
353
+ </button>
354
+ </div>
355
+ </template>
356
+ </CopilotChat>
357
+ </div>
358
+ `,
359
+ },
360
+ });
361
+
362
+ await dropFiles(
363
+ container,
364
+ [createFile("report.pdf", 128, "application/pdf")],
365
+ '[data-testid="attachment-submit-harness"]',
366
+ );
367
+
368
+ await waitFor(() => {
369
+ expect(screen.getByText("report.pdf")).toBeDefined();
370
+ });
371
+
372
+ await fireEvent.click(screen.getByTestId("submit-attachment-message"));
373
+
374
+ await waitFor(() => {
375
+ expect(agent.addedMessages).toHaveLength(1);
376
+ });
377
+ expect(agent.runErrors).toEqual([]);
378
+
379
+ await waitFor(() => {
380
+ expect(agent.capturedInputs).toHaveLength(1);
381
+ });
382
+
383
+ const userMessage = agent.capturedInputs[0].messages.find(
384
+ (message) => message.role === "user",
385
+ );
386
+ expect(userMessage).toBeDefined();
387
+ expect(Array.isArray(userMessage?.content)).toBe(true);
388
+
389
+ const content = userMessage?.content as InputContent[];
390
+ expect(() => structuredClone(content)).not.toThrow();
391
+ expect(content).toEqual([
392
+ { type: "text", text: "Review this report" },
393
+ {
394
+ type: "document",
395
+ source: {
396
+ type: "url",
397
+ value: "https://example.com/report.pdf",
398
+ mimeType: "application/pdf",
399
+ },
400
+ metadata: {
401
+ filename: "report.pdf",
402
+ source: "test-upload",
403
+ },
404
+ },
405
+ ]);
406
+ });
407
+ });
237
408
  });
@@ -1,4 +1,12 @@
1
- import { nextTick, defineComponent, h, onUpdated, ref } from "vue";
1
+ import {
2
+ nextTick,
3
+ defineComponent,
4
+ h,
5
+ isProxy,
6
+ isReactive,
7
+ onUpdated,
8
+ ref,
9
+ } from "vue";
2
10
  import { render } from "@testing-library/vue";
3
11
  import { describe, expect, it, vi } from "vitest";
4
12
  import type {
@@ -178,4 +186,39 @@ describe("useAttachments", () => {
178
186
  expect(onUploadFailed.mock.calls[0][0].message).toContain("audio/*");
179
187
  });
180
188
  });
189
+
190
+ describe("Vue-specific reactive semantics", () => {
191
+ it("keeps uploaded attachment source cloneable and non-reactive", async () => {
192
+ const harness = mountHook({
193
+ enabled: true,
194
+ onUpload: async () => ({
195
+ type: "url",
196
+ value: "https://example.com/report.pdf",
197
+ mimeType: "application/pdf",
198
+ metadata: { source: "test-upload" },
199
+ }),
200
+ });
201
+
202
+ await harness.api.processFiles([
203
+ createFile("report.pdf", 128, "application/pdf"),
204
+ ]);
205
+
206
+ const [attachment] = harness.api.attachments.value;
207
+ expect(attachment).toBeDefined();
208
+ expect(attachment.source).toEqual({
209
+ type: "url",
210
+ value: "https://example.com/report.pdf",
211
+ mimeType: "application/pdf",
212
+ });
213
+ expect(isProxy(attachment.source)).toBe(false);
214
+ expect(isReactive(attachment.source)).toBe(false);
215
+ expect(() => structuredClone(attachment.source)).not.toThrow();
216
+
217
+ const [consumed] = harness.api.consumeAttachments();
218
+ expect(consumed).toBeDefined();
219
+ expect(isProxy(consumed.source)).toBe(false);
220
+ expect(isReactive(consumed.source)).toBe(false);
221
+ expect(() => structuredClone(consumed.source)).not.toThrow();
222
+ });
223
+ });
181
224
  });
@@ -1,4 +1,12 @@
1
- import { computed, onBeforeUnmount, onMounted, ref, toValue, watch } from "vue";
1
+ import {
2
+ computed,
3
+ onBeforeUnmount,
4
+ onMounted,
5
+ ref,
6
+ shallowRef,
7
+ toValue,
8
+ watch,
9
+ } from "vue";
2
10
  import type { MaybeRefOrGetter, Ref } from "vue";
3
11
  import {
4
12
  randomUUID,
@@ -37,12 +45,12 @@ export interface UseAttachmentsReturn {
37
45
  export function useAttachments(
38
46
  props: UseAttachmentsProps,
39
47
  ): UseAttachmentsReturn {
40
- const attachments = ref<Attachment[]>([]);
48
+ const attachments = shallowRef<Attachment[]>([]);
41
49
  const dragOver = ref(false);
42
50
  const fileInputRef = ref<HTMLInputElement | null>(null);
43
51
  const containerRef = ref<HTMLElement | null>(null);
44
52
  const configRef = ref<AttachmentsConfig | undefined>(toValue(props.config));
45
- const attachmentsRef = ref<Attachment[]>(attachments.value);
53
+ const attachmentsRef = shallowRef<Attachment[]>(attachments.value);
46
54
  const enabled = computed(() => configRef.value?.enabled ?? false);
47
55
 
48
56
  watch(