@convex-dev/agent 0.6.0 → 0.6.2

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 (187) 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 +2093 -0
  22. package/dist/client/index.d.ts.map +1 -0
  23. package/dist/client/index.js +933 -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 +145 -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 +344 -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 +12 -0
  62. package/dist/client/utils.d.ts.map +1 -0
  63. package/dist/client/utils.js +35 -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 +44 -50
  174. package/src/UIMessages.ts +0 -9
  175. package/src/client/approval.test.ts +144 -0
  176. package/src/client/index.ts +26 -19
  177. package/src/client/search.ts +0 -29
  178. package/src/client/setup.test.ts +3 -5
  179. package/src/client/start.ts +2 -2
  180. package/src/client/streamText.test.ts +102 -0
  181. package/src/client/streamText.ts +35 -9
  182. package/src/client/streaming.ts +44 -23
  183. package/src/client/utils.ts +22 -1
  184. package/src/component/_generated/dataModel.ts +1 -1
  185. package/src/component/_generated/server.ts +0 -5
  186. package/src/mapping.ts +38 -22
  187. package/src/test.ts +3 -4
@@ -80,9 +80,21 @@ export async function streamText<
80
80
 
81
81
  const steps: StepResult<TOOLS>[] = [];
82
82
 
83
- // Track the final step for atomic save with stream finish (issue #181)
83
+ // Track the final step for atomic save with stream finish (issue #181).
84
+ // Only used when streamText awaits stream consumption itself; the
85
+ // `returnImmediately` path saves inline instead (see onStepFinish below).
84
86
  let pendingFinalStep: StepResult<TOOLS> | undefined;
85
87
 
88
+ // Whether streamText will await stream consumption before returning.
89
+ // When false (saveStreamDeltas.returnImmediately === true), we cannot
90
+ // defer the final-step save to a post-await block — the function has
91
+ // already returned by the time onStepFinish fires. See issue #265.
92
+ const willAwaitStream =
93
+ Boolean(threadId) &&
94
+ (options.saveStreamDeltas === true ||
95
+ (typeof options.saveStreamDeltas === "object" &&
96
+ !options.saveStreamDeltas.returnImmediately));
97
+
86
98
  const streamer =
87
99
  threadId && options.saveStreamDeltas
88
100
  ? new DeltaStreamer(
@@ -142,10 +154,19 @@ export async function streamText<
142
154
  steps.push(step);
143
155
  const createPendingMessage = await willContinue(steps, args.stopWhen);
144
156
  if (!createPendingMessage && streamer) {
145
- // This is the final step with streaming enabled.
146
- // Defer saving until stream consumption completes for atomic finish (issue #181).
157
+ // Final step with streaming enabled.
147
158
  streamer.markFinishedExternally();
148
- pendingFinalStep = step;
159
+ if (willAwaitStream) {
160
+ // We're about to `await stream` below — defer the save so it
161
+ // happens atomically with stream finish (issue #181).
162
+ pendingFinalStep = step;
163
+ } else {
164
+ // returnImmediately path: streamText is about to return without
165
+ // awaiting consumption, so the deferred-save block below won't
166
+ // see this step. Save inline now (issue #265).
167
+ const finishStreamId = await streamer.getOrCreateStreamId();
168
+ await call.save({ step }, false, finishStreamId);
169
+ }
149
170
  } else {
150
171
  await call.save({ step }, createPendingMessage);
151
172
  }
@@ -155,11 +176,7 @@ export async function streamText<
155
176
  const stream = streamer?.consumeStream(
156
177
  result.toUIMessageStream<AIUIMessage<TOOLS>>(),
157
178
  );
158
- if (
159
- (typeof options?.saveStreamDeltas === "object" &&
160
- !options.saveStreamDeltas.returnImmediately) ||
161
- options?.saveStreamDeltas === true
162
- ) {
179
+ if (willAwaitStream) {
163
180
  try {
164
181
  await stream;
165
182
  await result.consumeStream();
@@ -168,6 +185,15 @@ export async function streamText<
168
185
  // finish() was never called, leaving the streaming message stuck in
169
186
  // "streaming" state. Clean it up by marking it as aborted.
170
187
  await streamer?.fail(e instanceof Error ? e.message : String(e));
188
+ // Save the deferred final step if it was already generated but not yet persisted
189
+ if (pendingFinalStep) {
190
+ try {
191
+ await call.save({ step: pendingFinalStep }, false);
192
+ } catch (saveError) {
193
+ console.error("Failed to save deferred final step:", saveError);
194
+ }
195
+ pendingFinalStep = undefined;
196
+ }
171
197
  throw e;
172
198
  }
173
199
  }
@@ -244,16 +244,25 @@ export class DeltaStreamer<T> {
244
244
  this.abortController = new AbortController();
245
245
  if (config.abortSignal) {
246
246
  config.abortSignal.addEventListener("abort", async () => {
247
- if (this.abortController.signal.aborted) {
248
- return;
249
- }
250
- if (this.streamId) {
247
+ try {
248
+ if (this.abortController.signal.aborted) {
249
+ return;
250
+ }
251
251
  this.abortController.abort();
252
- await this.#ongoingWrite;
253
- await this.ctx.runMutation(this.component.streams.abort, {
254
- streamId: this.streamId,
255
- reason: "abortSignal",
256
- });
252
+ // Wait for in-flight stream creation before trying to abort it
253
+ if (this.#creatingStreamIdPromise) {
254
+ await this.#creatingStreamIdPromise;
255
+ }
256
+ if (this.streamId) {
257
+ await this.#ongoingWrite;
258
+ await this.ctx.runMutation(this.component.streams.abort, {
259
+ streamId: this.streamId,
260
+ reason: "abortSignal",
261
+ });
262
+ }
263
+ } catch {
264
+ // Best-effort cleanup — the stream will be garbage-collected
265
+ // by the 10-minute timeout if this fails.
257
266
  }
258
267
  });
259
268
  }
@@ -278,6 +287,13 @@ export class DeltaStreamer<T> {
278
287
  if (this.abortController.signal.aborted) {
279
288
  return;
280
289
  }
290
+ // Once the stream has been finished externally (e.g. by the inline
291
+ // save in streamText's onStepFinish for the returnImmediately path),
292
+ // the stream record is already "finished" in the DB. Late deltas
293
+ // would be silently dropped by streams.addDelta — skip the work.
294
+ if (this.#finishedExternally) {
295
+ return;
296
+ }
281
297
  await this.getStreamId();
282
298
  this.#nextParts.push(...parts);
283
299
  if (
@@ -293,7 +309,9 @@ export class DeltaStreamer<T> {
293
309
  await this.addParts([chunk]);
294
310
  }
295
311
  // Skip finish if it will be handled externally (atomically with message save)
296
- // or if the stream was aborted (e.g., due to a failed delta write)
312
+ // or if the stream was aborted (e.g., due to a failed delta write).
313
+ // Aborted streams are cleaned up via streams.abort (called by the abort
314
+ // signal handler), so we don't need to call finish() for them.
297
315
  if (!this.#finishedExternally && !this.abortController.signal.aborted) {
298
316
  await this.finish();
299
317
  }
@@ -331,6 +349,13 @@ export class DeltaStreamer<T> {
331
349
  delta,
332
350
  );
333
351
  if (!success) {
352
+ // An in-flight #sendDelta started before markFinishedExternally()
353
+ // will get `success === false` because the stream row is already
354
+ // "finished". That's a benign late-write miss, not a failure —
355
+ // don't convert it into an abort.
356
+ if (this.#finishedExternally) {
357
+ return;
358
+ }
334
359
  await this.config.onAsyncAbort("async abort");
335
360
  this.abortController.abort();
336
361
  return;
@@ -376,10 +401,7 @@ export class DeltaStreamer<T> {
376
401
  return;
377
402
  }
378
403
  await this.#ongoingWrite;
379
- if (this.abortController.signal.aborted) {
380
- return;
381
- }
382
- await this.#sendDelta();
404
+ await this.#sendDelta(); // #sendDelta checks aborted internally
383
405
  if (this.abortController.signal.aborted) {
384
406
  return;
385
407
  }
@@ -439,16 +461,15 @@ export function compressTextStreamParts(
439
461
  } else {
440
462
  compressed.push(part);
441
463
  }
464
+ } else if (part.type === "file") {
465
+ compressed.push({
466
+ type: "file",
467
+ file: {
468
+ ...part.file,
469
+ uint8Array: undefined as unknown as Uint8Array,
470
+ },
471
+ });
442
472
  } else {
443
- if (part.type === "file") {
444
- compressed.push({
445
- type: "file",
446
- file: {
447
- ...part.file,
448
- uint8Array: undefined as unknown as Uint8Array,
449
- },
450
- });
451
- }
452
473
  compressed.push(part);
453
474
  }
454
475
  }
@@ -1,5 +1,19 @@
1
1
  import type { StepResult, StopCondition } from "ai";
2
2
 
3
+ /**
4
+ * A stop condition that only matches tool calls which completed
5
+ * successfully (i.e. produced a `tool-result`, not a `tool-error`).
6
+ *
7
+ * Use this instead of the AI SDK's `hasToolCall` when you want the
8
+ * agent to retry on argument validation failures rather than stopping.
9
+ */
10
+ export function hasSuccessfulToolCall(toolName: string): StopCondition<any> {
11
+ return ({ steps }) =>
12
+ steps[steps.length - 1]?.toolResults?.some(
13
+ (result) => result.toolName === toolName,
14
+ ) ?? false;
15
+ }
16
+
3
17
  export async function willContinue(
4
18
  steps: StepResult<any>[],
5
19
 
@@ -9,8 +23,15 @@ export async function willContinue(
9
23
  // we aren't doing another round after a tool result
10
24
  // TODO: whether to handle continuing after too much context used..
11
25
  if (step.finishReason !== "tool-calls") return false;
26
+ // Count both successful results and errors as completed outputs.
27
+ // In AI SDK v6, failed tool calls produce tool-error content parts
28
+ // instead of tool-result, so only checking toolResults misses them.
29
+ const completedOutputs =
30
+ step.content?.filter(
31
+ (p) => p.type === "tool-result" || p.type === "tool-error",
32
+ ).length ?? step.toolResults.length;
12
33
  // we don't have a tool result, so we'll wait for more
13
- if (step.toolCalls.length > step.toolResults.length) return false;
34
+ if (step.toolCalls.length > completedOutputs) return false;
14
35
  if (Array.isArray(stopWhen)) {
15
36
  return (await Promise.all(stopWhen.map(async (s) => s({ steps })))).every(
16
37
  (stop) => !stop,
@@ -38,7 +38,7 @@ export type Doc<TableName extends TableNames> = DocumentByName<
38
38
  * Convex documents are uniquely identified by their `Id`, which is accessible
39
39
  * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
40
40
  *
41
- * Documents can be loaded using `db.get(id)` in query and mutation functions.
41
+ * Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
42
42
  *
43
43
  * IDs are just strings at runtime, but this type can be used to distinguish them from other
44
44
  * strings when type checking.
@@ -107,11 +107,6 @@ export const internalAction: ActionBuilder<DataModel, "internal"> =
107
107
  */
108
108
  export const httpAction: HttpActionBuilder = httpActionGeneric;
109
109
 
110
- type GenericCtx =
111
- | GenericActionCtx<DataModel>
112
- | GenericMutationCtx<DataModel>
113
- | GenericQueryCtx<DataModel>;
114
-
115
110
  /**
116
111
  * A set of services for use within Convex query functions.
117
112
  *
package/src/mapping.ts CHANGED
@@ -297,21 +297,49 @@ export function toModelMessageWarnings(
297
297
  }) as any;
298
298
  }
299
299
 
300
+ /**
301
+ * Serialize explicitly provided response messages for a step.
302
+ * Used by the streaming/generation loop where the caller tracks which
303
+ * messages are new via slicing.
304
+ */
305
+ export async function serializeResponseMessages<TOOLS extends ToolSet>(
306
+ ctx: ActionCtx,
307
+ component: AgentComponent,
308
+ step: StepResult<TOOLS>,
309
+ model: ModelOrMetadata | undefined,
310
+ responseMessages: ModelMessage[],
311
+ ): Promise<{ messages: MessageWithMetadata[] }> {
312
+ return serializeStepMessages(ctx, component, step, model, responseMessages);
313
+ }
314
+
315
+ /**
316
+ * Serialize the new messages from a step using a heuristic to determine
317
+ * which response messages are new (last 1-2 messages).
318
+ */
300
319
  export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
301
320
  ctx: ActionCtx,
302
321
  component: AgentComponent,
303
322
  step: StepResult<TOOLS>,
304
323
  model: ModelOrMetadata | undefined,
305
- /**
306
- * If provided, these are the new response messages for this step
307
- * (pre-sliced by the caller). When not provided, falls back to the
308
- * existing heuristic of slicing the last 1-2 messages.
309
- *
310
- * This is needed for tool approval flows where the SDK adds extra
311
- * messages (e.g. approval tool-results) at the beginning of
312
- * responseMessages that the old slice(-1/-2) logic would miss.
313
- */
314
- newResponseMessages?: ModelMessage[],
324
+ ): Promise<{ messages: MessageWithMetadata[] }> {
325
+ const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
326
+ let messagesToSerialize: ModelMessage[];
327
+ if (hasToolMessage) {
328
+ messagesToSerialize = step.response.messages.slice(-2);
329
+ } else if (step.content.length) {
330
+ messagesToSerialize = step.response.messages.slice(-1);
331
+ } else {
332
+ messagesToSerialize = [{ role: "assistant" as const, content: [] }];
333
+ }
334
+ return serializeStepMessages(ctx, component, step, model, messagesToSerialize);
335
+ }
336
+
337
+ async function serializeStepMessages<TOOLS extends ToolSet>(
338
+ ctx: ActionCtx,
339
+ component: AgentComponent,
340
+ step: StepResult<TOOLS>,
341
+ model: ModelOrMetadata | undefined,
342
+ messagesToSerialize: ModelMessage[],
315
343
  ): Promise<{ messages: MessageWithMetadata[] }> {
316
344
  // If there are tool results, there's another message with the tool results
317
345
  // ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
@@ -330,18 +358,6 @@ export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
330
358
  } satisfies Omit<MessageWithMetadata, "message" | "text" | "fileIds">;
331
359
  const toolFields = { sources: step.sources };
332
360
 
333
- // Determine which messages to serialize for this step
334
- let messagesToSerialize: ModelMessage[];
335
- if (newResponseMessages) {
336
- messagesToSerialize = newResponseMessages;
337
- } else if (hasToolMessage) {
338
- messagesToSerialize = step.response.messages.slice(-2);
339
- } else if (step.content.length) {
340
- messagesToSerialize = step.response.messages.slice(-1);
341
- } else {
342
- messagesToSerialize = [{ role: "assistant" as const, content: [] }];
343
- }
344
-
345
361
  const messages: MessageWithMetadata[] = await Promise.all(
346
362
  messagesToSerialize.map(async (msg): Promise<MessageWithMetadata> => {
347
363
  const { message, fileIds } = await serializeMessage(ctx, component, msg);
package/src/test.ts CHANGED
@@ -9,10 +9,9 @@ const modules = import.meta.glob("./component/**/*.ts");
9
9
  * @param t - The test convex instance, e.g. from calling `convexTest`.
10
10
  * @param name - The name of the component, as registered in convex.config.ts.
11
11
  */
12
- export function register(
13
- t: TestConvex<SchemaDefinition<GenericSchema, boolean>>,
14
- name: string = "agent",
15
- ) {
12
+ export function register<
13
+ Schema extends SchemaDefinition<GenericSchema, boolean>,
14
+ >(t: TestConvex<Schema>, name: string = "agent") {
16
15
  t.registerComponent(name, schema, modules);
17
16
  }
18
17
  export default { register, schema, modules };