@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.
- package/CHANGELOG.md +53 -0
- package/dist/docs/SKILL.md +6 -6
- package/dist/docs/assets/SOURCE_MAP.json +3 -205
- package/dist/docs/references/docs-agents-agent-approval.md +85 -11
- package/dist/docs/references/docs-agents-networks.md +2 -2
- package/dist/docs/references/docs-agents-supervisor-agents.md +19 -13
- package/dist/docs/references/docs-evals-evals-with-memory.md +4 -4
- package/dist/docs/references/docs-long-running-agents-background-tasks.md +10 -10
- package/dist/docs/references/docs-long-running-agents-goals.md +15 -11
- package/dist/docs/references/docs-memory-memory-processors.md +10 -10
- package/dist/docs/references/docs-memory-message-history.md +11 -5
- package/dist/docs/references/docs-memory-multi-user-threads.md +6 -6
- package/dist/docs/references/docs-memory-observational-memory.md +57 -38
- package/dist/docs/references/docs-memory-overview.md +24 -10
- package/dist/docs/references/docs-memory-semantic-recall.md +3 -3
- package/dist/docs/references/docs-memory-working-memory.md +8 -8
- package/dist/docs/references/docs-storage-overview.md +2 -2
- package/dist/docs/references/reference-memory-clone-utilities.md +1 -1
- package/dist/docs/references/reference-memory-cloneThread.md +3 -3
- package/dist/docs/references/reference-memory-listThreads.md +1 -1
- package/dist/docs/references/reference-memory-memory-class.md +4 -4
- package/dist/docs/references/reference-memory-observational-memory.md +88 -38
- package/dist/docs/references/reference-memory-summarizeConversation.md +3 -3
- package/dist/docs/references/reference-memory-summarizeThread.md +3 -3
- package/dist/docs/references/reference-processors-token-limiter-processor.md +4 -4
- package/dist/docs/references/reference-storage-dsql.md +3 -3
- package/dist/docs/references/reference-storage-dynamodb.md +7 -7
- package/dist/docs/references/reference-storage-mongodb.md +2 -2
- package/dist/docs/references/reference-storage-postgresql.md +2 -2
- package/dist/docs/references/reference-storage-redis.md +1 -1
- package/dist/docs/references/reference-storage-upstash.md +1 -1
- package/dist/docs/references/reference-vectors-libsql.md +2 -2
- package/dist/docs/references/reference-vectors-mongodb.md +188 -7
- package/dist/docs/references/reference-vectors-pg.md +5 -5
- package/dist/docs/references/reference-vectors-upstash.md +1 -1
- package/dist/index.cjs +30 -72
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/processors/index.cjs +32 -129
- package/dist/processors/index.js +2 -4
- package/dist/processors/observational-memory/index.d.ts +1 -1
- package/dist/processors/observational-memory/index.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/types.d.ts +6 -0
- package/dist/processors/observational-memory/observation-strategies/types.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +30 -1
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/processor.d.ts.map +1 -1
- package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +50 -6
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/dist/src-BYZh9IBv.js +28250 -0
- package/dist/src-BYZh9IBv.js.map +1 -0
- package/dist/src-naeQog_T.cjs +28504 -0
- package/dist/src-naeQog_T.cjs.map +1 -0
- package/package.json +9 -9
- package/dist/chunk-3UXBG7HA.js +0 -31429
- package/dist/chunk-3UXBG7HA.js.map +0 -1
- package/dist/chunk-43FJOLKM.cjs +0 -108
- package/dist/chunk-43FJOLKM.cjs.map +0 -1
- package/dist/chunk-D4J4XPGM.cjs +0 -111
- package/dist/chunk-D4J4XPGM.cjs.map +0 -1
- package/dist/chunk-EGRHWZRV.js +0 -3
- package/dist/chunk-EGRHWZRV.js.map +0 -1
- package/dist/chunk-LSJJAJAF.js +0 -105
- package/dist/chunk-LSJJAJAF.js.map +0 -1
- package/dist/chunk-NKIQRCOM.cjs +0 -4
- package/dist/chunk-NKIQRCOM.cjs.map +0 -1
- package/dist/chunk-RY3SV3BJ.cjs +0 -31496
- package/dist/chunk-RY3SV3BJ.cjs.map +0 -1
- package/dist/chunk-WCGXQIEN.js +0 -103
- package/dist/chunk-WCGXQIEN.js.map +0 -1
- package/dist/constants-RMCTOZE3.js +0 -3
- package/dist/constants-RMCTOZE3.js.map +0 -1
- package/dist/constants-ZEKSZ4UM.cjs +0 -28
- package/dist/constants-ZEKSZ4UM.cjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/observational-memory-FF4AVBKG.cjs +0 -129
- package/dist/observational-memory-FF4AVBKG.cjs.map +0 -1
- package/dist/observational-memory-OSTBP4LB.js +0 -4
- package/dist/observational-memory-OSTBP4LB.js.map +0 -1
- package/dist/processors/index.cjs.map +0 -1
- package/dist/processors/index.js.map +0 -1
- package/dist/working-memory-state-LDPWYVCG.cjs +0 -24
- package/dist/working-memory-state-LDPWYVCG.cjs.map +0 -1
- package/dist/working-memory-state-OMSMSYAW.js +0 -3
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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)
|
|
113
|
-
- [Signal providers](https://mastra.ai/docs/long-running-agents/signal-providers)
|
|
114
|
-
- [Memory storage](https://mastra.ai/docs/storage/overview)
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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`.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|