@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-alpha.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 (235) hide show
  1. package/MIGRATION.md +153 -0
  2. package/README.md +32 -27
  3. package/dist/UIMessages.d.ts +46 -0
  4. package/dist/UIMessages.d.ts.map +1 -0
  5. package/dist/UIMessages.js +546 -0
  6. package/dist/UIMessages.js.map +1 -0
  7. package/dist/client/createTool.d.ts +126 -27
  8. package/dist/client/createTool.d.ts.map +1 -1
  9. package/dist/client/createTool.js +67 -12
  10. package/dist/client/createTool.js.map +1 -1
  11. package/dist/client/defaultComponent.d.ts +11 -0
  12. package/dist/client/defaultComponent.d.ts.map +1 -0
  13. package/dist/client/defaultComponent.js +7 -0
  14. package/dist/client/defaultComponent.js.map +1 -0
  15. package/dist/client/definePlaygroundAPI.d.ts +1335 -204
  16. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  17. package/dist/client/definePlaygroundAPI.js +52 -28
  18. package/dist/client/definePlaygroundAPI.js.map +1 -1
  19. package/dist/client/files.d.ts +20 -7
  20. package/dist/client/files.d.ts.map +1 -1
  21. package/dist/client/files.js +68 -11
  22. package/dist/client/files.js.map +1 -1
  23. package/dist/client/index.d.ts +1116 -978
  24. package/dist/client/index.d.ts.map +1 -1
  25. package/dist/client/index.js +332 -747
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/client/messages.d.ts +461 -0
  28. package/dist/client/messages.d.ts.map +1 -0
  29. package/dist/client/messages.js +106 -0
  30. package/dist/client/messages.js.map +1 -0
  31. package/dist/client/mockModel.d.ts +42 -0
  32. package/dist/client/mockModel.d.ts.map +1 -0
  33. package/dist/client/mockModel.js +182 -0
  34. package/dist/client/mockModel.js.map +1 -0
  35. package/dist/client/saveInputMessages.d.ts +20 -0
  36. package/dist/client/saveInputMessages.d.ts.map +1 -0
  37. package/dist/client/saveInputMessages.js +58 -0
  38. package/dist/client/saveInputMessages.js.map +1 -0
  39. package/dist/client/search.d.ts +350 -39
  40. package/dist/client/search.d.ts.map +1 -1
  41. package/dist/client/search.js +350 -39
  42. package/dist/client/search.js.map +1 -1
  43. package/dist/client/start.d.ts +84 -0
  44. package/dist/client/start.d.ts.map +1 -0
  45. package/dist/client/start.js +185 -0
  46. package/dist/client/start.js.map +1 -0
  47. package/dist/client/streamText.d.ts +46 -0
  48. package/dist/client/streamText.d.ts.map +1 -0
  49. package/dist/client/streamText.js +117 -0
  50. package/dist/client/streamText.js.map +1 -0
  51. package/dist/client/streaming.d.ts +3716 -32
  52. package/dist/client/streaming.d.ts.map +1 -1
  53. package/dist/client/streaming.js +161 -59
  54. package/dist/client/streaming.js.map +1 -1
  55. package/dist/client/threads.d.ts +46 -0
  56. package/dist/client/threads.d.ts.map +1 -0
  57. package/dist/client/threads.js +49 -0
  58. package/dist/client/threads.js.map +1 -0
  59. package/dist/client/types.d.ts +266 -128
  60. package/dist/client/types.d.ts.map +1 -1
  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 +24 -2178
  66. package/dist/component/_generated/api.d.ts.map +1 -1
  67. package/dist/component/_generated/api.js +10 -1
  68. package/dist/component/_generated/api.js.map +1 -1
  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 +4 -18
  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 +10 -38
  78. package/dist/component/_generated/server.d.ts.map +1 -1
  79. package/dist/component/_generated/server.js +9 -5
  80. package/dist/component/_generated/server.js.map +1 -1
  81. package/dist/component/files.d.ts +16 -10
  82. package/dist/component/files.d.ts.map +1 -1
  83. package/dist/component/files.js +10 -2
  84. package/dist/component/files.js.map +1 -1
  85. package/dist/component/messages.d.ts +2578 -366
  86. package/dist/component/messages.d.ts.map +1 -1
  87. package/dist/component/messages.js +397 -154
  88. package/dist/component/messages.js.map +1 -1
  89. package/dist/component/schema.d.ts +5697 -3584
  90. package/dist/component/schema.d.ts.map +1 -1
  91. package/dist/component/schema.js +18 -41
  92. package/dist/component/schema.js.map +1 -1
  93. package/dist/component/streams.d.ts +39 -339
  94. package/dist/component/streams.d.ts.map +1 -1
  95. package/dist/component/streams.js +114 -73
  96. package/dist/component/streams.js.map +1 -1
  97. package/dist/component/threads.d.ts +13 -13
  98. package/dist/component/users.d.ts +7 -7
  99. package/dist/component/vector/index.d.ts +1 -1
  100. package/dist/component/vector/index.d.ts.map +1 -1
  101. package/dist/component/vector/index.js +1 -3
  102. package/dist/component/vector/index.js.map +1 -1
  103. package/dist/deltas.d.ts +43 -0
  104. package/dist/deltas.d.ts.map +1 -0
  105. package/dist/deltas.js +446 -0
  106. package/dist/deltas.js.map +1 -0
  107. package/dist/mapping.d.ts +38 -20
  108. package/dist/mapping.d.ts.map +1 -1
  109. package/dist/mapping.js +365 -97
  110. package/dist/mapping.js.map +1 -1
  111. package/dist/react/SmoothText.d.ts +5 -0
  112. package/dist/react/SmoothText.d.ts.map +1 -0
  113. package/dist/react/SmoothText.js +6 -0
  114. package/dist/react/SmoothText.js.map +1 -0
  115. package/dist/react/index.d.ts +5 -77
  116. package/dist/react/index.d.ts.map +1 -1
  117. package/dist/react/index.js +6 -160
  118. package/dist/react/index.js.map +1 -1
  119. package/dist/react/optimisticallySendMessage.d.ts +36 -3
  120. package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
  121. package/dist/react/optimisticallySendMessage.js +35 -9
  122. package/dist/react/optimisticallySendMessage.js.map +1 -1
  123. package/dist/react/types.d.ts +4 -18
  124. package/dist/react/types.d.ts.map +1 -1
  125. package/dist/react/useDeltaStreams.d.ts +10 -0
  126. package/dist/react/useDeltaStreams.d.ts.map +1 -0
  127. package/dist/react/useDeltaStreams.js +106 -0
  128. package/dist/react/useDeltaStreams.js.map +1 -0
  129. package/dist/react/useSmoothText.d.ts +13 -12
  130. package/dist/react/useSmoothText.d.ts.map +1 -1
  131. package/dist/react/useSmoothText.js +32 -15
  132. package/dist/react/useSmoothText.js.map +1 -1
  133. package/dist/react/useStreamingUIMessages.d.ts +22 -0
  134. package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
  135. package/dist/react/useStreamingUIMessages.js +92 -0
  136. package/dist/react/useStreamingUIMessages.js.map +1 -0
  137. package/dist/react/useThreadMessages.d.ts +104 -0
  138. package/dist/react/useThreadMessages.d.ts.map +1 -0
  139. package/dist/react/useThreadMessages.js +148 -0
  140. package/dist/react/useThreadMessages.js.map +1 -0
  141. package/dist/react/useUIMessages.d.ts +96 -0
  142. package/dist/react/useUIMessages.d.ts.map +1 -0
  143. package/dist/react/useUIMessages.js +108 -0
  144. package/dist/react/useUIMessages.js.map +1 -0
  145. package/dist/shared.d.ts +20 -4
  146. package/dist/shared.d.ts.map +1 -1
  147. package/dist/shared.js +45 -8
  148. package/dist/shared.js.map +1 -1
  149. package/dist/validators.d.ts +22981 -5666
  150. package/dist/validators.d.ts.map +1 -1
  151. package/dist/validators.js +245 -137
  152. package/dist/validators.js.map +1 -1
  153. package/package.json +101 -51
  154. package/src/UIMessages.combineUIMessages.test.ts +239 -0
  155. package/src/UIMessages.test.ts +273 -0
  156. package/src/UIMessages.ts +739 -0
  157. package/src/client/approval.test.ts +350 -0
  158. package/src/client/createTool.ts +291 -76
  159. package/src/client/defaultComponent.ts +17 -0
  160. package/src/client/definePlaygroundAPI.ts +67 -31
  161. package/src/client/files.ts +100 -20
  162. package/src/client/index.test.ts +40 -85
  163. package/src/client/index.ts +638 -1289
  164. package/src/client/messages.ts +237 -0
  165. package/src/client/mockModel.ts +252 -0
  166. package/src/client/saveInputMessages.test.ts +583 -0
  167. package/src/client/saveInputMessages.ts +101 -0
  168. package/src/client/search.test.ts +1207 -0
  169. package/src/client/search.ts +581 -70
  170. package/src/client/start.ts +327 -0
  171. package/src/client/streamText.ts +187 -0
  172. package/src/client/streaming.test.ts +186 -0
  173. package/src/client/streaming.ts +241 -97
  174. package/src/client/threads.ts +83 -0
  175. package/src/client/types.ts +370 -219
  176. package/src/client/utils.ts +27 -0
  177. package/src/component/_generated/api.ts +64 -0
  178. package/src/component/_generated/component.ts +4902 -0
  179. package/src/component/_generated/{server.d.ts → server.ts} +33 -21
  180. package/src/component/files.ts +11 -2
  181. package/src/component/messages.test.ts +195 -51
  182. package/src/component/messages.ts +500 -201
  183. package/src/component/schema.ts +20 -46
  184. package/src/component/setup.test.ts +7 -0
  185. package/src/component/streams.ts +184 -83
  186. package/src/component/users.test.ts +0 -1
  187. package/src/component/vector/index.ts +1 -3
  188. package/src/deltas.test.ts +626 -0
  189. package/src/deltas.ts +569 -0
  190. package/src/fromUIMessages.test.ts +497 -0
  191. package/src/mapping.test.ts +180 -6
  192. package/src/mapping.ts +479 -162
  193. package/src/react/SmoothText.tsx +9 -0
  194. package/src/react/index.ts +10 -230
  195. package/src/react/optimisticallySendMessage.ts +55 -12
  196. package/src/react/types.ts +6 -39
  197. package/src/react/useDeltaStreams.ts +160 -0
  198. package/src/react/useSmoothText.ts +56 -36
  199. package/src/react/useStreamingUIMessages.ts +143 -0
  200. package/src/react/useThreadMessages.ts +262 -0
  201. package/src/react/useUIMessages.test.ts +255 -0
  202. package/src/react/useUIMessages.ts +195 -0
  203. package/src/shared.ts +88 -12
  204. package/src/test.ts +18 -0
  205. package/src/toUIMessages.test.ts +1269 -0
  206. package/src/validators.test.ts +18 -19
  207. package/src/validators.ts +325 -185
  208. package/dist/client/_generated/_ignore.d.ts +0 -1
  209. package/dist/client/_generated/_ignore.d.ts.map +0 -1
  210. package/dist/client/_generated/_ignore.js +0 -3
  211. package/dist/client/_generated/_ignore.js.map +0 -1
  212. package/dist/client/listMessages.d.ts +0 -22
  213. package/dist/client/listMessages.d.ts.map +0 -1
  214. package/dist/client/listMessages.js +0 -25
  215. package/dist/client/listMessages.js.map +0 -1
  216. package/dist/package.json +0 -3
  217. package/dist/react/deltas.d.ts +0 -26
  218. package/dist/react/deltas.d.ts.map +0 -1
  219. package/dist/react/deltas.js +0 -384
  220. package/dist/react/deltas.js.map +0 -1
  221. package/dist/react/toUIMessages.d.ts +0 -15
  222. package/dist/react/toUIMessages.d.ts.map +0 -1
  223. package/dist/react/toUIMessages.js +0 -211
  224. package/dist/react/toUIMessages.js.map +0 -1
  225. package/src/client/listMessages.ts +0 -38
  226. package/src/component/_generated/api.d.ts +0 -2202
  227. package/src/component/_generated/api.js +0 -23
  228. package/src/component/_generated/server.js +0 -90
  229. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  230. package/src/react/deltas.test.ts +0 -315
  231. package/src/react/deltas.ts +0 -478
  232. package/src/react/toUIMessages.test.ts +0 -420
  233. package/src/react/toUIMessages.ts +0 -253
  234. package/src/vitest.config.ts +0 -7
  235. /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
@@ -2,18 +2,22 @@ import { describe, test, expect } from "vitest";
2
2
  import {
3
3
  guessMimeType,
4
4
  serializeDataOrUrl,
5
- deserializeUrl,
5
+ toModelMessageDataOrUrl,
6
6
  serializeMessage,
7
- deserializeMessage,
7
+ toModelMessage,
8
8
  serializeContent,
9
+ toModelMessageContent,
10
+ mergeApprovalResponseMessages,
9
11
  } from "./mapping.js";
10
12
  import { api } from "./component/_generated/api.js";
11
13
  import type { AgentComponent, ActionCtx } from "./client/types.js";
12
- import { vMessage } from "./validators.js";
14
+ import { vMessage, vToolResultPart } from "./validators.js";
13
15
  import fs from "fs";
14
16
  import path from "path";
15
17
  import type { SerializedContent } from "./mapping.js";
16
18
  import { validate } from "convex-helpers/validators";
19
+ import type { ToolResultPart } from "ai";
20
+ import type { Infer } from "convex/values";
17
21
 
18
22
  const testAssetsDir = path.join(__dirname, "../test-assets");
19
23
  const testFiles = [
@@ -54,8 +58,8 @@ describe("mapping", () => {
54
58
  const ser = serializeDataOrUrl(arr);
55
59
  expect(ser).toBeInstanceOf(ArrayBuffer);
56
60
  expect(new Uint8Array(ser as ArrayBuffer)).toEqual(arr);
57
- // deserializeUrl should return the same ArrayBuffer
58
- const deser = deserializeUrl(ser);
61
+ // toModelMessageDataOrUrl should return the same ArrayBuffer
62
+ const deser = toModelMessageDataOrUrl(ser);
59
63
  expect(deser).toBeInstanceOf(ArrayBuffer);
60
64
  expect(new Uint8Array(deser as ArrayBuffer)).toEqual(arr);
61
65
  });
@@ -80,10 +84,56 @@ describe("mapping", () => {
80
84
  const { message: ser } = await serializeMessage(ctx, component, message);
81
85
  // Use is for type validation
82
86
  expect(validate(vMessage, ser)).toBeTruthy();
83
- const round = deserializeMessage(ser);
87
+ const round = toModelMessage(ser);
84
88
  expect(round).toEqual(message);
85
89
  });
86
90
 
91
+ test("tool output round-trips", async () => {
92
+ const toolResult = {
93
+ type: "tool-result" as const,
94
+ toolCallId: "tool-call-id",
95
+ toolName: "tool-name",
96
+ output: {
97
+ type: "text",
98
+ value: "hello world",
99
+ },
100
+ } satisfies ToolResultPart;
101
+ const [result] = toModelMessageContent([toolResult]);
102
+ expect(result).toMatchObject(toolResult);
103
+ const {
104
+ content: [roundtrip],
105
+ } = await serializeContent({} as ActionCtx, {} as AgentComponent, [
106
+ result as ToolResultPart,
107
+ ]);
108
+ expect(roundtrip).toMatchObject(toolResult);
109
+ });
110
+
111
+ test("tool results get normalized to output", async () => {
112
+ const toolResult = {
113
+ type: "tool-result" as const,
114
+ toolCallId: "tool-call-id",
115
+ toolName: "tool-name",
116
+ result: "hello world",
117
+ } satisfies Infer<typeof vToolResultPart>;
118
+ const expected = {
119
+ type: "tool-result",
120
+ toolCallId: "tool-call-id",
121
+ toolName: "tool-name",
122
+ output: {
123
+ type: "text",
124
+ value: "hello world",
125
+ },
126
+ };
127
+ const [deserialized] = toModelMessageContent([toolResult]);
128
+ expect(deserialized).toMatchObject(expected);
129
+ const {
130
+ content: [serialized],
131
+ } = await serializeContent({} as ActionCtx, {} as AgentComponent, [
132
+ toolResult,
133
+ ]);
134
+ expect(serialized).toMatchObject(expected);
135
+ });
136
+
87
137
  test("saving files returns fileIds when too big", async () => {
88
138
  // Make a big file
89
139
  const bigArr = new Uint8Array(1024 * 65).fill(1);
@@ -160,4 +210,128 @@ describe("mapping", () => {
160
210
  const { fileIds } = await serializeContent(ctx, component, content);
161
211
  expect(fileIds).toBeUndefined();
162
212
  });
213
+
214
+ test("tool-approval-request is preserved after serialization", async () => {
215
+ const approvalRequest = {
216
+ type: "tool-approval-request" as const,
217
+ approvalId: "approval-123",
218
+ toolCallId: "tool-call-456",
219
+ };
220
+ const { content } = await serializeContent(
221
+ {} as ActionCtx,
222
+ {} as AgentComponent,
223
+ [approvalRequest],
224
+ );
225
+ expect(content).toHaveLength(1);
226
+ expect((content as unknown[])[0]).toMatchObject(approvalRequest);
227
+ });
228
+
229
+ test("tool-approval-response with approved: true is preserved", async () => {
230
+ const approvalResponse = {
231
+ type: "tool-approval-response" as const,
232
+ approvalId: "approval-123",
233
+ approved: true,
234
+ reason: "User approved",
235
+ };
236
+ const { content } = await serializeContent(
237
+ {} as ActionCtx,
238
+ {} as AgentComponent,
239
+ [approvalResponse],
240
+ );
241
+ expect(content).toHaveLength(1);
242
+ expect((content as unknown[])[0]).toMatchObject(approvalResponse);
243
+ });
244
+
245
+ test("tool-approval-response with approved: false is preserved", async () => {
246
+ const approvalResponse = {
247
+ type: "tool-approval-response" as const,
248
+ approvalId: "approval-123",
249
+ approved: false,
250
+ reason: "User denied",
251
+ providerExecuted: false,
252
+ };
253
+ const { content } = await serializeContent(
254
+ {} as ActionCtx,
255
+ {} as AgentComponent,
256
+ [approvalResponse],
257
+ );
258
+ expect(content).toHaveLength(1);
259
+ expect((content as unknown[])[0]).toMatchObject(approvalResponse);
260
+ });
261
+
262
+ test("mergeApprovalResponseMessages merges consecutive tool approval messages", () => {
263
+ const messages = [
264
+ { role: "user" as const, content: "hello" },
265
+ {
266
+ role: "assistant" as const,
267
+ content: [
268
+ { type: "tool-call", toolCallId: "tc1", toolName: "a", input: {} },
269
+ { type: "tool-call", toolCallId: "tc2", toolName: "b", input: {} },
270
+ { type: "tool-approval-request", approvalId: "ap1", toolCallId: "tc1" },
271
+ { type: "tool-approval-request", approvalId: "ap2", toolCallId: "tc2" },
272
+ ],
273
+ },
274
+ {
275
+ role: "tool" as const,
276
+ content: [
277
+ { type: "tool-approval-response", approvalId: "ap1", approved: true },
278
+ ],
279
+ },
280
+ {
281
+ role: "tool" as const,
282
+ content: [
283
+ { type: "tool-approval-response", approvalId: "ap2", approved: false, reason: "denied" },
284
+ ],
285
+ },
286
+ ] as any;
287
+
288
+ const merged = mergeApprovalResponseMessages(messages);
289
+ expect(merged).toHaveLength(3); // user, assistant, single tool
290
+ expect(merged[2].role).toBe("tool");
291
+ const toolContent = merged[2].content as Array<{ type: string; approvalId: string }>;
292
+ expect(toolContent).toHaveLength(2);
293
+ expect(toolContent[0].approvalId).toBe("ap1");
294
+ expect(toolContent[1].approvalId).toBe("ap2");
295
+ });
296
+
297
+ test("mergeApprovalResponseMessages does not mutate original message content arrays", () => {
298
+ const msg1Content = [
299
+ { type: "tool-approval-response", approvalId: "ap1", approved: true },
300
+ ];
301
+ const msg2Content = [
302
+ { type: "tool-approval-response", approvalId: "ap2", approved: false, reason: "denied" },
303
+ ];
304
+ const messages = [
305
+ { role: "tool" as const, content: msg1Content },
306
+ { role: "tool" as const, content: msg2Content },
307
+ ] as any;
308
+
309
+ const merged = mergeApprovalResponseMessages(messages);
310
+ // Merged result should combine both
311
+ expect(merged).toHaveLength(1);
312
+ expect((merged[0].content as any[]).length).toBe(2);
313
+ // Original arrays must be untouched
314
+ expect(msg1Content).toHaveLength(1);
315
+ expect(msg2Content).toHaveLength(1);
316
+ });
317
+
318
+ test("mergeApprovalResponseMessages does not merge non-approval tool messages", () => {
319
+ const messages = [
320
+ {
321
+ role: "tool" as const,
322
+ content: [
323
+ { type: "tool-result", toolCallId: "tc1", toolName: "a", output: { type: "text", value: "ok" } },
324
+ ],
325
+ },
326
+ {
327
+ role: "tool" as const,
328
+ content: [
329
+ { type: "tool-approval-response", approvalId: "ap1", approved: true },
330
+ ],
331
+ },
332
+ ] as any;
333
+
334
+ const merged = mergeApprovalResponseMessages(messages);
335
+ expect(merged).toHaveLength(2); // not merged since first has tool-result
336
+ });
163
337
  });