@convex-dev/agent 0.1.9-alpha.3 → 0.1.9-alpha.5

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 (79) hide show
  1. package/README.md +3 -3
  2. package/dist/commonjs/client/index.d.ts +72 -71
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +3 -1
  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 +7 -1
  8. package/dist/commonjs/client/streaming.js.map +1 -1
  9. package/dist/commonjs/client/types.d.ts +0 -2
  10. package/dist/commonjs/client/types.d.ts.map +1 -1
  11. package/dist/commonjs/component/_generated/api.d.ts +12 -1
  12. package/dist/commonjs/component/messages.d.ts +210 -210
  13. package/dist/commonjs/component/schema.d.ts +699 -699
  14. package/dist/commonjs/component/schema.d.ts.map +1 -1
  15. package/dist/commonjs/component/schema.js +2 -2
  16. package/dist/commonjs/component/schema.js.map +1 -1
  17. package/dist/commonjs/component/streams.d.ts +17 -10
  18. package/dist/commonjs/component/streams.d.ts.map +1 -1
  19. package/dist/commonjs/component/streams.js +42 -12
  20. package/dist/commonjs/component/streams.js.map +1 -1
  21. package/dist/commonjs/component/threads.d.ts +18 -18
  22. package/dist/commonjs/component/vector/index.d.ts +3 -3
  23. package/dist/commonjs/component/vector/tables.d.ts +2 -2
  24. package/dist/commonjs/react/deltas.d.ts.map +1 -1
  25. package/dist/commonjs/react/deltas.js +2 -2
  26. package/dist/commonjs/react/deltas.js.map +1 -1
  27. package/dist/commonjs/react/index.d.ts +3 -1
  28. package/dist/commonjs/react/index.d.ts.map +1 -1
  29. package/dist/commonjs/react/index.js +14 -3
  30. package/dist/commonjs/react/index.js.map +1 -1
  31. package/dist/commonjs/validators.d.ts +167 -162
  32. package/dist/commonjs/validators.d.ts.map +1 -1
  33. package/dist/commonjs/validators.js +2 -0
  34. package/dist/commonjs/validators.js.map +1 -1
  35. package/dist/commonjs.tsbuildinfo +1 -1
  36. package/dist/esm/client/index.d.ts +20 -19
  37. package/dist/esm/client/index.d.ts.map +1 -1
  38. package/dist/esm/client/index.js +3 -1
  39. package/dist/esm/client/index.js.map +1 -1
  40. package/dist/esm/client/streaming.d.ts.map +1 -1
  41. package/dist/esm/client/streaming.js +7 -1
  42. package/dist/esm/client/streaming.js.map +1 -1
  43. package/dist/esm/client/types.d.ts +0 -2
  44. package/dist/esm/client/types.d.ts.map +1 -1
  45. package/dist/esm/component/_generated/api.d.ts +12 -1
  46. package/dist/esm/component/messages.d.ts +37 -37
  47. package/dist/esm/component/schema.d.ts +699 -699
  48. package/dist/esm/component/schema.d.ts.map +1 -1
  49. package/dist/esm/component/schema.js +2 -2
  50. package/dist/esm/component/schema.js.map +1 -1
  51. package/dist/esm/component/streams.d.ts +7 -0
  52. package/dist/esm/component/streams.d.ts.map +1 -1
  53. package/dist/esm/component/streams.js +42 -12
  54. package/dist/esm/component/streams.js.map +1 -1
  55. package/dist/esm/component/threads.d.ts +15 -15
  56. package/dist/esm/component/vector/index.d.ts +3 -3
  57. package/dist/esm/component/vector/tables.d.ts +2 -2
  58. package/dist/esm/react/deltas.d.ts.map +1 -1
  59. package/dist/esm/react/deltas.js +2 -2
  60. package/dist/esm/react/deltas.js.map +1 -1
  61. package/dist/esm/react/index.d.ts +3 -1
  62. package/dist/esm/react/index.d.ts.map +1 -1
  63. package/dist/esm/react/index.js +14 -3
  64. package/dist/esm/react/index.js.map +1 -1
  65. package/dist/esm/validators.d.ts +167 -162
  66. package/dist/esm/validators.d.ts.map +1 -1
  67. package/dist/esm/validators.js +2 -0
  68. package/dist/esm/validators.js.map +1 -1
  69. package/dist/esm.tsbuildinfo +1 -1
  70. package/package.json +1 -1
  71. package/src/client/index.ts +5 -2
  72. package/src/client/streaming.ts +7 -1
  73. package/src/client/types.ts +0 -2
  74. package/src/component/_generated/api.d.ts +12 -1
  75. package/src/component/schema.ts +2 -2
  76. package/src/component/streams.ts +76 -27
  77. package/src/react/deltas.ts +4 -2
  78. package/src/react/index.ts +15 -4
  79. package/src/validators.ts +6 -0
@@ -994,6 +994,8 @@ 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")[];
997
999
  }
998
1000
  ): Promise<SyncStreamsReturnValue | undefined> {
999
1001
  if (!args.streamArgs) return undefined;
@@ -1002,6 +1004,8 @@ export class Agent<AgentTools extends ToolSet> {
1002
1004
  kind: "list",
1003
1005
  messages: await ctx.runQuery(this.component.streams.list, {
1004
1006
  threadId: args.threadId,
1007
+ startOrder: args.streamArgs.startOrder,
1008
+ statuses: args.includeStatuses,
1005
1009
  }),
1006
1010
  };
1007
1011
  } else {
@@ -1051,8 +1055,7 @@ export class Agent<AgentTools extends ToolSet> {
1051
1055
  this.component.messages.listMessagesByThreadId,
1052
1056
  {
1053
1057
  threadId: args.threadId,
1054
- excludeToolMessages:
1055
- opts.includeToolCalls === true ? false : opts.excludeToolMessages,
1058
+ excludeToolMessages: opts.excludeToolMessages,
1056
1059
  paginationOpts: {
1057
1060
  numItems: opts.recentMessages ?? DEFAULT_RECENT_MESSAGES,
1058
1061
  cursor: null,
@@ -96,7 +96,13 @@ 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", () => {
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
+ }
100
106
  this.abortController.abort();
101
107
  });
102
108
  }
@@ -48,8 +48,6 @@ import type { StreamingOptions } from "./streaming.js";
48
48
  * automatically with thread.generateText, or directly via search.
49
49
  */
50
50
  export type ContextOptions = {
51
- /** @deprecated Use excludeToolMessages instead. */
52
- includeToolCalls?: boolean;
53
51
  /**
54
52
  * Whether to include tool messages in the context.
55
53
  * By default, tool calls and results are not included.
@@ -1414,6 +1414,12 @@ 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
+ >;
1417
1423
  addDelta: FunctionReference<
1418
1424
  "mutation",
1419
1425
  "public",
@@ -1569,13 +1575,18 @@ export type Mounts = {
1569
1575
  list: FunctionReference<
1570
1576
  "query",
1571
1577
  "public",
1572
- { threadId: string },
1578
+ {
1579
+ startOrder?: number;
1580
+ statuses?: Array<"streaming" | "finished" | "aborted">;
1581
+ threadId: string;
1582
+ },
1573
1583
  Array<{
1574
1584
  agentName?: string;
1575
1585
  model?: string;
1576
1586
  order: number;
1577
1587
  provider?: string;
1578
1588
  providerOptions?: Record<string, Record<string, any>>;
1589
+ status: "streaming" | "finished" | "aborted";
1579
1590
  stepOrder: number;
1580
1591
  streamId: string;
1581
1592
  userId?: string;
@@ -110,8 +110,8 @@ export const schema = defineSchema({
110
110
  endedAt: v.number(),
111
111
  }),
112
112
  v.object({
113
- kind: v.literal("error"),
114
- error: v.string(),
113
+ kind: v.literal("aborted"),
114
+ reason: v.string(),
115
115
  })
116
116
  ),
117
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 { Id } from "./_generated/dataModel.js";
9
+ import type { Doc, Id } from "./_generated/dataModel.js";
10
10
  import {
11
11
  internalMutation,
12
12
  mutation,
@@ -102,33 +102,87 @@ 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()),
106
+ statuses: v.optional(
107
+ v.array(
108
+ v.union(
109
+ v.literal("streaming"),
110
+ v.literal("finished"),
111
+ v.literal("aborted")
112
+ )
113
+ )
114
+ ),
105
115
  },
106
116
  returns: v.array(vStreamMessage),
107
117
  handler: async (ctx, args) => {
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
- }))
118
+ const statuses = args.statuses ?? ["streaming"];
119
+ const messages = await mergedStream(
120
+ statuses.map((status) =>
121
+ stream(ctx.db, schema)
122
+ .query("streamingMessages")
123
+ .withIndex("threadId_state_order_stepOrder", (q) =>
124
+ q
125
+ .eq("threadId", args.threadId)
126
+ .eq("state.kind", status)
127
+ .gte("order", args.startOrder ?? 0)
128
+ )
129
+ .order("desc")
130
+ ),
131
+ ["order", "stepOrder"]
132
+ ).take(100);
133
+
134
+ return messages.map((m) => ({
135
+ streamId: m._id,
136
+ status: m.state.kind,
137
+ ...pick(m, [
138
+ "order",
139
+ "stepOrder",
140
+ "userId",
141
+ "agentName",
142
+ "model",
143
+ "provider",
144
+ "providerOptions",
145
+ ]),
146
+ }));
147
+ },
148
+ });
149
+
150
+ export const abort = mutation({
151
+ args: {
152
+ streamId: v.id("streamingMessages"),
153
+ reason: v.string(),
154
+ },
155
+ returns: v.null(),
156
+ handler: async (ctx, args) => {
157
+ const stream = await ctx.db.get(args.streamId);
158
+ if (!stream) {
159
+ throw new Error(`Stream not found: ${args.streamId}`);
160
+ }
161
+ if (stream.state.kind !== "streaming") {
162
+ console.warn(
163
+ `Stream trying to abort but not currently streaming (${stream.state.kind}): ${args.streamId}`
128
164
  );
165
+ return;
166
+ }
167
+ await cleanupTimeoutFn(ctx, stream);
168
+ await ctx.db.patch(args.streamId, {
169
+ state: { kind: "aborted", reason: args.reason },
170
+ });
129
171
  },
130
172
  });
131
173
 
174
+ async function cleanupTimeoutFn(
175
+ ctx: MutationCtx,
176
+ stream: Doc<"streamingMessages">
177
+ ) {
178
+ if (stream.state.kind === "streaming" && stream.state.timeoutFnId) {
179
+ const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
180
+ if (timeoutFn?.state.kind === "pending") {
181
+ await ctx.scheduler.cancel(stream.state.timeoutFnId);
182
+ }
183
+ }
184
+ }
185
+
132
186
  export const finish = mutation({
133
187
  args: {
134
188
  streamId: v.id("streamingMessages"),
@@ -149,12 +203,7 @@ export const finish = mutation({
149
203
  );
150
204
  return;
151
205
  }
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
- }
206
+ await cleanupTimeoutFn(ctx, stream);
158
207
  await ctx.db.patch(args.streamId, {
159
208
  state: { kind: "finished", endedAt: Date.now() },
160
209
  });
@@ -255,12 +255,14 @@ export function createStreamingMessage(
255
255
  ): MessageDoc {
256
256
  const { streamId, ...rest } = message;
257
257
  const metadata: MessageDoc = {
258
+ ...rest,
258
259
  _id: `${streamId}-${index}`,
259
260
  _creationTime: Date.now(),
260
- status: "pending",
261
+ status: (
262
+ { streaming: "pending", finished: "success", aborted: "failed" } as const
263
+ )[message.status],
261
264
  threadId,
262
265
  tool: false,
263
- ...rest,
264
266
  };
265
267
  switch (part.type) {
266
268
  case "text-delta":
@@ -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"),
@@ -471,6 +472,11 @@ export type StreamArgs = Infer<typeof vStreamArgs>;
471
472
 
472
473
  export const vStreamMessage = v.object({
473
474
  streamId: v.string(),
475
+ status: v.union(
476
+ v.literal("streaming"),
477
+ v.literal("finished"),
478
+ v.literal("aborted")
479
+ ),
474
480
  order: v.number(),
475
481
  stepOrder: v.number(),
476
482
  // metadata