@convex-dev/agent 0.1.9-alpha.2 → 0.1.9-alpha.4

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 (71) hide show
  1. package/README.md +3 -3
  2. package/dist/commonjs/client/index.d.ts.map +1 -1
  3. package/dist/commonjs/client/index.js +1 -1
  4. package/dist/commonjs/client/index.js.map +1 -1
  5. package/dist/commonjs/client/streaming.d.ts.map +1 -1
  6. package/dist/commonjs/client/streaming.js +7 -1
  7. package/dist/commonjs/client/streaming.js.map +1 -1
  8. package/dist/commonjs/client/types.d.ts +0 -2
  9. package/dist/commonjs/client/types.d.ts.map +1 -1
  10. package/dist/commonjs/component/_generated/api.d.ts +18 -4
  11. package/dist/commonjs/component/schema.d.ts +36 -6
  12. package/dist/commonjs/component/schema.d.ts.map +1 -1
  13. package/dist/commonjs/component/schema.js +3 -0
  14. package/dist/commonjs/component/schema.js.map +1 -1
  15. package/dist/commonjs/component/streams.d.ts +5 -0
  16. package/dist/commonjs/component/streams.d.ts.map +1 -1
  17. package/dist/commonjs/component/streams.js +35 -7
  18. package/dist/commonjs/component/streams.js.map +1 -1
  19. package/dist/commonjs/component/threads.d.ts +8 -2
  20. package/dist/commonjs/component/threads.d.ts.map +1 -1
  21. package/dist/commonjs/component/threads.js +44 -18
  22. package/dist/commonjs/component/threads.js.map +1 -1
  23. package/dist/commonjs/react/index.d.ts +3 -1
  24. package/dist/commonjs/react/index.d.ts.map +1 -1
  25. package/dist/commonjs/react/index.js +14 -3
  26. package/dist/commonjs/react/index.js.map +1 -1
  27. package/dist/commonjs/validators.d.ts +5 -2
  28. package/dist/commonjs/validators.d.ts.map +1 -1
  29. package/dist/commonjs/validators.js +1 -0
  30. package/dist/commonjs/validators.js.map +1 -1
  31. package/dist/commonjs.tsbuildinfo +1 -1
  32. package/dist/esm/client/index.d.ts.map +1 -1
  33. package/dist/esm/client/index.js +1 -1
  34. package/dist/esm/client/index.js.map +1 -1
  35. package/dist/esm/client/streaming.d.ts.map +1 -1
  36. package/dist/esm/client/streaming.js +7 -1
  37. package/dist/esm/client/streaming.js.map +1 -1
  38. package/dist/esm/client/types.d.ts +0 -2
  39. package/dist/esm/client/types.d.ts.map +1 -1
  40. package/dist/esm/component/_generated/api.d.ts +18 -4
  41. package/dist/esm/component/schema.d.ts +36 -6
  42. package/dist/esm/component/schema.d.ts.map +1 -1
  43. package/dist/esm/component/schema.js +3 -0
  44. package/dist/esm/component/schema.js.map +1 -1
  45. package/dist/esm/component/streams.d.ts +5 -0
  46. package/dist/esm/component/streams.d.ts.map +1 -1
  47. package/dist/esm/component/streams.js +35 -7
  48. package/dist/esm/component/streams.js.map +1 -1
  49. package/dist/esm/component/threads.d.ts +8 -2
  50. package/dist/esm/component/threads.d.ts.map +1 -1
  51. package/dist/esm/component/threads.js +44 -18
  52. package/dist/esm/component/threads.js.map +1 -1
  53. package/dist/esm/react/index.d.ts +3 -1
  54. package/dist/esm/react/index.d.ts.map +1 -1
  55. package/dist/esm/react/index.js +14 -3
  56. package/dist/esm/react/index.js.map +1 -1
  57. package/dist/esm/validators.d.ts +5 -2
  58. package/dist/esm/validators.d.ts.map +1 -1
  59. package/dist/esm/validators.js +1 -0
  60. package/dist/esm/validators.js.map +1 -1
  61. package/dist/esm.tsbuildinfo +1 -1
  62. package/package.json +1 -1
  63. package/src/client/index.ts +1 -2
  64. package/src/client/streaming.ts +7 -1
  65. package/src/client/types.ts +0 -2
  66. package/src/component/_generated/api.d.ts +18 -4
  67. package/src/component/schema.ts +4 -0
  68. package/src/component/streams.ts +43 -8
  69. package/src/component/threads.ts +48 -26
  70. package/src/react/index.ts +15 -4
  71. package/src/validators.ts +1 -0
@@ -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 { Id } from "./_generated/dataModel.js";
9
+ import type { Doc, Id } from "./_generated/dataModel.js";
10
10
  import {
11
11
  internalMutation,
12
12
  mutation,
@@ -102,13 +102,17 @@ export const create = mutation({
102
102
  export const list = query({
103
103
  args: {
104
104
  threadId: v.id("threads"),
105
+ startOrder: v.optional(v.number()),
105
106
  },
106
107
  returns: v.array(vStreamMessage),
107
108
  handler: async (ctx, args) => {
108
109
  return ctx.db
109
110
  .query("streamingMessages")
110
111
  .withIndex("threadId_state_order_stepOrder", (q) =>
111
- q.eq("threadId", args.threadId).eq("state.kind", "streaming")
112
+ q
113
+ .eq("threadId", args.threadId)
114
+ .eq("state.kind", "streaming")
115
+ .gte("order", args.startOrder ?? 0)
112
116
  )
113
117
  .order("desc")
114
118
  .take(100)
@@ -129,6 +133,42 @@ export const list = query({
129
133
  },
130
134
  });
131
135
 
136
+ export const abort = mutation({
137
+ args: {
138
+ streamId: v.id("streamingMessages"),
139
+ reason: v.string(),
140
+ },
141
+ returns: v.null(),
142
+ handler: async (ctx, args) => {
143
+ const stream = await ctx.db.get(args.streamId);
144
+ if (!stream) {
145
+ throw new Error(`Stream not found: ${args.streamId}`);
146
+ }
147
+ if (stream.state.kind !== "streaming") {
148
+ console.warn(
149
+ `Stream trying to abort but not currently streaming (${stream.state.kind}): ${args.streamId}`
150
+ );
151
+ return;
152
+ }
153
+ await cleanupTimeoutFn(ctx, stream);
154
+ await ctx.db.patch(args.streamId, {
155
+ state: { kind: "aborted", reason: args.reason },
156
+ });
157
+ },
158
+ });
159
+
160
+ async function cleanupTimeoutFn(
161
+ ctx: MutationCtx,
162
+ stream: Doc<"streamingMessages">
163
+ ) {
164
+ if (stream.state.kind === "streaming" && stream.state.timeoutFnId) {
165
+ const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
166
+ if (timeoutFn?.state.kind === "pending") {
167
+ await ctx.scheduler.cancel(stream.state.timeoutFnId);
168
+ }
169
+ }
170
+ }
171
+
132
172
  export const finish = mutation({
133
173
  args: {
134
174
  streamId: v.id("streamingMessages"),
@@ -149,12 +189,7 @@ export const finish = mutation({
149
189
  );
150
190
  return;
151
191
  }
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
- }
192
+ await cleanupTimeoutFn(ctx, stream);
158
193
  await ctx.db.patch(args.streamId, {
159
194
  state: { kind: "finished", endedAt: Date.now() },
160
195
  });
@@ -16,6 +16,7 @@ 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";
19
20
 
20
21
  function publicThreadOrNull(thread: Doc<"threads"> | null): ThreadDoc | null {
21
22
  if (thread === null) {
@@ -100,29 +101,17 @@ export const updateThread = mutation({
100
101
 
101
102
  // TODO: delete thread
102
103
 
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
-
115
104
  /**
116
105
  * Use this to delete a thread and everything it contains.
117
106
  * It will try to delete all pages synchronously.
118
107
  * If it times out or fails, you'll have to run it again.
119
108
  */
120
109
  export const deleteAllForThreadIdSync = action({
121
- args: deleteThreadArgs,
110
+ args: { threadId: v.id("threads"), limit: v.optional(v.number()) },
122
111
  handler: async (ctx, args) => {
123
- let cursor = args.cursor;
112
+ let cursor: string | undefined = undefined;
124
113
  while (true) {
125
- const result = await ctx.runMutation(
114
+ const result: DeleteThreadReturns = await ctx.runMutation(
126
115
  internal.threads._deletePageForThreadId,
127
116
  { threadId: args.threadId, cursor, limit: args.limit }
128
117
  );
@@ -138,6 +127,22 @@ export const deleteAllForThreadIdSync = action({
138
127
  returns: v.null(),
139
128
  });
140
129
 
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
+
141
146
  export const _deletePageForThreadId = internalMutation({
142
147
  args: deleteThreadArgs,
143
148
  handler: deletePageForThreadIdHandler,
@@ -151,22 +156,39 @@ export const _deletePageForThreadId = internalMutation({
151
156
  export const deleteAllForThreadIdAsync = mutation({
152
157
  args: deleteThreadArgs,
153
158
  handler: async (ctx, args) => {
154
- const result = await deletePageForThreadIdHandler(ctx, args);
155
- if (!result.isDone) {
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) {
156
180
  await ctx.scheduler.runAfter(0, api.threads.deleteAllForThreadIdAsync, {
157
181
  threadId: args.threadId,
158
- cursor: result.cursor,
182
+ cursor: messagesResult.cursor,
183
+ messagesDone: messagesResult.isDone,
184
+ streamsDone: streamResult.isDone,
185
+ streamOrder: streamResult.streamOrder,
186
+ deltaCursor: streamResult.deltaCursor,
159
187
  });
160
188
  }
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;
189
+ return { isDone };
168
190
  },
169
- returns: deleteThreadReturns,
191
+ returns: v.object({ isDone: v.boolean() }),
170
192
  });
171
193
 
172
194
  async function deletePageForThreadIdHandler(
@@ -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, useState } from "react";
10
+ import { useMemo, useRef, 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,7 +103,11 @@ export function useThreadMessages<
103
103
  ThreadMessagesArgs<Query>,
104
104
  ThreadMessagesResult<Query>
105
105
  >,
106
- !options.stream ? "skip" : args
106
+ !options.stream ||
107
+ args === "skip" ||
108
+ paginated.status === "LoadingFirstPage"
109
+ ? "skip"
110
+ : { ...args, startOrder: paginated.results.at(0)?.order }
107
111
  );
108
112
 
109
113
  const merged = useMemo(() => {
@@ -150,7 +154,7 @@ export function useStreamingThreadMessages<
150
154
  Query extends ThreadStreamQuery<any, any>,
151
155
  >(
152
156
  query: Query,
153
- args: ThreadMessagesArgs<Query> | "skip"
157
+ args: (ThreadMessagesArgs<Query> & { startOrder?: number }) | "skip"
154
158
  ): Array<ThreadMessagesResult<Query>> | undefined {
155
159
  // Invariant: streamMessages[streamId] is comprised of all deltas up to the
156
160
  // cursor. There can be multiple messages in the same stream, e.g. for tool
@@ -158,6 +162,10 @@ export function useStreamingThreadMessages<
158
162
  const [streams, setStreams] = useState<
159
163
  Array<{ streamId: string; cursor: number; messages: MessageDoc[] }>
160
164
  >([]);
165
+ const startOrderRef = useRef<number>(0);
166
+ if (args !== "skip" && !startOrderRef.current && args.startOrder) {
167
+ startOrderRef.current = args.startOrder;
168
+ }
161
169
  // Get all the active streams
162
170
  const streamList = useQuery(
163
171
  query,
@@ -166,7 +174,10 @@ export function useStreamingThreadMessages<
166
174
  : ({
167
175
  ...args,
168
176
  paginationOpts: { cursor: null, numItems: 0 },
169
- streamArgs: { kind: "list" } as StreamArgs,
177
+ streamArgs: {
178
+ kind: "list",
179
+ startOrder: startOrderRef.current,
180
+ } as StreamArgs,
170
181
  } as FunctionArgs<Query>)
171
182
  ) as
172
183
  | { streams: Extract<SyncStreamsReturnValue, { kind: "list" }> }
package/src/validators.ts CHANGED
@@ -460,6 +460,7 @@ 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()),
463
464
  }),
464
465
  v.object({
465
466
  kind: v.literal("deltas"),