@convex-dev/agent 0.6.3 → 0.7.0-alpha.0

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 (246) hide show
  1. package/MIGRATION.md +60 -116
  2. package/dist/client/messages.d.ts +1 -398
  3. package/dist/client/messages.d.ts.map +1 -1
  4. package/dist/client/messages.js +3 -47
  5. package/dist/client/messages.js.map +1 -1
  6. package/dist/client/types.d.ts +1 -455
  7. package/dist/client/types.d.ts.map +1 -1
  8. package/dist/component/_generated/component.d.ts +805 -45
  9. package/dist/component/_generated/component.d.ts.map +1 -1
  10. package/dist/component/apiKeys.js +5 -5
  11. package/dist/component/apiKeys.js.map +1 -1
  12. package/dist/component/files.d.ts.map +1 -1
  13. package/dist/component/files.js +13 -11
  14. package/dist/component/files.js.map +1 -1
  15. package/dist/component/messages.d.ts +1252 -201
  16. package/dist/component/messages.d.ts.map +1 -1
  17. package/dist/component/messages.js +83 -47
  18. package/dist/component/messages.js.map +1 -1
  19. package/dist/component/schema.d.ts +2593 -332
  20. package/dist/component/schema.d.ts.map +1 -1
  21. package/dist/component/streams.d.ts +7 -1
  22. package/dist/component/streams.d.ts.map +1 -1
  23. package/dist/component/streams.js +46 -47
  24. package/dist/component/streams.js.map +1 -1
  25. package/dist/component/threads.d.ts.map +1 -1
  26. package/dist/component/threads.js +8 -9
  27. package/dist/component/threads.js.map +1 -1
  28. package/dist/component/users.js +2 -2
  29. package/dist/component/users.js.map +1 -1
  30. package/dist/component/vector/index.d.ts.map +1 -1
  31. package/dist/component/vector/index.js +14 -8
  32. package/dist/component/vector/index.js.map +1 -1
  33. package/dist/react/index.d.ts +5 -5
  34. package/dist/react/index.d.ts.map +1 -1
  35. package/dist/react/index.js +5 -5
  36. package/dist/react/index.js.map +1 -1
  37. package/dist/shared.d.ts +19 -7
  38. package/dist/shared.d.ts.map +1 -1
  39. package/dist/shared.js +4 -4
  40. package/dist/shared.js.map +1 -1
  41. package/dist/streaming/materializePersistedUIMessageChunks.d.ts +16 -0
  42. package/dist/streaming/materializePersistedUIMessageChunks.d.ts.map +1 -0
  43. package/dist/streaming/materializePersistedUIMessageChunks.js +302 -0
  44. package/dist/streaming/materializePersistedUIMessageChunks.js.map +1 -0
  45. package/dist/streaming/persistedUIMessageChunks.d.ts +106 -0
  46. package/dist/streaming/persistedUIMessageChunks.d.ts.map +1 -0
  47. package/dist/streaming/persistedUIMessageChunks.js +475 -0
  48. package/dist/streaming/persistedUIMessageChunks.js.map +1 -0
  49. package/dist/validators.d.ts +10026 -1055
  50. package/dist/validators.d.ts.map +1 -1
  51. package/dist/validators.js +71 -9
  52. package/dist/validators.js.map +1 -1
  53. package/dist/{UIMessages.d.ts → vercel/UIMessages.d.ts} +1 -1
  54. package/dist/vercel/UIMessages.d.ts.map +1 -0
  55. package/dist/{UIMessages.js → vercel/UIMessages.js} +63 -1
  56. package/dist/vercel/UIMessages.js.map +1 -0
  57. package/dist/{client → vercel/client}/createTool.d.ts +23 -16
  58. package/dist/vercel/client/createTool.d.ts.map +1 -0
  59. package/dist/{client → vercel/client}/createTool.js +2 -0
  60. package/dist/vercel/client/createTool.js.map +1 -0
  61. package/dist/{client → vercel/client}/definePlaygroundAPI.d.ts +593 -134
  62. package/dist/{client → vercel/client}/definePlaygroundAPI.d.ts.map +1 -1
  63. package/dist/{client → vercel/client}/definePlaygroundAPI.js +13 -9
  64. package/dist/vercel/client/definePlaygroundAPI.js.map +1 -0
  65. package/dist/{client → vercel/client}/files.d.ts +2 -2
  66. package/dist/vercel/client/files.d.ts.map +1 -0
  67. package/dist/vercel/client/files.js.map +1 -0
  68. package/dist/vercel/client/messages.d.ts +542 -0
  69. package/dist/vercel/client/messages.d.ts.map +1 -0
  70. package/dist/vercel/client/messages.js +66 -0
  71. package/dist/vercel/client/messages.js.map +1 -0
  72. package/dist/{client → vercel/client}/mockModel.d.ts +17 -17
  73. package/dist/vercel/client/mockModel.d.ts.map +1 -0
  74. package/dist/{client → vercel/client}/mockModel.js +12 -13
  75. package/dist/vercel/client/mockModel.js.map +1 -0
  76. package/dist/{client → vercel/client}/saveInputMessages.d.ts +2 -2
  77. package/dist/vercel/client/saveInputMessages.d.ts.map +1 -0
  78. package/dist/vercel/client/saveInputMessages.js.map +1 -0
  79. package/dist/{client → vercel/client}/search.d.ts +119 -22
  80. package/dist/vercel/client/search.d.ts.map +1 -0
  81. package/dist/{client → vercel/client}/search.js +2 -2
  82. package/dist/vercel/client/search.js.map +1 -0
  83. package/dist/{client → vercel/client}/start.d.ts +26 -11
  84. package/dist/vercel/client/start.d.ts.map +1 -0
  85. package/dist/{client → vercel/client}/start.js +64 -22
  86. package/dist/vercel/client/start.js.map +1 -0
  87. package/dist/{client → vercel/client}/streamText.d.ts +13 -12
  88. package/dist/vercel/client/streamText.d.ts.map +1 -0
  89. package/dist/{client → vercel/client}/streamText.js +43 -9
  90. package/dist/vercel/client/streamText.js.map +1 -0
  91. package/dist/{client → vercel/client}/streaming.d.ts +1410 -183
  92. package/dist/vercel/client/streaming.d.ts.map +1 -0
  93. package/dist/{client → vercel/client}/streaming.js +120 -52
  94. package/dist/vercel/client/streaming.js.map +1 -0
  95. package/dist/vercel/client/types.d.ts +456 -0
  96. package/dist/vercel/client/types.d.ts.map +1 -0
  97. package/dist/vercel/client/types.js +3 -0
  98. package/dist/vercel/client/types.js.map +1 -0
  99. package/dist/{client → vercel/client}/utils.d.ts +3 -2
  100. package/dist/vercel/client/utils.d.ts.map +1 -0
  101. package/dist/{client → vercel/client}/utils.js +2 -2
  102. package/dist/vercel/client/utils.js.map +1 -0
  103. package/dist/vercel/deltas.d.ts +31 -0
  104. package/dist/vercel/deltas.d.ts.map +1 -0
  105. package/dist/vercel/deltas.js +525 -0
  106. package/dist/vercel/deltas.js.map +1 -0
  107. package/dist/{client → vercel}/index.d.ts +484 -97
  108. package/dist/vercel/index.d.ts.map +1 -0
  109. package/dist/{client → vercel}/index.js +65 -38
  110. package/dist/vercel/index.js.map +1 -0
  111. package/dist/{mapping.d.ts → vercel/mapping.d.ts} +5 -13
  112. package/dist/vercel/mapping.d.ts.map +1 -0
  113. package/dist/{mapping.js → vercel/mapping.js} +411 -112
  114. package/dist/vercel/mapping.js.map +1 -0
  115. package/dist/{react → vercel/react}/optimisticallySendMessage.d.ts +2 -2
  116. package/dist/vercel/react/optimisticallySendMessage.d.ts.map +1 -0
  117. package/dist/vercel/react/optimisticallySendMessage.js.map +1 -0
  118. package/dist/{react → vercel/react}/types.d.ts +1 -1
  119. package/dist/vercel/react/types.d.ts.map +1 -0
  120. package/dist/{react → vercel/react}/types.js.map +1 -1
  121. package/dist/{react → vercel/react}/useDeltaStreams.d.ts +1 -1
  122. package/dist/vercel/react/useDeltaStreams.d.ts.map +1 -0
  123. package/dist/{react → vercel/react}/useDeltaStreams.js +1 -1
  124. package/dist/vercel/react/useDeltaStreams.js.map +1 -0
  125. package/dist/{react → vercel/react}/useStreamingUIMessages.d.ts +1 -1
  126. package/dist/vercel/react/useStreamingUIMessages.d.ts.map +1 -0
  127. package/dist/vercel/react/useStreamingUIMessages.js +112 -0
  128. package/dist/vercel/react/useStreamingUIMessages.js.map +1 -0
  129. package/dist/{react → vercel/react}/useThreadMessages.d.ts +1 -1
  130. package/dist/vercel/react/useThreadMessages.d.ts.map +1 -0
  131. package/dist/{react → vercel/react}/useThreadMessages.js +1 -1
  132. package/dist/vercel/react/useThreadMessages.js.map +1 -0
  133. package/dist/{react → vercel/react}/useUIMessages.d.ts +2 -1
  134. package/dist/vercel/react/useUIMessages.d.ts.map +1 -0
  135. package/dist/{react → vercel/react}/useUIMessages.js +8 -4
  136. package/dist/vercel/react/useUIMessages.js.map +1 -0
  137. package/package.json +30 -22
  138. package/src/client/messages.ts +5 -107
  139. package/src/client/types.ts +0 -621
  140. package/src/component/_generated/component.ts +1089 -48
  141. package/src/component/apiKeys.ts +5 -5
  142. package/src/component/files.test.ts +1 -1
  143. package/src/component/files.ts +14 -12
  144. package/src/component/messages.test.ts +273 -1
  145. package/src/component/messages.ts +99 -57
  146. package/src/component/streams.ts +80 -64
  147. package/src/component/threads.ts +12 -9
  148. package/src/component/users.ts +2 -2
  149. package/src/component/vector/index.ts +14 -7
  150. package/src/eslintConfig.test.ts +44 -0
  151. package/src/react/index.ts +8 -5
  152. package/src/shared.ts +26 -36
  153. package/src/streaming/materializePersistedUIMessageChunks.test.ts +513 -0
  154. package/src/streaming/materializePersistedUIMessageChunks.ts +398 -0
  155. package/src/streaming/persistedUIMessageChunks.test.ts +205 -0
  156. package/src/streaming/persistedUIMessageChunks.ts +658 -0
  157. package/src/validators.test.ts +4 -1
  158. package/src/validators.ts +89 -8
  159. package/src/{UIMessages.combineUIMessages.test.ts → vercel/UIMessages.combineUIMessages.test.ts} +4 -1
  160. package/src/{UIMessages.ts → vercel/UIMessages.ts} +96 -5
  161. package/src/{client → vercel/client}/approval.test.ts +48 -19
  162. package/src/{client → vercel/client}/createTool.ts +63 -23
  163. package/src/{client → vercel/client}/definePlaygroundAPI.ts +44 -29
  164. package/src/{client → vercel/client}/files.ts +2 -2
  165. package/src/{client → vercel/client}/index.test.ts +162 -3
  166. package/src/vercel/client/messages.ts +182 -0
  167. package/src/{client → vercel/client}/mockModel.ts +38 -39
  168. package/src/{client → vercel/client}/saveInputMessages.test.ts +1 -1
  169. package/src/{client → vercel/client}/saveInputMessages.ts +7 -4
  170. package/src/{client → vercel/client}/search.test.ts +3 -3
  171. package/src/{client → vercel/client}/search.ts +4 -4
  172. package/src/{client → vercel/client}/setup.test.ts +6 -3
  173. package/src/vercel/client/start.test.ts +14 -0
  174. package/src/{client → vercel/client}/start.ts +147 -36
  175. package/src/vercel/client/streamText.test.ts +223 -0
  176. package/src/{client → vercel/client}/streamText.ts +102 -31
  177. package/src/{client → vercel/client}/streaming.integration.test.ts +40 -115
  178. package/src/{client → vercel/client}/streaming.test.ts +178 -4
  179. package/src/{client → vercel/client}/streaming.ts +114 -52
  180. package/src/vercel/client/types.test.ts +13 -0
  181. package/src/vercel/client/types.ts +679 -0
  182. package/src/{client → vercel/client}/utils.ts +13 -7
  183. package/src/vercel/deltas.test.ts +898 -0
  184. package/src/vercel/deltas.ts +633 -0
  185. package/src/{fromUIMessages.test.ts → vercel/fromUIMessages.test.ts} +17 -1
  186. package/src/{client → vercel}/index.ts +169 -62
  187. package/src/vercel/mapping.test.ts +965 -0
  188. package/src/{mapping.ts → vercel/mapping.ts} +501 -131
  189. package/src/{react → vercel/react}/optimisticallySendMessage.ts +2 -2
  190. package/src/{react → vercel/react}/types.ts +1 -1
  191. package/src/{react → vercel/react}/useDeltaStreams.ts +6 -2
  192. package/src/{react → vercel/react}/useStreamingUIMessages.ts +67 -35
  193. package/src/{react → vercel/react}/useThreadMessages.ts +2 -2
  194. package/src/{react → vercel/react}/useUIMessages.test.ts +80 -1
  195. package/src/{react → vercel/react}/useUIMessages.ts +13 -5
  196. package/src/{toUIMessages.test.ts → vercel/toUIMessages.test.ts} +49 -2
  197. package/dist/UIMessages.d.ts.map +0 -1
  198. package/dist/UIMessages.js.map +0 -1
  199. package/dist/client/createTool.d.ts.map +0 -1
  200. package/dist/client/createTool.js.map +0 -1
  201. package/dist/client/definePlaygroundAPI.js.map +0 -1
  202. package/dist/client/files.d.ts.map +0 -1
  203. package/dist/client/files.js.map +0 -1
  204. package/dist/client/index.d.ts.map +0 -1
  205. package/dist/client/index.js.map +0 -1
  206. package/dist/client/mockModel.d.ts.map +0 -1
  207. package/dist/client/mockModel.js.map +0 -1
  208. package/dist/client/saveInputMessages.d.ts.map +0 -1
  209. package/dist/client/saveInputMessages.js.map +0 -1
  210. package/dist/client/search.d.ts.map +0 -1
  211. package/dist/client/search.js.map +0 -1
  212. package/dist/client/start.d.ts.map +0 -1
  213. package/dist/client/start.js.map +0 -1
  214. package/dist/client/streamText.d.ts.map +0 -1
  215. package/dist/client/streamText.js.map +0 -1
  216. package/dist/client/streaming.d.ts.map +0 -1
  217. package/dist/client/streaming.js.map +0 -1
  218. package/dist/client/utils.d.ts.map +0 -1
  219. package/dist/client/utils.js.map +0 -1
  220. package/dist/deltas.d.ts +0 -43
  221. package/dist/deltas.d.ts.map +0 -1
  222. package/dist/deltas.js +0 -446
  223. package/dist/deltas.js.map +0 -1
  224. package/dist/mapping.d.ts.map +0 -1
  225. package/dist/mapping.js.map +0 -1
  226. package/dist/react/optimisticallySendMessage.d.ts.map +0 -1
  227. package/dist/react/optimisticallySendMessage.js.map +0 -1
  228. package/dist/react/types.d.ts.map +0 -1
  229. package/dist/react/useDeltaStreams.d.ts.map +0 -1
  230. package/dist/react/useDeltaStreams.js.map +0 -1
  231. package/dist/react/useStreamingUIMessages.d.ts.map +0 -1
  232. package/dist/react/useStreamingUIMessages.js +0 -92
  233. package/dist/react/useStreamingUIMessages.js.map +0 -1
  234. package/dist/react/useThreadMessages.d.ts.map +0 -1
  235. package/dist/react/useThreadMessages.js.map +0 -1
  236. package/dist/react/useUIMessages.d.ts.map +0 -1
  237. package/dist/react/useUIMessages.js.map +0 -1
  238. package/src/client/streamText.test.ts +0 -102
  239. package/src/deltas.test.ts +0 -626
  240. package/src/deltas.ts +0 -569
  241. package/src/mapping.test.ts +0 -402
  242. /package/dist/{client → vercel/client}/files.js +0 -0
  243. /package/dist/{client → vercel/client}/saveInputMessages.js +0 -0
  244. /package/dist/{react → vercel/react}/optimisticallySendMessage.js +0 -0
  245. /package/dist/{react → vercel/react}/types.js +0 -0
  246. /package/src/{UIMessages.test.ts → vercel/UIMessages.test.ts} +0 -0
package/src/validators.ts CHANGED
@@ -15,6 +15,11 @@ export const vProviderMetadata = vProviderOptions;
15
15
  const providerMetadata = providerOptions;
16
16
  export type ProviderMetadata = Infer<typeof providerMetadata>;
17
17
 
18
+ export const vProviderReference = v.object({
19
+ type: v.literal("reference"),
20
+ reference: v.record(v.string(), v.string()),
21
+ });
22
+
18
23
  export const vThreadStatus = v.union(
19
24
  v.literal("active"),
20
25
  v.literal("archived"), // unused
@@ -42,7 +47,7 @@ export const vTextPart = v.object({
42
47
 
43
48
  export const vImagePart = v.object({
44
49
  type: v.literal("image"),
45
- image: v.union(v.string(), v.bytes()),
50
+ image: v.union(v.string(), v.bytes(), vProviderReference),
46
51
  mediaType: v.optional(v.string()),
47
52
  /** @deprecated Use `mediaType` instead. */
48
53
  mimeType: v.optional(v.string()),
@@ -51,7 +56,7 @@ export const vImagePart = v.object({
51
56
 
52
57
  export const vFilePart = v.object({
53
58
  type: v.literal("file"),
54
- data: v.union(v.string(), v.bytes()),
59
+ data: v.union(v.string(), v.bytes(), vProviderReference),
55
60
  filename: v.optional(v.string()),
56
61
  mediaType: v.optional(v.string()),
57
62
  /** @deprecated Use `mediaType` instead. */
@@ -73,6 +78,21 @@ export const vReasoningPart = v.object({
73
78
  providerMetadata,
74
79
  });
75
80
 
81
+ export const vReasoningFilePart = v.object({
82
+ type: v.literal("reasoning-file"),
83
+ url: v.optional(v.string()),
84
+ data: v.optional(v.union(v.string(), v.bytes())),
85
+ mediaType: v.string(),
86
+ providerOptions,
87
+ providerMetadata,
88
+ });
89
+
90
+ export const vCustomContentPart = v.object({
91
+ type: v.literal("custom"),
92
+ kind: v.string(),
93
+ providerOptions,
94
+ providerMetadata,
95
+ });
76
96
  export const vRedactedReasoningPart = v.object({
77
97
  type: v.literal("redacted-reasoning"),
78
98
  data: v.string(),
@@ -127,6 +147,8 @@ export const vToolCallPart = v.union(
127
147
  /** @deprecated Use `input` instead. */
128
148
  args: v.optional(v.any()),
129
149
  providerExecuted: v.optional(v.boolean()),
150
+ title: v.optional(v.string()),
151
+ toolMetadata: v.optional(v.record(v.string(), v.any())),
130
152
  providerOptions,
131
153
  providerMetadata,
132
154
  }),
@@ -139,6 +161,8 @@ export const vToolCallPart = v.union(
139
161
  args: v.any(),
140
162
  input: v.optional(v.any()),
141
163
  providerExecuted: v.optional(v.boolean()),
164
+ title: v.optional(v.string()),
165
+ toolMetadata: v.optional(v.record(v.string(), v.any())),
142
166
  providerOptions,
143
167
  providerMetadata,
144
168
  }),
@@ -178,6 +202,21 @@ export const vToolResultOutput = v.union(
178
202
  text: v.string(),
179
203
  providerOptions,
180
204
  }),
205
+ v.object({
206
+ type: v.literal("file"),
207
+ data: v.union(
208
+ v.object({ type: v.literal("url"), url: v.string() }),
209
+ vProviderReference,
210
+ v.object({
211
+ type: v.literal("data"),
212
+ data: v.union(v.string(), v.bytes()),
213
+ }),
214
+ v.object({ type: v.literal("text"), text: v.string() }),
215
+ ),
216
+ mediaType: v.string(),
217
+ filename: v.optional(v.string()),
218
+ providerOptions,
219
+ }),
181
220
  /** @deprecated Use `image-data` or `file-data` instead. */
182
221
  v.object({
183
222
  type: v.literal("media"),
@@ -199,6 +238,7 @@ export const vToolResultOutput = v.union(
199
238
  v.object({
200
239
  type: v.literal("file-url"),
201
240
  url: v.string(),
241
+ mediaType: v.optional(v.string()),
202
242
  providerOptions,
203
243
  }),
204
244
  v.object({
@@ -214,6 +254,11 @@ export const vToolResultOutput = v.union(
214
254
  fileId: v.union(v.string(), v.record(v.string(), v.string())),
215
255
  providerOptions,
216
256
  }),
257
+ v.object({
258
+ type: v.literal("file-reference"),
259
+ providerReference: v.record(v.string(), v.string()),
260
+ providerOptions,
261
+ }),
217
262
  v.object({
218
263
  type: v.literal("image-data"),
219
264
  data: v.string(),
@@ -224,6 +269,11 @@ export const vToolResultOutput = v.union(
224
269
  mediaType: v.string(),
225
270
  providerOptions,
226
271
  }),
272
+ v.object({
273
+ type: v.literal("image-file-reference"),
274
+ providerReference: v.record(v.string(), v.string()),
275
+ providerOptions,
276
+ }),
227
277
  v.object({
228
278
  type: v.literal("image-url"),
229
279
  url: v.string(),
@@ -271,6 +321,8 @@ export const vToolApprovalRequest = v.object({
271
321
  * ID of the tool call that the approval request is for.
272
322
  */
273
323
  toolCallId: v.string(),
324
+ isAutomatic: v.optional(v.boolean()),
325
+ signature: v.optional(v.string()),
274
326
  /** @todo Should we continue to include? */
275
327
  providerMetadata,
276
328
  /** @todo Should we continue to include? */
@@ -334,6 +386,8 @@ export const vAssistantContent = v.union(
334
386
  vTextPart,
335
387
  vFilePart,
336
388
  vReasoningPart,
389
+ vReasoningFilePart,
390
+ vCustomContentPart,
337
391
  vRedactedReasoningPart,
338
392
  vToolCallPart,
339
393
  vToolResultPart,
@@ -383,6 +437,8 @@ export type MessageContentParts =
383
437
  | Infer<typeof vImagePart>
384
438
  | Infer<typeof vFilePart>
385
439
  | Infer<typeof vReasoningPart>
440
+ | Infer<typeof vReasoningFilePart>
441
+ | Infer<typeof vCustomContentPart>
386
442
  | Infer<typeof vRedactedReasoningPart>
387
443
  | Infer<typeof vToolCallPart>
388
444
  | Infer<typeof vToolResultPart>
@@ -431,15 +487,34 @@ export const vFinishReason = v.union(
431
487
  );
432
488
 
433
489
  export const vUsage = v.object({
434
- promptTokens: v.number(),
435
- completionTokens: v.number(),
436
- totalTokens: v.number(),
490
+ promptTokens: v.optional(v.number()),
491
+ completionTokens: v.optional(v.number()),
492
+ totalTokens: v.optional(v.number()),
437
493
  reasoningTokens: v.optional(v.number()),
438
494
  cachedInputTokens: v.optional(v.number()),
495
+ nonCachedInputTokens: v.optional(v.number()),
496
+ cacheWriteInputTokens: v.optional(v.number()),
497
+ textOutputTokens: v.optional(v.number()),
498
+ raw: v.optional(v.record(v.string(), v.any())),
439
499
  });
440
500
  export type Usage = Infer<typeof vUsage>;
441
501
 
442
502
  export const vLanguageModelCallWarning = v.union(
503
+ v.object({
504
+ type: v.literal("unsupported"),
505
+ feature: v.string(),
506
+ details: v.optional(v.string()),
507
+ }),
508
+ v.object({
509
+ type: v.literal("compatibility"),
510
+ feature: v.string(),
511
+ details: v.optional(v.string()),
512
+ }),
513
+ v.object({
514
+ type: v.literal("deprecated"),
515
+ setting: v.string(),
516
+ message: v.string(),
517
+ }),
443
518
  v.object({
444
519
  type: v.literal("unsupported-setting"),
445
520
  setting: v.string(),
@@ -515,8 +590,13 @@ export const vStorageOptions = v.object({
515
590
  ),
516
591
  });
517
592
 
518
- const vPromptFields = {
519
- system: v.optional(v.string()),
593
+ // Generated Convex actions keep the established `system` wire field. The
594
+ // Vercel adapter translates it to AI SDK 7's `instructions` before invoking
595
+ // the model, so action payloads never carry both spellings.
596
+ const vActionPromptFields = {
597
+ system: v.optional(
598
+ v.union(v.string(), vSystemMessage, v.array(vSystemMessage)),
599
+ ),
520
600
  prompt: v.optional(v.string()),
521
601
  messages: v.optional(v.array(vMessage)),
522
602
  promptMessageId: v.optional(v.string()),
@@ -543,7 +623,8 @@ const vCommonArgs = {
543
623
  storageOptions: v.optional(vStorageOptions),
544
624
  providerOptions,
545
625
  callSettings: v.optional(vCallSettings),
546
- ...vPromptFields,
626
+ allowSystemInMessages: v.optional(v.boolean()),
627
+ ...vActionPromptFields,
547
628
  };
548
629
 
549
630
  export const vTextArgs = v.object({
@@ -1,5 +1,8 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { combineUIMessages, type UIMessage } from "./UIMessages.js";
2
+ import {
3
+ combineUIMessages,
4
+ type UIMessage,
5
+ } from "./UIMessages.js";
3
6
 
4
7
  describe("combineUIMessages", () => {
5
8
  it("should preserve all tool calls when combining messages", () => {
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  convertToModelMessages,
3
3
  type UIMessage as AIUIMessage,
4
+ type CustomContentUIPart,
4
5
  type DeepPartial,
5
6
  type DynamicToolUIPart,
7
+ type ReasoningFileUIPart,
6
8
  type ReasoningUIPart,
7
9
  type SourceDocumentUIPart,
8
10
  type SourceUrlUIPart,
@@ -13,6 +15,10 @@ import {
13
15
  type UIDataTypes,
14
16
  type UITools,
15
17
  } from "ai";
18
+ import {
19
+ convertUint8ArrayToBase64,
20
+ type ReasoningFilePart,
21
+ } from "@ai-sdk/provider-utils";
16
22
  import type { Infer } from "convex/values";
17
23
  import { toModelMessage, fromModelMessage, toUIFilePart } from "./mapping.js";
18
24
  import {
@@ -21,14 +27,14 @@ import {
21
27
  isTool,
22
28
  joinText,
23
29
  sorted,
24
- } from "./shared.js";
30
+ } from "../shared.js";
25
31
  import type {
26
32
  MessageDoc,
27
33
  MessageStatus,
28
34
  ProviderOptions,
29
35
  SourcePart,
30
36
  vSource,
31
- } from "./validators.js";
37
+ } from "../validators.js";
32
38
  import { omit, pick } from "convex-helpers";
33
39
 
34
40
  export type UIStatus = "streaming" | MessageStatus;
@@ -153,6 +159,7 @@ function fromSourceParts(parts: UIMessage["parts"]): Infer<typeof vSource>[] {
153
159
  id: part.sourceId,
154
160
  providerMetadata: part.providerMetadata,
155
161
  title: part.title,
162
+ filename: part.filename,
156
163
  } satisfies Infer<typeof vSource>;
157
164
  }
158
165
  return undefined;
@@ -398,7 +405,13 @@ function createAssistantUIMessage<
398
405
 
399
406
  // Extract approval parts from raw message content for UI rendering
400
407
  type ApprovalPart =
401
- | { type: "tool-approval-request"; approvalId: string; toolCallId: string }
408
+ | {
409
+ type: "tool-approval-request";
410
+ approvalId: string;
411
+ toolCallId: string;
412
+ isAutomatic?: boolean;
413
+ signature?: string;
414
+ }
402
415
  | {
403
416
  type: "tool-approval-response";
404
417
  approvalId: string;
@@ -496,6 +509,16 @@ function createAssistantUIMessage<
496
509
  ...contentPart,
497
510
  } satisfies ReasoningUIPart);
498
511
  break;
512
+ case "reasoning-file":
513
+ allParts.push(toUIReasoningFilePart(contentPart));
514
+ break;
515
+ case "custom":
516
+ allParts.push({
517
+ type: "custom",
518
+ kind: contentPart.kind,
519
+ providerMetadata: contentPart.providerOptions,
520
+ } satisfies CustomContentUIPart);
521
+ break;
499
522
  case "file":
500
523
  case "image":
501
524
  allParts.push(toUIFilePart(contentPart));
@@ -613,6 +636,8 @@ function createAssistantUIMessage<
613
636
  const typedPart = contentPart as {
614
637
  toolCallId: string;
615
638
  approvalId: string;
639
+ isAutomatic?: boolean;
640
+ signature?: string;
616
641
  };
617
642
  const toolCallPart = allParts.find(
618
643
  (part) =>
@@ -623,6 +648,12 @@ function createAssistantUIMessage<
623
648
  toolCallPart.state = "approval-requested";
624
649
  (toolCallPart as ToolUIPart & { approval?: object }).approval = {
625
650
  id: typedPart.approvalId,
651
+ ...(typedPart.isAutomatic !== undefined
652
+ ? { isAutomatic: typedPart.isAutomatic }
653
+ : {}),
654
+ ...(typedPart.signature !== undefined
655
+ ? { signature: typedPart.signature }
656
+ : {}),
626
657
  };
627
658
  } else {
628
659
  console.warn(
@@ -647,9 +678,15 @@ function createAssistantUIMessage<
647
678
  ) as ToolUIPart | undefined;
648
679
 
649
680
  if (toolCallPart) {
681
+ const approval = (
682
+ toolCallPart as ToolUIPart & {
683
+ approval?: { isAutomatic?: boolean; signature?: string };
684
+ }
685
+ ).approval;
650
686
  if (typedPart.approved) {
651
687
  toolCallPart.state = "approval-responded";
652
688
  (toolCallPart as ToolUIPart & { approval?: object }).approval = {
689
+ ...approval,
653
690
  id: typedPart.approvalId,
654
691
  approved: true,
655
692
  reason: typedPart.reason,
@@ -657,6 +694,7 @@ function createAssistantUIMessage<
657
694
  } else {
658
695
  toolCallPart.state = "output-denied";
659
696
  (toolCallPart as ToolUIPart & { approval?: object }).approval = {
697
+ ...approval,
660
698
  id: typedPart.approvalId,
661
699
  approved: false,
662
700
  reason: typedPart.reason,
@@ -710,6 +748,12 @@ function createAssistantUIMessage<
710
748
  // update state if not in a final state
711
749
  (toolCallPart as ToolUIPart & { approval?: object }).approval = {
712
750
  id: approvalPart.approvalId,
751
+ ...(approvalPart.isAutomatic !== undefined
752
+ ? { isAutomatic: approvalPart.isAutomatic }
753
+ : {}),
754
+ ...(approvalPart.signature !== undefined
755
+ ? { signature: approvalPart.signature }
756
+ : {}),
713
757
  };
714
758
  if (!finalStates.has(toolCallPart.state)) {
715
759
  toolCallPart.state = "approval-requested";
@@ -725,7 +769,13 @@ function createAssistantUIMessage<
725
769
 
726
770
  if (toolCallPart) {
727
771
  // Always update approval info, but only update state if not in a final state
772
+ const approval = (
773
+ toolCallPart as ToolUIPart & {
774
+ approval?: { isAutomatic?: boolean; signature?: string };
775
+ }
776
+ ).approval;
728
777
  (toolCallPart as ToolUIPart & { approval?: object }).approval = {
778
+ ...approval,
729
779
  id: approvalPart.approvalId,
730
780
  approved: approvalPart.approved,
731
781
  reason: approvalPart.reason,
@@ -744,8 +794,7 @@ function createAssistantUIMessage<
744
794
  // Process execution-denied results to update tool call states
745
795
  for (const denied of executionDeniedResults) {
746
796
  const toolCallPart = allParts.find(
747
- (part) =>
748
- "toolCallId" in part && part.toolCallId === denied.toolCallId,
797
+ (part) => "toolCallId" in part && part.toolCallId === denied.toolCallId,
749
798
  ) as ToolUIPart | undefined;
750
799
 
751
800
  if (toolCallPart) {
@@ -778,6 +827,48 @@ function createAssistantUIMessage<
778
827
  };
779
828
  }
780
829
 
830
+ function toUIReasoningFilePart(part: ReasoningFilePart): ReasoningFileUIPart {
831
+ const data = part.data;
832
+ return {
833
+ type: "reasoning-file",
834
+ mediaType: part.mediaType,
835
+ url: toUIReasoningFileUrl(data, part.mediaType),
836
+ providerMetadata: part.providerOptions,
837
+ };
838
+ }
839
+
840
+ function toUIReasoningFileUrl(
841
+ data: ReasoningFilePart["data"],
842
+ mediaType: string,
843
+ ): string {
844
+ if (isTaggedReasoningFileData(data)) {
845
+ if (data.type === "url") return data.url.toString();
846
+ data = data.data;
847
+ }
848
+ if (data instanceof URL) return data.toString();
849
+ if (typeof data === "string") {
850
+ return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
851
+ }
852
+ const bytes =
853
+ data instanceof ArrayBuffer
854
+ ? new Uint8Array(data)
855
+ : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
856
+ return `data:${mediaType};base64,${convertUint8ArrayToBase64(bytes)}`;
857
+ }
858
+
859
+ function isTaggedReasoningFileData(
860
+ data: ReasoningFilePart["data"],
861
+ ): data is Extract<ReasoningFilePart["data"], { type: string }> {
862
+ return (
863
+ typeof data === "object" &&
864
+ data !== null &&
865
+ !(data instanceof URL) &&
866
+ !(data instanceof ArrayBuffer) &&
867
+ !(data instanceof Uint8Array) &&
868
+ "type" in data
869
+ );
870
+ }
871
+
781
872
  function toSourcePart(
782
873
  part: SourcePart | Infer<typeof vSource>,
783
874
  ): SourceUrlUIPart | SourceDocumentUIPart {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "vitest";
2
- import { Agent, createTool } from "./index.js";
2
+ import { Agent, createTool } from "../index.js";
3
3
  import type {
4
4
  DataModelFromSchemaDefinition,
5
5
  ApiFromModules,
@@ -65,7 +65,8 @@ const renameFileTool = createTool({
65
65
  newName: z.string(),
66
66
  }),
67
67
  needsApproval: () => true,
68
- execute: async (_ctx, input) => `Renamed: ${input.oldName} → ${input.newName}`,
68
+ execute: async (_ctx, input) =>
69
+ `Renamed: ${input.oldName} → ${input.newName}`,
69
70
  });
70
71
 
71
72
  // --- Agents (separate mock model instances to avoid shared callIndex) ---
@@ -144,6 +145,14 @@ export const testApproveFlow = action({
144
145
  threadId: thread.threadId,
145
146
  paginationOpts: { cursor: null, numItems: 20 },
146
147
  });
148
+ const toolResults = allMessages.page.flatMap((message) =>
149
+ Array.isArray(message.message?.content)
150
+ ? message.message.content.filter(
151
+ (part) =>
152
+ part.type === "tool-result" && part.toolCallId === "tc-approve",
153
+ )
154
+ : [],
155
+ );
147
156
 
148
157
  return {
149
158
  approvalId,
@@ -153,8 +162,9 @@ export const testApproveFlow = action({
153
162
  secondSavedCount: result2.savedMessages?.length ?? 0,
154
163
  totalThreadMessages: allMessages.page.length,
155
164
  threadMessageRoles: allMessages.page.map((m) => m.message?.role),
165
+ toolResults,
156
166
  usageCallCount: usageCalls.length,
157
- // Verify usage data includes detail fields (AI SDK v6)
167
+ // Verify usage data includes detail fields.
158
168
  lastUsage: usageCalls.at(-1),
159
169
  };
160
170
  },
@@ -291,7 +301,12 @@ const multiToolAgent = new Agent(components.agent, {
291
301
  },
292
302
  ],
293
303
  // Step 2: after both tools execute, model responds
294
- [{ type: "text", text: "Done! Deleted old.txt and renamed a.txt to b.txt." }],
304
+ [
305
+ {
306
+ type: "text",
307
+ text: "Done! Deleted old.txt and renamed a.txt to b.txt.",
308
+ },
309
+ ],
295
310
  ],
296
311
  }),
297
312
  stopWhen: stepCountIs(5),
@@ -377,14 +392,22 @@ export const submitApprovalForMultiToolAgent = mutation({
377
392
  });
378
393
 
379
394
  export const submitApprovalForApprovalAgent = mutation({
380
- args: { threadId: v.string(), approvalId: v.string(), reason: v.optional(v.string()) },
395
+ args: {
396
+ threadId: v.string(),
397
+ approvalId: v.string(),
398
+ reason: v.optional(v.string()),
399
+ },
381
400
  handler: async (ctx, { threadId, approvalId, reason }) => {
382
401
  return approvalAgent.approveToolCall(ctx, { threadId, approvalId, reason });
383
402
  },
384
403
  });
385
404
 
386
405
  export const submitDenialForDenialAgent = mutation({
387
- args: { threadId: v.string(), approvalId: v.string(), reason: v.optional(v.string()) },
406
+ args: {
407
+ threadId: v.string(),
408
+ approvalId: v.string(),
409
+ reason: v.optional(v.string()),
410
+ },
388
411
  handler: async (ctx, { threadId, approvalId, reason }) => {
389
412
  return denialAgent.denyToolCall(ctx, { threadId, approvalId, reason });
390
413
  },
@@ -417,8 +440,6 @@ describe("Tool Approval Workflow", () => {
417
440
  expect(result.firstSavedCount).toBeGreaterThanOrEqual(2);
418
441
  // Second call: tool-result + assistant text
419
442
  expect(result.secondSavedCount).toBeGreaterThanOrEqual(1);
420
- // Thread should have (ascending): user, assistant(tool-call+approval),
421
- // tool(approval-response), tool(tool-result), assistant(text)
422
443
  // listMessages returns descending order:
423
444
  expect(result.threadMessageRoles).toEqual([
424
445
  "assistant", // final text
@@ -427,9 +448,17 @@ describe("Tool Approval Workflow", () => {
427
448
  "assistant", // tool-call + approval-request
428
449
  "user", // prompt
429
450
  ]);
451
+ expect(result.toolResults).toEqual([
452
+ {
453
+ type: "tool-result",
454
+ toolCallId: "tc-approve",
455
+ toolName: "deleteFile",
456
+ output: { type: "text", value: "Deleted: test.txt" },
457
+ },
458
+ ]);
430
459
  // Usage handler should be called for each generateText call
431
460
  expect(result.usageCallCount).toBeGreaterThanOrEqual(2);
432
- // Usage data should include AI SDK v6 detail fields
461
+ // Usage data should include detail fields.
433
462
  expect(result.lastUsage).toBeDefined();
434
463
  expect(result.lastUsage!.inputTokenDetails).toBeDefined();
435
464
  expect(result.lastUsage!.outputTokenDetails).toBeDefined();
@@ -443,9 +472,7 @@ describe("Tool Approval Workflow", () => {
443
472
  expect(result.approvalId).toBeDefined();
444
473
  expect(result.firstText).toBe("");
445
474
  expect(result.secondText).toBe("OK, I won't delete that file.");
446
- // Same message ordering as approve flow:
447
- // user, assistant(tool-call+approval), tool(denial-response),
448
- // tool(execution-denied result), assistant(text)
475
+ // Same message ordering as approve flow.
449
476
  expect(result.threadMessageRoles).toEqual([
450
477
  "assistant",
451
478
  "tool",
@@ -469,14 +496,11 @@ describe("Tool Approval Workflow", () => {
469
496
  expect(result.secondText).toBe(
470
497
  "Done! Deleted old.txt and renamed a.txt to b.txt.",
471
498
  );
472
- // Both approval responses should be merged into one tool message
473
- // (write-time merge in respondToToolCallApproval via findApprovalContext)
474
- // Thread: user, assistant(2 tool-calls + 2 approvals),
475
- // tool(2 approval-responses merged), tool(2 tool-results), assistant(text)
499
+ // Executed results are persisted separately from approval responses.
476
500
  expect(result.threadMessageRoles).toEqual([
477
501
  "assistant", // final text
478
502
  "tool", // tool-results
479
- "tool", // approval-responses (merged)
503
+ "tool", // approval-responses
480
504
  "assistant", // tool-calls + approval-requests
481
505
  "user", // prompt
482
506
  ]);
@@ -485,10 +509,15 @@ describe("Tool Approval Workflow", () => {
485
509
  test("approve remains valid with an intervening thread message", async () => {
486
510
  usageCalls.length = 0;
487
511
  const t = initConvexTest(schema);
488
- const result = await t.action(testApi.testApproveFlowWithInterveningMessage, {});
512
+ const result = await t.action(
513
+ testApi.testApproveFlowWithInterveningMessage,
514
+ {},
515
+ );
489
516
 
490
517
  expect(result.secondText).toBe("Done! I deleted test.txt.");
491
518
  expect(result.approvalResponseOrder).toBe(result.approvalRequestOrder);
492
- expect(result.interveningOrder).toBeGreaterThan(result.approvalResponseOrder);
519
+ expect(result.interveningOrder).toBeGreaterThan(
520
+ result.approvalResponseOrder,
521
+ );
493
522
  });
494
523
  });