@standardagents/vue 0.10.1-dev.cea2b66 → 0.10.1-dev.d2d335e

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.
package/dist/index.cjs CHANGED
@@ -35,6 +35,18 @@ function useStandardAgents() {
35
35
 
36
36
  // src/composables/useThreadSetup.ts
37
37
  var uploadManager = new client.FileUploadManager();
38
+ function fileToBase64(file) {
39
+ return new Promise((resolve, reject) => {
40
+ const reader = new FileReader();
41
+ reader.onload = () => {
42
+ const result = reader.result;
43
+ const base64 = result.split(",")[1];
44
+ resolve(base64);
45
+ };
46
+ reader.onerror = () => reject(new Error("Failed to read file"));
47
+ reader.readAsDataURL(file);
48
+ });
49
+ }
38
50
  function useThreadSetup(threadId, options = {}) {
39
51
  const {
40
52
  preload = true,
@@ -58,6 +70,7 @@ function useThreadSetup(threadId, options = {}) {
58
70
  const isLoading = vue.ref(preload);
59
71
  const error = vue.ref(null);
60
72
  const pendingFiles = vue.ref([]);
73
+ const attachments = vue.ref([]);
61
74
  const committedFiles = vue.computed(() => {
62
75
  return client.messagesToFiles(messages.value);
63
76
  });
@@ -128,44 +141,53 @@ function useThreadSetup(threadId, options = {}) {
128
141
  }, { depth, includeSilent });
129
142
  connectionManager.connect();
130
143
  }
131
- function buildOptimisticAttachments(attachmentPaths) {
132
- if (!attachmentPaths || attachmentPaths.length === 0) return null;
133
- const refs = attachmentPaths.map((path) => {
134
- const file = pendingFiles.value.find((f) => f.path === path);
135
- const ref2 = {
136
- id: path,
137
- type: "file",
138
- path,
139
- name: file?.name || path.split("/").pop() || "file",
140
- mimeType: file?.mimeType || "application/octet-stream",
141
- size: file?.size || 0
142
- };
143
- if (file?.isImage) {
144
- if (file.width) ref2.width = file.width;
145
- if (file.height) ref2.height = file.height;
146
- if (file.localPreviewUrl) ref2.localPreviewUrl = file.localPreviewUrl;
147
- }
148
- return ref2;
149
- });
150
- return JSON.stringify(refs);
151
- }
152
144
  async function sendMessage(payload) {
153
145
  const optimisticId = `optimistic-${crypto.randomUUID()}`;
146
+ const optimisticAttachments = attachments.value.map((a) => ({
147
+ id: a.id,
148
+ type: "file",
149
+ path: "",
150
+ // No path yet - will be assigned by server
151
+ name: a.name,
152
+ mimeType: a.mimeType,
153
+ size: a.size,
154
+ width: a.width,
155
+ height: a.height,
156
+ localPreviewUrl: a.previewUrl || void 0
157
+ }));
154
158
  const optimisticMessage = {
155
159
  id: optimisticId,
156
160
  role: payload.role,
157
161
  content: payload.content,
158
- attachments: buildOptimisticAttachments(payload.attachments),
162
+ attachments: optimisticAttachments.length > 0 ? JSON.stringify(optimisticAttachments) : null,
159
163
  created_at: Date.now() * 1e3,
160
164
  // microseconds
161
165
  status: "pending"
162
166
  };
163
167
  messages.value = [...messages.value, optimisticMessage];
168
+ const currentAttachments = [...attachments.value];
169
+ attachments.value = [];
164
170
  try {
165
- const result = await client$1.sendMessage(threadId, payload);
171
+ const attachmentPayloads = await Promise.all(
172
+ currentAttachments.map(async (a) => ({
173
+ name: a.name,
174
+ mimeType: a.mimeType,
175
+ data: await fileToBase64(a.file),
176
+ width: a.width,
177
+ height: a.height
178
+ }))
179
+ );
180
+ const result = await client$1.sendMessage(threadId, {
181
+ ...payload,
182
+ attachments: attachmentPayloads.length > 0 ? attachmentPayloads : void 0
183
+ });
166
184
  messages.value = messages.value.filter((m) => m.id !== optimisticId);
185
+ currentAttachments.forEach((a) => {
186
+ if (a.previewUrl) URL.revokeObjectURL(a.previewUrl);
187
+ });
167
188
  return result;
168
189
  } catch (err) {
190
+ attachments.value = currentAttachments;
169
191
  messages.value = messages.value.filter((m) => m.id !== optimisticId);
170
192
  error.value = err instanceof Error ? err : new Error(String(err));
171
193
  throw err;
@@ -218,6 +240,35 @@ function useThreadSetup(threadId, options = {}) {
218
240
  }
219
241
  return null;
220
242
  }
243
+ function addAttachment(input) {
244
+ const filesToAdd = input instanceof FileList ? Array.from(input) : Array.isArray(input) ? input : [input];
245
+ const newAttachments = filesToAdd.map((file) => {
246
+ const isImage = file.type.startsWith("image/");
247
+ return {
248
+ id: crypto.randomUUID(),
249
+ file,
250
+ name: file.name,
251
+ mimeType: file.type,
252
+ size: file.size,
253
+ isImage,
254
+ previewUrl: isImage ? URL.createObjectURL(file) : null
255
+ };
256
+ });
257
+ attachments.value = [...attachments.value, ...newAttachments];
258
+ }
259
+ function removeAttachment(id) {
260
+ const attachment = attachments.value.find((a) => a.id === id);
261
+ if (attachment?.previewUrl) {
262
+ URL.revokeObjectURL(attachment.previewUrl);
263
+ }
264
+ attachments.value = attachments.value.filter((a) => a.id !== id);
265
+ }
266
+ function clearAttachments() {
267
+ attachments.value.forEach((a) => {
268
+ if (a.previewUrl) URL.revokeObjectURL(a.previewUrl);
269
+ });
270
+ attachments.value = [];
271
+ }
221
272
  function onEvent(eventType, callback) {
222
273
  if (!eventHandlers.has(eventType)) {
223
274
  eventHandlers.set(eventType, /* @__PURE__ */ new Set());
@@ -242,6 +293,9 @@ function useThreadSetup(threadId, options = {}) {
242
293
  vue.onUnmounted(() => {
243
294
  connectionManager?.disconnect();
244
295
  eventHandlers.clear();
296
+ attachments.value.forEach((a) => {
297
+ if (a.previewUrl) URL.revokeObjectURL(a.previewUrl);
298
+ });
245
299
  });
246
300
  return {
247
301
  threadId,
@@ -260,13 +314,18 @@ function useThreadSetup(threadId, options = {}) {
260
314
  onEvent,
261
315
  subscribeToEvent: onEvent,
262
316
  // alias
263
- // File management
317
+ // File management (uploads to filesystem)
264
318
  files,
265
319
  addFiles,
266
320
  removeFile,
267
321
  getFileUrl,
268
322
  getThumbnailUrl,
269
- getPreviewUrl
323
+ getPreviewUrl,
324
+ // Attachment management (sent inline with messages)
325
+ attachments,
326
+ addAttachment,
327
+ removeAttachment,
328
+ clearAttachments
270
329
  };
271
330
  }
272
331
 
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as vue from 'vue';
2
2
  import { Ref, ComputedRef, InjectionKey, Plugin, PropType } from 'vue';
3
3
  import * as _standardagents_client from '@standardagents/client';
4
- import { AgentBuilderClient, SendMessagePayload, Message, ThreadFile } from '@standardagents/client';
5
- export { AgentBuilderClient, AttachmentRef, ConnectionStatus, FileUploadManager, GetMessagesOptions, Message, SendMessagePayload, Thread, ThreadConnectionCallbacks, ThreadConnectionManager, ThreadConnectionOptions, ThreadFile, ThreadMessage, WorkItem, WorkMessage, generatePendingFileId, isImageMimeType, messagesToFiles, parseAttachments, readFileAsDataUrl, transformToWorkblocks } from '@standardagents/client';
4
+ import { AgentBuilderClient, SendMessagePayload, Message, ThreadFile, PendingAttachment } from '@standardagents/client';
5
+ export { AgentBuilderClient, AttachmentPayload, AttachmentRef, ConnectionStatus, FileUploadManager, GetMessagesOptions, Message, PendingAttachment, SendMessagePayload, Thread, ThreadConnectionCallbacks, ThreadConnectionManager, ThreadConnectionOptions, ThreadFile, ThreadMessage, WorkItem, WorkMessage, generatePendingFileId, isImageMimeType, messagesToFiles, parseAttachments, readFileAsDataUrl, transformToWorkblocks } from '@standardagents/client';
6
6
 
7
7
  /**
8
8
  * Configuration for the StandardAgents plugin
@@ -57,8 +57,8 @@ interface ThreadContext {
57
57
  loading: Ref<boolean>;
58
58
  /** Any error that occurred */
59
59
  error: Ref<Error | null>;
60
- /** Send a message to the thread */
61
- sendMessage: (payload: SendMessagePayload) => Promise<Message>;
60
+ /** Send a message to the thread (auto-includes pending attachments) */
61
+ sendMessage: (payload: Omit<SendMessagePayload, 'attachments'>) => Promise<Message>;
62
62
  /** Stop the current execution */
63
63
  stopExecution: () => Promise<void>;
64
64
  /** Subscribe to custom events */
@@ -67,7 +67,7 @@ interface ThreadContext {
67
67
  subscribeToEvent: <T = unknown>(eventType: string, callback: (data: T) => void) => () => void;
68
68
  /** All files in the thread (pending uploads + committed from messages) */
69
69
  files: ComputedRef<ThreadFile[]>;
70
- /** Add files and start uploading immediately */
70
+ /** Add files and start uploading immediately to filesystem */
71
71
  addFiles: (files: File[] | FileList) => void;
72
72
  /** Remove a pending file (cannot remove committed files) */
73
73
  removeFile: (id: string) => void;
@@ -77,6 +77,14 @@ interface ThreadContext {
77
77
  getThumbnailUrl: (file: ThreadFile) => string;
78
78
  /** Get preview URL - localPreviewUrl for pending images, thumbnail for committed */
79
79
  getPreviewUrl: (file: ThreadFile) => string | null;
80
+ /** Pending attachments to be sent with next message */
81
+ attachments: Ref<PendingAttachment[]>;
82
+ /** Add attachment(s) to be sent with next message (no upload, stored locally) */
83
+ addAttachment: (files: File | File[] | FileList) => void;
84
+ /** Remove a pending attachment */
85
+ removeAttachment: (id: string) => void;
86
+ /** Clear all pending attachments */
87
+ clearAttachments: () => void;
80
88
  }
81
89
  /**
82
90
  * Options for useThread composable
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as vue from 'vue';
2
2
  import { Ref, ComputedRef, InjectionKey, Plugin, PropType } from 'vue';
3
3
  import * as _standardagents_client from '@standardagents/client';
4
- import { AgentBuilderClient, SendMessagePayload, Message, ThreadFile } from '@standardagents/client';
5
- export { AgentBuilderClient, AttachmentRef, ConnectionStatus, FileUploadManager, GetMessagesOptions, Message, SendMessagePayload, Thread, ThreadConnectionCallbacks, ThreadConnectionManager, ThreadConnectionOptions, ThreadFile, ThreadMessage, WorkItem, WorkMessage, generatePendingFileId, isImageMimeType, messagesToFiles, parseAttachments, readFileAsDataUrl, transformToWorkblocks } from '@standardagents/client';
4
+ import { AgentBuilderClient, SendMessagePayload, Message, ThreadFile, PendingAttachment } from '@standardagents/client';
5
+ export { AgentBuilderClient, AttachmentPayload, AttachmentRef, ConnectionStatus, FileUploadManager, GetMessagesOptions, Message, PendingAttachment, SendMessagePayload, Thread, ThreadConnectionCallbacks, ThreadConnectionManager, ThreadConnectionOptions, ThreadFile, ThreadMessage, WorkItem, WorkMessage, generatePendingFileId, isImageMimeType, messagesToFiles, parseAttachments, readFileAsDataUrl, transformToWorkblocks } from '@standardagents/client';
6
6
 
7
7
  /**
8
8
  * Configuration for the StandardAgents plugin
@@ -57,8 +57,8 @@ interface ThreadContext {
57
57
  loading: Ref<boolean>;
58
58
  /** Any error that occurred */
59
59
  error: Ref<Error | null>;
60
- /** Send a message to the thread */
61
- sendMessage: (payload: SendMessagePayload) => Promise<Message>;
60
+ /** Send a message to the thread (auto-includes pending attachments) */
61
+ sendMessage: (payload: Omit<SendMessagePayload, 'attachments'>) => Promise<Message>;
62
62
  /** Stop the current execution */
63
63
  stopExecution: () => Promise<void>;
64
64
  /** Subscribe to custom events */
@@ -67,7 +67,7 @@ interface ThreadContext {
67
67
  subscribeToEvent: <T = unknown>(eventType: string, callback: (data: T) => void) => () => void;
68
68
  /** All files in the thread (pending uploads + committed from messages) */
69
69
  files: ComputedRef<ThreadFile[]>;
70
- /** Add files and start uploading immediately */
70
+ /** Add files and start uploading immediately to filesystem */
71
71
  addFiles: (files: File[] | FileList) => void;
72
72
  /** Remove a pending file (cannot remove committed files) */
73
73
  removeFile: (id: string) => void;
@@ -77,6 +77,14 @@ interface ThreadContext {
77
77
  getThumbnailUrl: (file: ThreadFile) => string;
78
78
  /** Get preview URL - localPreviewUrl for pending images, thumbnail for committed */
79
79
  getPreviewUrl: (file: ThreadFile) => string | null;
80
+ /** Pending attachments to be sent with next message */
81
+ attachments: Ref<PendingAttachment[]>;
82
+ /** Add attachment(s) to be sent with next message (no upload, stored locally) */
83
+ addAttachment: (files: File | File[] | FileList) => void;
84
+ /** Remove a pending attachment */
85
+ removeAttachment: (id: string) => void;
86
+ /** Clear all pending attachments */
87
+ clearAttachments: () => void;
80
88
  }
81
89
  /**
82
90
  * Options for useThread composable
package/dist/index.js CHANGED
@@ -34,6 +34,18 @@ function useStandardAgents() {
34
34
 
35
35
  // src/composables/useThreadSetup.ts
36
36
  var uploadManager = new FileUploadManager();
37
+ function fileToBase64(file) {
38
+ return new Promise((resolve, reject) => {
39
+ const reader = new FileReader();
40
+ reader.onload = () => {
41
+ const result = reader.result;
42
+ const base64 = result.split(",")[1];
43
+ resolve(base64);
44
+ };
45
+ reader.onerror = () => reject(new Error("Failed to read file"));
46
+ reader.readAsDataURL(file);
47
+ });
48
+ }
37
49
  function useThreadSetup(threadId, options = {}) {
38
50
  const {
39
51
  preload = true,
@@ -57,6 +69,7 @@ function useThreadSetup(threadId, options = {}) {
57
69
  const isLoading = ref(preload);
58
70
  const error = ref(null);
59
71
  const pendingFiles = ref([]);
72
+ const attachments = ref([]);
60
73
  const committedFiles = computed(() => {
61
74
  return messagesToFiles(messages.value);
62
75
  });
@@ -127,44 +140,53 @@ function useThreadSetup(threadId, options = {}) {
127
140
  }, { depth, includeSilent });
128
141
  connectionManager.connect();
129
142
  }
130
- function buildOptimisticAttachments(attachmentPaths) {
131
- if (!attachmentPaths || attachmentPaths.length === 0) return null;
132
- const refs = attachmentPaths.map((path) => {
133
- const file = pendingFiles.value.find((f) => f.path === path);
134
- const ref2 = {
135
- id: path,
136
- type: "file",
137
- path,
138
- name: file?.name || path.split("/").pop() || "file",
139
- mimeType: file?.mimeType || "application/octet-stream",
140
- size: file?.size || 0
141
- };
142
- if (file?.isImage) {
143
- if (file.width) ref2.width = file.width;
144
- if (file.height) ref2.height = file.height;
145
- if (file.localPreviewUrl) ref2.localPreviewUrl = file.localPreviewUrl;
146
- }
147
- return ref2;
148
- });
149
- return JSON.stringify(refs);
150
- }
151
143
  async function sendMessage(payload) {
152
144
  const optimisticId = `optimistic-${crypto.randomUUID()}`;
145
+ const optimisticAttachments = attachments.value.map((a) => ({
146
+ id: a.id,
147
+ type: "file",
148
+ path: "",
149
+ // No path yet - will be assigned by server
150
+ name: a.name,
151
+ mimeType: a.mimeType,
152
+ size: a.size,
153
+ width: a.width,
154
+ height: a.height,
155
+ localPreviewUrl: a.previewUrl || void 0
156
+ }));
153
157
  const optimisticMessage = {
154
158
  id: optimisticId,
155
159
  role: payload.role,
156
160
  content: payload.content,
157
- attachments: buildOptimisticAttachments(payload.attachments),
161
+ attachments: optimisticAttachments.length > 0 ? JSON.stringify(optimisticAttachments) : null,
158
162
  created_at: Date.now() * 1e3,
159
163
  // microseconds
160
164
  status: "pending"
161
165
  };
162
166
  messages.value = [...messages.value, optimisticMessage];
167
+ const currentAttachments = [...attachments.value];
168
+ attachments.value = [];
163
169
  try {
164
- const result = await client.sendMessage(threadId, payload);
170
+ const attachmentPayloads = await Promise.all(
171
+ currentAttachments.map(async (a) => ({
172
+ name: a.name,
173
+ mimeType: a.mimeType,
174
+ data: await fileToBase64(a.file),
175
+ width: a.width,
176
+ height: a.height
177
+ }))
178
+ );
179
+ const result = await client.sendMessage(threadId, {
180
+ ...payload,
181
+ attachments: attachmentPayloads.length > 0 ? attachmentPayloads : void 0
182
+ });
165
183
  messages.value = messages.value.filter((m) => m.id !== optimisticId);
184
+ currentAttachments.forEach((a) => {
185
+ if (a.previewUrl) URL.revokeObjectURL(a.previewUrl);
186
+ });
166
187
  return result;
167
188
  } catch (err) {
189
+ attachments.value = currentAttachments;
168
190
  messages.value = messages.value.filter((m) => m.id !== optimisticId);
169
191
  error.value = err instanceof Error ? err : new Error(String(err));
170
192
  throw err;
@@ -217,6 +239,35 @@ function useThreadSetup(threadId, options = {}) {
217
239
  }
218
240
  return null;
219
241
  }
242
+ function addAttachment(input) {
243
+ const filesToAdd = input instanceof FileList ? Array.from(input) : Array.isArray(input) ? input : [input];
244
+ const newAttachments = filesToAdd.map((file) => {
245
+ const isImage = file.type.startsWith("image/");
246
+ return {
247
+ id: crypto.randomUUID(),
248
+ file,
249
+ name: file.name,
250
+ mimeType: file.type,
251
+ size: file.size,
252
+ isImage,
253
+ previewUrl: isImage ? URL.createObjectURL(file) : null
254
+ };
255
+ });
256
+ attachments.value = [...attachments.value, ...newAttachments];
257
+ }
258
+ function removeAttachment(id) {
259
+ const attachment = attachments.value.find((a) => a.id === id);
260
+ if (attachment?.previewUrl) {
261
+ URL.revokeObjectURL(attachment.previewUrl);
262
+ }
263
+ attachments.value = attachments.value.filter((a) => a.id !== id);
264
+ }
265
+ function clearAttachments() {
266
+ attachments.value.forEach((a) => {
267
+ if (a.previewUrl) URL.revokeObjectURL(a.previewUrl);
268
+ });
269
+ attachments.value = [];
270
+ }
220
271
  function onEvent(eventType, callback) {
221
272
  if (!eventHandlers.has(eventType)) {
222
273
  eventHandlers.set(eventType, /* @__PURE__ */ new Set());
@@ -241,6 +292,9 @@ function useThreadSetup(threadId, options = {}) {
241
292
  onUnmounted(() => {
242
293
  connectionManager?.disconnect();
243
294
  eventHandlers.clear();
295
+ attachments.value.forEach((a) => {
296
+ if (a.previewUrl) URL.revokeObjectURL(a.previewUrl);
297
+ });
244
298
  });
245
299
  return {
246
300
  threadId,
@@ -259,13 +313,18 @@ function useThreadSetup(threadId, options = {}) {
259
313
  onEvent,
260
314
  subscribeToEvent: onEvent,
261
315
  // alias
262
- // File management
316
+ // File management (uploads to filesystem)
263
317
  files,
264
318
  addFiles,
265
319
  removeFile,
266
320
  getFileUrl,
267
321
  getThumbnailUrl,
268
- getPreviewUrl
322
+ getPreviewUrl,
323
+ // Attachment management (sent inline with messages)
324
+ attachments,
325
+ addAttachment,
326
+ removeAttachment,
327
+ clearAttachments
269
328
  };
270
329
  }
271
330
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standardagents/vue",
3
- "version": "0.10.1-dev.cea2b66",
3
+ "version": "0.10.1-dev.d2d335e",
4
4
  "description": "Vue SDK for Standard Agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -22,7 +22,7 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@standardagents/client": "0.10.1-dev.cea2b66"
25
+ "@standardagents/client": "0.10.1-dev.d2d335e"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "vue": "^3.3.0"