@convex-dev/agent 0.6.4 → 0.7.0-alpha.0

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 (225) hide show
  1. package/MIGRATION.md +60 -116
  2. package/dist/client/messages.d.ts +1 -398
  3. package/dist/client/messages.d.ts.map +1 -1
  4. package/dist/client/messages.js +3 -47
  5. package/dist/client/messages.js.map +1 -1
  6. package/dist/client/types.d.ts +1 -455
  7. package/dist/client/types.d.ts.map +1 -1
  8. package/dist/component/_generated/component.d.ts +805 -45
  9. package/dist/component/_generated/component.d.ts.map +1 -1
  10. package/dist/component/messages.d.ts +1252 -201
  11. package/dist/component/messages.d.ts.map +1 -1
  12. package/dist/component/messages.js +49 -23
  13. package/dist/component/messages.js.map +1 -1
  14. package/dist/component/schema.d.ts +2593 -332
  15. package/dist/component/schema.d.ts.map +1 -1
  16. package/dist/component/streams.d.ts +7 -1
  17. package/dist/component/streams.d.ts.map +1 -1
  18. package/dist/component/streams.js +24 -30
  19. package/dist/component/streams.js.map +1 -1
  20. package/dist/component/threads.d.ts.map +1 -1
  21. package/dist/component/threads.js +1 -2
  22. package/dist/component/threads.js.map +1 -1
  23. package/dist/react/index.d.ts +5 -5
  24. package/dist/react/index.d.ts.map +1 -1
  25. package/dist/react/index.js +5 -5
  26. package/dist/react/index.js.map +1 -1
  27. package/dist/shared.d.ts +19 -7
  28. package/dist/shared.d.ts.map +1 -1
  29. package/dist/shared.js +4 -4
  30. package/dist/shared.js.map +1 -1
  31. package/dist/streaming/materializePersistedUIMessageChunks.d.ts +16 -0
  32. package/dist/streaming/materializePersistedUIMessageChunks.d.ts.map +1 -0
  33. package/dist/streaming/materializePersistedUIMessageChunks.js +302 -0
  34. package/dist/streaming/materializePersistedUIMessageChunks.js.map +1 -0
  35. package/dist/streaming/persistedUIMessageChunks.d.ts +106 -0
  36. package/dist/streaming/persistedUIMessageChunks.d.ts.map +1 -0
  37. package/dist/streaming/persistedUIMessageChunks.js +475 -0
  38. package/dist/streaming/persistedUIMessageChunks.js.map +1 -0
  39. package/dist/validators.d.ts +10026 -1055
  40. package/dist/validators.d.ts.map +1 -1
  41. package/dist/validators.js +71 -9
  42. package/dist/validators.js.map +1 -1
  43. package/dist/{UIMessages.d.ts → vercel/UIMessages.d.ts} +1 -1
  44. package/dist/vercel/UIMessages.d.ts.map +1 -0
  45. package/dist/{UIMessages.js → vercel/UIMessages.js} +63 -1
  46. package/dist/vercel/UIMessages.js.map +1 -0
  47. package/dist/{client → vercel/client}/createTool.d.ts +23 -16
  48. package/dist/vercel/client/createTool.d.ts.map +1 -0
  49. package/dist/{client → vercel/client}/createTool.js +2 -0
  50. package/dist/vercel/client/createTool.js.map +1 -0
  51. package/dist/{client → vercel/client}/definePlaygroundAPI.d.ts +533 -79
  52. package/dist/{client → vercel/client}/definePlaygroundAPI.d.ts.map +1 -1
  53. package/dist/{client → vercel/client}/definePlaygroundAPI.js +8 -7
  54. package/dist/vercel/client/definePlaygroundAPI.js.map +1 -0
  55. package/dist/{client → vercel/client}/files.d.ts +2 -2
  56. package/dist/vercel/client/files.d.ts.map +1 -0
  57. package/dist/vercel/client/files.js.map +1 -0
  58. package/dist/vercel/client/messages.d.ts +542 -0
  59. package/dist/vercel/client/messages.d.ts.map +1 -0
  60. package/dist/vercel/client/messages.js +66 -0
  61. package/dist/vercel/client/messages.js.map +1 -0
  62. package/dist/{client → vercel/client}/mockModel.d.ts +17 -17
  63. package/dist/vercel/client/mockModel.d.ts.map +1 -0
  64. package/dist/{client → vercel/client}/mockModel.js +12 -13
  65. package/dist/vercel/client/mockModel.js.map +1 -0
  66. package/dist/{client → vercel/client}/saveInputMessages.d.ts +2 -2
  67. package/dist/vercel/client/saveInputMessages.d.ts.map +1 -0
  68. package/dist/vercel/client/saveInputMessages.js.map +1 -0
  69. package/dist/{client → vercel/client}/search.d.ts +119 -22
  70. package/dist/vercel/client/search.d.ts.map +1 -0
  71. package/dist/{client → vercel/client}/search.js +2 -2
  72. package/dist/vercel/client/search.js.map +1 -0
  73. package/dist/{client → vercel/client}/start.d.ts +26 -11
  74. package/dist/vercel/client/start.d.ts.map +1 -0
  75. package/dist/{client → vercel/client}/start.js +64 -22
  76. package/dist/vercel/client/start.js.map +1 -0
  77. package/dist/{client → vercel/client}/streamText.d.ts +13 -12
  78. package/dist/vercel/client/streamText.d.ts.map +1 -0
  79. package/dist/{client → vercel/client}/streamText.js +43 -9
  80. package/dist/vercel/client/streamText.js.map +1 -0
  81. package/dist/{client → vercel/client}/streaming.d.ts +1410 -183
  82. package/dist/vercel/client/streaming.d.ts.map +1 -0
  83. package/dist/{client → vercel/client}/streaming.js +120 -52
  84. package/dist/vercel/client/streaming.js.map +1 -0
  85. package/dist/vercel/client/types.d.ts +456 -0
  86. package/dist/vercel/client/types.d.ts.map +1 -0
  87. package/dist/vercel/client/types.js +3 -0
  88. package/dist/vercel/client/types.js.map +1 -0
  89. package/dist/{client → vercel/client}/utils.d.ts +3 -2
  90. package/dist/vercel/client/utils.d.ts.map +1 -0
  91. package/dist/{client → vercel/client}/utils.js +2 -2
  92. package/dist/vercel/client/utils.js.map +1 -0
  93. package/dist/{deltas.d.ts → vercel/deltas.d.ts} +3 -4
  94. package/dist/vercel/deltas.d.ts.map +1 -0
  95. package/dist/{deltas.js → vercel/deltas.js} +195 -99
  96. package/dist/vercel/deltas.js.map +1 -0
  97. package/dist/{client → vercel}/index.d.ts +482 -106
  98. package/dist/vercel/index.d.ts.map +1 -0
  99. package/dist/{client → vercel}/index.js +64 -46
  100. package/dist/vercel/index.js.map +1 -0
  101. package/dist/{mapping.d.ts → vercel/mapping.d.ts} +5 -19
  102. package/dist/vercel/mapping.d.ts.map +1 -0
  103. package/dist/{mapping.js → vercel/mapping.js} +411 -114
  104. package/dist/vercel/mapping.js.map +1 -0
  105. package/dist/{react → vercel/react}/optimisticallySendMessage.d.ts +2 -2
  106. package/dist/vercel/react/optimisticallySendMessage.d.ts.map +1 -0
  107. package/dist/vercel/react/optimisticallySendMessage.js.map +1 -0
  108. package/dist/{react → vercel/react}/types.d.ts +1 -1
  109. package/dist/vercel/react/types.d.ts.map +1 -0
  110. package/dist/{react → vercel/react}/types.js.map +1 -1
  111. package/dist/{react → vercel/react}/useDeltaStreams.d.ts +1 -1
  112. package/dist/vercel/react/useDeltaStreams.d.ts.map +1 -0
  113. package/dist/{react → vercel/react}/useDeltaStreams.js +1 -1
  114. package/dist/vercel/react/useDeltaStreams.js.map +1 -0
  115. package/dist/{react → vercel/react}/useStreamingUIMessages.d.ts +1 -1
  116. package/dist/vercel/react/useStreamingUIMessages.d.ts.map +1 -0
  117. package/dist/{react → vercel/react}/useStreamingUIMessages.js +7 -3
  118. package/dist/vercel/react/useStreamingUIMessages.js.map +1 -0
  119. package/dist/{react → vercel/react}/useThreadMessages.d.ts +1 -1
  120. package/dist/vercel/react/useThreadMessages.d.ts.map +1 -0
  121. package/dist/{react → vercel/react}/useThreadMessages.js +1 -1
  122. package/dist/vercel/react/useThreadMessages.js.map +1 -0
  123. package/dist/{react → vercel/react}/useUIMessages.d.ts +1 -1
  124. package/dist/vercel/react/useUIMessages.d.ts.map +1 -0
  125. package/dist/{react → vercel/react}/useUIMessages.js +1 -1
  126. package/dist/vercel/react/useUIMessages.js.map +1 -0
  127. package/package.json +29 -22
  128. package/src/client/messages.ts +5 -107
  129. package/src/client/types.ts +0 -608
  130. package/src/component/_generated/component.ts +1089 -48
  131. package/src/component/messages.test.ts +273 -1
  132. package/src/component/messages.ts +62 -32
  133. package/src/component/streams.ts +47 -47
  134. package/src/component/threads.ts +5 -2
  135. package/src/eslintConfig.test.ts +44 -0
  136. package/src/react/index.ts +8 -5
  137. package/src/shared.ts +26 -36
  138. package/src/streaming/materializePersistedUIMessageChunks.test.ts +513 -0
  139. package/src/streaming/materializePersistedUIMessageChunks.ts +398 -0
  140. package/src/streaming/persistedUIMessageChunks.test.ts +205 -0
  141. package/src/streaming/persistedUIMessageChunks.ts +658 -0
  142. package/src/validators.test.ts +4 -1
  143. package/src/validators.ts +89 -8
  144. package/src/{UIMessages.combineUIMessages.test.ts → vercel/UIMessages.combineUIMessages.test.ts} +4 -1
  145. package/src/{UIMessages.ts → vercel/UIMessages.ts} +95 -3
  146. package/src/{client → vercel/client}/approval.test.ts +23 -13
  147. package/src/{client → vercel/client}/createTool.ts +62 -22
  148. package/src/{client → vercel/client}/definePlaygroundAPI.ts +15 -16
  149. package/src/{client → vercel/client}/files.ts +2 -2
  150. package/src/{client → vercel/client}/index.test.ts +99 -31
  151. package/src/vercel/client/messages.ts +182 -0
  152. package/src/{client → vercel/client}/mockModel.ts +38 -39
  153. package/src/{client → vercel/client}/saveInputMessages.test.ts +1 -1
  154. package/src/{client → vercel/client}/saveInputMessages.ts +3 -3
  155. package/src/{client → vercel/client}/search.test.ts +3 -3
  156. package/src/{client → vercel/client}/search.ts +4 -4
  157. package/src/{client → vercel/client}/setup.test.ts +6 -3
  158. package/src/vercel/client/start.test.ts +14 -0
  159. package/src/{client → vercel/client}/start.ts +147 -36
  160. package/src/vercel/client/streamText.test.ts +223 -0
  161. package/src/{client → vercel/client}/streamText.ts +102 -31
  162. package/src/{client → vercel/client}/streaming.integration.test.ts +6 -3
  163. package/src/{client → vercel/client}/streaming.test.ts +178 -4
  164. package/src/{client → vercel/client}/streaming.ts +114 -52
  165. package/src/vercel/client/types.test.ts +13 -0
  166. package/src/vercel/client/types.ts +679 -0
  167. package/src/{client → vercel/client}/utils.ts +13 -7
  168. package/src/{deltas.test.ts → vercel/deltas.test.ts} +384 -93
  169. package/src/{deltas.ts → vercel/deltas.ts} +218 -115
  170. package/src/{fromUIMessages.test.ts → vercel/fromUIMessages.test.ts} +17 -1
  171. package/src/{client → vercel}/index.ts +167 -84
  172. package/src/{mapping.test.ts → vercel/mapping.test.ts} +377 -92
  173. package/src/{mapping.ts → vercel/mapping.ts} +501 -137
  174. package/src/{react → vercel/react}/optimisticallySendMessage.ts +2 -2
  175. package/src/{react → vercel/react}/types.ts +1 -1
  176. package/src/{react → vercel/react}/useDeltaStreams.ts +6 -2
  177. package/src/{react → vercel/react}/useStreamingUIMessages.ts +7 -3
  178. package/src/{react → vercel/react}/useThreadMessages.ts +2 -2
  179. package/src/{react → vercel/react}/useUIMessages.ts +2 -2
  180. package/src/{toUIMessages.test.ts → vercel/toUIMessages.test.ts} +49 -2
  181. package/dist/UIMessages.d.ts.map +0 -1
  182. package/dist/UIMessages.js.map +0 -1
  183. package/dist/client/createTool.d.ts.map +0 -1
  184. package/dist/client/createTool.js.map +0 -1
  185. package/dist/client/definePlaygroundAPI.js.map +0 -1
  186. package/dist/client/files.d.ts.map +0 -1
  187. package/dist/client/files.js.map +0 -1
  188. package/dist/client/index.d.ts.map +0 -1
  189. package/dist/client/index.js.map +0 -1
  190. package/dist/client/mockModel.d.ts.map +0 -1
  191. package/dist/client/mockModel.js.map +0 -1
  192. package/dist/client/saveInputMessages.d.ts.map +0 -1
  193. package/dist/client/saveInputMessages.js.map +0 -1
  194. package/dist/client/search.d.ts.map +0 -1
  195. package/dist/client/search.js.map +0 -1
  196. package/dist/client/start.d.ts.map +0 -1
  197. package/dist/client/start.js.map +0 -1
  198. package/dist/client/streamText.d.ts.map +0 -1
  199. package/dist/client/streamText.js.map +0 -1
  200. package/dist/client/streaming.d.ts.map +0 -1
  201. package/dist/client/streaming.js.map +0 -1
  202. package/dist/client/utils.d.ts.map +0 -1
  203. package/dist/client/utils.js.map +0 -1
  204. package/dist/deltas.d.ts.map +0 -1
  205. package/dist/deltas.js.map +0 -1
  206. package/dist/mapping.d.ts.map +0 -1
  207. package/dist/mapping.js.map +0 -1
  208. package/dist/react/optimisticallySendMessage.d.ts.map +0 -1
  209. package/dist/react/optimisticallySendMessage.js.map +0 -1
  210. package/dist/react/types.d.ts.map +0 -1
  211. package/dist/react/useDeltaStreams.d.ts.map +0 -1
  212. package/dist/react/useDeltaStreams.js.map +0 -1
  213. package/dist/react/useStreamingUIMessages.d.ts.map +0 -1
  214. package/dist/react/useStreamingUIMessages.js.map +0 -1
  215. package/dist/react/useThreadMessages.d.ts.map +0 -1
  216. package/dist/react/useThreadMessages.js.map +0 -1
  217. package/dist/react/useUIMessages.d.ts.map +0 -1
  218. package/dist/react/useUIMessages.js.map +0 -1
  219. package/src/client/streamText.test.ts +0 -102
  220. /package/dist/{client → vercel/client}/files.js +0 -0
  221. /package/dist/{client → vercel/client}/saveInputMessages.js +0 -0
  222. /package/dist/{react → vercel/react}/optimisticallySendMessage.js +0 -0
  223. /package/dist/{react → vercel/react}/types.js +0 -0
  224. /package/src/{UIMessages.test.ts → vercel/UIMessages.test.ts} +0 -0
  225. /package/src/{react → vercel/react}/useUIMessages.test.ts +0 -0
@@ -1,4 +1,9 @@
1
- import type { ToolResultOutput } from "@ai-sdk/provider-utils";
1
+ import type { JSONObject } from "@ai-sdk/provider";
2
+ import type {
3
+ Context,
4
+ Experimental_SandboxSession,
5
+ ToolResultOutput,
6
+ } from "@ai-sdk/provider-utils";
2
7
  import type {
3
8
  FlexibleSchema,
4
9
  ModelMessage,
@@ -8,8 +13,8 @@ import type {
8
13
  } from "ai";
9
14
  import { tool } from "ai";
10
15
  import type { GenericActionCtx, GenericDataModel } from "convex/server";
11
- import type { ProviderOptions } from "../validators.js";
12
- import type { Agent } from "./index.js";
16
+ import type { ProviderOptions } from "../../validators.js";
17
+ import type { Agent } from "../index.js";
13
18
 
14
19
  const MIGRATION_URL = "node_modules/@convex-dev/agent/MIGRATION.md";
15
20
  const warnedDeprecations = new Set<string>();
@@ -28,12 +33,16 @@ export type ToolCtx<DataModel extends GenericDataModel = GenericDataModel> =
28
33
  messageId?: string;
29
34
  };
30
35
 
36
+ export type AgentToolExecutionOptions<TOOL_CONTEXT extends Context = Context> =
37
+ ToolExecutionOptions<TOOL_CONTEXT>;
38
+
31
39
  /**
32
40
  * Function that is called to determine if the tool needs approval before it can be executed.
33
41
  */
34
42
  export type ToolNeedsApprovalFunctionCtx<
35
43
  INPUT,
36
44
  Ctx extends ToolCtx = ToolCtx,
45
+ TOOL_CONTEXT extends Context = Context,
37
46
  > = (
38
47
  ctx: Ctx,
39
48
  input: INPUT,
@@ -52,7 +61,7 @@ export type ToolNeedsApprovalFunctionCtx<
52
61
  *
53
62
  * Experimental (can break in patch releases).
54
63
  */
55
- experimental_context?: unknown;
64
+ context: TOOL_CONTEXT;
56
65
  },
57
66
  ) => boolean | PromiseLike<boolean>;
58
67
 
@@ -60,10 +69,11 @@ export type ToolExecuteFunctionCtx<
60
69
  INPUT,
61
70
  OUTPUT,
62
71
  Ctx extends ToolCtx = ToolCtx,
72
+ TOOL_CONTEXT extends Context = Context,
63
73
  > = (
64
74
  ctx: Ctx,
65
75
  input: INPUT,
66
- options: ToolExecutionOptions,
76
+ options: AgentToolExecutionOptions<TOOL_CONTEXT>,
67
77
  ) => AsyncIterable<OUTPUT> | PromiseLike<OUTPUT>;
68
78
 
69
79
  type NeverOptional<N, T> = 0 extends 1 & N
@@ -83,6 +93,7 @@ export type ToolOutputPropertiesCtx<
83
93
  INPUT,
84
94
  OUTPUT,
85
95
  Ctx extends ToolCtx = ToolCtx,
96
+ TOOL_CONTEXT extends Context = Context,
86
97
  > = NeverOptional<
87
98
  OUTPUT,
88
99
  {
@@ -93,7 +104,7 @@ export type ToolOutputPropertiesCtx<
93
104
  * @param input - The input of the tool call.
94
105
  * @param options.abortSignal - A signal that can be used to abort the tool call.
95
106
  */
96
- execute?: ToolExecuteFunctionCtx<INPUT, OUTPUT, Ctx>;
107
+ execute?: ToolExecuteFunctionCtx<INPUT, OUTPUT, Ctx, TOOL_CONTEXT>;
97
108
  outputSchema?: FlexibleSchema<OUTPUT>;
98
109
  /**
99
110
  * @deprecated Removed in v0.6.0. Use `execute` instead.
@@ -133,14 +144,24 @@ export type ToolInputProperties<INPUT> = {
133
144
  *
134
145
  * @returns A tool to be used with the AI SDK.
135
146
  */
136
- export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
147
+ export function createTool<
148
+ INPUT,
149
+ OUTPUT,
150
+ Ctx extends ToolCtx = ToolCtx,
151
+ TOOL_CONTEXT extends Context = Context,
152
+ >(
137
153
  def: {
138
154
  /**
139
155
  * An optional description of what the tool does.
140
156
  * Will be used by the language model to decide whether to use the tool.
141
157
  * Not used for provider-defined tools.
142
158
  */
143
- description?: string;
159
+ description?:
160
+ | string
161
+ | ((options: {
162
+ context: NoInfer<TOOL_CONTEXT>;
163
+ experimental_sandbox?: Experimental_SandboxSession;
164
+ }) => string);
144
165
  /**
145
166
  * An optional title of the tool.
146
167
  */
@@ -151,6 +172,8 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
151
172
  * functionality that can be fully encapsulated in the provider.
152
173
  */
153
174
  providerOptions?: ProviderOptions;
175
+ metadata?: JSONObject;
176
+ contextSchema?: FlexibleSchema<TOOL_CONTEXT>;
154
177
  } & ToolInputProperties<INPUT> & {
155
178
  /**
156
179
  * An optional list of input examples that show the language
@@ -166,7 +189,8 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
166
189
  | boolean
167
190
  | ToolNeedsApprovalFunctionCtx<
168
191
  [INPUT] extends [never] ? unknown : INPUT,
169
- Ctx
192
+ Ctx,
193
+ TOOL_CONTEXT
170
194
  >;
171
195
  /**
172
196
  * Strict mode setting for the tool.
@@ -186,7 +210,7 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
186
210
  */
187
211
  onInputStart?: (
188
212
  ctx: Ctx,
189
- options: ToolExecutionOptions,
213
+ options: AgentToolExecutionOptions<TOOL_CONTEXT>,
190
214
  ) => void | PromiseLike<void>;
191
215
  /**
192
216
  * Optional function that is called when an argument streaming delta is available.
@@ -194,7 +218,9 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
194
218
  */
195
219
  onInputDelta?: (
196
220
  ctx: Ctx,
197
- options: { inputTextDelta: string } & ToolExecutionOptions,
221
+ options: {
222
+ inputTextDelta: string;
223
+ } & AgentToolExecutionOptions<TOOL_CONTEXT>,
198
224
  ) => void | PromiseLike<void>;
199
225
  /**
200
226
  * Optional function that is called when a tool call can be started,
@@ -204,9 +230,9 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
204
230
  ctx: Ctx,
205
231
  options: {
206
232
  input: [INPUT] extends [never] ? unknown : INPUT;
207
- } & ToolExecutionOptions,
233
+ } & AgentToolExecutionOptions<TOOL_CONTEXT>,
208
234
  ) => void | PromiseLike<void>;
209
- } & ToolOutputPropertiesCtx<INPUT, OUTPUT, Ctx> & {
235
+ } & ToolOutputPropertiesCtx<INPUT, OUTPUT, Ctx, TOOL_CONTEXT> & {
210
236
  /**
211
237
  * Optional conversion function that maps the tool result to an output that can be used by the language model.
212
238
  *
@@ -234,7 +260,7 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
234
260
  },
235
261
  ) => ToolResultOutput | PromiseLike<ToolResultOutput>;
236
262
  },
237
- ): Tool<INPUT, OUTPUT> {
263
+ ): Tool<INPUT, OUTPUT, TOOL_CONTEXT> {
238
264
  // Runtime backwards compat - types will show errors but runtime still works
239
265
  const inputSchema = def.inputSchema ?? (def as any).args;
240
266
  if (!inputSchema)
@@ -260,16 +286,18 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
260
286
  " handler function, define an outputSchema, or both",
261
287
  );
262
288
 
263
- const t = tool<INPUT, OUTPUT>({
289
+ const t = tool<INPUT, OUTPUT, TOOL_CONTEXT>({
264
290
  type: "function",
265
291
  __acceptsCtx: true,
266
292
  ctx: def.ctx,
267
293
  description: def.description,
268
294
  title: def.title,
269
295
  providerOptions: def.providerOptions,
296
+ metadata: def.metadata,
270
297
  inputSchema,
298
+ contextSchema: def.contextSchema,
271
299
  inputExamples: def.inputExamples,
272
- needsApproval(this: Tool<INPUT, OUTPUT>, input, options) {
300
+ needsApproval(this: Tool<INPUT, OUTPUT, TOOL_CONTEXT>, input, options) {
273
301
  const needsApproval = def.needsApproval;
274
302
  if (!needsApproval || typeof needsApproval === "boolean")
275
303
  return Boolean(needsApproval);
@@ -287,9 +315,9 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
287
315
  ...(executeHandler
288
316
  ? {
289
317
  execute(
290
- this: Tool<INPUT, OUTPUT>,
318
+ this: Tool<INPUT, OUTPUT, TOOL_CONTEXT>,
291
319
  input: INPUT,
292
- options: ToolExecutionOptions,
320
+ options: ToolExecutionOptions<TOOL_CONTEXT>,
293
321
  ) {
294
322
  if (!getCtx(this)) {
295
323
  throw new Error(
@@ -306,25 +334,37 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
306
334
  });
307
335
  if (def.onInputStart) {
308
336
  const origOnInputStart = def.onInputStart;
309
- t.onInputStart = function (this: Tool<INPUT, OUTPUT>, options) {
337
+ t.onInputStart = function (
338
+ this: Tool<INPUT, OUTPUT, TOOL_CONTEXT>,
339
+ options,
340
+ ) {
310
341
  return origOnInputStart.call(this, getCtx(this), options);
311
342
  };
312
343
  }
313
344
  if (def.onInputDelta) {
314
345
  const origOnInputDelta = def.onInputDelta;
315
- t.onInputDelta = function (this: Tool<INPUT, OUTPUT>, options) {
346
+ t.onInputDelta = function (
347
+ this: Tool<INPUT, OUTPUT, TOOL_CONTEXT>,
348
+ options,
349
+ ) {
316
350
  return origOnInputDelta.call(this, getCtx(this), options);
317
351
  };
318
352
  }
319
353
  if (def.onInputAvailable) {
320
354
  const origOnInputAvailable = def.onInputAvailable;
321
- t.onInputAvailable = function (this: Tool<INPUT, OUTPUT>, options) {
355
+ t.onInputAvailable = function (
356
+ this: Tool<INPUT, OUTPUT, TOOL_CONTEXT>,
357
+ options,
358
+ ) {
322
359
  return origOnInputAvailable.call(this, getCtx(this), options);
323
360
  };
324
361
  }
325
362
  if (def.toModelOutput) {
326
363
  const origToModelOutput = def.toModelOutput;
327
- t.toModelOutput = function (this: Tool<INPUT, OUTPUT>, options) {
364
+ t.toModelOutput = function (
365
+ this: Tool<INPUT, OUTPUT, TOOL_CONTEXT>,
366
+ options,
367
+ ) {
328
368
  return origToModelOutput.call(this, getCtx(this), options);
329
369
  };
330
370
  }
@@ -11,26 +11,27 @@ import {
11
11
  } from "convex/server";
12
12
  import { v } from "convex/values";
13
13
  import {
14
- createThread as createThread_,
15
- listMessages as listMessages_,
16
- toModelMessage,
17
14
  vContextOptions,
18
15
  vMessage,
19
16
  vMessageDoc,
20
17
  vPaginationResult,
21
18
  vStorageOptions,
22
19
  vThreadDoc,
23
- type Agent,
24
- type AgentComponent,
25
20
  vStreamArgs,
26
- syncStreams,
27
- vStreamMessagesReturnValue,
28
- isTool,
29
- extractText,
30
21
  type MessageDoc,
31
- } from "./index.js";
32
- import { serializeNewMessagesInStep } from "../mapping.js";
33
- import { getModelName, getProviderName } from "../shared.js";
22
+ } from "../../validators.js";
23
+ import { createThread as createThread_ } from "../../client/threads.js";
24
+ import {
25
+ extractText,
26
+ getModelName,
27
+ getProviderName,
28
+ isTool,
29
+ } from "../../shared.js";
30
+ import { serializeResponseMessages, toModelMessage } from "../mapping.js";
31
+ import type { Agent } from "../index.js";
32
+ import { listMessages as listMessages_ } from "./messages.js";
33
+ import { syncStreams, vStreamMessagesReturnValue } from "./streaming.js";
34
+ import type { AgentComponent } from "./types.js";
34
35
 
35
36
  export type PlaygroundAPI = ApiFromModules<{
36
37
  playground: ReturnType<typeof definePlaygroundAPI>;
@@ -281,9 +282,8 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
281
282
  { contextOptions, storageOptions, saveStreamDeltas: true },
282
283
  );
283
284
  const outputMessages: MessageDoc[][] = [];
284
- let previousResponseMessageCount = 0;
285
285
  for (const step of await steps) {
286
- const { messages } = await serializeNewMessagesInStep(
286
+ const { messages } = await serializeResponseMessages(
287
287
  ctx,
288
288
  component,
289
289
  step,
@@ -291,9 +291,8 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
291
291
  model: getModelName(agent.options.languageModel),
292
292
  provider: getProviderName(agent.options.languageModel),
293
293
  },
294
- previousResponseMessageCount,
294
+ step.response.messages,
295
295
  );
296
- previousResponseMessageCount = step.response.messages.length;
297
296
  outputMessages.push(
298
297
  messages.map((messageWithMetadata, i) => {
299
298
  return {
@@ -5,14 +5,14 @@ import type {
5
5
  ModelMessage,
6
6
  UserContent,
7
7
  } from "ai";
8
- import type { Id } from "../component/_generated/dataModel.js";
8
+ import type { Id } from "../../component/_generated/dataModel.js";
9
9
  import type {
10
10
  ActionCtx,
11
11
  AgentComponent,
12
12
  MutationCtx,
13
13
  QueryCtx,
14
14
  } from "./types.js";
15
- import type { Message } from "../validators.js";
15
+ import type { Message } from "../../validators.js";
16
16
  import { assert } from "convex-helpers";
17
17
  import type { StorageReader } from "convex/server";
18
18
 
@@ -5,7 +5,7 @@ import {
5
5
  createTool,
6
6
  filterOutOrphanedToolMessages,
7
7
  type MessageDoc,
8
- } from "./index.js";
8
+ } from "../index.js";
9
9
  import type { DataModelFromSchemaDefinition } from "convex/server";
10
10
  import {
11
11
  anyApi,
@@ -24,7 +24,7 @@ import { defineSchema } from "convex/server";
24
24
  import { stepCountIs } from "ai";
25
25
  import { components, initConvexTest } from "./setup.test.js";
26
26
  import { z } from "zod/v4";
27
- import { mockModel } from "./mockModel.js";
27
+ import { MockLanguageModel, mockModel } from "./mockModel.js";
28
28
 
29
29
  const schema = defineSchema({});
30
30
  type DataModel = DataModelFromSchemaDefinition<typeof schema>;
@@ -35,12 +35,50 @@ const action = actionGeneric as ActionBuilder<DataModel, "public">;
35
35
 
36
36
  const TEST_TEXT = JSON.stringify({ hello: "world" });
37
37
 
38
+ const agentModel = new MockLanguageModel({
39
+ content: [{ type: "text", text: TEST_TEXT }],
40
+ });
38
41
  const agent = new Agent(components.agent, {
39
42
  name: "test",
40
43
  instructions: "You are a test agent",
44
+ languageModel: agentModel,
45
+ });
46
+
47
+ let capturedRawBodies: { request?: unknown; response?: unknown } | undefined;
48
+ const rawBodyAgent = new Agent(components.agent, {
49
+ name: "raw-body-test",
41
50
  languageModel: mockModel({
42
- content: [{ type: "text", text: TEST_TEXT }],
51
+ doGenerate: async () => ({
52
+ content: [{ type: "text", text: "raw" }],
53
+ finishReason: { unified: "stop", raw: undefined },
54
+ usage: {
55
+ inputTokens: { total: 1, noCache: 1, cacheRead: 0, cacheWrite: 0 },
56
+ outputTokens: { total: 1, text: 1, reasoning: 0 },
57
+ },
58
+ request: { body: { prompt: "request-body" } },
59
+ response: { body: { text: "response-body" }, headers: {} },
60
+ warnings: [],
61
+ }),
43
62
  }),
63
+ rawRequestResponseHandler: async (_ctx, event) => {
64
+ capturedRawBodies = {
65
+ request: event.request.body,
66
+ response: event.response.body,
67
+ };
68
+ },
69
+ });
70
+
71
+ export const captureRawBodies = action({
72
+ args: {},
73
+ handler: async (ctx) => {
74
+ capturedRawBodies = undefined;
75
+ await rawBodyAgent.generateText(
76
+ ctx,
77
+ { userId: "raw-body-user" },
78
+ { prompt: "raw" },
79
+ );
80
+ return capturedRawBodies;
81
+ },
44
82
  });
45
83
 
46
84
  export const testQuery = query({
@@ -90,7 +128,7 @@ const saveStepAgent = new Agent(components.agent, {
90
128
  });
91
129
 
92
130
  export const replayStepsViaSaveStep = action({
93
- args: { withWatermark: v.boolean() },
131
+ args: { withPreviousStep: v.boolean() },
94
132
  handler: async (ctx, args) => {
95
133
  const { thread } = await saveStepAgent.createThread(ctx, {
96
134
  userId: "ss-gen",
@@ -101,18 +139,21 @@ export const replayStepsViaSaveStep = action({
101
139
  const { threadId } = await saveStepAgent.createThread(ctx, {
102
140
  userId: "ss-replay",
103
141
  });
104
- const { messageId: promptMessageId } = await saveStepAgent.saveMessage(ctx, {
105
- threadId,
106
- message: { role: "user", content: "echo hi" },
107
- skipEmbeddings: true,
108
- });
142
+ const { messageId: promptMessageId } = await saveStepAgent.saveMessage(
143
+ ctx,
144
+ {
145
+ threadId,
146
+ message: { role: "user", content: "echo hi" },
147
+ skipEmbeddings: true,
148
+ },
149
+ );
109
150
  let previousStep: (typeof steps)[number] | undefined;
110
151
  for (const step of steps) {
111
152
  await saveStepAgent.saveStep(ctx, {
112
153
  threadId,
113
154
  promptMessageId,
114
155
  step,
115
- previousStep: args.withWatermark ? previousStep : undefined,
156
+ previousStep: args.withPreviousStep ? previousStep : undefined,
116
157
  });
117
158
  previousStep = step;
118
159
  }
@@ -231,6 +272,7 @@ const testApi: ApiFromModules<{
231
272
  generateTextAction: typeof generateTextAction;
232
273
  generateObjectAction: typeof generateObjectAction;
233
274
  saveMessageMutation: typeof saveMessageMutation;
275
+ captureRawBodies: typeof captureRawBodies;
234
276
  replayStepsViaSaveStep: typeof replayStepsViaSaveStep;
235
277
  };
236
278
  }>["fns"] = anyApi["index.test"] as any;
@@ -247,27 +289,26 @@ describe("Agent thick client", () => {
247
289
  expect(result).toBeDefined();
248
290
  expect(result).toMatch(TEST_TEXT);
249
291
  });
250
- test("saveStep with previousStep saves each step's new messages exactly once", async () => {
251
- const t = initConvexTest(schema);
252
- const res = await t.action(testApi.replayStepsViaSaveStep, {
253
- withWatermark: true,
254
- });
255
- expect(res.stepCount).toBe(2);
256
- const toolCalls = res.contentTypes.filter((t) => t === "tool-call").length;
257
- const toolResults = res.contentTypes.filter(
258
- (t) => t === "tool-result",
259
- ).length;
260
- expect(toolCalls).toBe(1);
261
- expect(toolResults).toBe(1);
262
- });
263
- test("saveStep without previousStep duplicates prior messages", async () => {
264
- const t = initConvexTest(schema);
265
- const res = await t.action(testApi.replayStepsViaSaveStep, {
266
- withWatermark: false,
267
- });
268
- const toolCalls = res.contentTypes.filter((t) => t === "tool-call").length;
269
- expect(toolCalls).toBeGreaterThan(1);
270
- });
292
+ test.each([true, false])(
293
+ "saveStep persists each SDK 7 step once (previousStep: %s)",
294
+ async (withPreviousStep) => {
295
+ const t = initConvexTest(schema);
296
+ const res = await t.action(testApi.replayStepsViaSaveStep, {
297
+ withPreviousStep,
298
+ });
299
+ expect(res.stepCount).toBe(2);
300
+ const toolCalls = res.contentTypes.filter(
301
+ (t) => t === "tool-call",
302
+ ).length;
303
+ const toolResults = res.contentTypes.filter(
304
+ (t) => t === "tool-result",
305
+ ).length;
306
+ expect({ toolCalls, toolResults }).toEqual({
307
+ toolCalls: 1,
308
+ toolResults: 1,
309
+ });
310
+ },
311
+ );
271
312
  });
272
313
 
273
314
  describe("filterOutOrphanedToolMessages", () => {
@@ -335,6 +376,14 @@ describe("filterOutOrphanedToolMessages", () => {
335
376
  });
336
377
 
337
378
  describe("Agent option variations and normal behavior", () => {
379
+ test("raw handler opts into retained SDK 7 request and response bodies", async () => {
380
+ const t = initConvexTest(schema);
381
+ await expect(t.action(testApi.captureRawBodies, {})).resolves.toEqual({
382
+ request: { prompt: "request-body" },
383
+ response: { text: "response-body" },
384
+ });
385
+ });
386
+
338
387
  test("Agent can be constructed with minimal options", () => {
339
388
  const a = new Agent(components.agent, {
340
389
  name: "minimal",
@@ -469,6 +518,25 @@ describe("Agent-generated mutations/actions/queries", () => {
469
518
  expect(objResult.object).toBeDefined();
470
519
  });
471
520
 
521
+ test("asTextAction maps its system wire field to AI SDK instructions", async () => {
522
+ const t = initConvexTest(schema);
523
+ const before = agentModel.doGenerateCalls.length;
524
+
525
+ await t.action(testApi.generateTextAction, {
526
+ userId: "8",
527
+ system: "Action-scoped instructions",
528
+ prompt: "Say hi",
529
+ });
530
+
531
+ const call = agentModel.doGenerateCalls.at(-1);
532
+ expect(agentModel.doGenerateCalls).toHaveLength(before + 1);
533
+ expect(call?.prompt[0]).toEqual({
534
+ role: "system",
535
+ content: "Action-scoped instructions",
536
+ });
537
+ expect(call).not.toHaveProperty("system");
538
+ });
539
+
472
540
  test("asSaveMessagesMutation works via t.mutation", async () => {
473
541
  const t = initConvexTest(schema);
474
542
  const threadId = await t.run(async (ctx) =>