@mastra/memory 1.24.0 → 1.25.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 (57) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/docs/SKILL.md +6 -6
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-agents-agent-approval.md +85 -11
  5. package/dist/docs/references/docs-agents-networks.md +2 -2
  6. package/dist/docs/references/docs-agents-supervisor-agents.md +19 -13
  7. package/dist/docs/references/docs-evals-evals-with-memory.md +4 -4
  8. package/dist/docs/references/docs-long-running-agents-background-tasks.md +10 -10
  9. package/dist/docs/references/docs-long-running-agents-goals.md +15 -11
  10. package/dist/docs/references/docs-memory-memory-processors.md +10 -10
  11. package/dist/docs/references/docs-memory-message-history.md +11 -5
  12. package/dist/docs/references/docs-memory-multi-user-threads.md +6 -6
  13. package/dist/docs/references/docs-memory-observational-memory.md +57 -38
  14. package/dist/docs/references/docs-memory-overview.md +24 -10
  15. package/dist/docs/references/docs-memory-semantic-recall.md +3 -3
  16. package/dist/docs/references/docs-memory-working-memory.md +8 -8
  17. package/dist/docs/references/docs-storage-overview.md +2 -2
  18. package/dist/docs/references/reference-memory-clone-utilities.md +1 -1
  19. package/dist/docs/references/reference-memory-cloneThread.md +3 -3
  20. package/dist/docs/references/reference-memory-listThreads.md +1 -1
  21. package/dist/docs/references/reference-memory-memory-class.md +4 -4
  22. package/dist/docs/references/reference-memory-observational-memory.md +88 -38
  23. package/dist/docs/references/reference-memory-summarizeConversation.md +3 -3
  24. package/dist/docs/references/reference-memory-summarizeThread.md +3 -3
  25. package/dist/docs/references/reference-processors-token-limiter-processor.md +4 -4
  26. package/dist/docs/references/reference-storage-dsql.md +3 -3
  27. package/dist/docs/references/reference-storage-dynamodb.md +7 -7
  28. package/dist/docs/references/reference-storage-mongodb.md +2 -2
  29. package/dist/docs/references/reference-storage-postgresql.md +2 -2
  30. package/dist/docs/references/reference-storage-redis.md +1 -1
  31. package/dist/docs/references/reference-storage-upstash.md +1 -1
  32. package/dist/docs/references/reference-vectors-libsql.md +2 -2
  33. package/dist/docs/references/reference-vectors-mongodb.md +3 -3
  34. package/dist/docs/references/reference-vectors-pg.md +5 -5
  35. package/dist/docs/references/reference-vectors-upstash.md +1 -1
  36. package/dist/index.cjs +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -1
  40. package/dist/processors/index.cjs +1 -1
  41. package/dist/processors/index.js +1 -1
  42. package/dist/processors/observational-memory/index.d.ts +1 -1
  43. package/dist/processors/observational-memory/index.d.ts.map +1 -1
  44. package/dist/processors/observational-memory/observation-strategies/types.d.ts +6 -0
  45. package/dist/processors/observational-memory/observation-strategies/types.d.ts.map +1 -1
  46. package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
  47. package/dist/processors/observational-memory/observational-memory.d.ts +30 -1
  48. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  49. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  50. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
  51. package/dist/processors/observational-memory/types.d.ts +50 -6
  52. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  53. package/dist/{src-Ci_GvBVm.js → src-BYZh9IBv.js} +171 -27
  54. package/dist/{src-Ci_GvBVm.js.map → src-BYZh9IBv.js.map} +1 -1
  55. package/dist/{src-VD6qMn-H.cjs → src-naeQog_T.cjs} +171 -27
  56. package/dist/{src-VD6qMn-H.cjs.map → src-naeQog_T.cjs.map} +1 -1
  57. package/package.json +7 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 1.25.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added config-level `hooks` to Observational Memory so apps can track what OM's background model calls cost. Previously the `ObserveHooks` lifecycle callbacks only fired when calling `observe()` manually — the automatic pipeline (turn-driven observation and fire-and-forget async buffering) computed token `usage` and `providerMetadata` and dropped them. Hooks set on the OM config (including through `Memory`'s `observationalMemory` options) now fire for every observation and reflection cycle, with `threadId`/`resourceId`/`trigger` call context: ([#19058](https://github.com/mastra-ai/mastra/pull/19058))
8
+
9
+ ```ts
10
+ const memory = new Memory({
11
+ storage,
12
+ options: {
13
+ observationalMemory: {
14
+ hooks: {
15
+ onObservationEnd: ({ usage, providerMetadata, error, threadId, trigger }) => {
16
+ recordOmSpend({ usage, providerMetadata, threadId, trigger });
17
+ },
18
+ },
19
+ },
20
+ },
21
+ });
22
+ ```
23
+
24
+ Failed async-buffered cycles never throw (they are fire-and-forget), so they report through the end hook's `error` field instead. Errors thrown by config-level hooks are caught and logged, never failing the cycle. Per-call `observe()` hooks keep their existing payloads and semantics.
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [[`594f7b2`](https://github.com/mastra-ai/mastra/commit/594f7b28f5263fb9982fd50d95c471fb971ea984), [`311f943`](https://github.com/mastra-ai/mastra/commit/311f943bee60e8fdf5c84499ea50e884276c936c), [`0c89896`](https://github.com/mastra-ai/mastra/commit/0c8989673fb7d106837098398131e570c6023b68), [`23b4238`](https://github.com/mastra-ai/mastra/commit/23b423844ad0bcf2a502a68dd62866d6160f9f6d), [`e320a76`](https://github.com/mastra-ai/mastra/commit/e320a763feaf65c6be3cebecf746defcbde161b3), [`03b4918`](https://github.com/mastra-ai/mastra/commit/03b4918c80d188ce375334c393e131c6e94bd7eb), [`14ef73a`](https://github.com/mastra-ai/mastra/commit/14ef73a4bbd73e7808414816eb0628ce1d80b5d7), [`1d677d5`](https://github.com/mastra-ai/mastra/commit/1d677d5f99d7db403f7828585e8c25f299f72628), [`93e28ec`](https://github.com/mastra-ai/mastra/commit/93e28ecce9031c02397e0ae8406593e5c7a95883), [`729dab4`](https://github.com/mastra-ai/mastra/commit/729dab408faccfaef0cbb048e5a4338f9172847e), [`484003d`](https://github.com/mastra-ai/mastra/commit/484003d33ff59330c86b19863e4a38732d7e4155), [`933d291`](https://github.com/mastra-ai/mastra/commit/933d291146b789c19442ad206f94da3e4be90c64)]:
29
+ - @mastra/core@1.56.0-alpha.3
30
+
3
31
  ## 1.24.0
4
32
 
5
33
  ### Minor Changes
@@ -3,7 +3,7 @@ name: mastra-memory
3
3
  description: Documentation for @mastra/memory. Use when working with @mastra/memory APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/memory"
6
- version: "1.24.0"
6
+ version: "1.25.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -16,11 +16,11 @@ Read the individual reference documents for detailed explanations and code examp
16
16
 
17
17
  ### Docs
18
18
 
19
- - [Agent approval](references/docs-agents-agent-approval.md) - Learn how to require approvals, suspend tool execution, and automatically resume suspended tools while keeping humans in control of agent workflows.
19
+ - [Agent approval](references/docs-agents-agent-approval.md) - Learn how to require approvals and suspend tool execution, plus automatically resume suspended tools while keeping humans in control of agent workflows.
20
20
  - [Agent networks](references/docs-agents-networks.md) - Coordinate multiple agents, workflows, and tools using agent networks for complex, non-deterministic task execution.
21
21
  - [Supervisor agents](references/docs-agents-supervisor-agents.md) - Learn how to coordinate multiple agents with delegation hooks, iteration monitoring, message filtering, and task completion scoring.
22
- - [Evals with memory](references/docs-evals-evals-with-memory.md) - Run scorers against memory-enabled agents including observational memory in thread scope using runEvals and dataset experiments.
23
- - [Background tasks](references/docs-long-running-agents-background-tasks.md) - Learn how to dispatch long-running tool calls in the background, keep the stream open until they complete, and orchestrate subagents asynchronously.
22
+ - [Evals with memory](references/docs-evals-evals-with-memory.md) - Run scorers against memory-enabled agents, including observational memory in thread scope, using runEvals and dataset experiments.
23
+ - [Background tasks](references/docs-long-running-agents-background-tasks.md) - Learn how to dispatch long-running tool calls in the background and keep the stream open until they complete, plus orchestrate subagents asynchronously.
24
24
  - [Goals](references/docs-long-running-agents-goals.md) - Learn how to set a durable objective on an agent that's judged in the execution loop, so the agent keeps working until the goal is complete or the run budget is exhausted.
25
25
  - [Memory processors](references/docs-memory-memory-processors.md) - Learn how to use memory processors in Mastra to filter, trim, and transform messages before they're sent to the language model to manage context window limits.
26
26
  - [Message history](references/docs-memory-message-history.md) - Learn how to configure message history in Mastra to store recent messages from the current conversation.
@@ -41,8 +41,8 @@ Read the individual reference documents for detailed explanations and code examp
41
41
  - [Reference: Memory.createThread()](references/reference-memory-createThread.md) - Documentation for the `Memory.createThread()` method in Mastra, which creates a new conversation thread in the memory system.
42
42
  - [Reference: Memory.getThreadById()](references/reference-memory-getThreadById.md) - Documentation for the `Memory.getThreadById()` method in Mastra, which retrieves a specific thread by its ID.
43
43
  - [Reference: Memory.listThreads()](references/reference-memory-listThreads.md) - Documentation for the `Memory.listThreads()` method in Mastra, which retrieves threads with optional filtering by resourceId and/or metadata.
44
- - [Reference: Memory class](references/reference-memory-memory-class.md) - Documentation for the `Memory` class in Mastra, which provides a robust system for managing conversation history and thread-based message storage.
45
- - [Reference: Observational Memory](references/reference-memory-observational-memory.md) - API reference for Observational Memory in Mastra a three-tier memory system that uses Observer and Reflector agents to maintain long-term memory across conversations.
44
+ - [Reference: Memory class](references/reference-memory-memory-class.md) - Documentation for the `Memory` class in Mastra, which provides a reliable system for managing conversation history and thread-based message storage.
45
+ - [Reference: Observational Memory](references/reference-memory-observational-memory.md) - API reference for Observational Memory in Mastra: a three-tier memory system that uses Observer and Reflector agents to maintain long-term memory across conversations.
46
46
  - [Reference: summarizeConversation()](references/reference-memory-summarizeConversation.md) - Documentation for the standalone `summarizeConversation()` function in Mastra, which summarizes messages you pass in and extracts structured values from them.
47
47
  - [Reference: Memory.summarizeThread()](references/reference-memory-summarizeThread.md) - Documentation for the `Memory.summarizeThread()` method in Mastra, which summarizes a thread's conversation in one shot and extracts structured values from it.
48
48
  - [Reference: TokenLimiterProcessor](references/reference-processors-token-limiter-processor.md) - Documentation for the TokenLimiterProcessor in Mastra, which limits the number of tokens in messages.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.24.0",
2
+ "version": "1.25.0-alpha.0",
3
3
  "package": "@mastra/memory",
4
4
  "exports": {},
5
5
  "modules": {
@@ -6,7 +6,7 @@ Agents sometimes require the same [human-in-the-loop](https://mastra.ai/docs/wor
6
6
 
7
7
  ## When to use agent approval
8
8
 
9
- - **Destructive or irreversible actions** such as deleting records, sending emails, or processing payments.
9
+ - **Destructive or irreversible actions** such as deleting records or sending emails, or alternatively processing payments.
10
10
  - **Cost-heavy operations** like calling expensive third-party APIs where you want to verify arguments first.
11
11
  - **Conditional confirmation** where a tool starts executing and then discovers it needs the user to confirm or supply extra data before finishing.
12
12
 
@@ -60,7 +60,7 @@ Mastra offers two distinct mechanisms for pausing tool calls: **pre-execution ap
60
60
 
61
61
  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.
62
62
 
63
- Two flags control this, combined with OR logic. If _either_ is `true`, the call pauses:
63
+ The flags control this, combined with OR logic. If _either_ is `true`, the call pauses:
64
64
 
65
65
  | Flag | Where to set it | Scope |
66
66
  | --------------------------- | --------------------------------- | ------------------------------------------- |
@@ -92,7 +92,7 @@ for await (const chunk of stream.fullStream) {
92
92
 
93
93
  #### Conditional approval with a function
94
94
 
95
- Instead of a boolean, `requireToolApproval` accepts a function that decides per tool call. It receives the `toolName`, the `args` the model passed, the `requestContext`, and the `workspace`. Return `true` to require approval for that call, or `false` to allow it. This lets you gate approval dynamically for example, only for tools whose name matches a pattern:
95
+ Instead of a boolean, `requireToolApproval` accepts a function that decides per tool call. It receives the `toolName`, the `args` the model passed, the `requestContext`, and the `workspace`. Return `true` to require approval for that call, or `false` to allow it. This lets you gate approval at runtime, for example, only for tools whose name matches a pattern:
96
96
 
97
97
  ```typescript
98
98
  const stream = await agent.stream('Clean up old records', {
@@ -100,10 +100,82 @@ const stream = await agent.stream('Clean up old records', {
100
100
  })
101
101
  ```
102
102
 
103
- A tool's own `requireApproval` setting still takes precedence: if a tool defines its own approval rule, that rule decides for that tool and the function above doesn't override it. If the function throws, the call requires approval (fail-safe).
103
+ A tool's own `requireApproval` setting takes precedence over the function above. Its rule decides whether that tool needs approval. If the function throws, the call requires approval as a fail-safe.
104
104
 
105
105
  > **Note:** Function-based `requireToolApproval` is only available on regular `stream()` / `generate()` calls. Durable agents and stored agents persist their options, and a function can't be serialized, so they accept only a boolean. If you pass a function in those contexts it falls back to requiring approval for every tool call.
106
106
 
107
+ #### Bind approval to the exact tool arguments
108
+
109
+ For sensitive tools, bind the approval to the exact tool name and arguments that were shown to the reviewer. If those arguments drift before execution, the tool shouldn't run under the old approval.
110
+
111
+ The `tool-call-approval` chunk already includes `toolName`, `toolCallId`, and `args`. You can fingerprint those fields when the approval request is shown. The example below uses a simple JSON string as the fingerprint, but in production you should use a stable hash of the tool name and arguments:
112
+
113
+ ```typescript
114
+ import { Agent } from '@mastra/core/agent'
115
+
116
+ // For your production usecase, build a stable hash of the tool name and args
117
+ function actionFingerprint(toolName: string, args: unknown) {
118
+ const payload = JSON.stringify({ toolName, args })
119
+ return `fingerprint-${payload}`
120
+ }
121
+
122
+ const sensitiveTools = new Set(['issue_refund', 'delete_record'])
123
+ const approvedFingerprints = new Set<string>()
124
+
125
+ export const approvalBoundAgent = new Agent({
126
+ id: 'approval-bound-agent',
127
+ name: 'Approval Bound Agent',
128
+ model: 'openai/gpt-5.6-sol',
129
+ tools: { issueRefundTool, deleteRecordTool },
130
+ hooks: {
131
+ beforeToolCall: ({ toolName, input }) => {
132
+ if (!sensitiveTools.has(toolName)) return
133
+
134
+ const fingerprint = actionFingerprint(toolName, input)
135
+ if (!approvedFingerprints.delete(fingerprint)) {
136
+ return {
137
+ proceed: false,
138
+ output: `Tool call blocked: approval did not match ${toolName} arguments.`,
139
+ }
140
+ }
141
+ },
142
+ },
143
+ })
144
+ ```
145
+
146
+ ```typescript
147
+ const stream = await approvalBoundAgent.stream('Refund order ord-1042', {
148
+ requireToolApproval: ({ toolName }) => sensitiveTools.has(toolName),
149
+ })
150
+
151
+ async function consumeApprovalStream(currentStream: typeof stream) {
152
+ for await (const chunk of currentStream.fullStream) {
153
+ if (chunk.type === 'tool-call-approval') {
154
+ const { toolName, toolCallId, args } = chunk.payload
155
+ const fingerprint = actionFingerprint(toolName, args)
156
+
157
+ // Present toolName, args, and fingerprint to your approval UI.
158
+ const approved = await showApprovalDialog({ toolName, args, fingerprint })
159
+
160
+ const nextStream = approved
161
+ ? await approveReviewedToolCall(currentStream.runId, toolCallId, fingerprint)
162
+ : await approvalBoundAgent.declineToolCall({ runId: currentStream.runId, toolCallId })
163
+
164
+ await consumeApprovalStream(nextStream)
165
+ }
166
+ }
167
+ }
168
+
169
+ async function approveReviewedToolCall(runId: string, toolCallId: string, fingerprint: string) {
170
+ approvedFingerprints.add(fingerprint)
171
+ return approvalBoundAgent.approveToolCall({ runId, toolCallId })
172
+ }
173
+
174
+ await consumeApprovalStream(stream)
175
+ ```
176
+
177
+ In production, store the approved fingerprint in durable storage scoped to the user, run, tool call, and policy version. The `Set` above is intentionally small so the boundary is easy to see: the approval is consumed once, and only for the same canonical tool arguments that were reviewed.
178
+
107
179
  ### Runtime suspension with `suspend()`
108
180
 
109
181
  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.
@@ -133,7 +205,7 @@ const weatherTool = createTool({
133
205
  })
134
206
  ```
135
207
 
136
- > **Note:** `suspend()` doesn't throw return immediately after calling it (e.g. `return await suspend({ ... })`). Code after `await suspend(...)` still runs before the tool pauses.
208
+ > **Note:** `suspend()` doesn't throw, return immediately after calling it (e.g. `return await suspend({ ... })`). Code after `await suspend(...)` still runs before the tool pauses.
137
209
 
138
210
  ## Tool approval with `generate()`
139
211
 
@@ -172,11 +244,11 @@ if (output.finishReason === 'suspended') {
172
244
  | Decline method | `declineToolCall({ runId })` | `declineToolCallGenerate({ runId, toolCallId })` |
173
245
  | Result | Stream to iterate | Full output object |
174
246
 
175
- > **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.
247
+ > **Note:** `toolCallId` is optional on all four methods. Pass it when multiple tool calls may be pending (common in supervisor agents). When omitted, the agent resumes the most recent suspended tool call.
176
248
 
177
249
  ## Tool-level approval
178
250
 
179
- 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.
251
+ Instead of pausing every tool call at the agent level, you can mark individual tools as requiring approval. You get fine-grained control: only specific tools pause, while others execute immediately.
180
252
 
181
253
  ### Approval using `requireApproval`
182
254
 
@@ -303,7 +375,9 @@ const agent = new Agent({
303
375
  })
304
376
  ```
305
377
 
306
- 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:
378
+ When enabled, the agent detects suspended tools from message history on the next user message. It extracts `resumeData` based on the tool's `resumeSchema`, then automatically resumes the tool.
379
+
380
+ The following example shows a complete conversational flow:
307
381
 
308
382
  ```typescript
309
383
  import { createTool } from '@mastra/core/tools'
@@ -386,7 +460,7 @@ Both approaches work with the same tool definitions. Automatic resumption trigge
386
460
 
387
461
  ## Resuming after a restart
388
462
 
389
- The examples above hold on to `stream.runId` between suspension and approval. That works while the process stays alive, but in production the approval often arrives later after a page refresh, a server restart, or on a different server instance behind a load balancer.
463
+ The examples above hold on to `stream.runId` between suspension and approval. That works while the process stays alive, but in production the approval often arrives later, after a page refresh, a server restart, or on a different server instance behind a load balancer.
390
464
 
391
465
  Use [`listSuspendedRuns()`](https://mastra.ai/reference/agents/listSuspendedRuns) to rediscover the pending run for a conversation from storage:
392
466
 
@@ -414,7 +488,7 @@ if (run && toolCall) {
414
488
  }
415
489
  ```
416
490
 
417
- Each returned run includes the suspended tool calls (`toolCallId`, `toolName`, `args`, and `requiresApproval`). Approval suspensions (`requiresApproval: true`) are answered with `approveToolCall()` / `declineToolCall()`, while `suspend()`-based suspensions carry their `suspendPayload` and expect `resumeStream()` with resume data so you can rebuild the right UI for either flow without keeping any state in memory.
491
+ Each returned run includes the suspended tool calls (`toolCallId`, `toolName`, `args`, and `requiresApproval`). Approval suspensions (`requiresApproval: true`) are answered with `approveToolCall()` / `declineToolCall()`, while `suspend()`-based suspensions carry their `suspendPayload` and expect `resumeStream()` with resume data, so you can rebuild the right UI for either flow without keeping any state in memory.
418
492
 
419
493
  `sendToolApproval()` uses the same storage-backed discovery automatically: when no active run is found in memory for the thread, it looks up the suspended run in storage before failing. If several suspended runs match the thread, pass a `toolCallId` to disambiguate.
420
494
 
@@ -477,7 +551,7 @@ const supervisorAgent = new Agent({
477
551
  name: 'Supervisor Agent',
478
552
  instructions: `You coordinate data retrieval tasks.
479
553
  Delegate to data-agent for user lookups.`,
480
- model: 'openai/gpt-5.5',
554
+ model: 'openai/gpt-5.6-sol',
481
555
  agents: { dataAgent },
482
556
  memory: new Memory(),
483
557
  })
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Agent networks
4
4
 
5
- > **Deprecated — Use supervisor agents:** Agent networks are deprecated and will be removed in a future major release. [Supervisor agents](https://mastra.ai/docs/agents/supervisor-agents) using `agent.stream()` or `agent.generate()` are now the recommended approach. It provides the same multi-agent coordination with better control, a simpler API, and easier debugging.
5
+ > **Deprecated:** Agent networks are deprecated and will be removed in a future major release. [Supervisor agents](https://mastra.ai/docs/agents/supervisor-agents) using `agent.stream()` or `agent.generate()` are now the recommended approach. It provides the same multi-agent coordination with better control, a simpler API, and easier debugging.
6
6
  >
7
7
  > See the [migration guide](https://mastra.ai/guides/migrations/network-to-supervisor) to upgrade.
8
8
 
@@ -29,7 +29,7 @@ export const routingAgent = new Agent({
29
29
  name: 'Routing Agent',
30
30
  instructions: `
31
31
  You are a network of writers and researchers. The user will ask you to research a topic. Always respond with a complete report—no bullet points. Write in full paragraphs, like a blog post. Do not answer with incomplete or uncertain information.`,
32
- model: 'openai/gpt-5.5',
32
+ model: 'openai/gpt-5.6-sol',
33
33
  agents: {
34
34
  researchAgent,
35
35
  writingAgent,
@@ -8,7 +8,7 @@ A supervisor agent coordinates multiple subagents using [`Agent.stream()`](https
8
8
 
9
9
  ## When to use supervisor agents
10
10
 
11
- Use supervisor agents when a task requires multiple agents with different specializations to work together. The supervisor handles delegation decisions, context passing, and result synthesis.
11
+ Use supervisor agents when a task requires multiple agents with different specializations to work together. The supervisor handles delegation decisions and context passing, plus result synthesis.
12
12
 
13
13
  Common use cases:
14
14
 
@@ -43,7 +43,7 @@ const supervisor = new Agent({
43
43
  id: 'supervisor',
44
44
  instructions: `You coordinate research and writing using specialized agents.
45
45
  Delegate to research-agent for facts, then writing-agent for content.`,
46
- model: 'openai/gpt-5.5',
46
+ model: 'openai/gpt-5.6-sol',
47
47
  agents: { researchAgent, writingAgent },
48
48
  memory: new Memory({
49
49
  storage: new LibSQLStore({ id: 'storage', url: 'file:mastra.db' }),
@@ -112,7 +112,7 @@ The `context` object includes:
112
112
 
113
113
  ### `onDelegationComplete`
114
114
 
115
- Called after a delegation finishes. Use it to inspect results, provide feedback, or stop execution:
115
+ Called after a delegation finishes. Use it to inspect results or provide feedback, or alternatively stop execution:
116
116
 
117
117
  - `context.bail()`: Stop the supervisor loop immediately
118
118
  - Return `{ feedback: '...' }`: Add feedback that gets saved to the supervisor's memory and is visible to subsequent iterations
@@ -147,7 +147,7 @@ The `context` object includes:
147
147
 
148
148
  ## Message filtering
149
149
 
150
- By default, subagents receive the full conversation context from the supervisor. Use `messageFilter` to control what messages are shared for example, to remove sensitive data or limit context size.
150
+ By default, subagents receive the full conversation context from the supervisor. Use `messageFilter` to control what messages are shared, for example, to remove sensitive data or limit context size.
151
151
 
152
152
  ```typescript
153
153
  const stream = await supervisor.stream('Research AI trends', {
@@ -173,7 +173,9 @@ The callback receives `messages` (the full conversation history), `primitiveId`
173
173
 
174
174
  When a subagent completes, the supervisor model receives the subagent's text response in later iterations. Nested tool calls and subagent metadata, such as thread and resource IDs, aren't added to the supervisor model context.
175
175
 
176
- Application code and UI integrations can still inspect the raw delegation result, including `subAgentToolResults`, from the tool result payload. This keeps debugging and display data available without sending nested tool arguments or outputs back into the supervisor's next model call.
176
+ Application code and UI integrations can still inspect `subAgentToolResults` and the rest of the raw delegation result in the tool result payload.
177
+
178
+ This keeps debugging and display data available without sending nested tool arguments or outputs back into the supervisor's next model call.
177
179
 
178
180
  Set `includeSubAgentToolResultsInModelContext` to include the full subagent result, including nested tool results and subagent metadata, in the supervisor model context.
179
181
 
@@ -187,7 +189,7 @@ await supervisor.generate('Research AI trends', {
187
189
 
188
190
  ## Iteration monitoring
189
191
 
190
- `onIterationComplete` is called after each iteration of the supervisor loop. Use it to log progress, inject feedback, or stop execution early.
192
+ `onIterationComplete` is called after each iteration of the supervisor loop. Use it to log progress or inject feedback, or alternatively stop execution early.
191
193
 
192
194
  ```typescript
193
195
  const stream = await supervisor.stream('Research AI trends', {
@@ -214,7 +216,7 @@ const stream = await supervisor.stream('Research AI trends', {
214
216
  })
215
217
  ```
216
218
 
217
- Return `{ continue: true }` to keep iterating, or `{ continue: false }` to stop. Include optional `feedback` to inject guidance into the conversation. When `feedback` is combined with `continue: false`, the model may get one final turn to produce a text response incorporating the feedback, but only if the current iteration is still active (e.g., after tool calls) otherwise no extra turn is granted.
219
+ Return `{ continue: true }` to keep iterating, or `{ continue: false }` to stop. Include optional `feedback` to inject guidance into the conversation. When `feedback` is combined with `continue: false`, the model may get one final turn to produce a text response incorporating the feedback, but only if the current iteration is still active (e.g., after tool calls), otherwise no extra turn is granted.
218
220
 
219
221
  ## Memory isolation
220
222
 
@@ -226,7 +228,7 @@ How it works:
226
228
  2. **Scoped memory saves**: Only the delegation prompt and the subagent's response are saved to the subagent's memory
227
229
  3. **Fresh thread per invocation**: Each delegation uses a unique thread ID, ensuring clean separation
228
230
 
229
- This ensures subagents have the context they need without cluttering their memory with the entire supervisor conversation. Visit [memory in multi-agent systems](https://mastra.ai/docs/memory/overview) for more details.
231
+ As a result, subagents have the context they need without cluttering their memory with the entire supervisor conversation. Visit [memory in multi-agent systems](https://mastra.ai/docs/memory/overview) for more details.
230
232
 
231
233
  ## Tool approval propagation
232
234
 
@@ -309,7 +311,7 @@ const stream = await supervisor.stream('Research AI in education', {
309
311
 
310
312
  The built-in rubric scorer lets you define what "correct" looks like as a checklist and have the agent self-evaluate and iterate until every criterion is satisfied or `maxSteps` is reached.
311
313
 
312
- It works as an **LLM-as-judge** scorer: a separate grader model reviews the agent's output against the rubric after each iteration. If every required criterion passes, the loop ends. If anything falls short, per-criterion feedback is injected back into the conversation and the agent tries again.
314
+ It works as an **LLM-as-judge** scorer. After each iteration, a separate grader model reviews the agent's output against the rubric. The loop ends when every required criterion passes. A failed criterion adds its feedback to the conversation so the agent can try again.
313
315
 
314
316
  This is most effective for tasks with clear, verifiable success criteria. You can use it like so:
315
317
 
@@ -320,7 +322,7 @@ import { createRubricScorer } from '@mastra/evals/scorers/prebuilt'
320
322
  const supervisor = new Agent({
321
323
  id: 'supervisor',
322
324
  instructions: 'You coordinate research and writing using specialized agents.',
323
- model: 'openai/gpt-5.5',
325
+ model: 'openai/gpt-5.6-sol',
324
326
  agents: { researchAgent, writingAgent },
325
327
  })
326
328
 
@@ -345,9 +347,13 @@ For full API details, see the [rubric scorer reference](https://mastra.ai/refere
345
347
 
346
348
  ## Writing effective instructions
347
349
 
348
- Clear instructions are essential for effective delegation. Your supervisor's `instructions` should specify available resources, when to use each one, how to coordinate them, and success criteria.
350
+ Clear instructions are essential for effective delegation.
351
+
352
+ Your supervisor's `instructions` should specify the available resources and when to use each one. They should also define coordination behavior and success criteria.
353
+
354
+ Each subagent should have a clear `description` that explains its purpose and return format, including when the supervisor should use it.
349
355
 
350
- Each subagent should have a clear `description` that explains what it does, what format it returns, and when to use it. The supervisor uses these descriptions to make delegation decisions.
356
+ The supervisor uses these descriptions to make delegation decisions.
351
357
 
352
358
  ```typescript
353
359
  const supervisor = new Agent({
@@ -380,7 +386,7 @@ Enable the [backgroundTasks manager](https://mastra.ai/reference/configuration)
380
386
  const supervisor = new Agent({
381
387
  id: 'supervisor',
382
388
  instructions: 'Coordinate research and writing using the available agents.',
383
- model: 'openai/gpt-5.5',
389
+ model: 'openai/gpt-5.6-sol',
384
390
  agents: { researchAgent, writingAgent },
385
391
  backgroundTasks: {
386
392
  tools: {
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Evals with memory
4
4
 
5
- Agents that use memory in `thread` scope including observational memory require a thread ID at run time. When an eval invokes the agent without one, you'll see:
5
+ Agents that use memory in `thread` scope, including observational memory, require a thread ID at run time. When an eval invokes the agent without one, you'll see:
6
6
 
7
7
  ```text
8
8
  ObservationalMemory (scope: 'thread') requires a threadId, but none was found in RequestContext or MessageList.
@@ -18,11 +18,11 @@ This page covers the three working patterns for running Mastra evals against mem
18
18
  | One independent thread per item, focused CI loop | [`runEvals` per item](#per-item-threads-with-runevals) |
19
19
  | Per-item threads driven by a stored `Dataset` | [`dataset.startExperiment` with an inline task](#dataset-experiments-with-an-inline-task) |
20
20
 
21
- Pre-seeding `RequestContext` with `MastraMemory` **isn't** a supported way to drive memory into an agent. Thread resolution reads `args.memory.thread` `RequestContext.MastraMemory` is populated by `prepare-memory-step` after the agent has already resolved its thread.
21
+ Pre-seeding `RequestContext` with `MastraMemory` **isn't** a supported way to drive memory into an agent. Thread resolution reads `args.memory.thread`, `RequestContext.MastraMemory` is populated by `prepare-memory-step` after the agent has already resolved its thread.
22
22
 
23
23
  ## Shared thread with `runEvals`
24
24
 
25
- `runEvals` accepts `targetOptions`, which is forwarded to `agent.generate()`. Passing `memory: { thread, resource }` runs every data item against the same thread useful for testing recall across a multi-turn conversation.
25
+ `runEvals` accepts `targetOptions`, which is forwarded to `agent.generate()`. Passing `memory: { thread, resource }` runs every data item against the same thread, useful for testing recall across a multi-turn conversation.
26
26
 
27
27
  ```typescript
28
28
  import { runEvals } from '@mastra/core/evals'
@@ -88,7 +88,7 @@ const average = scores.reduce((a, b) => a + b, 0) / scores.length
88
88
 
89
89
  ## Dataset experiments with an inline task
90
90
 
91
- `dataset.startExperiment({ target: agent })` **doesn't** forward a `memory` option to the agent only `requestContext`. To run a stored dataset against a memory-enabled agent, use an inline `task` function and stash `{ threadId, resourceId }` in each item's `metadata`. The scorer pipeline still runs as normal.
91
+ `dataset.startExperiment({ target: agent })` **doesn't** forward a `memory` option to the agent, only `requestContext`. To run a stored dataset against a memory-enabled agent, use an inline `task` function and stash `{ threadId, resourceId }` in each item's `metadata`. The scorer pipeline still runs as normal.
92
92
 
93
93
  ```typescript
94
94
  import { randomUUID } from 'node:crypto'
@@ -74,7 +74,7 @@ export const researchTool = createTool({
74
74
 
75
75
  ### Agent-level
76
76
 
77
- Use `backgroundTasks.tools` on the agent to opt in specific tools, override timeouts for individual tools, or run all background-eligible tools in the background. Use `disabled: true` to short-circuit background dispatch for the agent entirely.
77
+ Use `backgroundTasks.tools` on the agent to opt in specific tools or override timeouts for individual tools, or alternatively run all background-eligible tools in the background. Use `disabled: true` to short-circuit background dispatch for the agent entirely.
78
78
 
79
79
  ```typescript
80
80
  import { Agent } from '@mastra/core/agent'
@@ -82,7 +82,7 @@ import { Agent } from '@mastra/core/agent'
82
82
  export const researcher = new Agent({
83
83
  id: 'researcher',
84
84
  instructions: 'You research topics and answer questions.',
85
- model: 'openai/gpt-5.5',
85
+ model: 'openai/gpt-5.6-sol',
86
86
  tools: { researchTool, summarizeTool },
87
87
  backgroundTasks: {
88
88
  tools: {
@@ -106,7 +106,7 @@ When a tool is registered on an agent that has background tasks enabled, the mod
106
106
  }
107
107
  ```
108
108
 
109
- The `_background` override is a _modifier_ on tools the developer has already opted in at the tool or agent layer it's not a standalone opt-in. If a tool hasn't been opted in, `_background.enabled: true` from the model is ignored and the tool runs in the foreground. This keeps deterministic, foreground-only tools (calculators, lookups, schema validators) from being silently dispatched as tasks.
109
+ The `_background` override is a _modifier_ on tools the developer has already opted in at the tool or agent layer, it's not a standalone opt-in. If a tool hasn't been opted in, `_background.enabled: true` from the model is ignored and the tool runs in the foreground. This keeps deterministic, foreground-only tools (calculators, lookups, schema validators) from being silently dispatched as tasks.
110
110
 
111
111
  ### Resolution order
112
112
 
@@ -172,7 +172,7 @@ Visit [`Agent.stream()`](https://mastra.ai/reference/streaming/agents/stream) fo
172
172
 
173
173
  ### Aggregate properties
174
174
 
175
- `stream()` with `untilIdle` returns a `MastraModelOutput` that looks like the one from a regular `stream()` call, but only `fullStream` spans the initial turn **and** any auto-continuations. Aggregate properties (`text`, `toolCalls`, `toolResults`, `finishReason`, `messageList`, `getFullOutput()`) still resolve against the **first turn's** internal buffer. If you need an aggregate view across continuations, consume `fullStream` yourself and accumulate.
175
+ `stream()` with `untilIdle` returns a `MastraModelOutput` that looks like the one from a regular `stream()` call, but `fullStream` alone spans the initial turn and any auto-continuations. Aggregate properties (`text`, `toolCalls`, `toolResults`, `finishReason`, `messageList`, `getFullOutput()`) still resolve against the **first turn's** internal buffer. If you need an aggregate view across continuations, consume `fullStream` yourself and accumulate.
176
176
 
177
177
  ## Subagents in the background
178
178
 
@@ -184,7 +184,7 @@ import { Agent } from '@mastra/core/agent'
184
184
  const supervisor = new Agent({
185
185
  id: 'supervisor',
186
186
  instructions: 'Coordinate research and writing using the available agents.',
187
- model: 'openai/gpt-5.5',
187
+ model: 'openai/gpt-5.6-sol',
188
188
  agents: { researchAgent, writingAgent },
189
189
  backgroundTasks: {
190
190
  tools: {
@@ -260,7 +260,7 @@ export const reviewTool = createTool({
260
260
  })
261
261
  ```
262
262
 
263
- The first invocation of `execute` sees `resumeData === undefined` and calls `suspend`. After the task is resumed, the runtime restarts the tool with `resumeData` populated; the `if` branch falls through and the tool returns its real result.
263
+ The first invocation of `execute` sees `resumeData === undefined` and calls `suspend`. After the task is resumed, the runtime restarts the tool with `resumeData` populated. The `if` condition is false, so the tool returns its real result.
264
264
 
265
265
  To resume the task once an approval arrives:
266
266
 
@@ -273,19 +273,19 @@ await mastra.backgroundTaskManager?.resume(taskId, {
273
273
 
274
274
  ### What happens to the agent loop
275
275
 
276
- When a task suspends mid-`stream()` with `untilIdle`, the wrapper treats it as terminal for the current iteration and closes. To continue the agent immediately when the resume payload is in hand, call `agent.resumeStream(resumeData, { runId, toolCallId, memory, untilIdle: true })`: the resumed bg task runs to completion, its result lands in the message list, and the agent runs a follow-up turn all on the same SSE connection. If you'd rather drive the resume out-of-band, call `mastra.backgroundTaskManager.resume(taskId, resumeData)` directly and the result still writes into the thread for the next user turn to pick up.
276
+ When a task suspends mid-`stream()` with `untilIdle`, the wrapper treats it as terminal for the current iteration and closes. To continue the agent immediately when the resume payload is in hand, call `agent.resumeStream(resumeData, { runId, toolCallId, memory, untilIdle: true })`: the resumed bg task runs to completion, its result is added to the message list, and the agent runs a follow-up turn, all on the same SSE connection. If you'd rather drive the resume out-of-band, call `mastra.backgroundTaskManager.resume(taskId, resumeData)` directly and the result still writes into the thread for the next user turn to pick up.
277
277
 
278
278
  ### Re-registering the executor on resume
279
279
 
280
- The manager keeps tool executors in process memory. If the process restarts while a task is suspended, the executor closure is gone the caller of `resume()` must re-register it first via `manager.registerTaskContext(taskId, ...)`. Tasks dispatched and resumed inside the same process don't need this.
280
+ The manager keeps tool executors in process memory. If the process restarts while a task is suspended, the executor closure is gone, the caller of `resume()` must re-register it first via `manager.registerTaskContext(taskId, ...)`. Tasks dispatched and resumed inside the same process don't need this.
281
281
 
282
282
  ### Cancelling a suspended task
283
283
 
284
- `manager.cancel(taskId)` works against suspended tasks the same way it works for running ones: the row flips to `cancelled`, the workflow snapshot is cleaned up, and a `task.cancelled` event fires.
284
+ `manager.cancel(taskId)` works against suspended tasks the same way it works for running ones. The row changes to `cancelled` and the workflow snapshot is cleaned up. A `task.cancelled` event then fires.
285
285
 
286
286
  ## Lifecycle callbacks
287
287
 
288
- Each layer can register terminal-state callbacks. They don't replace one another, and success/failure hooks fire for their respective outcomes:
288
+ Each layer can register terminal-state callbacks. They don't replace one another, and success/failure hooks fire for their outcomes:
289
289
 
290
290
  - Tool-level `background.onComplete` / `onFailed`: scoped to one tool.
291
291
  - Agent-level `backgroundTasks.onTaskComplete` / `onTaskFailed`: scoped to all tasks dispatched by this agent.
@@ -6,7 +6,9 @@
6
6
 
7
7
  > **Beta:** The Goals feature is in beta stage and subject to breaking changes in minor versions until it graduates from its beta status.
8
8
 
9
- A goal is a durable, thread-scoped objective: a standing instruction the agent keeps working toward across loop iterations until a judge model decides it's satisfied or a run budget is exhausted. The objective is persisted in thread state, so it survives reloads and is evaluated in-loop — even when a new message arrives in the middle of an already-running turn.
9
+ A goal is a durable, thread-scoped objective: a standing instruction the agent keeps working toward across loop iterations until a judge model decides it's satisfied or a run budget is exhausted.
10
+
11
+ The objective is persisted in thread state, so it survives reloads and is evaluated in-loop, even when a new message arrives in the middle of an already-running turn.
10
12
 
11
13
  Goals build on the same machinery as [`isTaskComplete`](https://mastra.ai/docs/agents/supervisor-agents): an LLM-as-judge scores the agent's output each iteration and gates the loop. The difference is that a goal is **durable** (stored in thread state, not passed per call) and is set and updated through `Agent` methods rather than per-`stream()` options.
12
14
 
@@ -22,7 +24,7 @@ For a one-off completion check within a single `stream()` call, use [`isTaskComp
22
24
 
23
25
  ## Quickstart
24
26
 
25
- Goals require a configured [storage](https://mastra.ai/docs/storage/overview) backend and a memory-backed thread. Add a `goal` config to the agent a judge model is required for the goal to do anything then set an objective for a thread:
27
+ Goals require a configured [storage](https://mastra.ai/docs/storage/overview) backend and a memory-backed thread. Add a `goal` config to the agent, a judge model is required for the goal to do anything, then set an objective for a thread:
26
28
 
27
29
  ```typescript
28
30
  import { Agent } from '@mastra/core/agent'
@@ -31,7 +33,7 @@ const worker = new Agent({
31
33
  id: 'worker',
32
34
  name: 'worker',
33
35
  instructions: 'You complete software tasks end to end.',
34
- model: 'openai/gpt-5.5',
36
+ model: 'openai/gpt-5.6-sol',
35
37
  memory,
36
38
  goal: {
37
39
  judge: 'openai/gpt-5-mini',
@@ -51,19 +53,21 @@ const stream = await worker.stream('Start working on the goal', {
51
53
  })
52
54
  ```
53
55
 
54
- The `goal` config auto-registers the state-signal projection, so the model always sees the current objective as `<current-objective>` in its context no extra setup needed.
56
+ The `goal` config auto-registers the state-signal projection, so the model always sees the current objective as `<current-objective>` in its context without extra setup.
55
57
 
56
58
  ## How the goal step works
57
59
 
58
60
  A goal step runs inside the agentic execution loop, right after `isTaskComplete`. On a real candidate answer it scores the conversation against the objective and gates the loop:
59
61
 
60
- - **Not satisfied, budget remaining** → the loop continues; per-evaluation feedback is injected so the agent iterates.
62
+ - **Not satisfied, budget remaining** → the loop continues. Per-evaluation feedback is injected so the agent iterates.
61
63
  - **Satisfied** → the loop stops and the objective is marked `done`.
62
64
  - **Budget exhausted** (`runsUsed >= maxRuns`) → the loop stops and the objective is marked `paused`. Raise `maxRuns`, then resume the objective to continue.
63
65
 
64
- The step is a no-op for background-task, mid-tool-loop, and working-memory-only iterations the same gating as `isTaskComplete`.
66
+ The step is a no-op for background-task, mid-tool-loop, and working-memory-only iterations, the same gating as `isTaskComplete`.
67
+
68
+ **The judge model is the activation switch.** If no judge resolves (neither the per-objective override nor the agent's `goal.judge`), the goal step performs no scoring or budget consumption and emits no `goal` chunk.
65
69
 
66
- **The judge model is the activation switch.** If no judge resolves (neither the per-objective override nor the agent's `goal.judge`), the goal step does nothing: no scoring, no budget consumed, no `goal` chunk. Effective settings resolve as per-objective record value → agent `goal` config → built-in default (`maxRuns` `50`, a default judge prompt).
70
+ Effective settings resolve as per-objective record value → agent `goal` config → built-in default (`maxRuns` `50`, a default judge prompt).
67
71
 
68
72
  By default the step uses a built-in LLM-as-judge scorer that returns `1` when the objective is achieved and `0` otherwise. Supply your own scorer with `goal.scorer` to customize judging.
69
73
 
@@ -72,7 +76,7 @@ const worker = new Agent({
72
76
  id: 'worker',
73
77
  name: 'worker',
74
78
  instructions: 'You complete software tasks end to end.',
75
- model: 'openai/gpt-5.5',
79
+ model: 'openai/gpt-5.6-sol',
76
80
  memory,
77
81
  goal: {
78
82
  // A resolver function lets you inject provider credentials and read the
@@ -109,6 +113,6 @@ Per-objective values written by `setObjective` / `updateObjectiveOptions` take p
109
113
 
110
114
  ## Related
111
115
 
112
- - [Supervisor agents](https://mastra.ai/docs/agents/supervisor-agents) `isTaskComplete` and the rubric scorer
113
- - [Signal providers](https://mastra.ai/docs/long-running-agents/signal-providers) how the objective is projected into context
114
- - [Memory storage](https://mastra.ai/docs/storage/overview) the storage backend goals require
116
+ - [Supervisor agents](https://mastra.ai/docs/agents/supervisor-agents): `isTaskComplete` and the rubric scorer
117
+ - [Signal providers](https://mastra.ai/docs/long-running-agents/signal-providers): how the objective is projected into context
118
+ - [Memory storage](https://mastra.ai/docs/storage/overview): the storage backend goals require