@convex-dev/agent 0.6.0 → 0.6.1

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 (184) hide show
  1. package/dist/UIMessages.d.ts +46 -0
  2. package/dist/UIMessages.d.ts.map +1 -0
  3. package/dist/UIMessages.js +634 -0
  4. package/dist/UIMessages.js.map +1 -0
  5. package/dist/client/createTool.d.ts +167 -0
  6. package/dist/client/createTool.d.ts.map +1 -0
  7. package/dist/client/createTool.js +116 -0
  8. package/dist/client/createTool.js.map +1 -0
  9. package/dist/client/defaultComponent.d.ts +11 -0
  10. package/dist/client/defaultComponent.d.ts.map +1 -0
  11. package/dist/client/defaultComponent.js +7 -0
  12. package/dist/client/defaultComponent.js.map +1 -0
  13. package/dist/client/definePlaygroundAPI.d.ts +1725 -0
  14. package/dist/client/definePlaygroundAPI.d.ts.map +1 -0
  15. package/dist/client/definePlaygroundAPI.js +271 -0
  16. package/dist/client/definePlaygroundAPI.js.map +1 -0
  17. package/dist/client/files.d.ts +69 -0
  18. package/dist/client/files.d.ts.map +1 -0
  19. package/dist/client/files.js +181 -0
  20. package/dist/client/files.js.map +1 -0
  21. package/dist/client/index.d.ts +2091 -0
  22. package/dist/client/index.d.ts.map +1 -0
  23. package/dist/client/index.js +929 -0
  24. package/dist/client/index.js.map +1 -0
  25. package/dist/client/messages.d.ts +461 -0
  26. package/dist/client/messages.d.ts.map +1 -0
  27. package/dist/client/messages.js +106 -0
  28. package/dist/client/messages.js.map +1 -0
  29. package/dist/client/mockModel.d.ts +42 -0
  30. package/dist/client/mockModel.d.ts.map +1 -0
  31. package/dist/client/mockModel.js +182 -0
  32. package/dist/client/mockModel.js.map +1 -0
  33. package/dist/client/saveInputMessages.d.ts +20 -0
  34. package/dist/client/saveInputMessages.d.ts.map +1 -0
  35. package/dist/client/saveInputMessages.js +58 -0
  36. package/dist/client/saveInputMessages.js.map +1 -0
  37. package/dist/client/search.d.ts +493 -0
  38. package/dist/client/search.d.ts.map +1 -0
  39. package/dist/client/search.js +435 -0
  40. package/dist/client/search.js.map +1 -0
  41. package/dist/client/start.d.ts +84 -0
  42. package/dist/client/start.d.ts.map +1 -0
  43. package/dist/client/start.js +185 -0
  44. package/dist/client/start.js.map +1 -0
  45. package/dist/client/streamText.d.ts +46 -0
  46. package/dist/client/streamText.d.ts.map +1 -0
  47. package/dist/client/streamText.js +127 -0
  48. package/dist/client/streamText.js.map +1 -0
  49. package/dist/client/streaming.d.ts +3778 -0
  50. package/dist/client/streaming.d.ts.map +1 -0
  51. package/dist/client/streaming.js +330 -0
  52. package/dist/client/streaming.js.map +1 -0
  53. package/dist/client/threads.d.ts +46 -0
  54. package/dist/client/threads.d.ts.map +1 -0
  55. package/dist/client/threads.js +49 -0
  56. package/dist/client/threads.js.map +1 -0
  57. package/dist/client/types.d.ts +461 -0
  58. package/dist/client/types.d.ts.map +1 -0
  59. package/dist/client/types.js +2 -0
  60. package/dist/client/types.js.map +1 -0
  61. package/dist/client/utils.d.ts +4 -0
  62. package/dist/client/utils.d.ts.map +1 -0
  63. package/dist/client/utils.js +21 -0
  64. package/dist/client/utils.js.map +1 -0
  65. package/dist/component/_generated/api.d.ts +48 -0
  66. package/dist/component/_generated/api.d.ts.map +1 -0
  67. package/dist/component/_generated/api.js +31 -0
  68. package/dist/component/_generated/api.js.map +1 -0
  69. package/dist/component/_generated/component.d.ts +3120 -0
  70. package/dist/component/_generated/component.d.ts.map +1 -0
  71. package/dist/component/_generated/component.js +11 -0
  72. package/dist/component/_generated/component.js.map +1 -0
  73. package/dist/component/_generated/dataModel.d.ts +46 -0
  74. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  75. package/dist/component/_generated/dataModel.js +11 -0
  76. package/dist/component/_generated/dataModel.js.map +1 -0
  77. package/dist/component/_generated/server.d.ts +121 -0
  78. package/dist/component/_generated/server.d.ts.map +1 -0
  79. package/dist/component/_generated/server.js +78 -0
  80. package/dist/component/_generated/server.js.map +1 -0
  81. package/dist/component/apiKeys.d.ts +11 -0
  82. package/dist/component/apiKeys.d.ts.map +1 -0
  83. package/dist/component/apiKeys.js +69 -0
  84. package/dist/component/apiKeys.js.map +1 -0
  85. package/dist/component/convex.config.d.ts +3 -0
  86. package/dist/component/convex.config.d.ts.map +1 -0
  87. package/dist/component/convex.config.js +3 -0
  88. package/dist/component/convex.config.js.map +1 -0
  89. package/dist/component/files.d.ts +97 -0
  90. package/dist/component/files.d.ts.map +1 -0
  91. package/dist/component/files.js +190 -0
  92. package/dist/component/files.js.map +1 -0
  93. package/dist/component/messages.d.ts +3851 -0
  94. package/dist/component/messages.d.ts.map +1 -0
  95. package/dist/component/messages.js +757 -0
  96. package/dist/component/messages.js.map +1 -0
  97. package/dist/component/schema.d.ts +8029 -0
  98. package/dist/component/schema.d.ts.map +1 -0
  99. package/dist/component/schema.js +147 -0
  100. package/dist/component/schema.js.map +1 -0
  101. package/dist/component/streams.d.ts +128 -0
  102. package/dist/component/streams.d.ts.map +1 -0
  103. package/dist/component/streams.js +413 -0
  104. package/dist/component/streams.js.map +1 -0
  105. package/dist/component/threads.d.ts +115 -0
  106. package/dist/component/threads.d.ts.map +1 -0
  107. package/dist/component/threads.js +208 -0
  108. package/dist/component/threads.js.map +1 -0
  109. package/dist/component/users.d.ts +52 -0
  110. package/dist/component/users.d.ts.map +1 -0
  111. package/dist/component/users.js +229 -0
  112. package/dist/component/users.js.map +1 -0
  113. package/dist/component/vector/index.d.ts +61 -0
  114. package/dist/component/vector/index.d.ts.map +1 -0
  115. package/dist/component/vector/index.js +146 -0
  116. package/dist/component/vector/index.js.map +1 -0
  117. package/dist/component/vector/tables.d.ts +58 -0
  118. package/dist/component/vector/tables.d.ts.map +1 -0
  119. package/dist/component/vector/tables.js +56 -0
  120. package/dist/component/vector/tables.js.map +1 -0
  121. package/dist/deltas.d.ts +43 -0
  122. package/dist/deltas.d.ts.map +1 -0
  123. package/dist/deltas.js +446 -0
  124. package/dist/deltas.js.map +1 -0
  125. package/dist/mapping.d.ts +76 -0
  126. package/dist/mapping.d.ts.map +1 -0
  127. package/dist/mapping.js +720 -0
  128. package/dist/mapping.js.map +1 -0
  129. package/dist/react/SmoothText.d.ts +5 -0
  130. package/dist/react/SmoothText.d.ts.map +1 -0
  131. package/dist/react/SmoothText.js +6 -0
  132. package/dist/react/SmoothText.js.map +1 -0
  133. package/dist/react/index.d.ts +25 -0
  134. package/dist/react/index.d.ts.map +1 -0
  135. package/dist/react/index.js +70 -0
  136. package/dist/react/index.js.map +1 -0
  137. package/dist/react/optimisticallySendMessage.d.ts +42 -0
  138. package/dist/react/optimisticallySendMessage.d.ts.map +1 -0
  139. package/dist/react/optimisticallySendMessage.js +74 -0
  140. package/dist/react/optimisticallySendMessage.js.map +1 -0
  141. package/dist/react/types.d.ts +12 -0
  142. package/dist/react/types.d.ts.map +1 -0
  143. package/dist/react/types.js +2 -0
  144. package/dist/react/types.js.map +1 -0
  145. package/dist/react/useDeltaStreams.d.ts +10 -0
  146. package/dist/react/useDeltaStreams.d.ts.map +1 -0
  147. package/dist/react/useDeltaStreams.js +106 -0
  148. package/dist/react/useDeltaStreams.js.map +1 -0
  149. package/dist/react/useSmoothText.d.ts +27 -0
  150. package/dist/react/useSmoothText.d.ts.map +1 -0
  151. package/dist/react/useSmoothText.js +68 -0
  152. package/dist/react/useSmoothText.js.map +1 -0
  153. package/dist/react/useStreamingUIMessages.d.ts +22 -0
  154. package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
  155. package/dist/react/useStreamingUIMessages.js +92 -0
  156. package/dist/react/useStreamingUIMessages.js.map +1 -0
  157. package/dist/react/useThreadMessages.d.ts +104 -0
  158. package/dist/react/useThreadMessages.d.ts.map +1 -0
  159. package/dist/react/useThreadMessages.js +148 -0
  160. package/dist/react/useThreadMessages.js.map +1 -0
  161. package/dist/react/useUIMessages.d.ts +96 -0
  162. package/dist/react/useUIMessages.d.ts.map +1 -0
  163. package/dist/react/useUIMessages.js +108 -0
  164. package/dist/react/useUIMessages.js.map +1 -0
  165. package/dist/shared.d.ts +26 -0
  166. package/dist/shared.d.ts.map +1 -0
  167. package/dist/shared.js +67 -0
  168. package/dist/shared.js.map +1 -0
  169. package/dist/validators.d.ts +24516 -0
  170. package/dist/validators.d.ts.map +1 -0
  171. package/dist/validators.js +475 -0
  172. package/dist/validators.js.map +1 -0
  173. package/package.json +39 -39
  174. package/src/UIMessages.ts +0 -9
  175. package/src/client/approval.test.ts +144 -0
  176. package/src/client/index.ts +17 -9
  177. package/src/client/search.ts +0 -29
  178. package/src/client/start.ts +2 -2
  179. package/src/client/streamText.ts +9 -0
  180. package/src/client/streaming.ts +30 -23
  181. package/src/component/_generated/dataModel.ts +1 -1
  182. package/src/component/_generated/server.ts +0 -5
  183. package/src/mapping.ts +38 -22
  184. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
@@ -0,0 +1,720 @@
1
+ import {} from "ai";
2
+ import { vMessageWithMetadata, vToolResultOutput, vToolApprovalRequest, vToolApprovalResponse, } from "./validators.js";
3
+ import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
4
+ import { convertUint8ArrayToBase64, } from "@ai-sdk/provider-utils";
5
+ import { parse, validate } from "convex-helpers/validators";
6
+ import { getModelName, getProviderName, } from "./shared.js";
7
+ export async function serializeMessage(ctx, component, message) {
8
+ const { content, fileIds } = await serializeContent(ctx, component, message.content);
9
+ return {
10
+ message: {
11
+ role: message.role,
12
+ content,
13
+ ...(message.providerOptions
14
+ ? { providerOptions: message.providerOptions }
15
+ : {}),
16
+ },
17
+ fileIds,
18
+ };
19
+ }
20
+ // Similar to serializeMessage, but doesn't save any files and is looser
21
+ // For use on the frontend / in synchronous environments.
22
+ export function fromModelMessage(message) {
23
+ const content = fromModelMessageContent(message.content);
24
+ return {
25
+ role: message.role,
26
+ content,
27
+ ...(message.providerOptions
28
+ ? { providerOptions: message.providerOptions }
29
+ : {}),
30
+ };
31
+ }
32
+ export async function serializeOrThrow(message) {
33
+ const { content } = await serializeContent({}, {}, message.content);
34
+ return {
35
+ role: message.role,
36
+ content,
37
+ ...(message.providerOptions
38
+ ? { providerOptions: message.providerOptions }
39
+ : {}),
40
+ };
41
+ }
42
+ export function toModelMessage(message) {
43
+ return {
44
+ ...message,
45
+ content: toModelMessageContent(message.content),
46
+ };
47
+ }
48
+ export function docsToModelMessages(messages) {
49
+ return messages
50
+ .map((m) => m.message)
51
+ .filter((m) => !!m)
52
+ .filter((m) => !!m.content.length)
53
+ .map(toModelMessage);
54
+ }
55
+ /**
56
+ * Scan messages for unresolved `tool-approval-request` parts and inject
57
+ * synthetic `tool-approval-response` denials so that the AI SDK receives
58
+ * a complete history (every tool-call has a corresponding result or denial).
59
+ *
60
+ * This handles the case where a user sends a new message instead of
61
+ * resolving pending approvals — the old approvals are auto-denied rather
62
+ * than silently dropped.
63
+ */
64
+ export function autoDenyUnresolvedApprovals(messages) {
65
+ // Collect all approval requests: approvalId → { toolCallId, messageIndex }
66
+ const requests = new Map();
67
+ // Collect all resolved approval IDs
68
+ const resolvedIds = new Set();
69
+ for (let i = 0; i < messages.length; i++) {
70
+ const msg = messages[i];
71
+ if (!Array.isArray(msg.content))
72
+ continue;
73
+ for (const part of msg.content) {
74
+ if (part.type === "tool-approval-request") {
75
+ requests.set(part.approvalId, {
76
+ toolCallId: part.toolCallId,
77
+ messageIndex: i,
78
+ });
79
+ }
80
+ else if (part.type === "tool-approval-response") {
81
+ resolvedIds.add(part.approvalId);
82
+ }
83
+ }
84
+ }
85
+ // Find unresolved approvals
86
+ const unresolved = [];
87
+ for (const [approvalId, info] of requests) {
88
+ if (!resolvedIds.has(approvalId)) {
89
+ unresolved.push({ approvalId, ...info });
90
+ }
91
+ }
92
+ if (unresolved.length === 0) {
93
+ return messages;
94
+ }
95
+ // Group unresolved approvals by the assistant message index they came from
96
+ const byMessageIndex = new Map();
97
+ for (const entry of unresolved) {
98
+ console.warn(`Auto-denying unresolved tool approval ${entry.approvalId} ` +
99
+ `(toolCallId: ${entry.toolCallId}): new generation started`);
100
+ let group = byMessageIndex.get(entry.messageIndex);
101
+ if (!group) {
102
+ group = [];
103
+ byMessageIndex.set(entry.messageIndex, group);
104
+ }
105
+ group.push(entry);
106
+ }
107
+ // Build result by inserting synthetic denial messages after each relevant
108
+ // assistant message
109
+ const result = [];
110
+ for (let i = 0; i < messages.length; i++) {
111
+ result.push(messages[i]);
112
+ const group = byMessageIndex.get(i);
113
+ if (group) {
114
+ result.push({
115
+ role: "tool",
116
+ content: group.map((entry) => ({
117
+ type: "tool-approval-response",
118
+ approvalId: entry.approvalId,
119
+ approved: false,
120
+ reason: "auto-denied: new generation started",
121
+ })),
122
+ });
123
+ }
124
+ }
125
+ return result;
126
+ }
127
+ export function serializeUsage(usage) {
128
+ return {
129
+ promptTokens: usage.inputTokens ?? 0,
130
+ completionTokens: usage.outputTokens ?? 0,
131
+ totalTokens: usage.totalTokens ?? 0,
132
+ reasoningTokens: usage.reasoningTokens,
133
+ cachedInputTokens: usage.cachedInputTokens,
134
+ };
135
+ }
136
+ export function toModelMessageUsage(usage) {
137
+ return {
138
+ inputTokens: usage.promptTokens,
139
+ outputTokens: usage.completionTokens,
140
+ totalTokens: usage.totalTokens,
141
+ reasoningTokens: usage.reasoningTokens,
142
+ cachedInputTokens: usage.cachedInputTokens,
143
+ // These detail fields are required by LanguageModelUsage type but we don't
144
+ // have the granular data, so we provide empty objects with undefined values.
145
+ inputTokenDetails: {
146
+ cacheReadTokens: undefined,
147
+ cacheWriteTokens: undefined,
148
+ noCacheTokens: undefined,
149
+ },
150
+ outputTokenDetails: {
151
+ textTokens: undefined,
152
+ reasoningTokens: undefined,
153
+ },
154
+ };
155
+ }
156
+ export function serializeWarnings(warnings) {
157
+ if (!warnings) {
158
+ return undefined;
159
+ }
160
+ return warnings.map((warning) => {
161
+ if (warning.type === "compatibility") {
162
+ return {
163
+ type: "unsupported-setting",
164
+ setting: warning.feature,
165
+ details: warning.details,
166
+ };
167
+ }
168
+ return warning;
169
+ });
170
+ }
171
+ export function toModelMessageWarnings(warnings) {
172
+ if (!warnings) {
173
+ return undefined;
174
+ }
175
+ return warnings.map((warning) => {
176
+ if (warning.type === "unsupported-setting") {
177
+ return {
178
+ type: "compatibility",
179
+ feature: warning.setting,
180
+ details: warning.details,
181
+ };
182
+ }
183
+ return warning;
184
+ });
185
+ }
186
+ /**
187
+ * Serialize explicitly provided response messages for a step.
188
+ * Used by the streaming/generation loop where the caller tracks which
189
+ * messages are new via slicing.
190
+ */
191
+ export async function serializeResponseMessages(ctx, component, step, model, responseMessages) {
192
+ return serializeStepMessages(ctx, component, step, model, responseMessages);
193
+ }
194
+ /**
195
+ * Serialize the new messages from a step using a heuristic to determine
196
+ * which response messages are new (last 1-2 messages).
197
+ */
198
+ export async function serializeNewMessagesInStep(ctx, component, step, model) {
199
+ const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
200
+ let messagesToSerialize;
201
+ if (hasToolMessage) {
202
+ messagesToSerialize = step.response.messages.slice(-2);
203
+ }
204
+ else if (step.content.length) {
205
+ messagesToSerialize = step.response.messages.slice(-1);
206
+ }
207
+ else {
208
+ messagesToSerialize = [{ role: "assistant", content: [] }];
209
+ }
210
+ return serializeStepMessages(ctx, component, step, model, messagesToSerialize);
211
+ }
212
+ async function serializeStepMessages(ctx, component, step, model, messagesToSerialize) {
213
+ // If there are tool results, there's another message with the tool results
214
+ // ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
215
+ const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
216
+ const assistantFields = {
217
+ model: model ? getModelName(model) : undefined,
218
+ provider: model ? getProviderName(model) : undefined,
219
+ providerMetadata: step.providerMetadata,
220
+ reasoning: step.reasoningText,
221
+ reasoningDetails: step.reasoning,
222
+ usage: serializeUsage(step.usage),
223
+ warnings: serializeWarnings(step.warnings),
224
+ finishReason: step.finishReason,
225
+ // Only store the sources on one message
226
+ sources: hasToolMessage ? undefined : step.sources,
227
+ };
228
+ const toolFields = { sources: step.sources };
229
+ const messages = await Promise.all(messagesToSerialize.map(async (msg) => {
230
+ const { message, fileIds } = await serializeMessage(ctx, component, msg);
231
+ return parse(vMessageWithMetadata, {
232
+ message,
233
+ ...(message.role === "tool" ? toolFields : assistantFields),
234
+ text: step.text,
235
+ fileIds,
236
+ });
237
+ }));
238
+ // TODO: capture step.files separately?
239
+ return { messages };
240
+ }
241
+ export async function serializeObjectResult(ctx, component, result, model) {
242
+ const text = JSON.stringify(result.object);
243
+ const { message, fileIds } = await serializeMessage(ctx, component, {
244
+ role: "assistant",
245
+ content: text,
246
+ });
247
+ return {
248
+ messages: [
249
+ {
250
+ message,
251
+ model: model ? getModelName(model) : undefined,
252
+ provider: model ? getProviderName(model) : undefined,
253
+ providerMetadata: result.providerMetadata,
254
+ finishReason: result.finishReason,
255
+ text,
256
+ usage: serializeUsage(result.usage),
257
+ warnings: serializeWarnings(result.warnings),
258
+ fileIds,
259
+ },
260
+ ],
261
+ };
262
+ }
263
+ function getMimeOrMediaType(part) {
264
+ if ("mediaType" in part) {
265
+ return part.mediaType;
266
+ }
267
+ if ("mimeType" in part) {
268
+ return part.mimeType;
269
+ }
270
+ return undefined;
271
+ }
272
+ export async function serializeContent(ctx, component, content) {
273
+ if (typeof content === "string") {
274
+ return { content };
275
+ }
276
+ const fileIds = [];
277
+ const serialized = await Promise.all(content.map(async (part) => {
278
+ const metadata = {};
279
+ if ("providerOptions" in part) {
280
+ metadata.providerOptions = part.providerOptions;
281
+ }
282
+ if ("providerMetadata" in part) {
283
+ metadata.providerMetadata = part.providerMetadata;
284
+ }
285
+ switch (part.type) {
286
+ case "text": {
287
+ return {
288
+ type: part.type,
289
+ text: part.text,
290
+ ...metadata,
291
+ };
292
+ }
293
+ case "image": {
294
+ let image = serializeDataOrUrl(part.image);
295
+ if (image instanceof ArrayBuffer &&
296
+ image.byteLength > MAX_FILE_SIZE) {
297
+ const { file } = await storeFile(ctx, component, new Blob([image], {
298
+ type: getMimeOrMediaType(part) || guessMimeType(image),
299
+ }));
300
+ image = file.url;
301
+ fileIds.push(file.fileId);
302
+ }
303
+ return {
304
+ type: part.type,
305
+ mediaType: getMimeOrMediaType(part),
306
+ ...metadata,
307
+ image,
308
+ };
309
+ }
310
+ case "file": {
311
+ let data = serializeDataOrUrl(part.data);
312
+ if (data instanceof ArrayBuffer && data.byteLength > MAX_FILE_SIZE) {
313
+ const { file } = await storeFile(ctx, component, new Blob([data], { type: getMimeOrMediaType(part) }));
314
+ data = file.url;
315
+ fileIds.push(file.fileId);
316
+ }
317
+ return {
318
+ type: part.type,
319
+ data,
320
+ filename: part.filename,
321
+ mediaType: getMimeOrMediaType(part),
322
+ ...metadata,
323
+ };
324
+ }
325
+ case "tool-call": {
326
+ // Handle legacy data where only args field exists
327
+ const input = part.input ?? part?.args ?? {};
328
+ return {
329
+ type: part.type,
330
+ input,
331
+ /** @deprecated Use `input` instead. */
332
+ args: input,
333
+ toolCallId: part.toolCallId,
334
+ toolName: part.toolName,
335
+ providerExecuted: part.providerExecuted,
336
+ ...metadata,
337
+ };
338
+ }
339
+ case "tool-result": {
340
+ return normalizeToolResult(part, metadata);
341
+ }
342
+ case "reasoning": {
343
+ return {
344
+ type: part.type,
345
+ text: part.text,
346
+ ...metadata,
347
+ };
348
+ }
349
+ // Not in current generation output, but could be in historical messages
350
+ case "redacted-reasoning": {
351
+ return {
352
+ type: part.type,
353
+ data: part.data,
354
+ ...metadata,
355
+ };
356
+ }
357
+ case "source": {
358
+ return part;
359
+ }
360
+ case "tool-approval-request": {
361
+ return {
362
+ type: part.type,
363
+ approvalId: part.approvalId,
364
+ toolCallId: part.toolCallId,
365
+ ...metadata,
366
+ };
367
+ }
368
+ case "tool-approval-response": {
369
+ return {
370
+ type: part.type,
371
+ approvalId: part.approvalId,
372
+ approved: part.approved,
373
+ reason: part.reason,
374
+ providerExecuted: part.providerExecuted,
375
+ ...metadata,
376
+ };
377
+ }
378
+ default:
379
+ return null;
380
+ }
381
+ }));
382
+ return {
383
+ content: serialized.filter((p) => p !== null),
384
+ fileIds: fileIds.length > 0 ? fileIds : undefined,
385
+ };
386
+ }
387
+ export function fromModelMessageContent(content) {
388
+ if (typeof content === "string") {
389
+ return content;
390
+ }
391
+ return content
392
+ .map((part) => {
393
+ const metadata = {};
394
+ if ("providerOptions" in part) {
395
+ metadata.providerOptions = part.providerOptions;
396
+ }
397
+ if ("providerMetadata" in part) {
398
+ metadata.providerMetadata = part.providerMetadata;
399
+ }
400
+ switch (part.type) {
401
+ case "text":
402
+ return part;
403
+ case "image":
404
+ return {
405
+ type: part.type,
406
+ mediaType: getMimeOrMediaType(part),
407
+ ...metadata,
408
+ image: serializeDataOrUrl(part.image),
409
+ };
410
+ case "file":
411
+ return {
412
+ type: part.type,
413
+ data: serializeDataOrUrl(part.data),
414
+ filename: part.filename,
415
+ mediaType: getMimeOrMediaType(part),
416
+ ...metadata,
417
+ };
418
+ case "tool-call":
419
+ // Handle legacy data where only args field exists
420
+ return {
421
+ type: part.type,
422
+ input: part.input ?? part?.args ?? {},
423
+ /** @deprecated Use `input` instead. */
424
+ args: part.input ?? part?.args ?? {},
425
+ toolCallId: part.toolCallId,
426
+ toolName: part.toolName,
427
+ providerExecuted: part.providerExecuted,
428
+ ...metadata,
429
+ };
430
+ case "tool-result":
431
+ return normalizeToolResult(part, metadata);
432
+ case "reasoning":
433
+ return {
434
+ type: part.type,
435
+ text: part.text,
436
+ ...metadata,
437
+ };
438
+ case "tool-approval-request":
439
+ return {
440
+ type: part.type,
441
+ approvalId: part.approvalId,
442
+ toolCallId: part.toolCallId,
443
+ ...metadata,
444
+ };
445
+ case "tool-approval-response":
446
+ return {
447
+ type: part.type,
448
+ approvalId: part.approvalId,
449
+ approved: part.approved,
450
+ reason: part.reason,
451
+ providerExecuted: part.providerExecuted,
452
+ ...metadata,
453
+ };
454
+ // Not in current generation output, but could be in historical messages
455
+ default:
456
+ return null;
457
+ }
458
+ })
459
+ .filter((p) => p !== null);
460
+ }
461
+ export function toModelMessageContent(content) {
462
+ if (typeof content === "string") {
463
+ return content;
464
+ }
465
+ return content
466
+ .map((part) => {
467
+ const metadata = {};
468
+ if ("providerOptions" in part) {
469
+ metadata.providerOptions = part.providerOptions;
470
+ }
471
+ if ("providerMetadata" in part) {
472
+ metadata.providerMetadata = part.providerMetadata;
473
+ }
474
+ switch (part.type) {
475
+ case "text":
476
+ return {
477
+ type: part.type,
478
+ text: part.text,
479
+ ...metadata,
480
+ };
481
+ case "image":
482
+ return {
483
+ type: part.type,
484
+ image: toModelMessageDataOrUrl(part.image),
485
+ mediaType: getMimeOrMediaType(part),
486
+ ...metadata,
487
+ };
488
+ case "file":
489
+ return {
490
+ type: part.type,
491
+ data: toModelMessageDataOrUrl(part.data),
492
+ filename: part.filename,
493
+ mediaType: getMimeOrMediaType(part),
494
+ ...metadata,
495
+ };
496
+ case "tool-call": {
497
+ // Handle legacy data where only args field exists
498
+ const input = part.input ?? part?.args ?? {};
499
+ return {
500
+ type: part.type,
501
+ input,
502
+ toolCallId: part.toolCallId,
503
+ toolName: part.toolName,
504
+ providerExecuted: part.providerExecuted,
505
+ ...metadata,
506
+ };
507
+ }
508
+ case "tool-result": {
509
+ return normalizeToolResult(part, metadata);
510
+ }
511
+ case "reasoning":
512
+ return {
513
+ type: part.type,
514
+ text: part.text,
515
+ ...metadata,
516
+ };
517
+ case "redacted-reasoning":
518
+ // TODO: should we just drop this?
519
+ return {
520
+ type: "reasoning",
521
+ text: "",
522
+ ...metadata,
523
+ providerOptions: metadata.providerOptions
524
+ ? {
525
+ ...Object.fromEntries(Object.entries(metadata.providerOptions ?? {}).map(([key, value]) => [
526
+ key,
527
+ { ...value, redactedData: part.data },
528
+ ])),
529
+ }
530
+ : undefined,
531
+ };
532
+ case "source":
533
+ return part;
534
+ case "tool-approval-request":
535
+ return {
536
+ type: part.type,
537
+ approvalId: part.approvalId,
538
+ toolCallId: part.toolCallId,
539
+ ...metadata,
540
+ };
541
+ case "tool-approval-response":
542
+ return {
543
+ type: part.type,
544
+ approvalId: part.approvalId,
545
+ approved: part.approved,
546
+ reason: part.reason,
547
+ providerExecuted: part.providerExecuted,
548
+ ...metadata,
549
+ };
550
+ default:
551
+ return null;
552
+ }
553
+ })
554
+ .filter((p) => p !== null);
555
+ }
556
+ export function normalizeToolOutput(result) {
557
+ if (typeof result === "string") {
558
+ return {
559
+ type: "text",
560
+ value: result,
561
+ };
562
+ }
563
+ if (validate(vToolResultOutput, result)) {
564
+ return result;
565
+ }
566
+ return {
567
+ type: "json",
568
+ value: result ?? null,
569
+ };
570
+ }
571
+ function normalizeToolResult(part, metadata) {
572
+ return {
573
+ type: part.type,
574
+ output: part.output
575
+ ? validate(vToolResultOutput, part.output)
576
+ ? part.output
577
+ : normalizeToolOutput(JSON.stringify(part.output))
578
+ : normalizeToolOutput("result" in part ? part.result : undefined),
579
+ toolCallId: part.toolCallId,
580
+ toolName: part.toolName,
581
+ // Preserve isError flag for error reporting
582
+ ...("isError" in part && part.isError ? { isError: true } : {}),
583
+ ...metadata,
584
+ };
585
+ }
586
+ /**
587
+ * Return a best-guess MIME type based on the magic-number signature
588
+ * found at the start of an ArrayBuffer.
589
+ *
590
+ * @param buf – the source ArrayBuffer
591
+ * @returns the detected MIME type, or `"application/octet-stream"` if unknown
592
+ */
593
+ export function guessMimeType(buf) {
594
+ if (typeof buf === "string") {
595
+ if (buf.match(/^data:\w+\/\w+;base64/)) {
596
+ return buf.split(";")[0].split(":")[1];
597
+ }
598
+ return "text/plain";
599
+ }
600
+ if (buf.byteLength < 4)
601
+ return "application/octet-stream";
602
+ // Read the first 12 bytes (enough for all signatures below)
603
+ const bytes = new Uint8Array(buf.slice(0, 12));
604
+ const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
605
+ // Helper so we can look at only the needed prefix
606
+ const startsWith = (sig) => hex.startsWith(sig.toLowerCase());
607
+ // --- image formats ---
608
+ if (startsWith("89504e47"))
609
+ return "image/png"; // PNG - 89 50 4E 47
610
+ if (startsWith("ffd8ffdb") ||
611
+ startsWith("ffd8ffe0") ||
612
+ startsWith("ffd8ffee") ||
613
+ startsWith("ffd8ffe1"))
614
+ return "image/jpeg"; // JPEG
615
+ if (startsWith("47494638"))
616
+ return "image/gif"; // GIF
617
+ if (startsWith("424d"))
618
+ return "image/bmp"; // BMP
619
+ if (startsWith("52494646") && hex.substr(16, 8) === "57454250")
620
+ return "image/webp"; // WEBP (RIFF....WEBP)
621
+ if (startsWith("49492a00"))
622
+ return "image/tiff"; // TIFF
623
+ // <svg in hex is 3c 3f 78 6d 6c
624
+ if (startsWith("3c737667"))
625
+ return "image/svg+xml"; // <svg
626
+ if (startsWith("3c3f786d"))
627
+ return "image/svg+xml"; // <?xm
628
+ // --- audio/video ---
629
+ if (startsWith("494433"))
630
+ return "audio/mpeg"; // MP3 (ID3)
631
+ if (startsWith("000001ba") || startsWith("000001b3"))
632
+ return "video/mpeg"; // MPEG container
633
+ if (startsWith("1a45dfa3"))
634
+ return "video/webm"; // WEBM / Matroska
635
+ if (startsWith("00000018") && hex.substr(16, 8) === "66747970")
636
+ return "video/mp4"; // MP4
637
+ if (startsWith("4f676753"))
638
+ return "audio/ogg"; // OGG / Opus
639
+ // --- documents & archives ---
640
+ if (startsWith("25504446"))
641
+ return "application/pdf"; // PDF
642
+ if (startsWith("504b0304") ||
643
+ startsWith("504b0506") ||
644
+ startsWith("504b0708"))
645
+ return "application/zip"; // ZIP / DOCX / PPTX / XLSX / EPUB
646
+ if (startsWith("52617221"))
647
+ return "application/x-rar-compressed"; // RAR
648
+ if (startsWith("7f454c46"))
649
+ return "application/x-elf"; // ELF binaries
650
+ if (startsWith("1f8b08"))
651
+ return "application/gzip"; // GZIP
652
+ if (startsWith("425a68"))
653
+ return "application/x-bzip2"; // BZIP2
654
+ if (startsWith("3c3f786d6c"))
655
+ return "application/xml"; // XML
656
+ // Plain text, JSON and others are trickier—fallback:
657
+ return "application/octet-stream";
658
+ }
659
+ /**
660
+ * Serialize an AI SDK `DataContent` or `URL` to a Convex-serializable format.
661
+ * @param dataOrUrl - The data or URL to serialize.
662
+ * @returns The serialized data as an ArrayBuffer or the URL as a string.
663
+ */
664
+ export function serializeDataOrUrl(dataOrUrl) {
665
+ if (typeof dataOrUrl === "string") {
666
+ return dataOrUrl;
667
+ }
668
+ if (dataOrUrl instanceof ArrayBuffer) {
669
+ return dataOrUrl; // Already an ArrayBuffer
670
+ }
671
+ if (dataOrUrl instanceof URL) {
672
+ return dataOrUrl.toString();
673
+ }
674
+ return dataOrUrl.buffer.slice(dataOrUrl.byteOffset, dataOrUrl.byteOffset + dataOrUrl.byteLength);
675
+ }
676
+ export function toModelMessageDataOrUrl(urlOrString) {
677
+ if (urlOrString instanceof URL) {
678
+ return urlOrString;
679
+ }
680
+ if (typeof urlOrString === "string") {
681
+ if (urlOrString.startsWith("http://") ||
682
+ urlOrString.startsWith("https://")) {
683
+ return new URL(urlOrString);
684
+ }
685
+ return urlOrString;
686
+ }
687
+ return urlOrString;
688
+ }
689
+ export function toUIFilePart(part) {
690
+ const dataOrUrl = part.type === "image" ? part.image : part.data;
691
+ const url = dataOrUrl instanceof ArrayBuffer
692
+ ? convertUint8ArrayToBase64(new Uint8Array(dataOrUrl))
693
+ : dataOrUrl.toString();
694
+ return {
695
+ type: "file",
696
+ mediaType: part.mediaType,
697
+ filename: part.type === "file" ? part.filename : undefined,
698
+ url,
699
+ providerMetadata: part.providerOptions,
700
+ };
701
+ }
702
+ // Currently unused
703
+ // export function toModelMessages(args: {
704
+ // messages?: ModelMessage[] | AIMessageWithoutId[];
705
+ // }): ModelMessage[] {
706
+ // const messages: ModelMessage[] = [];
707
+ // if (args.messages) {
708
+ // if (
709
+ // args.messages.every(
710
+ // (m) => typeof m === "object" && m !== null && "parts" in m,
711
+ // )
712
+ // ) {
713
+ // messages.push(...convertToModelMessages(args.messages));
714
+ // } else {
715
+ // messages.push(...modelMessageSchema.array().parse(args.messages));
716
+ // }
717
+ // }
718
+ // return messages;
719
+ // }
720
+ //# sourceMappingURL=mapping.js.map