@mastra/memory 1.24.0-alpha.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 (89) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/docs/SKILL.md +6 -6
  3. package/dist/docs/assets/SOURCE_MAP.json +3 -205
  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 +188 -7
  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 +30 -72
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +2 -4
  40. package/dist/processors/index.cjs +32 -129
  41. package/dist/processors/index.js +2 -4
  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-BYZh9IBv.js +28250 -0
  54. package/dist/src-BYZh9IBv.js.map +1 -0
  55. package/dist/src-naeQog_T.cjs +28504 -0
  56. package/dist/src-naeQog_T.cjs.map +1 -0
  57. package/package.json +9 -9
  58. package/dist/chunk-3UXBG7HA.js +0 -31429
  59. package/dist/chunk-3UXBG7HA.js.map +0 -1
  60. package/dist/chunk-43FJOLKM.cjs +0 -108
  61. package/dist/chunk-43FJOLKM.cjs.map +0 -1
  62. package/dist/chunk-D4J4XPGM.cjs +0 -111
  63. package/dist/chunk-D4J4XPGM.cjs.map +0 -1
  64. package/dist/chunk-EGRHWZRV.js +0 -3
  65. package/dist/chunk-EGRHWZRV.js.map +0 -1
  66. package/dist/chunk-LSJJAJAF.js +0 -105
  67. package/dist/chunk-LSJJAJAF.js.map +0 -1
  68. package/dist/chunk-NKIQRCOM.cjs +0 -4
  69. package/dist/chunk-NKIQRCOM.cjs.map +0 -1
  70. package/dist/chunk-RY3SV3BJ.cjs +0 -31496
  71. package/dist/chunk-RY3SV3BJ.cjs.map +0 -1
  72. package/dist/chunk-WCGXQIEN.js +0 -103
  73. package/dist/chunk-WCGXQIEN.js.map +0 -1
  74. package/dist/constants-RMCTOZE3.js +0 -3
  75. package/dist/constants-RMCTOZE3.js.map +0 -1
  76. package/dist/constants-ZEKSZ4UM.cjs +0 -28
  77. package/dist/constants-ZEKSZ4UM.cjs.map +0 -1
  78. package/dist/index.cjs.map +0 -1
  79. package/dist/index.js.map +0 -1
  80. package/dist/observational-memory-FF4AVBKG.cjs +0 -129
  81. package/dist/observational-memory-FF4AVBKG.cjs.map +0 -1
  82. package/dist/observational-memory-OSTBP4LB.js +0 -4
  83. package/dist/observational-memory-OSTBP4LB.js.map +0 -1
  84. package/dist/processors/index.cjs.map +0 -1
  85. package/dist/processors/index.js.map +0 -1
  86. package/dist/working-memory-state-LDPWYVCG.cjs +0 -24
  87. package/dist/working-memory-state-LDPWYVCG.cjs.map +0 -1
  88. package/dist/working-memory-state-OMSMSYAW.js +0 -3
  89. package/dist/working-memory-state-OMSMSYAW.js.map +0 -1
@@ -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
@@ -2,9 +2,9 @@
2
2
 
3
3
  # Memory processors
4
4
 
5
- Memory processors transform and filter messages as they pass through an agent with memory enabled. They manage context window limits, remove unnecessary content, and optimize the information sent to the language model.
5
+ Memory processors transform and filter messages as they pass through an agent with memory enabled. They manage context window limits and remove unnecessary content, plus optimize the information sent to the language model.
6
6
 
7
- When memory is enabled on an agent, Mastra adds memory processors to the agent's processor pipeline. These processors retrieve message history, working memory, and semantically relevant messages, then persist new messages after the model responds.
7
+ When memory is enabled on an agent, Mastra adds memory processors to the agent's processor pipeline. These processors retrieve message history and working memory, plus semantically relevant messages, then persist new messages after the model responds.
8
8
 
9
9
  Memory processors are [processors](https://mastra.ai/docs/agents/processors) that operate specifically on memory-related messages and state.
10
10
 
@@ -47,7 +47,7 @@ const agent = new Agent({
47
47
  id: 'test-agent',
48
48
  name: 'Test Agent',
49
49
  instructions: 'You are a helpful assistant',
50
- model: 'openai/gpt-5.5',
50
+ model: 'openai/gpt-5.6-sol',
51
51
  memory: new Memory({
52
52
  storage: new LibSQLStore({
53
53
  id: 'memory-store',
@@ -97,7 +97,7 @@ import { openai } from '@ai-sdk/openai'
97
97
  const agent = new Agent({
98
98
  name: 'semantic-agent',
99
99
  instructions: 'You are a helpful assistant with semantic memory',
100
- model: 'openai/gpt-5.5',
100
+ model: 'openai/gpt-5.6-sol',
101
101
  memory: new Memory({
102
102
  storage: new LibSQLStore({
103
103
  id: 'memory-store',
@@ -150,7 +150,7 @@ import { openai } from '@ai-sdk/openai'
150
150
  const agent = new Agent({
151
151
  name: 'working-memory-agent',
152
152
  instructions: 'You are an assistant with working memory',
153
- model: 'openai/gpt-5.5',
153
+ model: 'openai/gpt-5.6-sol',
154
154
  memory: new Memory({
155
155
  storage: new LibSQLStore({
156
156
  id: 'memory-store',
@@ -182,7 +182,7 @@ const customMessageHistory = new MessageHistory({
182
182
  const agent = new Agent({
183
183
  name: 'custom-memory-agent',
184
184
  instructions: 'You are a helpful assistant',
185
- model: 'openai/gpt-5.5',
185
+ model: 'openai/gpt-5.6-sol',
186
186
  memory: new Memory({
187
187
  storage: new LibSQLStore({ id: 'memory-store', url: 'file:memory.db' }),
188
188
  lastMessages: 10, // This would normally add MessageHistory(10)
@@ -207,7 +207,7 @@ Understanding the execution order is important when combining guardrails with me
207
207
  1. **Memory processors run FIRST**: `WorkingMemory`, `MessageHistory`, `SemanticRecall`
208
208
  2. **Your input processors run AFTER**: guardrails, filters, validators
209
209
 
210
- This means memory loads message history before your processors can validate or filter the input.
210
+ As a result, memory loads message history before your processors can validate or filter the input.
211
211
 
212
212
  ### Output Processors
213
213
 
@@ -253,7 +253,7 @@ const agent = new Agent({
253
253
  id: 'safe-agent',
254
254
  name: 'safe-agent',
255
255
  instructions: 'You are a helpful assistant',
256
- model: 'openai/gpt-5.5',
256
+ model: 'openai/gpt-5.6-sol',
257
257
  memory: new Memory({ lastMessages: 10 }),
258
258
  // Your guardrail runs BEFORE memory saves
259
259
  outputProcessors: [contentBlocker],
@@ -293,7 +293,7 @@ const agent = new Agent({
293
293
  id: 'validated-agent',
294
294
  name: 'validated-agent',
295
295
  instructions: 'You are a helpful assistant',
296
- model: 'openai/gpt-5.5',
296
+ model: 'openai/gpt-5.6-sol',
297
297
  memory: new Memory({ lastMessages: 10 }),
298
298
  // Your guardrail runs AFTER memory loads history
299
299
  inputProcessors: [inputValidator],
@@ -370,7 +370,7 @@ export const supportAgent = new Agent({
370
370
  id: 'support-agent',
371
371
  name: 'Support agent',
372
372
  instructions: 'Answer customer support questions.',
373
- model: 'openai/gpt-5.5',
373
+ model: 'openai/gpt-5.6-sol',
374
374
  memory: new Memory({ lastMessages: 10 }),
375
375
  inputProcessors: [new AttachmentUploader()],
376
376
  })
@@ -134,7 +134,7 @@ export const supportAgent = new Agent({
134
134
  id: 'support-agent',
135
135
  name: 'Support agent',
136
136
  instructions: 'Answer customer support questions.',
137
- model: 'openai/gpt-5.5',
137
+ model: 'openai/gpt-5.6-sol',
138
138
  memory: new Memory({
139
139
  options: {
140
140
  generateTitle: true,
@@ -155,7 +155,7 @@ export const supportAgent = new Agent({
155
155
  id: 'support-agent',
156
156
  name: 'Support agent',
157
157
  instructions: 'Answer customer support questions.',
158
- model: 'openai/gpt-5.5',
158
+ model: 'openai/gpt-5.6-sol',
159
159
  memory: new Memory({
160
160
  options: {
161
161
  generateTitle: {
@@ -176,7 +176,7 @@ const agent = mastra.getAgentById('test-agent')
176
176
  const memory = await agent.getMemory()
177
177
  ```
178
178
 
179
- The `Memory` instance gives you access to functions for listing threads, recalling messages, cloning conversations, and more.
179
+ The `Memory` instance gives you access to functions for listing threads and recalling messages, plus cloning conversations, and more.
180
180
 
181
181
  ## Querying
182
182
 
@@ -279,7 +279,13 @@ const { messages } = await memory.recall({
279
279
  })
280
280
  ```
281
281
 
282
- Metadata filters match shallow scalar values only: `string`, finite `number`, `boolean`, and `null`. All specified metadata keys must match with AND semantics, and `null` matches an explicit `null` value, not a missing metadata key. Metadata keys must start with a letter or underscore, may contain only alphanumeric characters and underscores, must be 128 characters or fewer, and can't use reserved prototype keys such as `__proto__`, `constructor`, or `prototype`. Performance depends on the storage backend. Some backends can push parts of the filter into the database, while others scan candidate messages after thread, resource, and date constraints are applied but before pagination.
282
+ Metadata filters match shallow scalar values only: `string`, finite `number`, `boolean`, and `null`.
283
+
284
+ All specified metadata keys use AND semantics. A `null` filter matches only an explicit `null` value. A missing metadata key doesn't match.
285
+
286
+ Metadata keys must start with a letter or underscore and contain only alphanumeric characters. They must be 128 characters or fewer and can't use reserved prototype keys such as `__proto__`, `constructor`, or `prototype`.
287
+
288
+ Performance depends on the storage backend. Some backends can push parts of the filter into the database, while others scan candidate messages after thread, resource, and date constraints are applied but before pagination.
283
289
 
284
290
  Fetch a single message by ID:
285
291
 
@@ -324,7 +330,7 @@ Message queries return `MastraDBMessage[]` format. To display messages in a fron
324
330
 
325
331
  ## Thread cloning
326
332
 
327
- Thread cloning creates a copy of an existing thread with its messages. This is useful for branching conversations, creating checkpoints before a potentially destructive operation, or testing variations of a conversation.
333
+ Thread cloning creates a copy of an existing thread with its messages. This is useful for branching conversations or creating checkpoints before a potentially destructive operation, or alternatively testing variations of a conversation.
328
334
 
329
335
  ```typescript
330
336
  const { thread, clonedMessages } = await memory.cloneThread({
@@ -14,13 +14,13 @@ Use multi-user threads when several people collaborate on the same subject throu
14
14
 
15
15
  ## Share one `resourceId` across all participants
16
16
 
17
- A thread belongs to exactly one `resourceId`, so all participants on a shared thread need to pass the same value. Instead of using a user id (the default for single-user apps), key `resourceId` on the conversation itself for example `doc_${docId}` for a shared document, or `room_${roomId}` for a group chat. With everyone pointing at the same `resourceId`, they read and write the same history.
17
+ A thread belongs to exactly one `resourceId`, so all participants on a shared thread need to pass the same value. Instead of using a user id (the default for single-user apps), key `resourceId` on the conversation itself, for example `doc_${docId}` for a shared document, or `room_${roomId}` for a group chat. With everyone pointing at the same `resourceId`, they read and write the same history.
18
18
 
19
19
  ## Tag each user message with the speaker's identity
20
20
 
21
21
  The model needs to know who's talking on every turn. Since the message body is the one place that survives into history and back into context, wrap each user message in a small `<turn>` tag with the speaker's id, name, and role. The tag stays attached to the message, so when prior turns are recalled the model still sees who said what.
22
22
 
23
- Build the tag with a small helper. The example below is one way to do it copy it into your project and adapt it to your shape of user data:
23
+ Build the tag with a small helper. The example below is one way to do it, copy it into your project and adapt it to your shape of user data:
24
24
 
25
25
  ```typescript
26
26
  export type Speaker = {
@@ -113,11 +113,11 @@ The `<turn>` tag persists in the message body, so when history is recalled on la
113
113
 
114
114
  The user-tagging pattern composes with every memory layer. Pick the layer based on how long the conversation needs to remember per-user facts:
115
115
 
116
- - **Short conversations** (a single session, or a thread small enough to fit in `lastMessages`), or when you need a verbatim record of who said what: use [message history alone](#message-history-alone). The user tags in history are enough; no extra memory layer needed.
116
+ - **Short conversations** (a single session, or a thread small enough to fit in `lastMessages`), or when you need a verbatim record of who said what: use [message history alone](#message-history-alone). The user tags in history are enough. No extra memory layer needed.
117
117
  - **Long-running threads** (conversations that outgrow `lastMessages`, where you need per-user facts to survive history eviction): use [observational memory](#with-observational-memory-recommended).
118
118
  - **Need a structured participants list, or your storage adapter doesn't support OM** (OM requires LibSQL, PG, or MongoDB): use [working memory](#with-working-memory).
119
119
 
120
- We recommend using observational memory or working memory, not both — they cover overlapping needs, and running both at once adds latency and token cost without much benefit.
120
+ We recommend using either observational memory or working memory because they cover overlapping needs. Running both adds latency and token cost without much benefit.
121
121
 
122
122
  ### Message history alone
123
123
 
@@ -139,7 +139,7 @@ The model reads identity from the `<turn>` tag on the current message and from p
139
139
 
140
140
  ### With Observational Memory (recommended)
141
141
 
142
- [Observational Memory](https://mastra.ai/docs/memory/observational-memory) (OM) extracts per-user facts into a background log without burning the agent's tool budget. The default Observer model reads `<turn>` tags natively and produces named attribution like `Alice stated her favorite color is teal.` and `Bob asked for QA sign-off before publish.`
142
+ [Observational Memory](https://mastra.ai/docs/memory/observational-memory) (OM) extracts per-user facts into a background log without burning the agent's tool budget. The default Observer model reads `<turn>` tags natively and produces attribution like `Alice stated her favorite color is teal.` and `Bob asked for QA sign-off before publish.`
143
143
 
144
144
  Prefer OM over working memory for multi-user threads when your storage supports it. OM extracts facts automatically, scales to any number of participants, and doesn't need template upkeep. Enable it with no overrides:
145
145
 
@@ -162,7 +162,7 @@ OM requires a storage adapter that supports it: `@mastra/libsql`, `@mastra/pg`,
162
162
 
163
163
  ### With working memory
164
164
 
165
- Use working memory when OM isn't an option for example, when your storage adapter doesn't support OM, or when you need a structured, deterministic participants list the agent can read and write on every turn.
165
+ Use working memory when OM isn't an option, for example, when your storage adapter doesn't support OM, or when you need a structured, deterministic participants list the agent can read and write on every turn.
166
166
 
167
167
  The default [working memory](https://mastra.ai/docs/memory/working-memory) template assumes one user per thread ("First Name", "Last Name", etc.). For multi-user threads, provide a template with a participants list:
168
168