@convex-dev/agent 0.1.9-alpha.11 → 0.1.9-alpha.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 (89) hide show
  1. package/README.md +3 -3
  2. package/dist/commonjs/client/index.d.ts +0 -1
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +1 -3
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/commonjs/client/streaming.d.ts.map +1 -1
  7. package/dist/commonjs/client/streaming.js +1 -7
  8. package/dist/commonjs/client/streaming.js.map +1 -1
  9. package/dist/commonjs/client/types.d.ts +2 -0
  10. package/dist/commonjs/client/types.d.ts.map +1 -1
  11. package/dist/commonjs/component/_generated/api.d.ts +4 -23
  12. package/dist/commonjs/component/schema.d.ts +26 -26
  13. package/dist/commonjs/component/schema.d.ts.map +1 -1
  14. package/dist/commonjs/component/schema.js +2 -2
  15. package/dist/commonjs/component/schema.js.map +1 -1
  16. package/dist/commonjs/component/streams.d.ts +3 -10
  17. package/dist/commonjs/component/streams.d.ts.map +1 -1
  18. package/dist/commonjs/component/streams.js +20 -54
  19. package/dist/commonjs/component/streams.js.map +1 -1
  20. package/dist/commonjs/component/threads.d.ts +2 -8
  21. package/dist/commonjs/component/threads.d.ts.map +1 -1
  22. package/dist/commonjs/component/threads.js +18 -44
  23. package/dist/commonjs/component/threads.js.map +1 -1
  24. package/dist/commonjs/component/vector/tables.d.ts +2 -4
  25. package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
  26. package/dist/commonjs/react/deltas.d.ts.map +1 -1
  27. package/dist/commonjs/react/deltas.js +2 -3
  28. package/dist/commonjs/react/deltas.js.map +1 -1
  29. package/dist/commonjs/react/index.d.ts +2 -4
  30. package/dist/commonjs/react/index.d.ts.map +1 -1
  31. package/dist/commonjs/react/index.js +9 -22
  32. package/dist/commonjs/react/index.js.map +1 -1
  33. package/dist/commonjs/react/optimisticallySendMessage.js +1 -1
  34. package/dist/commonjs/react/optimisticallySendMessage.js.map +1 -1
  35. package/dist/commonjs/validators.d.ts +3 -8
  36. package/dist/commonjs/validators.d.ts.map +1 -1
  37. package/dist/commonjs/validators.js +0 -2
  38. package/dist/commonjs/validators.js.map +1 -1
  39. package/dist/commonjs.tsbuildinfo +1 -1
  40. package/dist/esm/client/index.d.ts +0 -1
  41. package/dist/esm/client/index.d.ts.map +1 -1
  42. package/dist/esm/client/index.js +1 -3
  43. package/dist/esm/client/index.js.map +1 -1
  44. package/dist/esm/client/streaming.d.ts.map +1 -1
  45. package/dist/esm/client/streaming.js +1 -7
  46. package/dist/esm/client/streaming.js.map +1 -1
  47. package/dist/esm/client/types.d.ts +2 -0
  48. package/dist/esm/client/types.d.ts.map +1 -1
  49. package/dist/esm/component/_generated/api.d.ts +4 -23
  50. package/dist/esm/component/schema.d.ts +26 -26
  51. package/dist/esm/component/schema.d.ts.map +1 -1
  52. package/dist/esm/component/schema.js +2 -2
  53. package/dist/esm/component/schema.js.map +1 -1
  54. package/dist/esm/component/streams.d.ts +3 -10
  55. package/dist/esm/component/streams.d.ts.map +1 -1
  56. package/dist/esm/component/streams.js +20 -54
  57. package/dist/esm/component/streams.js.map +1 -1
  58. package/dist/esm/component/threads.d.ts +2 -8
  59. package/dist/esm/component/threads.d.ts.map +1 -1
  60. package/dist/esm/component/threads.js +18 -44
  61. package/dist/esm/component/threads.js.map +1 -1
  62. package/dist/esm/component/vector/tables.d.ts +2 -4
  63. package/dist/esm/component/vector/tables.d.ts.map +1 -1
  64. package/dist/esm/react/deltas.d.ts.map +1 -1
  65. package/dist/esm/react/deltas.js +2 -3
  66. package/dist/esm/react/deltas.js.map +1 -1
  67. package/dist/esm/react/index.d.ts +2 -4
  68. package/dist/esm/react/index.d.ts.map +1 -1
  69. package/dist/esm/react/index.js +9 -22
  70. package/dist/esm/react/index.js.map +1 -1
  71. package/dist/esm/react/optimisticallySendMessage.js +1 -1
  72. package/dist/esm/react/optimisticallySendMessage.js.map +1 -1
  73. package/dist/esm/validators.d.ts +3 -8
  74. package/dist/esm/validators.d.ts.map +1 -1
  75. package/dist/esm/validators.js +0 -2
  76. package/dist/esm/validators.js.map +1 -1
  77. package/dist/esm.tsbuildinfo +1 -1
  78. package/package.json +1 -1
  79. package/src/client/index.ts +2 -5
  80. package/src/client/streaming.ts +1 -7
  81. package/src/client/types.ts +2 -0
  82. package/src/component/_generated/api.d.ts +4 -23
  83. package/src/component/schema.ts +2 -2
  84. package/src/component/streams.ts +35 -89
  85. package/src/component/threads.ts +26 -48
  86. package/src/react/deltas.ts +2 -5
  87. package/src/react/index.ts +11 -23
  88. package/src/react/optimisticallySendMessage.ts +1 -1
  89. package/src/validators.ts +0 -6
@@ -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,
@@ -17,15 +17,11 @@ import {
17
17
  import schema from "./schema.js";
18
18
  import { stream } from "convex-helpers/server/stream";
19
19
  import { mergedStream } from "convex-helpers/server/stream";
20
- import { paginator } from "convex-helpers/server/pagination";
21
-
22
- const SECOND = 1000;
23
- const MINUTE = 60 * SECOND;
24
20
 
25
21
  const MAX_DELTAS_PER_REQUEST = 1000;
26
22
  const MAX_DELTAS_PER_STREAM = 100;
27
- const TIMEOUT_INTERVAL = 10 * MINUTE;
28
- const DELETE_STREAM_DELAY = MINUTE * 5; // 5 minutes
23
+ const TIMEOUT_INTERVAL = 1000 * 60; // 1 minute
24
+ const DELETE_STREAM_DELAY = 1000 * 60 * 5; // 5 minutes
29
25
 
30
26
  const deltaValidator = schema.tables.streamDeltas.validator;
31
27
 
@@ -106,87 +102,33 @@ export const create = mutation({
106
102
  export const list = query({
107
103
  args: {
108
104
  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
105
  },
120
106
  returns: v.array(vStreamMessage),
121
107
  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}`
108
+ return ctx.db
109
+ .query("streamingMessages")
110
+ .withIndex("threadId_state_order_stepOrder", (q) =>
111
+ q.eq("threadId", args.threadId).eq("state.kind", "streaming")
112
+ )
113
+ .order("desc")
114
+ .take(100)
115
+ .then((msgs) =>
116
+ msgs.map((m) => ({
117
+ streamId: m._id,
118
+ ...pick(m, [
119
+ "order",
120
+ "stepOrder",
121
+ "userId",
122
+ "agentName",
123
+ "model",
124
+ "provider",
125
+ "providerOptions",
126
+ ]),
127
+ }))
168
128
  );
169
- return;
170
- }
171
- await cleanupTimeoutFn(ctx, stream);
172
- await ctx.db.patch(args.streamId, {
173
- state: { kind: "aborted", reason: args.reason },
174
- });
175
129
  },
176
130
  });
177
131
 
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
132
  export const finish = mutation({
191
133
  args: {
192
134
  streamId: v.id("streamingMessages"),
@@ -207,7 +149,12 @@ export const finish = mutation({
207
149
  );
208
150
  return;
209
151
  }
210
- await cleanupTimeoutFn(ctx, stream);
152
+ if (stream.state.timeoutFnId) {
153
+ const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
154
+ if (timeoutFn?.state.kind === "pending") {
155
+ await ctx.scheduler.cancel(stream.state.timeoutFnId);
156
+ }
157
+ }
211
158
  await ctx.db.patch(args.streamId, {
212
159
  state: { kind: "finished", endedAt: Date.now() },
213
160
  });
@@ -272,8 +219,8 @@ export const timeoutStream = internalMutation({
272
219
  }
273
220
  await ctx.db.patch(args.streamId, {
274
221
  state: {
275
- kind: "aborted",
276
- reason: "timeout",
222
+ kind: "finished",
223
+ endedAt: Date.now(),
277
224
  },
278
225
  });
279
226
  },
@@ -283,7 +230,7 @@ async function deletePageForStreamId(
283
230
  ctx: MutationCtx,
284
231
  args: { streamId: Id<"streamingMessages">; cursor?: string }
285
232
  ) {
286
- const deltas = await paginator(ctx.db, schema)
233
+ const deltas = await ctx.db
287
234
  .query("streamDeltas")
288
235
  .withIndex("streamId_start_end", (q) => q.eq("streamId", args.streamId))
289
236
  .paginate({
@@ -291,9 +238,6 @@ async function deletePageForStreamId(
291
238
  cursor: args.cursor ?? null,
292
239
  });
293
240
  await Promise.all(deltas.page.map((d) => ctx.db.delete(d._id)));
294
- if (deltas.isDone) {
295
- await ctx.db.delete(args.streamId);
296
- }
297
241
  return deltas;
298
242
  }
299
243
 
@@ -333,12 +277,13 @@ export async function deleteStreamsPageForThreadId(
333
277
  cursor: deltaCursor,
334
278
  });
335
279
  if (result.isDone) {
280
+ await ctx.db.delete(streamMessage._id);
336
281
  deltaCursor = undefined;
337
282
  }
338
283
  return {
339
284
  isDone: false,
340
285
  streamOrder: streamMessage.order,
341
- deltaCursor,
286
+ deltaCursor: result.continueCursor,
342
287
  };
343
288
  }
344
289
 
@@ -397,6 +342,7 @@ export const deleteStreamSync = mutation({
397
342
  cursor: deltas.continueCursor,
398
343
  });
399
344
  }
345
+ await ctx.db.delete(args.streamId);
400
346
  },
401
347
  });
402
348
 
@@ -16,7 +16,6 @@ import {
16
16
  } from "./_generated/server.js";
17
17
  import { deleteMessage } from "./messages.js";
18
18
  import { schema, v } from "./schema.js";
19
- import { deleteStreamsPageForThreadId } from "./streams.js";
20
19
 
21
20
  function publicThreadOrNull(thread: Doc<"threads"> | null): ThreadDoc | null {
22
21
  if (thread === null) {
@@ -101,17 +100,29 @@ export const updateThread = mutation({
101
100
 
102
101
  // TODO: delete thread
103
102
 
103
+ const deleteThreadArgs = {
104
+ threadId: v.id("threads"),
105
+ cursor: v.optional(v.string()),
106
+ limit: v.optional(v.number()),
107
+ };
108
+ type DeleteThreadArgs = ObjectType<typeof deleteThreadArgs>;
109
+ const deleteThreadReturns = {
110
+ cursor: v.string(),
111
+ isDone: v.boolean(),
112
+ };
113
+ type DeleteThreadReturns = ObjectType<typeof deleteThreadReturns>;
114
+
104
115
  /**
105
116
  * Use this to delete a thread and everything it contains.
106
117
  * It will try to delete all pages synchronously.
107
118
  * If it times out or fails, you'll have to run it again.
108
119
  */
109
120
  export const deleteAllForThreadIdSync = action({
110
- args: { threadId: v.id("threads"), limit: v.optional(v.number()) },
121
+ args: deleteThreadArgs,
111
122
  handler: async (ctx, args) => {
112
- let cursor: string | undefined = undefined;
123
+ let cursor = args.cursor;
113
124
  while (true) {
114
- const result: DeleteThreadReturns = await ctx.runMutation(
125
+ const result = await ctx.runMutation(
115
126
  internal.threads._deletePageForThreadId,
116
127
  { threadId: args.threadId, cursor, limit: args.limit }
117
128
  );
@@ -127,22 +138,6 @@ export const deleteAllForThreadIdSync = action({
127
138
  returns: v.null(),
128
139
  });
129
140
 
130
- const deleteThreadArgs = {
131
- threadId: v.id("threads"),
132
- cursor: v.optional(v.string()),
133
- messagesDone: v.optional(v.boolean()),
134
- streamsDone: v.optional(v.boolean()),
135
- streamOrder: v.optional(v.number()),
136
- deltaCursor: v.optional(v.string()),
137
- limit: v.optional(v.number()),
138
- };
139
- type DeleteThreadArgs = ObjectType<typeof deleteThreadArgs>;
140
- const deleteThreadReturns = {
141
- cursor: v.string(),
142
- isDone: v.boolean(),
143
- };
144
- type DeleteThreadReturns = ObjectType<typeof deleteThreadReturns>;
145
-
146
141
  export const _deletePageForThreadId = internalMutation({
147
142
  args: deleteThreadArgs,
148
143
  handler: deletePageForThreadIdHandler,
@@ -156,39 +151,22 @@ export const _deletePageForThreadId = internalMutation({
156
151
  export const deleteAllForThreadIdAsync = mutation({
157
152
  args: deleteThreadArgs,
158
153
  handler: async (ctx, args) => {
159
- let messagesResult = {
160
- isDone: args.messagesDone ?? false,
161
- cursor: args.cursor,
162
- };
163
- if (!args.messagesDone) {
164
- messagesResult = await deletePageForThreadIdHandler(ctx, args);
165
- }
166
- let streamResult = {
167
- isDone: args.streamsDone ?? false,
168
- streamOrder: args.streamOrder,
169
- deltaCursor: args.deltaCursor,
170
- };
171
- if (!args.streamsDone) {
172
- streamResult = await deleteStreamsPageForThreadId(ctx, {
173
- threadId: args.threadId,
174
- streamOrder: args.streamOrder,
175
- deltaCursor: args.deltaCursor,
176
- });
177
- }
178
- const isDone = messagesResult.isDone && streamResult.isDone;
179
- if (!isDone) {
154
+ const result = await deletePageForThreadIdHandler(ctx, args);
155
+ if (!result.isDone) {
180
156
  await ctx.scheduler.runAfter(0, api.threads.deleteAllForThreadIdAsync, {
181
157
  threadId: args.threadId,
182
- cursor: messagesResult.cursor,
183
- messagesDone: messagesResult.isDone,
184
- streamsDone: streamResult.isDone,
185
- streamOrder: streamResult.streamOrder,
186
- deltaCursor: streamResult.deltaCursor,
158
+ cursor: result.cursor,
187
159
  });
188
160
  }
189
- return { isDone };
161
+ // Kick off the streams deletion
162
+ await ctx.scheduler.runAfter(
163
+ 0,
164
+ api.streams.deleteAllStreamsForThreadIdSync,
165
+ { threadId: args.threadId }
166
+ );
167
+ return result;
190
168
  },
191
- returns: v.object({ isDone: v.boolean() }),
169
+ returns: deleteThreadReturns,
192
170
  });
193
171
 
194
172
  async function deletePageForThreadIdHandler(
@@ -189,7 +189,6 @@ export function applyDeltasToStreamMessage(
189
189
  contentToAdd = part;
190
190
  break;
191
191
  case "reasoning":
192
- currentMessage.reasoning += part.textDelta;
193
192
  if (lastContent?.type === "reasoning") {
194
193
  lastContent.text += part.textDelta;
195
194
  } else {
@@ -256,14 +255,12 @@ export function createStreamingMessage(
256
255
  ): MessageDoc {
257
256
  const { streamId, ...rest } = message;
258
257
  const metadata: MessageDoc = {
259
- ...rest,
260
258
  _id: `${streamId}-${index}`,
261
259
  _creationTime: Date.now(),
262
- status: (
263
- { streaming: "pending", finished: "success", aborted: "failed" } as const
264
- )[message.status],
260
+ status: "pending",
265
261
  threadId,
266
262
  tool: false,
263
+ ...rest,
267
264
  };
268
265
  switch (part.type) {
269
266
  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