@mastra/libsql 0.0.0-workflow-timeout-issue-20260220185702 → 0.0.0-workflow-graph-crash-20260527191009

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 (73) hide show
  1. package/CHANGELOG.md +1085 -3
  2. package/LICENSE.md +15 -0
  3. package/dist/docs/SKILL.md +18 -19
  4. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  5. package/dist/docs/references/docs-agent-builder-deploying.md +121 -0
  6. package/dist/docs/references/docs-agent-builder-overview.md +97 -0
  7. package/dist/docs/references/docs-agents-agent-approval.md +336 -202
  8. package/dist/docs/references/docs-agents-networks.md +107 -215
  9. package/dist/docs/references/docs-memory-memory-processors.md +86 -88
  10. package/dist/docs/references/docs-memory-message-history.md +71 -63
  11. package/dist/docs/references/docs-memory-multi-user-threads.md +206 -0
  12. package/dist/docs/references/docs-memory-overview.md +221 -24
  13. package/dist/docs/references/docs-memory-semantic-recall.md +162 -75
  14. package/dist/docs/references/docs-memory-storage.md +72 -73
  15. package/dist/docs/references/docs-memory-working-memory.md +62 -62
  16. package/dist/docs/references/docs-rag-retrieval.md +132 -135
  17. package/dist/docs/references/docs-workflows-snapshots.md +31 -31
  18. package/dist/docs/references/guides-agent-frameworks-ai-sdk.md +36 -36
  19. package/dist/docs/references/reference-core-getMemory.md +15 -16
  20. package/dist/docs/references/reference-core-listMemory.md +17 -18
  21. package/dist/docs/references/reference-core-mastra-class.md +37 -27
  22. package/dist/docs/references/reference-memory-memory-class.md +41 -43
  23. package/dist/docs/references/reference-storage-composite.md +65 -55
  24. package/dist/docs/references/reference-storage-dynamodb.md +36 -36
  25. package/dist/docs/references/reference-storage-libsql.md +27 -27
  26. package/dist/docs/references/reference-vectors-libsql.md +72 -72
  27. package/dist/index.cjs +7896 -3257
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.js +7888 -3259
  30. package/dist/index.js.map +1 -1
  31. package/dist/storage/db/index.d.ts +14 -1
  32. package/dist/storage/db/index.d.ts.map +1 -1
  33. package/dist/storage/db/utils.d.ts +16 -0
  34. package/dist/storage/db/utils.d.ts.map +1 -1
  35. package/dist/storage/domains/agents/index.d.ts.map +1 -1
  36. package/dist/storage/domains/background-tasks/index.d.ts +18 -0
  37. package/dist/storage/domains/background-tasks/index.d.ts.map +1 -0
  38. package/dist/storage/domains/blobs/index.d.ts +17 -0
  39. package/dist/storage/domains/blobs/index.d.ts.map +1 -0
  40. package/dist/storage/domains/channels/index.d.ts +20 -0
  41. package/dist/storage/domains/channels/index.d.ts.map +1 -0
  42. package/dist/storage/domains/datasets/index.d.ts +43 -0
  43. package/dist/storage/domains/datasets/index.d.ts.map +1 -0
  44. package/dist/storage/domains/experiments/index.d.ts +31 -0
  45. package/dist/storage/domains/experiments/index.d.ts.map +1 -0
  46. package/dist/storage/domains/favorites/index.d.ts +17 -0
  47. package/dist/storage/domains/favorites/index.d.ts.map +1 -0
  48. package/dist/storage/domains/mcp-clients/index.d.ts.map +1 -1
  49. package/dist/storage/domains/mcp-servers/index.d.ts +26 -0
  50. package/dist/storage/domains/mcp-servers/index.d.ts.map +1 -0
  51. package/dist/storage/domains/memory/index.d.ts +7 -3
  52. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  53. package/dist/storage/domains/observability/index.d.ts +2 -1
  54. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  55. package/dist/storage/domains/prompt-blocks/index.d.ts.map +1 -1
  56. package/dist/storage/domains/schedules/index.d.ts +19 -0
  57. package/dist/storage/domains/schedules/index.d.ts.map +1 -0
  58. package/dist/storage/domains/scorer-definitions/index.d.ts.map +1 -1
  59. package/dist/storage/domains/skills/index.d.ts +26 -0
  60. package/dist/storage/domains/skills/index.d.ts.map +1 -0
  61. package/dist/storage/domains/workflows/index.d.ts +1 -0
  62. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  63. package/dist/storage/domains/workspaces/index.d.ts +26 -0
  64. package/dist/storage/domains/workspaces/index.d.ts.map +1 -0
  65. package/dist/storage/index.d.ts +38 -1
  66. package/dist/storage/index.d.ts.map +1 -1
  67. package/dist/vector/index.d.ts +14 -1
  68. package/dist/vector/index.d.ts.map +1 -1
  69. package/package.json +12 -12
  70. package/dist/docs/references/docs-agents-agent-memory.md +0 -212
  71. package/dist/docs/references/docs-agents-network-approval.md +0 -275
  72. package/dist/docs/references/docs-observability-overview.md +0 -70
  73. package/dist/docs/references/docs-observability-tracing-exporters-default.md +0 -211
@@ -1,119 +1,127 @@
1
- # Agent Approval
1
+ # Agent approval
2
2
 
3
- Agents sometimes require the same [human-in-the-loop](https://mastra.ai/docs/workflows/human-in-the-loop) oversight used in workflows when calling tools that handle sensitive operations, like deleting resources or performing running long processes. With agent approval you can suspend a tool call and provide feedback to the user, or approve or decline a tool call based on targeted application conditions.
3
+ Agents sometimes require the same [human-in-the-loop](https://mastra.ai/docs/workflows/human-in-the-loop) oversight used in workflows when calling tools that handle sensitive operations, like deleting resources or running long processes. With agent approval you can suspend a tool call before it executes so a human can approve or decline it, or let tools suspend themselves to request additional context from the user.
4
4
 
5
- ## Tool call approval
5
+ ## When to use agent approval
6
6
 
7
- Tool call approval can be enabled at the agent level and apply to every tool the agent uses, or at the tool level providing more granular control over individual tool calls.
7
+ - **Destructive or irreversible actions** such as deleting records, sending emails, or processing payments.
8
+ - **Cost-heavy operations** like calling expensive third-party APIs where you want to verify arguments first.
9
+ - **Conditional confirmation** where a tool starts executing and then discovers it needs the user to confirm or supply extra data before finishing.
8
10
 
9
- ### Storage
11
+ ## Quickstart
10
12
 
11
- Agent approval uses a snapshot to capture the state of the request. Ensure you've enabled a storage provider in your main Mastra instance. If storage isn't enabled you'll see an error relating to snapshot not found.
13
+ Mark a tool with `requireApproval: true`, then check for the `tool-call-approval` chunk in the stream to approve or decline:
12
14
 
13
15
  ```typescript
14
- import { Mastra } from "@mastra/core/mastra";
15
- import { LibSQLStore } from "@mastra/libsql";
16
-
17
- export const mastra = new Mastra({
18
- storage: new LibSQLStore({
19
- id: "mastra-storage",
20
- url: ":memory:"
21
- })
22
- });
23
- ```
16
+ import { Agent } from '@mastra/core/agent'
17
+ import { createTool } from '@mastra/core/tools'
18
+ import { z } from 'zod'
19
+
20
+ const deleteTool = createTool({
21
+ id: 'delete-record',
22
+ description: 'Delete a record by ID',
23
+ inputSchema: z.object({ id: z.string() }),
24
+ outputSchema: z.object({ deleted: z.boolean() }),
25
+ requireApproval: true,
26
+ execute: async ({ id }) => {
27
+ await db.delete(id)
28
+ return { deleted: true }
29
+ },
30
+ })
24
31
 
25
- ## Agent-level approval
32
+ const agent = new Agent({
33
+ id: 'my-agent',
34
+ name: 'My Agent',
35
+ model: 'openai/gpt-5-mini',
36
+ tools: { deleteTool },
37
+ })
26
38
 
27
- When calling an agent using `.stream()` set `requireToolApproval` to `true` which will prevent the agent from calling any of the tools defined in its configuration.
39
+ const stream = await agent.stream('Delete record abc-123')
28
40
 
29
- ```typescript
30
- const stream = await agent.stream("What's the weather in London?", {
31
- requireToolApproval: true
32
- });
41
+ for await (const chunk of stream.fullStream) {
42
+ if (chunk.type === 'tool-call-approval') {
43
+ const approved = await agent.approveToolCall({ runId: stream.runId })
44
+ for await (const c of approved.textStream) process.stdout.write(c)
45
+ }
46
+ }
33
47
  ```
34
48
 
35
- ### Approving tool calls
49
+ > **Note:** Agent approval uses snapshots to capture request state. Configure a [storage provider](https://mastra.ai/docs/memory/storage) on your Mastra instance or you'll see a "snapshot not found" error.
36
50
 
37
- To approve a tool call, access `approveToolCall` from the `agent`, passing in the `runId` of the stream. This will let the agent know its now OK to call its tools.
51
+ ## How approval works
38
52
 
39
- ```typescript
40
- const handleApproval = async () => {
41
- const approvedStream = await agent.approveToolCall({ runId: stream.runId });
53
+ Mastra offers two distinct mechanisms for pausing tool calls: **pre-execution approval** and **runtime suspension**.
42
54
 
43
- for await (const chunk of approvedStream.textStream) {
44
- process.stdout.write(chunk);
45
- }
46
- process.stdout.write("\n");
47
- };
48
- ```
55
+ ### Pre-execution approval
56
+
57
+ Pre-execution approval pauses a tool call _before_ its `execute` function runs. The LLM still decides which tool to call and provides arguments, but `execute` doesn't run until you explicitly approve.
58
+
59
+ Two flags control this, combined with OR logic. If _either_ is `true`, the call pauses:
49
60
 
50
- ### Declining tool calls
61
+ | Flag | Where to set it | Scope |
62
+ | --------------------------- | --------------------------------- | ------------------------------------------- |
63
+ | `requireToolApproval: true` | `stream()` / `generate()` options | Pauses **every** tool call for that request |
64
+ | `requireApproval: true` | `createTool()` definition | Pauses calls to **that specific tool** |
51
65
 
52
- To decline a tool call, access the `declineToolCall` from the `agent`. You will see the streamed response from the agent, but it won't call its tools.
66
+ The stream emits a `tool-call-approval` chunk containing the `toolCallId`, `toolName`, and `args`. Call `approveToolCall()` or `declineToolCall()` with the stream's `runId` to continue:
53
67
 
54
68
  ```typescript
55
- const handleDecline = async () => {
56
- const declinedStream = await agent.declineToolCall({ runId: stream.runId });
69
+ const stream = await agent.stream("What's the weather in London?", {
70
+ requireToolApproval: true,
71
+ })
72
+
73
+ for await (const chunk of stream.fullStream) {
74
+ if (chunk.type === 'tool-call-approval') {
75
+ console.log('Tool:', chunk.payload.toolName)
76
+ console.log('Args:', chunk.payload.args)
77
+
78
+ // Approve
79
+ const approved = await agent.approveToolCall({ runId: stream.runId })
80
+ for await (const c of approved.textStream) process.stdout.write(c)
57
81
 
58
- for await (const chunk of declinedStream.textStream) {
59
- process.stdout.write(chunk);
82
+ // Or decline
83
+ const declined = await agent.declineToolCall({ runId: stream.runId })
84
+ for await (const c of declined.textStream) process.stdout.write(c)
60
85
  }
61
- process.stdout.write("\n");
62
- };
86
+ }
63
87
  ```
64
88
 
65
- ## Tool approval with generate()
66
-
67
- Tool approval also works with the `generate()` method for non-streaming use cases. When using `generate()` with `requireToolApproval: true`, the method returns immediately when a tool requires approval instead of executing it.
89
+ ### Runtime suspension with `suspend()`
68
90
 
69
- ### How it works
91
+ A tool can also pause _during_ its `execute` function by calling `suspend()`. This is useful when the tool starts running and then discovers it needs additional user input or confirmation before it can finish.
70
92
 
71
- When a tool requires approval during a `generate()` call, the response includes:
93
+ The stream emits a `tool-call-suspended` chunk with a custom payload defined by the tool's `suspendSchema`. You resume by calling `resumeStream()` with data matching the tool's `resumeSchema`.
72
94
 
73
- - `finishReason: 'suspended'` - indicates the agent is waiting for approval
74
- - `suspendPayload` - contains tool call details (`toolCallId`, `toolName`, `args`)
75
- - `runId` - needed to approve or decline the tool call
95
+ > **Note:** `suspend()` does not throw — return immediately after calling it (e.g. `return await suspend({ ... })`). Code after `await suspend(...)` still runs before the tool pauses.
76
96
 
77
- ### Approving tool calls
97
+ ## Tool approval with `generate()`
78
98
 
79
- To approve a tool call with `generate()`, use the `approveToolCallGenerate` method:
99
+ Tool approval also works with `generate()` for non-streaming use cases. When a tool requires approval, `generate()` returns immediately with `finishReason: 'suspended'`, a `suspendPayload` containing the tool call details (`toolCallId`, `toolName`, `args`), and a `runId`:
80
100
 
81
101
  ```typescript
82
- const output = await agent.generate("Find user John", {
102
+ const output = await agent.generate('Find user John', {
83
103
  requireToolApproval: true,
84
- });
104
+ })
85
105
 
86
- if (output.finishReason === "suspended") {
87
- console.log("Tool requires approval:", output.suspendPayload.toolName);
88
- console.log("Arguments:", output.suspendPayload.args);
106
+ if (output.finishReason === 'suspended') {
107
+ console.log('Tool requires approval:', output.suspendPayload.toolName)
89
108
 
90
- // Approve the tool call and get the final result
109
+ // Approve
91
110
  const result = await agent.approveToolCallGenerate({
92
111
  runId: output.runId,
93
112
  toolCallId: output.suspendPayload.toolCallId,
94
- });
95
-
96
- console.log("Final result:", result.text);
97
- }
98
- ```
99
-
100
- ### Declining tool calls
101
-
102
- To decline a tool call, use the `declineToolCallGenerate` method:
113
+ })
114
+ console.log('Final result:', result.text)
103
115
 
104
- ```typescript
105
- if (output.finishReason === "suspended") {
116
+ // Or decline
106
117
  const result = await agent.declineToolCallGenerate({
107
118
  runId: output.runId,
108
119
  toolCallId: output.suspendPayload.toolCallId,
109
- });
110
-
111
- // Agent will respond acknowledging the declined tool
112
- console.log(result.text);
120
+ })
113
121
  }
114
122
  ```
115
123
 
116
- ### Stream vs Generate comparison
124
+ ### Stream vs generate comparison
117
125
 
118
126
  | Aspect | `stream()` | `generate()` |
119
127
  | ------------------ | ---------------------------- | ------------------------------------------------ |
@@ -123,224 +131,191 @@ if (output.finishReason === "suspended") {
123
131
  | Decline method | `declineToolCall({ runId })` | `declineToolCallGenerate({ runId, toolCallId })` |
124
132
  | Result | Stream to iterate | Full output object |
125
133
 
134
+ > **Note:** `toolCallId` is optional on all four methods. Pass it when multiple tool calls may be pending at the same time (common in supervisor agents). When omitted, the agent resumes the most recent suspended tool call.
135
+
126
136
  ## Tool-level approval
127
137
 
128
- There are two types of tool call approval. The first uses `requireApproval`, which is a property on the tool definition, while `requireToolApproval` is a parameter passed to `agent.stream()`. The second uses `suspend` and lets the agent provide context or confirmation prompts so the user can decide whether the tool call should continue.
138
+ Instead of pausing every tool call at the agent level, you can mark individual tools as requiring approval. This gives you granular control: only specific tools pause, while others execute immediately.
129
139
 
130
- ### Tool approval using `requireToolApproval`
140
+ ### Approval using `requireApproval`
131
141
 
132
- In this approach, `requireApproval` is configured on the tool definition (shown below) rather than on the agent.
142
+ Set `requireApproval: true` on a tool definition. The tool pauses before execution regardless of whether `requireToolApproval` is set on the agent:
133
143
 
134
144
  ```typescript
135
145
  export const testTool = createTool({
136
- id: "test-tool",
137
- description: "Fetches weather for a location",
146
+ id: 'test-tool',
147
+ description: 'Fetches weather for a location',
138
148
  inputSchema: z.object({
139
- location: z.string()
149
+ location: z.string(),
140
150
  }),
141
151
  outputSchema: z.object({
142
- weather: z.string()
152
+ weather: z.string(),
143
153
  }),
144
154
  resumeSchema: z.object({
145
- approved: z.boolean()
155
+ approved: z.boolean(),
146
156
  }),
147
- execute: async (inputData) => {
148
- const response = await fetch(`https://wttr.in/${inputData.location}?format=3`);
149
- const weather = await response.text();
157
+ execute: async inputData => {
158
+ const response = await fetch(`https://wttr.in/${inputData.location}?format=3`)
159
+ const weather = await response.text()
150
160
 
151
- return { weather };
161
+ return { weather }
152
162
  },
153
- requireApproval: true
154
- });
163
+ requireApproval: true,
164
+ })
155
165
  ```
156
166
 
157
- When `requireApproval` is true for a tool, the stream will include chunks of type `tool-call-approval` to indicate that the call is paused. To continue the call, invoke `resumeStream` with the required `resumeSchema` and the `runId`.
167
+ When `requireApproval` is `true`, the stream emits `tool-call-approval` chunks the same way agent-level approval does. Use `approveToolCall()` or `declineToolCall()` to continue:
158
168
 
159
169
  ```typescript
160
- const stream = await agent.stream("What's the weather in London?");
170
+ const stream = await agent.stream("What's the weather in London?")
161
171
 
162
172
  for await (const chunk of stream.fullStream) {
163
- if (chunk.type === "tool-call-approval") {
164
- console.log("Approval required.");
173
+ if (chunk.type === 'tool-call-approval') {
174
+ console.log('Approval required for:', chunk.payload.toolName)
165
175
  }
166
176
  }
167
177
 
168
- const handleResume = async () => {
169
- const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });
178
+ const handleApproval = async () => {
179
+ const approvedStream = await agent.approveToolCall({ runId: stream.runId })
170
180
 
171
- for await (const chunk of resumedStream.textStream) {
172
- process.stdout.write(chunk);
181
+ for await (const chunk of approvedStream.textStream) {
182
+ process.stdout.write(chunk)
173
183
  }
174
- process.stdout.write("\n");
175
- };
184
+ process.stdout.write('\n')
185
+ }
176
186
  ```
177
187
 
178
- ### Tool approval using `suspend`
188
+ ### Approval using `suspend()`
179
189
 
180
- With this approach, neither the agent nor the tool uses `requireApproval`. Instead, the tool implementation calls `suspend` to pause execution and return context or confirmation prompts to the user.
190
+ With this approach, neither the agent nor the tool uses `requireApproval`. Instead, the tool's `execute` function calls `suspend()` to pause at a specific point and return context or confirmation prompts to the user. This is useful when approval depends on runtime conditions rather than being unconditional.
181
191
 
182
192
  ```typescript
183
193
  export const testToolB = createTool({
184
- id: "test-tool-b",
185
- description: "Fetches weather for a location",
194
+ id: 'test-tool-b',
195
+ description: 'Fetches weather for a location',
186
196
  inputSchema: z.object({
187
- location: z.string()
197
+ location: z.string(),
188
198
  }),
189
199
  outputSchema: z.object({
190
- weather: z.string()
200
+ weather: z.string(),
191
201
  }),
192
202
  resumeSchema: z.object({
193
- approved: z.boolean()
203
+ approved: z.boolean(),
194
204
  }),
195
205
  suspendSchema: z.object({
196
- reason: z.string()
206
+ reason: z.string(),
197
207
  }),
198
208
  execute: async (inputData, context) => {
199
- const { resumeData: { approved } = {}, suspend } = context?.agent ?? {};
209
+ const { resumeData: { approved } = {}, suspend } = context?.agent ?? {}
200
210
 
201
211
  if (!approved) {
202
- return suspend?.({ reason: "Approval required." });
212
+ return suspend?.({ reason: 'Approval required.' })
203
213
  }
204
214
 
205
- const response = await fetch(`https://wttr.in/${inputData.location}?format=3`);
206
- const weather = await response.text();
215
+ const response = await fetch(`https://wttr.in/${inputData.location}?format=3`)
216
+ const weather = await response.text()
207
217
 
208
- return { weather };
209
- }
210
- });
218
+ return { weather }
219
+ },
220
+ })
211
221
  ```
212
222
 
213
- With this approach the stream will include a `tool-call-suspended` chunk, and the `suspendPayload` will contain the `reason` defined by the tool's `suspendSchema`. To continue the call, invoke `resumeStream` with the required `resumeSchema` and the `runId`.
223
+ With this approach the stream includes a `tool-call-suspended` chunk, and the `suspendPayload` contains the `reason` defined by the tool's `suspendSchema`. Call `resumeStream` with the `resumeSchema` data and `runId` to continue:
214
224
 
215
225
  ```typescript
216
- const stream = await agent.stream("What's the weather in London?");
226
+ const stream = await agent.stream("What's the weather in London?")
217
227
 
218
228
  for await (const chunk of stream.fullStream) {
219
- if (chunk.type === "tool-call-suspended") {
220
- console.log(chunk.payload.suspendPayload);
229
+ if (chunk.type === 'tool-call-suspended') {
230
+ console.log(chunk.payload.suspendPayload)
221
231
  }
222
232
  }
223
233
 
224
234
  const handleResume = async () => {
225
- const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });
235
+ const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId })
226
236
 
227
237
  for await (const chunk of resumedStream.textStream) {
228
- process.stdout.write(chunk);
238
+ process.stdout.write(chunk)
229
239
  }
230
- process.stdout.write("\n");
231
- };
240
+ process.stdout.write('\n')
241
+ }
232
242
  ```
233
243
 
234
244
  ## Automatic tool resumption
235
245
 
236
- When using tools that call `suspend()`, you can enable automatic resumption so the agent resumes suspended tools based on the user's next message. This creates a conversational flow where users provide the required information naturally, without your application needing to call `resumeStream()` explicitly.
237
-
238
- ### Enabling auto-resume
239
-
240
- Set `autoResumeSuspendedTools` to `true` in the agent's default options or when calling `stream()`:
246
+ When using tools that call `suspend()`, you can enable automatic resumption so the agent resumes suspended tools based on the user's next message. Set `autoResumeSuspendedTools` to `true` in the agent's default options or per-request:
241
247
 
242
248
  ```typescript
243
- import { Agent } from "@mastra/core/agent";
244
- import { Memory } from "@mastra/memory";
249
+ import { Agent } from '@mastra/core/agent'
250
+ import { Memory } from '@mastra/memory'
245
251
 
246
- // Option 1: In agent configuration
247
252
  const agent = new Agent({
248
- id: "my-agent",
249
- name: "My Agent",
250
- instructions: "You are a helpful assistant",
251
- model: "openai/gpt-4o-mini",
253
+ id: 'my-agent',
254
+ name: 'My Agent',
255
+ instructions: 'You are a helpful assistant',
256
+ model: 'openai/gpt-5-mini',
252
257
  tools: { weatherTool },
253
258
  memory: new Memory(),
254
259
  defaultOptions: {
255
260
  autoResumeSuspendedTools: true,
256
261
  },
257
- });
258
-
259
- // Option 2: Per-request
260
- const stream = await agent.stream("What's the weather?", {
261
- autoResumeSuspendedTools: true,
262
- });
262
+ })
263
263
  ```
264
264
 
265
- ### How it works
266
-
267
- When `autoResumeSuspendedTools` is enabled:
268
-
269
- 1. A tool suspends execution by calling `suspend()` with a payload (e.g., requesting more information)
270
-
271
- 2. The suspension is persisted to memory along with the conversation
272
-
273
- 3. When the user sends their next message on the same thread, the agent:
274
-
275
- - Detects the suspended tool from message history
276
- - Extracts `resumeData` from the user's message based on the tool's `resumeSchema`
277
- - Automatically resumes the tool with the extracted data
278
-
279
- ### Example
265
+ When enabled, the agent detects suspended tools from message history on the next user message, extracts `resumeData` based on the tool's `resumeSchema`, and automatically resumes the tool. The following example shows a complete conversational flow:
280
266
 
281
267
  ```typescript
282
- import { createTool } from "@mastra/core/tools";
283
- import { z } from "zod";
268
+ import { createTool } from '@mastra/core/tools'
269
+ import { z } from 'zod'
284
270
 
285
- export const weatherTool = createTool({
286
- id: "weather-info",
287
- description: "Fetches weather information for a city",
271
+ const weatherTool = createTool({
272
+ id: 'weather-tool',
273
+ description: 'Fetches weather for a city',
274
+ inputSchema: z.object({
275
+ city: z.string(),
276
+ }),
277
+ outputSchema: z.object({
278
+ weather: z.string(),
279
+ }),
288
280
  suspendSchema: z.object({
289
281
  message: z.string(),
290
282
  }),
291
283
  resumeSchema: z.object({
292
284
  city: z.string(),
293
285
  }),
294
- execute: async (_inputData, context) => {
295
- // Check if this is a resume with data
296
- if (!context?.agent?.resumeData) {
297
- // First call - suspend and ask for the city
298
- return context?.agent?.suspend({
299
- message: "What city do you want to know the weather for?",
300
- });
301
- }
286
+ execute: async (inputData, context) => {
287
+ const { resumeData, suspend } = context?.agent ?? {}
302
288
 
303
- // Resume call - city was extracted from user's message
304
- const { city } = context.agent.resumeData;
305
- const response = await fetch(`https://wttr.in/${city}?format=3`);
306
- const weather = await response.text();
289
+ // If no city provided, ask the user
290
+ if (!inputData.city && !resumeData?.city) {
291
+ return suspend?.({ message: 'What city do you want to know the weather for?' })
292
+ }
307
293
 
308
- return { city, weather };
309
- },
310
- });
294
+ const city = resumeData?.city ?? inputData.city
295
+ const response = await fetch(`https://wttr.in/${city}?format=3`)
296
+ const weather = await response.text()
311
297
 
312
- const agent = new Agent({
313
- id: "my-agent",
314
- name: "My Agent",
315
- instructions: "You are a helpful assistant",
316
- model: "openai/gpt-4o-mini",
317
- tools: { weatherTool },
318
- memory: new Memory(),
319
- defaultOptions: {
320
- autoResumeSuspendedTools: true,
298
+ return { weather: `${city}: ${weather}` }
321
299
  },
322
- });
300
+ })
301
+ ```
323
302
 
324
- const stream = await agent.stream("What's the weather like?");
303
+ ```typescript
304
+ const stream = await agent.stream("What's the weather like?")
325
305
 
326
306
  for await (const chunk of stream.fullStream) {
327
- if (chunk.type === "tool-call-suspended") {
328
- console.log(chunk.payload.suspendPayload);
307
+ if (chunk.type === 'tool-call-suspended') {
308
+ console.log(chunk.payload.suspendPayload)
329
309
  }
330
310
  }
331
311
 
332
- const handleResume = async () => {
333
- const resumedStream = await agent.stream("San Francisco");
334
-
335
- for await (const chunk of resumedStream.textStream) {
336
- process.stdout.write(chunk);
337
- }
338
- process.stdout.write("\n");
339
- };
312
+ // User sends follow-up on the same thread
313
+ const resumedStream = await agent.stream('San Francisco')
314
+ for await (const chunk of resumedStream.textStream) {
315
+ process.stdout.write(chunk)
316
+ }
340
317
  ```
341
318
 
342
- **Conversation flow:**
343
-
344
319
  ```text
345
320
  User: "What's the weather like?"
346
321
  Agent: "What city do you want to know the weather for?"
@@ -349,7 +324,7 @@ User: "San Francisco"
349
324
  Agent: "The weather in San Francisco is: San Francisco: ☀️ +72°F"
350
325
  ```
351
326
 
352
- The second message automatically resumes the suspended tool - the agent extracts `{ city: "San Francisco" }` from the user's message and passes it as `resumeData`.
327
+ The second message automatically resumes the suspended tool. The agent extracts `{ city: "San Francisco" }` from the user's message and passes it as `resumeData`.
353
328
 
354
329
  ### Requirements
355
330
 
@@ -368,10 +343,169 @@ For automatic tool resumption to work:
368
343
 
369
344
  Both approaches work with the same tool definitions. Automatic resumption triggers only when suspended tools exist in the message history and the user sends a new message on the same thread.
370
345
 
346
+ ## Tool approval: Supervisor agents
347
+
348
+ A [supervisor agent](https://mastra.ai/docs/agents/supervisor-agents) coordinates multiple subagents using `.stream()` or `.generate()`. When a subagent calls a tool that requires approval, the request propagates up through the delegation chain and surfaces at the supervisor level:
349
+
350
+ 1. The supervisor delegates a task to a subagent.
351
+ 2. The subagent calls a tool that has `requireApproval: true` or uses `suspend()`.
352
+ 3. The approval request bubbles up to the supervisor.
353
+ 4. You approve or decline at the supervisor level.
354
+ 5. The decision propagates back down to the subagent.
355
+
356
+ Tool approvals also propagate through multiple levels of delegation. If a supervisor delegates to subagent A, which delegates to subagent B that has a tool with `requireApproval: true`, the approval request still surfaces at the top-level supervisor.
357
+
358
+ ### Approve and decline in supervisor agents
359
+
360
+ The example below creates a subagent with a tool requiring approval. When the tool triggers an approval request, it surfaces in the supervisor's stream as a `tool-call-approval` chunk:
361
+
362
+ ```typescript
363
+ import { Agent } from '@mastra/core/agent'
364
+ import { createTool } from '@mastra/core/tools'
365
+ import { Memory } from '@mastra/memory'
366
+ import { z } from 'zod'
367
+
368
+ const findUserTool = createTool({
369
+ id: 'find-user',
370
+ description: 'Finds user by ID in the database',
371
+ inputSchema: z.object({
372
+ userId: z.string(),
373
+ }),
374
+ outputSchema: z.object({
375
+ user: z.object({
376
+ id: z.string(),
377
+ name: z.string(),
378
+ email: z.string(),
379
+ }),
380
+ }),
381
+ requireApproval: true,
382
+ execute: async input => {
383
+ const user = await database.findUser(input.userId)
384
+ return { user }
385
+ },
386
+ })
387
+
388
+ const dataAgent = new Agent({
389
+ id: 'data-agent',
390
+ name: 'Data Agent',
391
+ description: 'Handles database queries and user data retrieval',
392
+ model: 'openai/gpt-5-mini',
393
+ tools: { findUserTool },
394
+ })
395
+
396
+ const supervisorAgent = new Agent({
397
+ id: 'supervisor',
398
+ name: 'Supervisor Agent',
399
+ instructions: `You coordinate data retrieval tasks.
400
+ Delegate to data-agent for user lookups.`,
401
+ model: 'openai/gpt-5.4',
402
+ agents: { dataAgent },
403
+ memory: new Memory(),
404
+ })
405
+
406
+ const stream = await supervisorAgent.stream('Find user with ID 12345')
407
+
408
+ for await (const chunk of stream.fullStream) {
409
+ if (chunk.type === 'tool-call-approval') {
410
+ console.log('Tool requires approval:', chunk.payload.toolName)
411
+ console.log('Arguments:', chunk.payload.args)
412
+
413
+ // Approve the tool call
414
+ const resumeStream = await supervisorAgent.approveToolCall({
415
+ runId: stream.runId,
416
+ toolCallId: chunk.payload.toolCallId,
417
+ })
418
+
419
+ for await (const resumeChunk of resumeStream.textStream) {
420
+ process.stdout.write(resumeChunk)
421
+ }
422
+
423
+ // To decline instead, use:
424
+ const declineStream = await supervisorAgent.declineToolCall({
425
+ runId: stream.runId,
426
+ toolCallId: chunk.payload.toolCallId,
427
+ })
428
+ }
429
+ }
430
+ ```
431
+
432
+ ### Use `suspend()` in supervisor agents
433
+
434
+ Tools can also use [`suspend()`](#approval-using-suspend) to pause execution and return context to the user. This approach works through the supervisor delegation chain the same way `requireApproval` does: the suspension surfaces at the supervisor level:
435
+
436
+ ```typescript
437
+ const conditionalTool = createTool({
438
+ id: 'conditional-operation',
439
+ description: 'Performs an operation that may require confirmation',
440
+ inputSchema: z.object({
441
+ operation: z.string(),
442
+ }),
443
+ suspendSchema: z.object({
444
+ message: z.string(),
445
+ }),
446
+ resumeSchema: z.object({
447
+ confirmed: z.boolean(),
448
+ }),
449
+ execute: async (input, context) => {
450
+ const { resumeData } = context?.agent ?? {}
451
+
452
+ if (!resumeData?.confirmed) {
453
+ return context?.agent?.suspend({
454
+ message: `Confirm: ${input.operation}?`,
455
+ })
456
+ }
457
+
458
+ // Proceed with operation
459
+ return await performOperation(input.operation)
460
+ },
461
+ })
462
+ ```
463
+
464
+ ```typescript
465
+ // When using this tool through a subagent in supervisor agents
466
+ for await (const chunk of stream.fullStream) {
467
+ if (chunk.type === 'tool-call-suspended') {
468
+ console.log('Tool suspended:', chunk.payload.suspendPayload.message)
469
+
470
+ // Resume with confirmation
471
+ const resumeStream = await supervisorAgent.resumeStream(
472
+ { confirmed: true },
473
+ { runId: stream.runId },
474
+ )
475
+
476
+ for await (const resumeChunk of resumeStream.textStream) {
477
+ process.stdout.write(resumeChunk)
478
+ }
479
+ }
480
+ }
481
+ ```
482
+
483
+ ### Supervisor approval with `generate()`
484
+
485
+ Tool approval propagation also works with `generate()` in supervisor agents:
486
+
487
+ ```typescript
488
+ const output = await supervisorAgent.generate('Find user with ID 12345', {
489
+ maxSteps: 10,
490
+ })
491
+
492
+ if (output.finishReason === 'suspended') {
493
+ console.log('Tool requires approval:', output.suspendPayload.toolName)
494
+
495
+ // Approve
496
+ const result = await supervisorAgent.approveToolCallGenerate({
497
+ runId: output.runId,
498
+ toolCallId: output.suspendPayload.toolCallId,
499
+ })
500
+
501
+ console.log('Final result:', result.text)
502
+ }
503
+ ```
504
+
371
505
  ## Related
372
506
 
373
- - [Using Tools](https://mastra.ai/docs/agents/using-tools)
374
- - [Agent Overview](https://mastra.ai/docs/agents/overview)
375
- - [Tools Overview](https://mastra.ai/docs/mcp/overview)
376
- - [Agent Memory](https://mastra.ai/docs/agents/agent-memory)
377
- - [Request Context](https://mastra.ai/docs/server/request-context)
507
+ - [Tools](https://mastra.ai/docs/agents/using-tools)
508
+ - [Agent overview](https://mastra.ai/docs/agents/overview)
509
+ - [MCP overview](https://mastra.ai/docs/mcp/overview)
510
+ - [Memory](https://mastra.ai/docs/memory/overview)
511
+ - [Request context](https://mastra.ai/docs/server/request-context)