@convex-dev/agent 0.5.0-alpha.1 → 0.6.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 (233) hide show
  1. package/README.md +32 -27
  2. package/dist/UIMessages.d.ts +46 -0
  3. package/dist/UIMessages.d.ts.map +1 -0
  4. package/dist/UIMessages.js +546 -0
  5. package/dist/UIMessages.js.map +1 -0
  6. package/dist/client/createTool.d.ts +129 -27
  7. package/dist/client/createTool.d.ts.map +1 -1
  8. package/dist/client/createTool.js +66 -12
  9. package/dist/client/createTool.js.map +1 -1
  10. package/dist/client/defaultComponent.d.ts +11 -0
  11. package/dist/client/defaultComponent.d.ts.map +1 -0
  12. package/dist/client/defaultComponent.js +7 -0
  13. package/dist/client/defaultComponent.js.map +1 -0
  14. package/dist/client/definePlaygroundAPI.d.ts +1323 -192
  15. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  16. package/dist/client/definePlaygroundAPI.js +52 -28
  17. package/dist/client/definePlaygroundAPI.js.map +1 -1
  18. package/dist/client/files.d.ts +20 -7
  19. package/dist/client/files.d.ts.map +1 -1
  20. package/dist/client/files.js +68 -11
  21. package/dist/client/files.js.map +1 -1
  22. package/dist/client/index.d.ts +1056 -965
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/index.js +242 -748
  25. package/dist/client/index.js.map +1 -1
  26. package/dist/client/messages.d.ts +461 -0
  27. package/dist/client/messages.d.ts.map +1 -0
  28. package/dist/client/messages.js +106 -0
  29. package/dist/client/messages.js.map +1 -0
  30. package/dist/client/mockModel.d.ts +42 -0
  31. package/dist/client/mockModel.d.ts.map +1 -0
  32. package/dist/client/mockModel.js +175 -0
  33. package/dist/client/mockModel.js.map +1 -0
  34. package/dist/client/saveInputMessages.d.ts +20 -0
  35. package/dist/client/saveInputMessages.d.ts.map +1 -0
  36. package/dist/client/saveInputMessages.js +58 -0
  37. package/dist/client/saveInputMessages.js.map +1 -0
  38. package/dist/client/search.d.ts +346 -35
  39. package/dist/client/search.d.ts.map +1 -1
  40. package/dist/client/search.js +350 -39
  41. package/dist/client/search.js.map +1 -1
  42. package/dist/client/start.d.ts +84 -0
  43. package/dist/client/start.d.ts.map +1 -0
  44. package/dist/client/start.js +171 -0
  45. package/dist/client/start.js.map +1 -0
  46. package/dist/client/streamText.d.ts +46 -0
  47. package/dist/client/streamText.d.ts.map +1 -0
  48. package/dist/client/streamText.js +93 -0
  49. package/dist/client/streamText.js.map +1 -0
  50. package/dist/client/streaming.d.ts +3705 -32
  51. package/dist/client/streaming.d.ts.map +1 -1
  52. package/dist/client/streaming.js +141 -59
  53. package/dist/client/streaming.js.map +1 -1
  54. package/dist/client/threads.d.ts +46 -0
  55. package/dist/client/threads.d.ts.map +1 -0
  56. package/dist/client/threads.js +49 -0
  57. package/dist/client/threads.js.map +1 -0
  58. package/dist/client/types.d.ts +265 -128
  59. package/dist/client/types.d.ts.map +1 -1
  60. package/dist/client/utils.d.ts +4 -0
  61. package/dist/client/utils.d.ts.map +1 -0
  62. package/dist/client/utils.js +21 -0
  63. package/dist/client/utils.js.map +1 -0
  64. package/dist/component/_generated/api.d.ts +24 -2178
  65. package/dist/component/_generated/api.d.ts.map +1 -1
  66. package/dist/component/_generated/api.js +10 -1
  67. package/dist/component/_generated/api.js.map +1 -1
  68. package/dist/component/_generated/component.d.ts +3119 -0
  69. package/dist/component/_generated/component.d.ts.map +1 -0
  70. package/dist/component/_generated/component.js +11 -0
  71. package/dist/component/_generated/component.js.map +1 -0
  72. package/dist/component/_generated/dataModel.d.ts +4 -18
  73. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  74. package/dist/component/_generated/dataModel.js +11 -0
  75. package/dist/component/_generated/dataModel.js.map +1 -0
  76. package/dist/component/_generated/server.d.ts +10 -38
  77. package/dist/component/_generated/server.d.ts.map +1 -1
  78. package/dist/component/_generated/server.js +9 -5
  79. package/dist/component/_generated/server.js.map +1 -1
  80. package/dist/component/files.d.ts +16 -10
  81. package/dist/component/files.d.ts.map +1 -1
  82. package/dist/component/files.js +10 -2
  83. package/dist/component/files.js.map +1 -1
  84. package/dist/component/messages.d.ts +2553 -342
  85. package/dist/component/messages.d.ts.map +1 -1
  86. package/dist/component/messages.js +387 -154
  87. package/dist/component/messages.js.map +1 -1
  88. package/dist/component/schema.d.ts +5697 -3584
  89. package/dist/component/schema.d.ts.map +1 -1
  90. package/dist/component/schema.js +18 -41
  91. package/dist/component/schema.js.map +1 -1
  92. package/dist/component/streams.d.ts +35 -335
  93. package/dist/component/streams.d.ts.map +1 -1
  94. package/dist/component/streams.js +114 -73
  95. package/dist/component/streams.js.map +1 -1
  96. package/dist/component/threads.d.ts +16 -16
  97. package/dist/component/users.d.ts +4 -4
  98. package/dist/component/vector/index.d.ts +1 -1
  99. package/dist/component/vector/index.d.ts.map +1 -1
  100. package/dist/component/vector/index.js +1 -3
  101. package/dist/component/vector/index.js.map +1 -1
  102. package/dist/deltas.d.ts +43 -0
  103. package/dist/deltas.d.ts.map +1 -0
  104. package/dist/deltas.js +447 -0
  105. package/dist/deltas.js.map +1 -0
  106. package/dist/mapping.d.ts +20 -20
  107. package/dist/mapping.d.ts.map +1 -1
  108. package/dist/mapping.js +313 -96
  109. package/dist/mapping.js.map +1 -1
  110. package/dist/react/SmoothText.d.ts +5 -0
  111. package/dist/react/SmoothText.d.ts.map +1 -0
  112. package/dist/react/SmoothText.js +6 -0
  113. package/dist/react/SmoothText.js.map +1 -0
  114. package/dist/react/index.d.ts +5 -77
  115. package/dist/react/index.d.ts.map +1 -1
  116. package/dist/react/index.js +6 -160
  117. package/dist/react/index.js.map +1 -1
  118. package/dist/react/optimisticallySendMessage.d.ts +36 -3
  119. package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
  120. package/dist/react/optimisticallySendMessage.js +35 -9
  121. package/dist/react/optimisticallySendMessage.js.map +1 -1
  122. package/dist/react/types.d.ts +4 -18
  123. package/dist/react/types.d.ts.map +1 -1
  124. package/dist/react/useDeltaStreams.d.ts +10 -0
  125. package/dist/react/useDeltaStreams.d.ts.map +1 -0
  126. package/dist/react/useDeltaStreams.js +101 -0
  127. package/dist/react/useDeltaStreams.js.map +1 -0
  128. package/dist/react/useSmoothText.d.ts +13 -12
  129. package/dist/react/useSmoothText.d.ts.map +1 -1
  130. package/dist/react/useSmoothText.js +32 -15
  131. package/dist/react/useSmoothText.js.map +1 -1
  132. package/dist/react/useStreamingUIMessages.d.ts +22 -0
  133. package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
  134. package/dist/react/useStreamingUIMessages.js +92 -0
  135. package/dist/react/useStreamingUIMessages.js.map +1 -0
  136. package/dist/react/useThreadMessages.d.ts +104 -0
  137. package/dist/react/useThreadMessages.d.ts.map +1 -0
  138. package/dist/react/useThreadMessages.js +148 -0
  139. package/dist/react/useThreadMessages.js.map +1 -0
  140. package/dist/react/useUIMessages.d.ts +96 -0
  141. package/dist/react/useUIMessages.d.ts.map +1 -0
  142. package/dist/react/useUIMessages.js +108 -0
  143. package/dist/react/useUIMessages.js.map +1 -0
  144. package/dist/shared.d.ts +20 -4
  145. package/dist/shared.d.ts.map +1 -1
  146. package/dist/shared.js +45 -8
  147. package/dist/shared.js.map +1 -1
  148. package/dist/validators.d.ts +22981 -5666
  149. package/dist/validators.d.ts.map +1 -1
  150. package/dist/validators.js +245 -137
  151. package/dist/validators.js.map +1 -1
  152. package/package.json +98 -50
  153. package/src/UIMessages.combineUIMessages.test.ts +239 -0
  154. package/src/UIMessages.test.ts +273 -0
  155. package/src/UIMessages.ts +739 -0
  156. package/src/client/createTool.ts +293 -76
  157. package/src/client/defaultComponent.ts +17 -0
  158. package/src/client/definePlaygroundAPI.ts +67 -31
  159. package/src/client/files.ts +100 -20
  160. package/src/client/index.test.ts +40 -85
  161. package/src/client/index.ts +520 -1290
  162. package/src/client/messages.ts +237 -0
  163. package/src/client/mockModel.ts +245 -0
  164. package/src/client/saveInputMessages.test.ts +583 -0
  165. package/src/client/saveInputMessages.ts +101 -0
  166. package/src/client/search.test.ts +1207 -0
  167. package/src/client/search.ts +577 -70
  168. package/src/client/start.ts +310 -0
  169. package/src/client/streamText.ts +163 -0
  170. package/src/client/streaming.test.ts +186 -0
  171. package/src/client/streaming.ts +219 -97
  172. package/src/client/threads.ts +83 -0
  173. package/src/client/types.ts +368 -219
  174. package/src/client/utils.ts +27 -0
  175. package/src/component/_generated/api.ts +64 -0
  176. package/src/component/_generated/component.ts +4913 -0
  177. package/src/component/_generated/{server.d.ts → server.ts} +33 -21
  178. package/src/component/files.ts +11 -2
  179. package/src/component/messages.test.ts +195 -51
  180. package/src/component/messages.ts +490 -201
  181. package/src/component/schema.ts +20 -46
  182. package/src/component/setup.test.ts +7 -0
  183. package/src/component/streams.ts +184 -83
  184. package/src/component/users.test.ts +0 -1
  185. package/src/component/vector/index.ts +1 -3
  186. package/src/deltas.test.ts +626 -0
  187. package/src/deltas.ts +570 -0
  188. package/src/fromUIMessages.test.ts +497 -0
  189. package/src/mapping.test.ts +103 -6
  190. package/src/mapping.ts +422 -161
  191. package/src/react/SmoothText.tsx +9 -0
  192. package/src/react/index.ts +10 -230
  193. package/src/react/optimisticallySendMessage.ts +55 -12
  194. package/src/react/types.ts +6 -39
  195. package/src/react/useDeltaStreams.ts +154 -0
  196. package/src/react/useSmoothText.ts +56 -36
  197. package/src/react/useStreamingUIMessages.ts +143 -0
  198. package/src/react/useThreadMessages.ts +262 -0
  199. package/src/react/useUIMessages.test.ts +255 -0
  200. package/src/react/useUIMessages.ts +195 -0
  201. package/src/shared.ts +88 -12
  202. package/src/test.ts +18 -0
  203. package/src/toUIMessages.test.ts +1269 -0
  204. package/src/validators.test.ts +18 -19
  205. package/src/validators.ts +325 -185
  206. package/dist/client/_generated/_ignore.d.ts +0 -1
  207. package/dist/client/_generated/_ignore.d.ts.map +0 -1
  208. package/dist/client/_generated/_ignore.js +0 -3
  209. package/dist/client/_generated/_ignore.js.map +0 -1
  210. package/dist/client/listMessages.d.ts +0 -22
  211. package/dist/client/listMessages.d.ts.map +0 -1
  212. package/dist/client/listMessages.js +0 -25
  213. package/dist/client/listMessages.js.map +0 -1
  214. package/dist/package.json +0 -3
  215. package/dist/react/deltas.d.ts +0 -26
  216. package/dist/react/deltas.d.ts.map +0 -1
  217. package/dist/react/deltas.js +0 -384
  218. package/dist/react/deltas.js.map +0 -1
  219. package/dist/react/toUIMessages.d.ts +0 -15
  220. package/dist/react/toUIMessages.d.ts.map +0 -1
  221. package/dist/react/toUIMessages.js +0 -211
  222. package/dist/react/toUIMessages.js.map +0 -1
  223. package/src/client/listMessages.ts +0 -38
  224. package/src/component/_generated/api.d.ts +0 -2202
  225. package/src/component/_generated/api.js +0 -23
  226. package/src/component/_generated/server.js +0 -90
  227. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  228. package/src/react/deltas.test.ts +0 -315
  229. package/src/react/deltas.ts +0 -478
  230. package/src/react/toUIMessages.test.ts +0 -420
  231. package/src/react/toUIMessages.ts +0 -253
  232. package/src/vitest.config.ts +0 -7
  233. /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
@@ -1,11 +1,20 @@
1
- import type { FilePart, ImagePart } from "ai";
1
+ import type {
2
+ AssistantContent,
3
+ FilePart,
4
+ ImagePart,
5
+ ModelMessage,
6
+ UserContent,
7
+ } from "ai";
2
8
  import type { Id } from "../component/_generated/dataModel.js";
3
9
  import type {
4
10
  ActionCtx,
5
11
  AgentComponent,
12
+ MutationCtx,
6
13
  QueryCtx,
7
- RunMutationCtx,
8
14
  } from "./types.js";
15
+ import type { Message } from "../validators.js";
16
+ import { assert } from "convex-helpers";
17
+ import type { StorageReader } from "convex/server";
9
18
 
10
19
  export const MAX_FILE_SIZE = 1024 * 64;
11
20
 
@@ -22,18 +31,17 @@ type File = {
22
31
  * @param ctx A ctx object from an action.
23
32
  * @param component The agent component.
24
33
  * @param blob The blob to store.
25
- * @param filename The filename to store.
26
- * @param sha256 The sha256 hash of the file. If not provided, it will be
34
+ * @param args.filename The filename to store.
35
+ * @param args.sha256 The sha256 hash of the file. If not provided, it will be
27
36
  * computed. However, to ensure no corruption during transfer, you can
28
37
  * calculate this on the client to enforce integrity.
29
38
  * @returns The URL, fileId, and storageId of the stored file.
30
39
  */
31
40
  export async function storeFile(
32
- ctx: ActionCtx | RunMutationCtx,
41
+ ctx: ActionCtx | MutationCtx,
33
42
  component: AgentComponent,
34
43
  blob: Blob,
35
- filename?: string,
36
- sha256?: string,
44
+ { filename, sha256 }: { filename?: string; sha256?: string } = {},
37
45
  ): Promise<{
38
46
  file: File;
39
47
  filePart: FilePart;
@@ -84,7 +92,7 @@ export async function storeFile(
84
92
  storageId: newStorageId,
85
93
  hash,
86
94
  filename,
87
- mimeType: blob.type,
95
+ mediaType: blob.type,
88
96
  });
89
97
  const url = (await ctx.storage.getUrl(storageId as Id<"_storage">))!;
90
98
  if (storageId !== newStorageId) {
@@ -108,7 +116,7 @@ export async function storeFile(
108
116
  /**
109
117
  * Get file metadata from the component.
110
118
  * This also returns filePart (and imagePart if the file is an image),
111
- * which are useful to construct a CoreMessage like
119
+ * which are useful to construct a ModelMessage like
112
120
  * ```ts
113
121
  * const { filePart, imagePart } = await getFile(ctx, components.agent, fileId);
114
122
  * const message: UserMessage = {
@@ -122,7 +130,7 @@ export async function storeFile(
122
130
  * @returns The file metadata and content parts.
123
131
  */
124
132
  export async function getFile(
125
- ctx: ActionCtx | QueryCtx,
133
+ ctx: ActionCtx | (QueryCtx & { storage: StorageReader }),
126
134
  component: AgentComponent,
127
135
  fileId: string,
128
136
  ) {
@@ -134,8 +142,10 @@ export async function getFile(
134
142
  if (!url) {
135
143
  throw new Error(`File not found in storage: ${file.storageId}`);
136
144
  }
145
+ // Support both mediaType (preferred) and mimeType (deprecated)
146
+ const mediaType = file.mediaType ?? file.mimeType ?? "";
137
147
  return {
138
- ...getParts(url, file.mimeType, file.filename),
148
+ ...getParts(url, mediaType, file.filename),
139
149
  file: {
140
150
  fileId,
141
151
  url,
@@ -150,10 +160,7 @@ function getParts(
150
160
  url: string,
151
161
  mediaType: string,
152
162
  filename: string | undefined,
153
- ): {
154
- filePart: FilePart;
155
- imagePart: ImagePart | undefined;
156
- } {
163
+ ): { filePart: FilePart; imagePart: ImagePart | undefined } {
157
164
  const filePart: FilePart = {
158
165
  type: "file",
159
166
  data: new URL(url),
@@ -161,11 +168,84 @@ function getParts(
161
168
  filename,
162
169
  };
163
170
  const imagePart: ImagePart | undefined = mediaType.startsWith("image/")
164
- ? {
165
- type: "image",
166
- image: new URL(url),
167
- mediaType,
168
- }
171
+ ? { type: "image", image: new URL(url), mediaType }
169
172
  : undefined;
170
173
  return { filePart, imagePart };
171
174
  }
175
+
176
+ /**
177
+ * Check if a URL points to localhost
178
+ */
179
+ function isLocalhostUrl(url: URL): boolean {
180
+ return (
181
+ url.hostname === "localhost" ||
182
+ url.hostname === "127.0.0.1" ||
183
+ url.hostname === "::1" ||
184
+ url.hostname === "0.0.0.0"
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Download a file from a URL
190
+ */
191
+ async function downloadFile(url: URL): Promise<ArrayBuffer> {
192
+ // Fetch the file
193
+ const response = await fetch(url);
194
+ if (!response.ok) {
195
+ throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
196
+ }
197
+
198
+ return await response.arrayBuffer();
199
+ }
200
+
201
+ /**
202
+ * Process messages to inline file and image URLs that point to localhost
203
+ * by converting them to base64. This solves the problem of LLMs not being
204
+ * able to access localhost URLs.
205
+ */
206
+ export async function inlineMessagesFiles<T extends ModelMessage | Message>(
207
+ messages: T[],
208
+ ): Promise<T[]> {
209
+ // Process each message to convert localhost URLs to base64
210
+ return Promise.all(
211
+ messages.map(async (message): Promise<T> => {
212
+ if (
213
+ (message.role !== "user" && message.role !== "assistant") ||
214
+ typeof message.content === "string" ||
215
+ !Array.isArray(message.content)
216
+ ) {
217
+ return message;
218
+ }
219
+
220
+ const processedContent = await Promise.all(
221
+ message.content.map(async (part) => {
222
+ if (part.type === "image" && part.image instanceof URL) {
223
+ assert(
224
+ message.role === "user",
225
+ "Images can only be in user messages",
226
+ );
227
+ if (isLocalhostUrl(part.image)) {
228
+ const imageData = await downloadFile(part.image);
229
+ return { ...part, image: imageData } as ImagePart;
230
+ }
231
+ }
232
+
233
+ // Handle file parts
234
+ if (part.type === "file" && part.data instanceof URL) {
235
+ if (isLocalhostUrl(part.data)) {
236
+ const fileData = await downloadFile(part.data);
237
+ return { ...part, data: fileData } as FilePart;
238
+ }
239
+ }
240
+
241
+ return part;
242
+ }),
243
+ );
244
+ if (message.role === "user") {
245
+ return { ...message, content: processedContent as UserContent };
246
+ } else {
247
+ return { ...message, content: processedContent as AssistantContent };
248
+ }
249
+ }),
250
+ );
251
+ }
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, test } from "vitest";
2
2
  import {
3
3
  Agent,
4
+ createThread,
4
5
  filterOutOrphanedToolMessages,
5
6
  type MessageDoc,
6
7
  } from "./index.js";
@@ -19,14 +20,10 @@ import type {
19
20
  } from "convex/server";
20
21
  import { v } from "convex/values";
21
22
  import { defineSchema } from "convex/server";
22
- import { MockLanguageModelV2 } from "ai/test";
23
- import type {
24
- LanguageModelV2,
25
- LanguageModelV2StreamPart,
26
- } from "@ai-sdk/provider";
27
- import { simulateReadableStream, stepCountIs } from "ai";
23
+ import { stepCountIs } from "ai";
28
24
  import { components, initConvexTest } from "./setup.test.js";
29
- import { z } from "zod";
25
+ import { z } from "zod/v4";
26
+ import { mockModel } from "./mockModel.js";
30
27
 
31
28
  const schema = defineSchema({});
32
29
  type DataModel = DataModelFromSchemaDefinition<typeof schema>;
@@ -35,11 +32,14 @@ const query = queryGeneric as QueryBuilder<DataModel, "public">;
35
32
  const mutation = mutationGeneric as MutationBuilder<DataModel, "public">;
36
33
  const action = actionGeneric as ActionBuilder<DataModel, "public">;
37
34
 
35
+ const TEST_TEXT = JSON.stringify({ hello: "world" });
36
+
38
37
  const agent = new Agent(components.agent, {
39
38
  name: "test",
40
39
  instructions: "You are a test agent",
41
- // TODO: get mock model that works in v8
42
- chat: mockModel(),
40
+ languageModel: mockModel({
41
+ content: [{ type: "text", text: TEST_TEXT }],
42
+ }),
43
43
  });
44
44
 
45
45
  export const testQuery = query({
@@ -47,31 +47,24 @@ export const testQuery = query({
47
47
  handler: async (ctx, args) => {
48
48
  return await agent.listMessages(ctx, {
49
49
  threadId: args.threadId,
50
- paginationOpts: {
51
- cursor: null,
52
- numItems: 10,
53
- },
50
+ paginationOpts: { cursor: null, numItems: 10 },
54
51
  excludeToolMessages: true,
55
52
  statuses: ["success"],
56
53
  });
57
54
  },
58
55
  });
59
56
 
60
- export const createThread = mutation({
57
+ export const createThreadManually = mutation({
61
58
  args: {},
62
59
  handler: async (ctx) => {
63
- const { threadId } = await agent.createThread(ctx, {
64
- userId: "1",
65
- });
60
+ const { threadId } = await agent.createThread(ctx, { userId: "1" });
66
61
  return { threadId };
67
62
  },
68
63
  });
69
64
 
70
65
  export const createThreadMutation = agent.createThreadMutation();
71
66
  export const generateObjectAction = agent.asObjectAction({
72
- schema: z.object({
73
- prompt: z.any().describe("The prompt passed in"),
74
- }),
67
+ schema: z.object({ hello: z.string().describe("A string for testing") }),
75
68
  });
76
69
  export const generateTextAction = agent.asTextAction({});
77
70
  export const streamTextAction = agent.asTextAction({ stream: true });
@@ -80,9 +73,7 @@ export const saveMessageMutation = agent.asSaveMessagesMutation();
80
73
  export const createAndGenerate = action({
81
74
  args: {},
82
75
  handler: async (ctx) => {
83
- const { thread } = await agent.createThread(ctx, {
84
- userId: "1",
85
- });
76
+ const { thread } = await agent.createThread(ctx, { userId: "1" });
86
77
  const result = await thread.generateText({
87
78
  messages: [{ role: "user", content: "Hello" }],
88
79
  });
@@ -162,7 +153,7 @@ export const fetchContextAction = action({
162
153
  const testApi: ApiFromModules<{
163
154
  fns: {
164
155
  createAndGenerate: typeof createAndGenerate;
165
- createThread: typeof createThread;
156
+ createThreadManually: typeof createThreadManually;
166
157
  testQuery: typeof testQuery;
167
158
  continueThreadAction: typeof continueThreadAction;
168
159
  generateTextWithThread: typeof generateTextWithThread;
@@ -172,20 +163,19 @@ const testApi: ApiFromModules<{
172
163
  generateObjectAction: typeof generateObjectAction;
173
164
  saveMessageMutation: typeof saveMessageMutation;
174
165
  };
175
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
166
  }>["fns"] = anyApi["index.test"] as any;
177
167
 
178
168
  describe("Agent thick client", () => {
179
169
  test("should create a thread", async () => {
180
170
  const t = initConvexTest(schema);
181
- const result = await t.mutation(testApi.createThread, {});
171
+ const result = await t.mutation(testApi.createThreadManually, {});
182
172
  expect(result.threadId).toBeTypeOf("string");
183
173
  });
184
174
  test("should create a thread and generate text", async () => {
185
175
  const t = initConvexTest(schema);
186
176
  const result = await t.action(testApi.createAndGenerate, {});
187
177
  expect(result).toBeDefined();
188
- expect(result).toMatch("Hello");
178
+ expect(result).toMatch(TEST_TEXT);
189
179
  });
190
180
  });
191
181
 
@@ -203,6 +193,7 @@ describe("filterOutOrphanedToolMessages", () => {
203
193
  type: "tool-call",
204
194
  toolCallId: "1",
205
195
  toolName: "tool1",
196
+ input: { test: "test" },
206
197
  args: { test: "test" },
207
198
  },
208
199
  ],
@@ -236,10 +227,7 @@ describe("filterOutOrphanedToolMessages", () => {
236
227
  order: 1,
237
228
  stepOrder: 2,
238
229
  tool: true,
239
- message: {
240
- role: "assistant",
241
- content: [{ type: "text", text: "Hello" }],
242
- },
230
+ message: { role: "assistant", content: [{ type: "text", text: "Hello" }] },
243
231
  status: "success",
244
232
  threadId: "1",
245
233
  };
@@ -255,55 +243,24 @@ describe("filterOutOrphanedToolMessages", () => {
255
243
  });
256
244
  });
257
245
 
258
- function mockModel(): LanguageModelV2 {
259
- return new MockLanguageModelV2({
260
- provider: "mock",
261
- modelId: "mock",
262
- // supportsStructuredOutputs: true,
263
- doGenerate: async ({ prompt }) => ({
264
- finishReason: "stop",
265
- content: [{ type: "text", text: JSON.stringify({ prompt }) }],
266
- warnings: [],
267
- usage: { outputTokens: 10, inputTokens: 3, totalTokens: 13 },
268
- rawCall: { rawPrompt: null, rawSettings: {} },
269
- text: JSON.stringify({ prompt }),
270
- }),
271
- doStream: async ({ prompt }) => ({
272
- stream: simulateReadableStream({
273
- chunkDelayInMs: 50,
274
- initialDelayInMs: 100,
275
- chunks: [
276
- {
277
- type: "text-delta",
278
- textDelta: `This is a sample response to ${JSON.stringify(prompt)}`,
279
- },
280
- {
281
- type: "finish",
282
- finishReason: "stop",
283
- usage: { outputTokens: 10, inputTokens: 3, totalTokens: 13 },
284
- },
285
- ] as LanguageModelV2StreamPart[],
286
- }),
287
- rawCall: { rawPrompt: null, rawSettings: {} },
288
- }),
289
- });
290
- }
291
-
292
246
  describe("Agent option variations and normal behavior", () => {
293
247
  test("Agent can be constructed with minimal options", () => {
294
- const a = new Agent(components.agent, { chat: mockModel() });
248
+ const a = new Agent(components.agent, {
249
+ name: "minimal",
250
+ languageModel: mockModel(),
251
+ });
295
252
  expect(a).toBeInstanceOf(Agent);
296
253
  });
297
254
 
298
255
  test("Agent can be constructed with all options", () => {
299
256
  const a = new Agent(components.agent, {
300
257
  name: "full",
301
- chat: mockModel(),
258
+ languageModel: mockModel(),
302
259
  instructions: "Test instructions",
303
260
  contextOptions: { recentMessages: 5 },
304
261
  storageOptions: { saveMessages: "all" },
305
262
  stopWhen: stepCountIs(2),
306
- maxRetries: 1,
263
+ callSettings: { maxRetries: 1 },
307
264
  usageHandler: async () => {},
308
265
  rawRequestResponseHandler: async () => {},
309
266
  });
@@ -315,7 +272,7 @@ describe("Agent thread management", () => {
315
272
  test("createThread returns threadId (mutation context)", async () => {
316
273
  const t = initConvexTest(schema);
317
274
  const threadId = await t.run(async (ctx) =>
318
- agent.createThread(ctx, { userId: "2" }).then(({ threadId }) => threadId),
275
+ createThread(ctx, components.agent, { userId: "2" }),
319
276
  );
320
277
  expect(threadId).toBeTypeOf("string");
321
278
  });
@@ -323,7 +280,7 @@ describe("Agent thread management", () => {
323
280
  test("continueThread returns thread object", async () => {
324
281
  const t = initConvexTest(schema);
325
282
  const threadId = await t.run(async (ctx) =>
326
- agent.createThread(ctx, { userId: "3" }).then(({ threadId }) => threadId),
283
+ createThread(ctx, components.agent, { userId: "3" }),
327
284
  );
328
285
  const result = await t.action(testApi.continueThreadAction, {
329
286
  threadId,
@@ -337,7 +294,7 @@ describe("Agent message operations", () => {
337
294
  test("saveMessage and saveMessages store messages", async () => {
338
295
  const t = initConvexTest(schema);
339
296
  const threadId = await t.run(async (ctx) =>
340
- agent.createThread(ctx, { userId: "4" }).then(({ threadId }) => threadId),
297
+ createThread(ctx, components.agent, { userId: "4" }),
341
298
  );
342
299
  const { messageId } = await t.run(async (ctx) =>
343
300
  agent.saveMessage(ctx, {
@@ -348,7 +305,7 @@ describe("Agent message operations", () => {
348
305
  );
349
306
  expect(messageId).toBeTypeOf("string");
350
307
 
351
- const { lastMessageId, messages } = await t.run(async (ctx) =>
308
+ const { messages } = await t.run(async (ctx) =>
352
309
  agent.saveMessages(ctx, {
353
310
  threadId,
354
311
  userId: "4",
@@ -359,7 +316,7 @@ describe("Agent message operations", () => {
359
316
  }),
360
317
  );
361
318
  expect(messages.length).toBe(2);
362
- expect(lastMessageId).toBe(messages[1]._id);
319
+ expect(messages[1]._id).toBeDefined();
363
320
  });
364
321
  });
365
322
 
@@ -367,7 +324,7 @@ describe("Agent text/object generation", () => {
367
324
  test("generateText with custom context and storage options", async () => {
368
325
  const t = initConvexTest(schema);
369
326
  const threadId = await t.run(async (ctx) =>
370
- agent.createThread(ctx, { userId: "5" }).then(({ threadId }) => threadId),
327
+ createThread(ctx, components.agent, { userId: "5" }),
371
328
  );
372
329
  const result = await t.action(testApi.generateTextWithThread, {
373
330
  threadId,
@@ -376,13 +333,13 @@ describe("Agent text/object generation", () => {
376
333
  contextOptions: { recentMessages: 1 },
377
334
  storageOptions: { saveMessages: "all" },
378
335
  });
379
- expect(result.text).toMatch(/Test/);
336
+ expect(result.text).toEqual(TEST_TEXT);
380
337
  });
381
338
 
382
339
  test("generateObject returns object", async () => {
383
340
  const t = initConvexTest(schema);
384
341
  const threadId = await t.run(async (ctx) =>
385
- agent.createThread(ctx, { userId: "6" }).then(({ threadId }) => threadId),
342
+ createThread(ctx, components.agent, { userId: "6" }),
386
343
  );
387
344
  const result = await t.action(testApi.generateObjectWithThread, {
388
345
  threadId,
@@ -397,21 +354,21 @@ describe("Agent-generated mutations/actions/queries", () => {
397
354
  test("createThreadMutation works via t.mutation", async () => {
398
355
  const t = initConvexTest(schema);
399
356
  // This test is for the registered mutation, not the agent method
400
- const result = await t.mutation(testApi.createThread, {});
357
+ const result = await t.mutation(testApi.createThreadManually, {});
401
358
  expect(result.threadId).toBeTypeOf("string");
402
359
  });
403
360
 
404
361
  test("asTextAction and asObjectAction work via t.action", async () => {
405
362
  const t = initConvexTest(schema);
406
363
  const threadId = await t.run(async (ctx) =>
407
- agent.createThread(ctx, { userId: "8" }).then(({ threadId }) => threadId),
364
+ createThread(ctx, components.agent, { userId: "8" }),
408
365
  );
409
366
  const textResult = await t.action(testApi.generateTextAction, {
410
367
  userId: "8",
411
368
  threadId,
412
369
  messages: [{ role: "user", content: "Say hi" }],
413
370
  });
414
- expect(textResult.text).toMatch(/Say hi/);
371
+ expect(textResult.text).toEqual(TEST_TEXT);
415
372
 
416
373
  const objResult = await t.action(testApi.generateObjectAction, {
417
374
  userId: "8",
@@ -424,7 +381,7 @@ describe("Agent-generated mutations/actions/queries", () => {
424
381
  test("asSaveMessagesMutation works via t.mutation", async () => {
425
382
  const t = initConvexTest(schema);
426
383
  const threadId = await t.run(async (ctx) =>
427
- agent.createThread(ctx, { userId: "9" }).then(({ threadId }) => threadId),
384
+ createThread(ctx, components.agent, { userId: "9" }),
428
385
  );
429
386
  const result = await t.mutation(testApi.saveMessageMutation, {
430
387
  threadId,
@@ -435,8 +392,8 @@ describe("Agent-generated mutations/actions/queries", () => {
435
392
  },
436
393
  ],
437
394
  });
438
- expect(result.lastMessageId).toBeDefined();
439
- expect(result.messageIds.length).toBe(1);
395
+ expect(result.messages.length).toBe(1);
396
+ expect(result.messages[0]._id).toBeDefined();
440
397
  });
441
398
  });
442
399
 
@@ -444,9 +401,7 @@ describe("Agent context and search options", () => {
444
401
  test("fetchContextMessages returns context messages", async () => {
445
402
  const t = initConvexTest(schema);
446
403
  const threadId = await t.run(async (ctx) =>
447
- agent
448
- .createThread(ctx, { userId: "10" })
449
- .then(({ threadId }) => threadId),
404
+ createThread(ctx, components.agent, { userId: "10" }),
450
405
  );
451
406
  await t.run(async (ctx) =>
452
407
  agent.saveMessage(ctx, {