@convex-dev/agent 0.1.10-alpha.1 → 0.1.11

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 (175) hide show
  1. package/README.md +1 -1
  2. package/dist/esm/client/index.d.ts +10 -11
  3. package/dist/esm/client/index.d.ts.map +1 -1
  4. package/dist/esm/client/index.js +27 -28
  5. package/dist/esm/client/index.js.map +1 -1
  6. package/dist/esm/client/streaming.d.ts.map +1 -1
  7. package/dist/esm/client/streaming.js +1 -7
  8. package/dist/esm/client/streaming.js.map +1 -1
  9. package/dist/esm/component/_generated/api.d.ts +2 -13
  10. package/dist/esm/component/files.d.ts +1 -1
  11. package/dist/esm/component/files.d.ts.map +1 -1
  12. package/dist/esm/component/files.js +6 -4
  13. package/dist/esm/component/files.js.map +1 -1
  14. package/dist/esm/component/messages.d.ts +30 -30
  15. package/dist/esm/component/schema.d.ts +74 -84
  16. package/dist/esm/component/schema.d.ts.map +1 -1
  17. package/dist/esm/component/schema.js +2 -3
  18. package/dist/esm/component/schema.js.map +1 -1
  19. package/dist/esm/component/streams.d.ts +0 -7
  20. package/dist/esm/component/streams.d.ts.map +1 -1
  21. package/dist/esm/component/streams.js +22 -49
  22. package/dist/esm/component/streams.js.map +1 -1
  23. package/dist/esm/component/threads.d.ts +8 -8
  24. package/dist/esm/component/vector/tables.d.ts +3 -5
  25. package/dist/esm/component/vector/tables.d.ts.map +1 -1
  26. package/dist/esm/mapping.d.ts +4 -3
  27. package/dist/esm/mapping.d.ts.map +1 -1
  28. package/dist/esm/mapping.js +7 -5
  29. package/dist/esm/mapping.js.map +1 -1
  30. package/dist/esm/react/deltas.d.ts.map +1 -1
  31. package/dist/esm/react/deltas.js +2 -2
  32. package/dist/esm/react/deltas.js.map +1 -1
  33. package/dist/esm/react/index.d.ts +2 -4
  34. package/dist/esm/react/index.d.ts.map +1 -1
  35. package/dist/esm/react/index.js +9 -22
  36. package/dist/esm/react/index.js.map +1 -1
  37. package/dist/esm/react/optimisticallySendMessage.js +1 -1
  38. package/dist/esm/react/optimisticallySendMessage.js.map +1 -1
  39. package/dist/esm/validators.d.ts +10 -15
  40. package/dist/esm/validators.d.ts.map +1 -1
  41. package/dist/esm/validators.js +0 -2
  42. package/dist/esm/validators.js.map +1 -1
  43. package/dist/esm.tsbuildinfo +1 -1
  44. package/package.json +24 -49
  45. package/src/client/index.ts +42 -39
  46. package/src/client/streaming.ts +1 -7
  47. package/src/component/_generated/api.d.ts +2 -13
  48. package/src/component/files.ts +6 -4
  49. package/src/component/schema.ts +2 -3
  50. package/src/component/streams.ts +39 -85
  51. package/src/component/vector/tables.ts +1 -1
  52. package/src/mapping.ts +17 -6
  53. package/src/react/deltas.ts +2 -4
  54. package/src/react/index.ts +11 -23
  55. package/src/react/optimisticallySendMessage.ts +1 -1
  56. package/src/validators.ts +0 -6
  57. package/dist/commonjs/client/_generated/_ignore.d.ts +0 -1
  58. package/dist/commonjs/client/_generated/_ignore.d.ts.map +0 -1
  59. package/dist/commonjs/client/_generated/_ignore.js +0 -3
  60. package/dist/commonjs/client/_generated/_ignore.js.map +0 -1
  61. package/dist/commonjs/client/createTool.d.ts +0 -47
  62. package/dist/commonjs/client/createTool.d.ts.map +0 -1
  63. package/dist/commonjs/client/createTool.js +0 -46
  64. package/dist/commonjs/client/createTool.js.map +0 -1
  65. package/dist/commonjs/client/files.d.ts +0 -56
  66. package/dist/commonjs/client/files.d.ts.map +0 -1
  67. package/dist/commonjs/client/files.js +0 -124
  68. package/dist/commonjs/client/files.js.map +0 -1
  69. package/dist/commonjs/client/index.d.ts +0 -1356
  70. package/dist/commonjs/client/index.d.ts.map +0 -1
  71. package/dist/commonjs/client/index.js +0 -1148
  72. package/dist/commonjs/client/index.js.map +0 -1
  73. package/dist/commonjs/client/streaming.d.ts +0 -57
  74. package/dist/commonjs/client/streaming.d.ts.map +0 -1
  75. package/dist/commonjs/client/streaming.js +0 -149
  76. package/dist/commonjs/client/streaming.js.map +0 -1
  77. package/dist/commonjs/client/types.d.ts +0 -352
  78. package/dist/commonjs/client/types.d.ts.map +0 -1
  79. package/dist/commonjs/client/types.js +0 -2
  80. package/dist/commonjs/client/types.js.map +0 -1
  81. package/dist/commonjs/component/_generated/api.d.ts +0 -1949
  82. package/dist/commonjs/component/_generated/api.d.ts.map +0 -1
  83. package/dist/commonjs/component/_generated/api.js +0 -22
  84. package/dist/commonjs/component/_generated/api.js.map +0 -1
  85. package/dist/commonjs/component/_generated/dataModel.d.ts +0 -60
  86. package/dist/commonjs/component/_generated/server.d.ts +0 -149
  87. package/dist/commonjs/component/_generated/server.d.ts.map +0 -1
  88. package/dist/commonjs/component/_generated/server.js +0 -74
  89. package/dist/commonjs/component/_generated/server.js.map +0 -1
  90. package/dist/commonjs/component/apiKeys.d.ts +0 -11
  91. package/dist/commonjs/component/apiKeys.d.ts.map +0 -1
  92. package/dist/commonjs/component/apiKeys.js +0 -69
  93. package/dist/commonjs/component/apiKeys.js.map +0 -1
  94. package/dist/commonjs/component/convex.config.d.ts +0 -3
  95. package/dist/commonjs/component/convex.config.d.ts.map +0 -1
  96. package/dist/commonjs/component/convex.config.js +0 -3
  97. package/dist/commonjs/component/convex.config.js.map +0 -1
  98. package/dist/commonjs/component/files.d.ts +0 -90
  99. package/dist/commonjs/component/files.d.ts.map +0 -1
  100. package/dist/commonjs/component/files.js +0 -150
  101. package/dist/commonjs/component/files.js.map +0 -1
  102. package/dist/commonjs/component/messages.d.ts +0 -1479
  103. package/dist/commonjs/component/messages.d.ts.map +0 -1
  104. package/dist/commonjs/component/messages.js +0 -478
  105. package/dist/commonjs/component/messages.js.map +0 -1
  106. package/dist/commonjs/component/schema.d.ts +0 -4592
  107. package/dist/commonjs/component/schema.d.ts.map +0 -1
  108. package/dist/commonjs/component/schema.js +0 -165
  109. package/dist/commonjs/component/schema.js.map +0 -1
  110. package/dist/commonjs/component/streams.d.ts +0 -249
  111. package/dist/commonjs/component/streams.d.ts.map +0 -1
  112. package/dist/commonjs/component/streams.js +0 -346
  113. package/dist/commonjs/component/streams.js.map +0 -1
  114. package/dist/commonjs/component/threads.d.ts +0 -101
  115. package/dist/commonjs/component/threads.d.ts.map +0 -1
  116. package/dist/commonjs/component/threads.js +0 -185
  117. package/dist/commonjs/component/threads.js.map +0 -1
  118. package/dist/commonjs/component/users.d.ts +0 -52
  119. package/dist/commonjs/component/users.d.ts.map +0 -1
  120. package/dist/commonjs/component/users.js +0 -230
  121. package/dist/commonjs/component/users.js.map +0 -1
  122. package/dist/commonjs/component/vector/index.d.ts +0 -61
  123. package/dist/commonjs/component/vector/index.d.ts.map +0 -1
  124. package/dist/commonjs/component/vector/index.js +0 -148
  125. package/dist/commonjs/component/vector/index.js.map +0 -1
  126. package/dist/commonjs/component/vector/tables.d.ts +0 -60
  127. package/dist/commonjs/component/vector/tables.d.ts.map +0 -1
  128. package/dist/commonjs/component/vector/tables.js +0 -56
  129. package/dist/commonjs/component/vector/tables.js.map +0 -1
  130. package/dist/commonjs/mapping.d.ts +0 -50
  131. package/dist/commonjs/mapping.d.ts.map +0 -1
  132. package/dist/commonjs/mapping.js +0 -257
  133. package/dist/commonjs/mapping.js.map +0 -1
  134. package/dist/commonjs/package.json +0 -3
  135. package/dist/commonjs/react/deltas.d.ts +0 -29
  136. package/dist/commonjs/react/deltas.d.ts.map +0 -1
  137. package/dist/commonjs/react/deltas.js +0 -268
  138. package/dist/commonjs/react/deltas.js.map +0 -1
  139. package/dist/commonjs/react/index.d.ts +0 -97
  140. package/dist/commonjs/react/index.d.ts.map +0 -1
  141. package/dist/commonjs/react/index.js +0 -224
  142. package/dist/commonjs/react/index.js.map +0 -1
  143. package/dist/commonjs/react/optimisticallySendMessage.d.ts +0 -8
  144. package/dist/commonjs/react/optimisticallySendMessage.d.ts.map +0 -1
  145. package/dist/commonjs/react/optimisticallySendMessage.js +0 -40
  146. package/dist/commonjs/react/optimisticallySendMessage.js.map +0 -1
  147. package/dist/commonjs/react/toUIMessages.d.ts +0 -13
  148. package/dist/commonjs/react/toUIMessages.d.ts.map +0 -1
  149. package/dist/commonjs/react/toUIMessages.js +0 -146
  150. package/dist/commonjs/react/toUIMessages.js.map +0 -1
  151. package/dist/commonjs/react/types.d.ts +0 -26
  152. package/dist/commonjs/react/types.d.ts.map +0 -1
  153. package/dist/commonjs/react/types.js +0 -2
  154. package/dist/commonjs/react/types.js.map +0 -1
  155. package/dist/commonjs/react/useSmoothText.d.ts +0 -20
  156. package/dist/commonjs/react/useSmoothText.d.ts.map +0 -1
  157. package/dist/commonjs/react/useSmoothText.js +0 -51
  158. package/dist/commonjs/react/useSmoothText.js.map +0 -1
  159. package/dist/commonjs/shared.d.ts +0 -10
  160. package/dist/commonjs/shared.d.ts.map +0 -1
  161. package/dist/commonjs/shared.js +0 -30
  162. package/dist/commonjs/shared.js.map +0 -1
  163. package/dist/commonjs/validators.d.ts +0 -5721
  164. package/dist/commonjs/validators.d.ts.map +0 -1
  165. package/dist/commonjs/validators.js +0 -317
  166. package/dist/commonjs/validators.js.map +0 -1
  167. package/dist/commonjs/vitest.config.d.ts +0 -3
  168. package/dist/commonjs/vitest.config.d.ts.map +0 -1
  169. package/dist/commonjs/vitest.config.js +0 -7
  170. package/dist/commonjs/vitest.config.js.map +0 -1
  171. package/dist/commonjs.tsbuildinfo +0 -1
  172. package/dist/esm/vitest.config.d.ts +0 -3
  173. package/dist/esm/vitest.config.d.ts.map +0 -1
  174. package/dist/esm/vitest.config.js +0 -7
  175. package/dist/esm/vitest.config.js.map +0 -1
@@ -624,7 +624,7 @@ export class Agent<AgentTools extends ToolSet> {
624
624
  * to a thread (and optionally userId).
625
625
  */
626
626
  async generateObject<T>(
627
- ctx: RunActionCtx,
627
+ ctx: ActionCtx,
628
628
  {
629
629
  userId: argsUserId,
630
630
  threadId,
@@ -705,7 +705,7 @@ export class Agent<AgentTools extends ToolSet> {
705
705
  * to a thread (and optionally userId).
706
706
  */
707
707
  async streamObject<T>(
708
- ctx: RunActionCtx,
708
+ ctx: ActionCtx,
709
709
  {
710
710
  userId: argsUserId,
711
711
  threadId,
@@ -994,8 +994,6 @@ export class Agent<AgentTools extends ToolSet> {
994
994
  args: {
995
995
  threadId: string;
996
996
  streamArgs: StreamArgs | undefined;
997
- // By default, only streaming messages are included.
998
- includeStatuses?: ("streaming" | "finished" | "aborted")[];
999
997
  }
1000
998
  ): Promise<SyncStreamsReturnValue | undefined> {
1001
999
  if (!args.streamArgs) return undefined;
@@ -1004,8 +1002,6 @@ export class Agent<AgentTools extends ToolSet> {
1004
1002
  kind: "list",
1005
1003
  messages: await ctx.runQuery(this.component.streams.list, {
1006
1004
  threadId: args.threadId,
1007
- startOrder: args.streamArgs.startOrder,
1008
- statuses: args.includeStatuses,
1009
1005
  }),
1010
1006
  };
1011
1007
  } else {
@@ -1075,9 +1071,7 @@ export class Agent<AgentTools extends ToolSet> {
1075
1071
  const targetMessage = contextMessages.find(
1076
1072
  (m) => m._id === args.upToAndIncludingMessageId
1077
1073
  )?.message;
1078
- const messagesToSearch = targetMessage
1079
- ? [targetMessage, ...args.messages]
1080
- : args.messages;
1074
+ const messagesToSearch = targetMessage ? [targetMessage] : args.messages;
1081
1075
  if (!("runAction" in ctx)) {
1082
1076
  throw new Error("searchUserMessages only works in an action");
1083
1077
  }
@@ -1351,7 +1345,7 @@ export class Agent<AgentTools extends ToolSet> {
1351
1345
  * @param args The arguments to the saveObject function.
1352
1346
  */
1353
1347
  async saveObject(
1354
- ctx: RunActionCtx,
1348
+ ctx: ActionCtx,
1355
1349
  args: {
1356
1350
  userId: string | undefined;
1357
1351
  threadId: string;
@@ -1360,10 +1354,15 @@ export class Agent<AgentTools extends ToolSet> {
1360
1354
  metadata?: Omit<MessageWithMetadata, "message">;
1361
1355
  }
1362
1356
  ): Promise<void> {
1363
- const { messages } = serializeObjectResult(args.result, {
1364
- model: this.options.chat.modelId,
1365
- provider: this.options.chat.provider,
1366
- });
1357
+ const { messages } = await serializeObjectResult(
1358
+ ctx,
1359
+ this.component,
1360
+ args.result,
1361
+ {
1362
+ model: this.options.chat.modelId,
1363
+ provider: this.options.chat.provider,
1364
+ }
1365
+ );
1367
1366
  const embeddings = await this.generateEmbeddings(
1368
1367
  ctx,
1369
1368
  { userId: args.userId, threadId: args.threadId },
@@ -1442,18 +1441,16 @@ export class Agent<AgentTools extends ToolSet> {
1442
1441
  }> {
1443
1442
  contextOptions ||= this.options.contextOptions;
1444
1443
  storageOptions ||= this.options.storageOptions;
1445
- // If only a messageId is provided, this will be empty.
1446
- const messages = args.promptMessageId
1447
- ? []
1448
- : promptOrMessagesToCoreMessages(args);
1444
+ // If only a promptMessageId is provided, this will be empty.
1445
+ const messages = promptOrMessagesToCoreMessages(args);
1449
1446
  const userId =
1450
1447
  argsUserId ??
1451
1448
  (threadId &&
1452
1449
  (await ctx.runQuery(this.component.threads.getThread, { threadId }))
1453
1450
  ?.userId);
1454
1451
  assert(
1455
- !args.promptMessageId || !(args.prompt || args.messages),
1456
- "you can't specify a prompt or message if you specify a promptMessageId"
1452
+ !args.promptMessageId || !args.prompt,
1453
+ "you can't specify a prompt if you specify a promptMessageId"
1457
1454
  );
1458
1455
  // If only a messageId is provided, this will add that message to the end.
1459
1456
  const contextMessages = await this.fetchContextMessages(ctx, {
@@ -1463,29 +1460,33 @@ export class Agent<AgentTools extends ToolSet> {
1463
1460
  messages,
1464
1461
  contextOptions,
1465
1462
  });
1466
- // Lazily generate embeddings for the prompt message, if it doesn't have
1467
- // embeddings yet. This can happen if the message was saved in a mutation
1468
- // where the LLM is not available.
1469
- if (
1470
- args.promptMessageId &&
1471
- !contextMessages.at(-1)?.embeddingId &&
1472
- this.options.textEmbedding
1473
- ) {
1474
- await this.generateAndSaveEmbeddings(ctx, {
1475
- messageIds: [args.promptMessageId],
1476
- });
1463
+ // If it was a promptMessageId, pop it off context messages
1464
+ // and add to the end of messages.
1465
+ const promptMessage =
1466
+ !!args.promptMessageId &&
1467
+ contextMessages.at(-1)?._id === args.promptMessageId
1468
+ ? contextMessages.pop()
1469
+ : undefined;
1470
+ if (promptMessage?.message) {
1471
+ messages.push(deserializeMessage(promptMessage.message));
1472
+ // Lazily generate embeddings for the prompt message, if it doesn't have
1473
+ // embeddings yet. This can happen if the message was saved in a mutation
1474
+ // where the LLM is not available.
1475
+ if (!promptMessage.embeddingId && this.options.textEmbedding) {
1476
+ await this.generateAndSaveEmbeddings(ctx, {
1477
+ messageIds: [promptMessage._id],
1478
+ });
1479
+ }
1477
1480
  }
1478
- let messageId = args.promptMessageId;
1479
- let order = args.promptMessageId
1480
- ? contextMessages.at(-1)?.order
1481
- : undefined;
1482
- let stepOrder = args.promptMessageId
1483
- ? contextMessages.at(-1)?.stepOrder
1484
- : undefined;
1481
+ let messageId = promptMessage?._id;
1482
+ let order = promptMessage?.order;
1483
+ let stepOrder = promptMessage?.stepOrder;
1485
1484
  if (
1486
1485
  threadId &&
1487
1486
  messages.length &&
1488
1487
  storageOptions?.saveMessages !== "none" &&
1488
+ // If it was a promptMessageId, we don't want to save it again.
1489
+ (!args.promptMessageId || storageOptions?.saveMessages === "all") &&
1489
1490
  storageOptions?.saveAnyInputMessages !== false
1490
1491
  ) {
1491
1492
  const saveAll = storageOptions?.saveMessages === "all";
@@ -1494,7 +1495,9 @@ export class Agent<AgentTools extends ToolSet> {
1494
1495
  threadId,
1495
1496
  userId,
1496
1497
  messages: coreMessages,
1497
- metadata: coreMessages.length === 1 ? [{ id: args.id }] : undefined,
1498
+ metadata: coreMessages.map((_, i) =>
1499
+ i === coreMessages.length - 1 ? { id: args.id } : {}
1500
+ ),
1498
1501
  pending: true,
1499
1502
  failPendingSteps: true,
1500
1503
  });
@@ -96,13 +96,7 @@ export class DeltaStreamer {
96
96
  this.#nextStepOrder = (metadata.stepOrder ?? 0) + 1;
97
97
  this.abortController = new AbortController();
98
98
  if (metadata.abortSignal) {
99
- metadata.abortSignal.addEventListener("abort", async () => {
100
- if (this.streamId) {
101
- await this.ctx.runMutation(this.component.streams.abort, {
102
- streamId: this.streamId,
103
- reason: "abortSignal",
104
- });
105
- }
99
+ metadata.abortSignal.addEventListener("abort", () => {
106
100
  this.abortController.abort();
107
101
  });
108
102
  }
@@ -67,7 +67,7 @@ export type Mounts = {
67
67
  "mutation",
68
68
  "public",
69
69
  { fileIds: Array<string>; force?: boolean },
70
- null
70
+ Array<string>
71
71
  >;
72
72
  get: FunctionReference<
73
73
  "query",
@@ -1414,12 +1414,6 @@ export type Mounts = {
1414
1414
  >;
1415
1415
  };
1416
1416
  streams: {
1417
- abort: FunctionReference<
1418
- "mutation",
1419
- "public",
1420
- { reason: string; streamId: string },
1421
- null
1422
- >;
1423
1417
  addDelta: FunctionReference<
1424
1418
  "mutation",
1425
1419
  "public",
@@ -1575,18 +1569,13 @@ export type Mounts = {
1575
1569
  list: FunctionReference<
1576
1570
  "query",
1577
1571
  "public",
1578
- {
1579
- startOrder?: number;
1580
- statuses?: Array<"streaming" | "finished" | "aborted">;
1581
- threadId: string;
1582
- },
1572
+ { threadId: string },
1583
1573
  Array<{
1584
1574
  agentName?: string;
1585
1575
  model?: string;
1586
1576
  order: number;
1587
1577
  provider?: string;
1588
1578
  providerOptions?: Record<string, Record<string, any>>;
1589
- status: "streaming" | "finished" | "aborted";
1590
1579
  stepOrder: number;
1591
1580
  streamId: string;
1592
1581
  userId?: string;
@@ -149,25 +149,27 @@ export const deleteFiles = mutation({
149
149
  fileIds: v.array(v.id("files")),
150
150
  force: v.optional(v.boolean()),
151
151
  },
152
+ returns: v.array(v.id("files")),
152
153
  handler: async (ctx, args) => {
153
- await Promise.all(
154
+ const deletedFileIds = await Promise.all(
154
155
  args.fileIds.map(async (fileId) => {
155
156
  const file = await ctx.db.get(fileId);
156
157
  if (!file) {
157
158
  console.error(`File ${fileId} not found when deleting, skipping...`);
158
- return;
159
+ return null;
159
160
  }
160
161
  if (file.refcount && file.refcount > 0) {
161
162
  if (!args.force) {
162
163
  console.error(
163
164
  `File ${fileId} has refcount ${file.refcount} > 0, skipping...`
164
165
  );
165
- return;
166
+ return null;
166
167
  }
167
168
  }
168
169
  await ctx.db.delete(fileId);
170
+ return fileId;
169
171
  })
170
172
  );
173
+ return deletedFileIds.filter((fileId) => fileId !== null);
171
174
  },
172
- returns: v.null(),
173
175
  });
@@ -108,11 +108,10 @@ export const schema = defineSchema({
108
108
  v.object({
109
109
  kind: v.literal("finished"),
110
110
  endedAt: v.number(),
111
- cleanupFnId: v.optional(v.id("_scheduled_functions")),
112
111
  }),
113
112
  v.object({
114
- kind: v.literal("aborted"),
115
- reason: v.string(),
113
+ kind: v.literal("error"),
114
+ error: v.string(),
116
115
  })
117
116
  ),
118
117
  })
@@ -6,7 +6,7 @@ import {
6
6
  vStreamMessage,
7
7
  } from "../validators.js";
8
8
  import { api, internal } from "./_generated/api.js";
9
- import type { Doc, Id } from "./_generated/dataModel.js";
9
+ import type { Id } from "./_generated/dataModel.js";
10
10
  import {
11
11
  internalMutation,
12
12
  mutation,
@@ -106,87 +106,33 @@ export const create = mutation({
106
106
  export const list = query({
107
107
  args: {
108
108
  threadId: v.id("threads"),
109
- startOrder: v.optional(v.number()),
110
- statuses: v.optional(
111
- v.array(
112
- v.union(
113
- v.literal("streaming"),
114
- v.literal("finished"),
115
- v.literal("aborted")
116
- )
117
- )
118
- ),
119
109
  },
120
110
  returns: v.array(vStreamMessage),
121
111
  handler: async (ctx, args) => {
122
- const statuses = args.statuses ?? ["streaming"];
123
- const messages = await mergedStream(
124
- statuses.map((status) =>
125
- stream(ctx.db, schema)
126
- .query("streamingMessages")
127
- .withIndex("threadId_state_order_stepOrder", (q) =>
128
- q
129
- .eq("threadId", args.threadId)
130
- .eq("state.kind", status)
131
- .gte("order", args.startOrder ?? 0)
132
- )
133
- .order("desc")
134
- ),
135
- ["order", "stepOrder"]
136
- ).take(100);
137
-
138
- return messages.map((m) => ({
139
- streamId: m._id,
140
- status: m.state.kind,
141
- ...pick(m, [
142
- "order",
143
- "stepOrder",
144
- "userId",
145
- "agentName",
146
- "model",
147
- "provider",
148
- "providerOptions",
149
- ]),
150
- }));
151
- },
152
- });
153
-
154
- export const abort = mutation({
155
- args: {
156
- streamId: v.id("streamingMessages"),
157
- reason: v.string(),
158
- },
159
- returns: v.null(),
160
- handler: async (ctx, args) => {
161
- const stream = await ctx.db.get(args.streamId);
162
- if (!stream) {
163
- throw new Error(`Stream not found: ${args.streamId}`);
164
- }
165
- if (stream.state.kind !== "streaming") {
166
- console.warn(
167
- `Stream trying to abort but not currently streaming (${stream.state.kind}): ${args.streamId}`
112
+ return ctx.db
113
+ .query("streamingMessages")
114
+ .withIndex("threadId_state_order_stepOrder", (q) =>
115
+ q.eq("threadId", args.threadId).eq("state.kind", "streaming")
116
+ )
117
+ .order("desc")
118
+ .take(100)
119
+ .then((msgs) =>
120
+ msgs.map((m) => ({
121
+ streamId: m._id,
122
+ ...pick(m, [
123
+ "order",
124
+ "stepOrder",
125
+ "userId",
126
+ "agentName",
127
+ "model",
128
+ "provider",
129
+ "providerOptions",
130
+ ]),
131
+ }))
168
132
  );
169
- return;
170
- }
171
- await cleanupTimeoutFn(ctx, stream);
172
- await ctx.db.patch(args.streamId, {
173
- state: { kind: "aborted", reason: args.reason },
174
- });
175
133
  },
176
134
  });
177
135
 
178
- async function cleanupTimeoutFn(
179
- ctx: MutationCtx,
180
- stream: Doc<"streamingMessages">
181
- ) {
182
- if (stream.state.kind === "streaming" && stream.state.timeoutFnId) {
183
- const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
184
- if (timeoutFn?.state.kind === "pending") {
185
- await ctx.scheduler.cancel(stream.state.timeoutFnId);
186
- }
187
- }
188
- }
189
-
190
136
  export const finish = mutation({
191
137
  args: {
192
138
  streamId: v.id("streamingMessages"),
@@ -207,15 +153,20 @@ export const finish = mutation({
207
153
  );
208
154
  return;
209
155
  }
210
- await cleanupTimeoutFn(ctx, stream);
211
- const cleanupFnId = await ctx.scheduler.runAfter(
156
+ if (stream.state.timeoutFnId) {
157
+ const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
158
+ if (timeoutFn?.state.kind === "pending") {
159
+ await ctx.scheduler.cancel(stream.state.timeoutFnId);
160
+ }
161
+ }
162
+ await ctx.db.patch(args.streamId, {
163
+ state: { kind: "finished", endedAt: Date.now() },
164
+ });
165
+ await ctx.scheduler.runAfter(
212
166
  DELETE_STREAM_DELAY,
213
167
  api.streams.deleteStreamAsync,
214
168
  { streamId: args.streamId }
215
169
  );
216
- await ctx.db.patch(args.streamId, {
217
- state: { kind: "finished", endedAt: Date.now(), cleanupFnId },
218
- });
219
170
  },
220
171
  });
221
172
 
@@ -272,8 +223,8 @@ export const timeoutStream = internalMutation({
272
223
  }
273
224
  await ctx.db.patch(args.streamId, {
274
225
  state: {
275
- kind: "aborted",
276
- reason: "timeout",
226
+ kind: "finished",
227
+ endedAt: Date.now(),
277
228
  },
278
229
  });
279
230
  },
@@ -294,9 +245,12 @@ async function deletePageForStreamId(
294
245
  if (deltas.isDone) {
295
246
  const stream = await ctx.db.get(args.streamId);
296
247
  if (stream) {
297
- await cleanupTimeoutFn(ctx, stream);
298
- if (stream.state.kind === "finished" && stream.state.cleanupFnId) {
299
- await ctx.scheduler.cancel(stream.state.cleanupFnId);
248
+ const state = stream.state;
249
+ if (state.kind === "streaming" && state.timeoutFnId) {
250
+ const timeoutFn = await ctx.db.system.get(state.timeoutFnId);
251
+ if (timeoutFn?.state.kind === "pending") {
252
+ await ctx.scheduler.cancel(state.timeoutFnId);
253
+ }
300
254
  }
301
255
  await ctx.db.delete(args.streamId);
302
256
  }
@@ -15,7 +15,7 @@ import {
15
15
  type VObject,
16
16
  type VUnion,
17
17
  } from "convex/values";
18
- import type { QueryCtx } from "../_generated/server";
18
+ import type { QueryCtx } from "../_generated/server.js";
19
19
 
20
20
  // We only generate embeddings for non-tool, non-system messages
21
21
  const embeddings = {
package/src/mapping.ts CHANGED
@@ -85,6 +85,7 @@ export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
85
85
  usage: step.usage,
86
86
  warnings: step.warnings,
87
87
  finishReason: step.finishReason,
88
+ sources: step.stepType === "tool-result" ? undefined : step.sources,
88
89
  };
89
90
  const toolFields = {
90
91
  sources: step.sources,
@@ -113,17 +114,19 @@ export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
113
114
  return messages;
114
115
  }
115
116
 
116
- export function serializeObjectResult(
117
+ export async function serializeObjectResult(
118
+ ctx: ActionCtx,
119
+ component: AgentComponent,
117
120
  result: GenerateObjectResult<unknown>,
118
121
  metadata: { model: string; provider: string }
119
- ): { messages: MessageWithMetadata[] } {
122
+ ): Promise<{ messages: MessageWithMetadata[] }> {
120
123
  const text = JSON.stringify(result.object);
121
124
 
122
- const message = {
125
+ const { message, fileIds } = await serializeMessage(ctx, component, {
123
126
  role: "assistant" as const,
124
127
  content: text,
125
128
  id: result.response.id,
126
- };
129
+ });
127
130
  return {
128
131
  messages: [
129
132
  {
@@ -136,6 +139,7 @@ export function serializeObjectResult(
136
139
  text,
137
140
  usage: result.usage,
138
141
  warnings: result.warnings,
142
+ fileIds,
139
143
  },
140
144
  ],
141
145
  };
@@ -330,9 +334,17 @@ function encodeBase64(data: ArrayBuffer): string {
330
334
  export function promptOrMessagesToCoreMessages(args: {
331
335
  prompt?: string;
332
336
  messages?: CoreMessage[] | AIMessageWithoutId[];
337
+ promptMessageId?: string;
333
338
  }): CoreMessage[] {
334
339
  const messages: CoreMessage[] = [];
335
- assert(args.prompt || args.messages, "messages or prompt is required");
340
+ assert(
341
+ args.prompt || args.messages || args.promptMessageId,
342
+ "messages or prompt or promptMessageId is required"
343
+ );
344
+ assert(
345
+ !args.promptMessageId || !args.prompt,
346
+ "you can't specify a prompt if you specify a promptMessageId. use messages instead."
347
+ );
336
348
  if (args.messages) {
337
349
  if (
338
350
  args.messages.some(
@@ -353,6 +365,5 @@ export function promptOrMessagesToCoreMessages(args: {
353
365
  if (args.prompt) {
354
366
  messages.push({ role: "user", content: args.prompt });
355
367
  }
356
- assert(messages.length > 0, "Messages must contain at least one message");
357
368
  return messages;
358
369
  }
@@ -256,14 +256,12 @@ export function createStreamingMessage(
256
256
  ): MessageDoc {
257
257
  const { streamId, ...rest } = message;
258
258
  const metadata: MessageDoc = {
259
- ...rest,
260
259
  _id: `${streamId}-${index}`,
261
260
  _creationTime: Date.now(),
262
- status: (
263
- { streaming: "pending", finished: "success", aborted: "failed" } as const
264
- )[message.status],
261
+ status: "pending",
265
262
  threadId,
266
263
  tool: false,
264
+ ...rest,
267
265
  };
268
266
  switch (part.type) {
269
267
  case "text-delta":
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { omit, type ErrorMessage } from "convex-helpers";
2
+ import type { ErrorMessage } from "convex-helpers";
3
3
  import {
4
4
  type PaginatedQueryArgs,
5
5
  type UsePaginatedQueryResult,
@@ -7,7 +7,7 @@ import {
7
7
  } from "convex/react";
8
8
  import { usePaginatedQuery } from "convex-helpers/react";
9
9
  import type { FunctionArgs } from "convex/server";
10
- import { useMemo, useRef, useState } from "react";
10
+ import { useMemo, useState } from "react";
11
11
  import type { MessageDoc } from "../client/index.js";
12
12
  import type { SyncStreamsReturnValue } from "../client/types.js";
13
13
  import type { StreamArgs } from "../validators.js";
@@ -103,18 +103,14 @@ export function useThreadMessages<
103
103
  ThreadMessagesArgs<Query>,
104
104
  ThreadMessagesResult<Query>
105
105
  >,
106
- !options.stream ||
107
- args === "skip" ||
108
- paginated.status === "LoadingFirstPage"
109
- ? "skip"
110
- : { ...args, startOrder: paginated.results.at(-1)?.order }
106
+ !options.stream ? "skip" : args
111
107
  );
112
108
 
113
109
  const merged = useMemo(() => {
114
110
  const streamListMessages =
115
111
  streamMessages?.map((m) => ({
116
112
  ...m,
117
- streaming: !m.status || m.status === "streaming",
113
+ streaming: true,
118
114
  })) ?? [];
119
115
  return {
120
116
  ...paginated,
@@ -154,7 +150,7 @@ export function useStreamingThreadMessages<
154
150
  Query extends ThreadStreamQuery<any, any>,
155
151
  >(
156
152
  query: Query,
157
- args: (ThreadMessagesArgs<Query> & { startOrder?: number }) | "skip"
153
+ args: ThreadMessagesArgs<Query> | "skip"
158
154
  ): Array<ThreadMessagesResult<Query>> | undefined {
159
155
  // Invariant: streamMessages[streamId] is comprised of all deltas up to the
160
156
  // cursor. There can be multiple messages in the same stream, e.g. for tool
@@ -162,23 +158,15 @@ export function useStreamingThreadMessages<
162
158
  const [streams, setStreams] = useState<
163
159
  Array<{ streamId: string; cursor: number; messages: MessageDoc[] }>
164
160
  >([]);
165
- const startOrderRef = useRef<number>(0);
166
- const queryArgs = args === "skip" ? args : omit(args, ["startOrder"]);
167
- if (args !== "skip" && !startOrderRef.current && args.startOrder) {
168
- startOrderRef.current = args.startOrder;
169
- }
170
161
  // Get all the active streams
171
162
  const streamList = useQuery(
172
163
  query,
173
- queryArgs === "skip"
174
- ? queryArgs
164
+ args === "skip"
165
+ ? args
175
166
  : ({
176
- ...queryArgs,
167
+ ...args,
177
168
  paginationOpts: { cursor: null, numItems: 0 },
178
- streamArgs: {
179
- kind: "list",
180
- startOrder: startOrderRef.current,
181
- } as StreamArgs,
169
+ streamArgs: { kind: "list" } as StreamArgs,
182
170
  } as FunctionArgs<Query>)
183
171
  ) as
184
172
  | { streams: Extract<SyncStreamsReturnValue, { kind: "list" }> }
@@ -198,10 +186,10 @@ export function useStreamingThreadMessages<
198
186
  // Get the deltas for all the active streams, if any.
199
187
  const cursorQuery = useQuery(
200
188
  query,
201
- queryArgs === "skip" || !streamList
189
+ args === "skip" || !streamList
202
190
  ? ("skip" as const)
203
191
  : ({
204
- ...queryArgs,
192
+ ...args,
205
193
  paginationOpts: { cursor: null, numItems: 0 },
206
194
  streamArgs: { kind: "deltas", cursors } as StreamArgs,
207
195
  } as FunctionArgs<Query>)
@@ -11,7 +11,7 @@ export function optimisticallySendMessage(
11
11
  ) => void {
12
12
  return (store, args) => {
13
13
  const queries = store.getAllQueries(query);
14
- let maxOrder = -1;
14
+ let maxOrder = 0;
15
15
  let maxStepOrder = 0;
16
16
  for (const q of queries) {
17
17
  if (q.args?.threadId !== args.threadId) continue;
package/src/validators.ts CHANGED
@@ -460,7 +460,6 @@ export const vStreamArgs = v.optional(
460
460
  v.union(
461
461
  v.object({
462
462
  kind: v.literal("list"),
463
- startOrder: v.optional(v.number()),
464
463
  }),
465
464
  v.object({
466
465
  kind: v.literal("deltas"),
@@ -472,11 +471,6 @@ export type StreamArgs = Infer<typeof vStreamArgs>;
472
471
 
473
472
  export const vStreamMessage = v.object({
474
473
  streamId: v.string(),
475
- status: v.union(
476
- v.literal("streaming"),
477
- v.literal("finished"),
478
- v.literal("aborted")
479
- ),
480
474
  order: v.number(),
481
475
  stepOrder: v.number(),
482
476
  // metadata
@@ -1 +0,0 @@
1
- //# sourceMappingURL=_ignore.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_ignore.d.ts","sourceRoot":"","sources":["../../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":""}
@@ -1,3 +0,0 @@
1
- "use strict";
2
- // This is only here so convex-test can detect a _generated folder
3
- //# sourceMappingURL=_ignore.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_ignore.js","sourceRoot":"","sources":["../../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":";AAAA,kEAAkE"}