@mastra/memory 1.19.0-alpha.1 → 1.19.1-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 (28) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/{chunk-NZXH5WER.js → chunk-BCYGTJF2.js} +22 -34
  3. package/dist/chunk-BCYGTJF2.js.map +1 -0
  4. package/dist/{chunk-5IJQOXJM.cjs → chunk-IRMLP4QH.cjs} +22 -34
  5. package/dist/chunk-IRMLP4QH.cjs.map +1 -0
  6. package/dist/docs/SKILL.md +2 -1
  7. package/dist/docs/assets/SOURCE_MAP.json +27 -27
  8. package/dist/docs/references/docs-agents-agent-approval.md +2 -0
  9. package/dist/docs/references/docs-agents-background-tasks.md +9 -6
  10. package/dist/docs/references/docs-memory-multi-user-threads.md +206 -0
  11. package/dist/docs/references/docs-memory-observational-memory.md +1 -0
  12. package/dist/docs/references/docs-memory-overview.md +1 -0
  13. package/dist/docs/references/docs-memory-working-memory.md +1 -1
  14. package/dist/index.cjs +13 -13
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +4 -4
  17. package/dist/{observational-memory-KFKHBTCB.js → observational-memory-CSI3OZXQ.js} +3 -3
  18. package/dist/{observational-memory-KFKHBTCB.js.map → observational-memory-CSI3OZXQ.js.map} +1 -1
  19. package/dist/{observational-memory-V2APY3TO.cjs → observational-memory-RWXMKXJP.cjs} +26 -26
  20. package/dist/{observational-memory-V2APY3TO.cjs.map → observational-memory-RWXMKXJP.cjs.map} +1 -1
  21. package/dist/processors/index.cjs +24 -24
  22. package/dist/processors/index.js +1 -1
  23. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/temporal-markers.d.ts +1 -1
  25. package/dist/processors/observational-memory/temporal-markers.d.ts.map +1 -1
  26. package/package.json +7 -7
  27. package/dist/chunk-5IJQOXJM.cjs.map +0 -1
  28. package/dist/chunk-NZXH5WER.js.map +0 -1
@@ -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.19.0-alpha.1"
6
+ version: "1.19.1-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -23,6 +23,7 @@ Read the individual reference documents for detailed explanations and code examp
23
23
  - [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.
24
24
  - [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.
25
25
  - [Message history](references/docs-memory-message-history.md) - Learn how to configure message history in Mastra to store recent messages from the current conversation.
26
+ - [Multi-user threads](references/docs-memory-multi-user-threads.md) - Share one Mastra thread between multiple users by carrying speaker identity in the message body.
26
27
  - [Observational Memory](references/docs-memory-observational-memory.md) - Learn how Observational Memory keeps your agent's context window small while preserving long-term memory across conversations.
27
28
  - [Memory overview](references/docs-memory-overview.md) - Learn how Mastra's memory system works with working memory, message history, semantic recall, and observational memory.
28
29
  - [Semantic recall](references/docs-memory-semantic-recall.md) - Learn how to use semantic recall in Mastra to retrieve relevant messages from past conversations using vector search and embeddings.
@@ -1,119 +1,119 @@
1
1
  {
2
- "version": "1.19.0-alpha.1",
2
+ "version": "1.19.1-alpha.0",
3
3
  "package": "@mastra/memory",
4
4
  "exports": {
5
5
  "ModelByInputTokens": {
6
6
  "types": "dist/processors/index.d.ts",
7
- "implementation": "dist/chunk-NZXH5WER.js",
7
+ "implementation": "dist/chunk-BCYGTJF2.js",
8
8
  "line": 786
9
9
  },
10
10
  "OBSERVER_SYSTEM_PROMPT": {
11
11
  "types": "dist/processors/index.d.ts",
12
- "implementation": "dist/chunk-NZXH5WER.js"
12
+ "implementation": "dist/chunk-BCYGTJF2.js"
13
13
  },
14
14
  "ObservationalMemory": {
15
15
  "types": "dist/processors/index.d.ts",
16
- "implementation": "dist/chunk-NZXH5WER.js",
16
+ "implementation": "dist/chunk-BCYGTJF2.js",
17
17
  "line": 7009
18
18
  },
19
19
  "ObservationalMemoryProcessor": {
20
20
  "types": "dist/processors/index.d.ts",
21
- "implementation": "dist/chunk-NZXH5WER.js",
22
- "line": 9589
21
+ "implementation": "dist/chunk-BCYGTJF2.js",
22
+ "line": 9577
23
23
  },
24
24
  "TokenCounter": {
25
25
  "types": "dist/processors/index.d.ts",
26
- "implementation": "dist/chunk-NZXH5WER.js",
26
+ "implementation": "dist/chunk-BCYGTJF2.js",
27
27
  "line": 6455
28
28
  },
29
29
  "buildObserverPrompt": {
30
30
  "types": "dist/processors/index.d.ts",
31
- "implementation": "dist/chunk-NZXH5WER.js",
31
+ "implementation": "dist/chunk-BCYGTJF2.js",
32
32
  "line": 3760
33
33
  },
34
34
  "buildObserverSystemPrompt": {
35
35
  "types": "dist/processors/index.d.ts",
36
- "implementation": "dist/chunk-NZXH5WER.js",
36
+ "implementation": "dist/chunk-BCYGTJF2.js",
37
37
  "line": 3031
38
38
  },
39
39
  "combineObservationGroupRanges": {
40
40
  "types": "dist/processors/index.d.ts",
41
- "implementation": "dist/chunk-NZXH5WER.js",
41
+ "implementation": "dist/chunk-BCYGTJF2.js",
42
42
  "line": 878
43
43
  },
44
44
  "deriveObservationGroupProvenance": {
45
45
  "types": "dist/processors/index.d.ts",
46
- "implementation": "dist/chunk-NZXH5WER.js",
46
+ "implementation": "dist/chunk-BCYGTJF2.js",
47
47
  "line": 912
48
48
  },
49
49
  "extractCurrentTask": {
50
50
  "types": "dist/processors/index.d.ts",
51
- "implementation": "dist/chunk-NZXH5WER.js",
51
+ "implementation": "dist/chunk-BCYGTJF2.js",
52
52
  "line": 3874
53
53
  },
54
54
  "formatMessagesForObserver": {
55
55
  "types": "dist/processors/index.d.ts",
56
- "implementation": "dist/chunk-NZXH5WER.js",
56
+ "implementation": "dist/chunk-BCYGTJF2.js",
57
57
  "line": 3486
58
58
  },
59
59
  "getObservationsAsOf": {
60
60
  "types": "dist/processors/index.d.ts",
61
- "implementation": "dist/chunk-NZXH5WER.js",
62
- "line": 9801
61
+ "implementation": "dist/chunk-BCYGTJF2.js",
62
+ "line": 9789
63
63
  },
64
64
  "hasCurrentTaskSection": {
65
65
  "types": "dist/processors/index.d.ts",
66
- "implementation": "dist/chunk-NZXH5WER.js",
66
+ "implementation": "dist/chunk-BCYGTJF2.js",
67
67
  "line": 3862
68
68
  },
69
69
  "injectAnchorIds": {
70
70
  "types": "dist/processors/index.d.ts",
71
- "implementation": "dist/chunk-NZXH5WER.js",
71
+ "implementation": "dist/chunk-BCYGTJF2.js",
72
72
  "line": 2579
73
73
  },
74
74
  "optimizeObservationsForContext": {
75
75
  "types": "dist/processors/index.d.ts",
76
- "implementation": "dist/chunk-NZXH5WER.js",
76
+ "implementation": "dist/chunk-BCYGTJF2.js",
77
77
  "line": 3885
78
78
  },
79
79
  "parseAnchorId": {
80
80
  "types": "dist/processors/index.d.ts",
81
- "implementation": "dist/chunk-NZXH5WER.js",
81
+ "implementation": "dist/chunk-BCYGTJF2.js",
82
82
  "line": 2552
83
83
  },
84
84
  "parseObservationGroups": {
85
85
  "types": "dist/processors/index.d.ts",
86
- "implementation": "dist/chunk-NZXH5WER.js",
86
+ "implementation": "dist/chunk-BCYGTJF2.js",
87
87
  "line": 847
88
88
  },
89
89
  "parseObserverOutput": {
90
90
  "types": "dist/processors/index.d.ts",
91
- "implementation": "dist/chunk-NZXH5WER.js",
91
+ "implementation": "dist/chunk-BCYGTJF2.js",
92
92
  "line": 3770
93
93
  },
94
94
  "reconcileObservationGroupsFromReflection": {
95
95
  "types": "dist/processors/index.d.ts",
96
- "implementation": "dist/chunk-NZXH5WER.js",
96
+ "implementation": "dist/chunk-BCYGTJF2.js",
97
97
  "line": 936
98
98
  },
99
99
  "renderObservationGroupsForReflection": {
100
100
  "types": "dist/processors/index.d.ts",
101
- "implementation": "dist/chunk-NZXH5WER.js",
101
+ "implementation": "dist/chunk-BCYGTJF2.js",
102
102
  "line": 892
103
103
  },
104
104
  "stripEphemeralAnchorIds": {
105
105
  "types": "dist/processors/index.d.ts",
106
- "implementation": "dist/chunk-NZXH5WER.js",
106
+ "implementation": "dist/chunk-BCYGTJF2.js",
107
107
  "line": 2609
108
108
  },
109
109
  "stripObservationGroups": {
110
110
  "types": "dist/processors/index.d.ts",
111
- "implementation": "dist/chunk-NZXH5WER.js",
111
+ "implementation": "dist/chunk-BCYGTJF2.js",
112
112
  "line": 869
113
113
  },
114
114
  "wrapInObservationGroup": {
115
115
  "types": "dist/processors/index.d.ts",
116
- "implementation": "dist/chunk-NZXH5WER.js",
116
+ "implementation": "dist/chunk-BCYGTJF2.js",
117
117
  "line": 840
118
118
  },
119
119
  "OBSERVATIONAL_MEMORY_DEFAULTS": {
@@ -149,7 +149,7 @@
149
149
  "processors": {
150
150
  "index": "dist/processors/index.js",
151
151
  "chunks": [
152
- "chunk-NZXH5WER.js",
152
+ "chunk-BCYGTJF2.js",
153
153
  "chunk-LSJJAJAF.js"
154
154
  ]
155
155
  }
@@ -92,6 +92,8 @@ A tool can also pause _during_ its `execute` function by calling `suspend()`. Th
92
92
 
93
93
  The stream emits a `tool-call-suspended` chunk with a custom payload defined by the tool's `suspendSchema`. You resume by calling `resumeStream()` with data matching the tool's `resumeSchema`.
94
94
 
95
+ > **Note:** `suspend()` does not throw — return immediately after calling it (e.g. `return await suspend({ ... })`). Code after `await suspend(...)` still runs before the tool pauses.
96
+
95
97
  ## Tool approval with `generate()`
96
98
 
97
99
  Tool approval also works with `generate()` for non-streaming use cases. When a tool requires approval, `generate()` returns immediately with `finishReason: 'suspended'`, a `suspendPayload` containing the tool call details (`toolCallId`, `toolName`, `args`), and a `runId`:
@@ -40,11 +40,12 @@ The full set of options is listed in the [backgroundTasks configuration referenc
40
40
 
41
41
  ## Run a tool in the background
42
42
 
43
- Enabling the manager doesn't run anything in the background by itself as every tool defaults to foreground execution. You can run a tool in the background at one of three layers, in priority order:
43
+ Enabling the manager doesn't run anything in the background by itself as every tool defaults to foreground execution. Tools opt in at one of two layers:
44
44
 
45
- 1. **LLM per-call override**: the model decides it should run in the background and includes a `_background` field in the tool arguments.
45
+ 1. **Tool-level config**: the tool itself declares it as background-eligible.
46
46
  2. **Agent-level config**: the agent declares which of its tools are background-eligible.
47
- 3. **Tool-level config**: the tool itself declares it as background-eligible.
47
+
48
+ Once a tool has opted in, the LLM can optionally include a `_background` field in the tool arguments to override the resolved config for a specific call (timeout, retries, or to flip the call back to foreground).
48
49
 
49
50
  ### Tool-level
50
51
 
@@ -103,13 +104,15 @@ When a tool is registered on an agent that has background tasks enabled, the mod
103
104
  }
104
105
  ```
105
106
 
107
+ The `_background` override is a _modifier_ on tools the developer has already opted in at the tool or agent layer — it is 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.
108
+
106
109
  ### Resolution order
107
110
 
108
111
  When a tool call is dispatched, the resolved background config is computed in this priority order:
109
112
 
110
- 1. LLM `_background` override (if present in the call's arguments).
111
- 2. Agent-level `backgroundTasks.tools` entry for the tool.
112
- 3. Tool-level `backgroundTasks` config.
113
+ 1. Agent-level `backgroundTasks.tools` entry for the tool.
114
+ 2. Tool-level `backgroundTasks` config.
115
+ 3. LLM `_background.enabled` override (only used to enable background dispatch when the tool was opted in at one of the layers above).
113
116
  4. Manager defaults (`defaultTimeoutMs`, `defaultRetries`).
114
117
 
115
118
  If the agent has `backgroundTasks.disabled: true`, every tool call runs synchronously regardless of the layers above.
@@ -0,0 +1,206 @@
1
+ # Multi-user threads
2
+
3
+ A single Mastra thread can be shared by multiple users, each with their own name and functional role. You carry speaker identity in the message body so the agent can tell users apart while reading from a single shared thread.
4
+
5
+ ## When to use multi-user threads
6
+
7
+ Use multi-user threads when several people collaborate on the same subject through one agent:
8
+
9
+ - Collaborative documents with editors, reviewers, and approvers
10
+ - Group chats where one assistant serves many participants
11
+ - Multi-stakeholder reviews where different roles have different authority
12
+
13
+ ## Share one `resourceId` across all participants
14
+
15
+ 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.
16
+
17
+ ## Tag each user message with the speaker's identity
18
+
19
+ 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.
20
+
21
+ 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:
22
+
23
+ ```typescript
24
+ export type Speaker = {
25
+ id: string
26
+ name: string
27
+ role: string
28
+ }
29
+
30
+ function escapeAttr(value: string) {
31
+ return value
32
+ .replace(/&/g, '&amp;')
33
+ .replace(/"/g, '&quot;')
34
+ .replace(/</g, '&lt;')
35
+ .replace(/>/g, '&gt;')
36
+ }
37
+
38
+ export function asUserTurn(speaker: Speaker, text: string) {
39
+ const id = escapeAttr(speaker.id)
40
+ const name = escapeAttr(speaker.name)
41
+ const role = escapeAttr(speaker.role)
42
+ return {
43
+ role: 'user' as const,
44
+ content: `<turn author_id="${id}" author_name="${name}" functional_role="${role}">
45
+ ${text}
46
+ </turn>`,
47
+ }
48
+ }
49
+ ```
50
+
51
+ Teach the agent how to read the `<turn>` tag in its instructions. The agent must have `memory` configured so it can be called with a `thread` and `resource`:
52
+
53
+ ```typescript
54
+ import { Agent } from '@mastra/core/agent'
55
+ import { Memory } from '@mastra/memory'
56
+ import { LibSQLStore } from '@mastra/libsql'
57
+
58
+ const memory = new Memory({
59
+ storage: new LibSQLStore({ url: 'file:./collab.db' }),
60
+ options: {
61
+ lastMessages: 20,
62
+ },
63
+ })
64
+
65
+ export const collabAgent = new Agent({
66
+ id: 'collab',
67
+ name: 'CollabAgent',
68
+ model: 'openai/gpt-5.4-mini',
69
+ memory,
70
+ instructions: `
71
+ You are a collaborative document assistant. Multiple users talk to you in the SAME thread.
72
+
73
+ Every user message is wrapped in a <turn> tag carrying the user's identity:
74
+
75
+ <turn author_id="u_alice" author_name="Alice" functional_role="editor">
76
+ ...message text...
77
+ </turn>
78
+
79
+ Rules:
80
+ 1. Address users by their author_name.
81
+ 2. Respect functional_role: editors propose changes, reviewers approve.
82
+ 3. When attributing past statements, read author_name from the surrounding <turn> tag.
83
+ 4. Do not echo the <turn> tags back at users.
84
+ `.trim(),
85
+ })
86
+ ```
87
+
88
+ Call the agent with the wrapped message. Every participant shares the same `thread` and `resource`:
89
+
90
+ ```typescript
91
+ import { asUserTurn } from './identity'
92
+
93
+ const docResourceId = 'doc_42'
94
+ const docThreadId = 'doc_42'
95
+
96
+ const alice = { id: 'u_alice', name: 'Alice', role: 'editor' }
97
+ const bob = { id: 'u_bob', name: 'Bob', role: 'reviewer' }
98
+
99
+ await collabAgent.generate([asUserTurn(alice, 'My favorite color is teal.')], {
100
+ memory: { thread: docThreadId, resource: docResourceId },
101
+ })
102
+
103
+ await collabAgent.generate([asUserTurn(bob, 'I want QA sign-off before publish.')], {
104
+ memory: { thread: docThreadId, resource: docResourceId },
105
+ })
106
+ ```
107
+
108
+ The `<turn>` tag persists in the message body, so when history is recalled on later turns the model still sees who said what.
109
+
110
+ ## Combining with memory layers
111
+
112
+ The user-tagging pattern composes with every memory layer. Pick the layer based on how long the conversation needs to remember per-user facts:
113
+
114
+ - **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.
115
+ - **Long-running threads** (conversations that outgrow `lastMessages`, where you need per-user facts to survive history eviction): use [observational memory](#with-observational-memory-recommended).
116
+ - **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).
117
+
118
+ 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.
119
+
120
+ ### Message history alone
121
+
122
+ For short conversations, or when you need a verbatim record of who said what, the user tags in history are enough. `lastMessages` brings prior turns back into context with their attribution intact:
123
+
124
+ ```typescript
125
+ import { Memory } from '@mastra/memory'
126
+ import { LibSQLStore } from '@mastra/libsql'
127
+
128
+ const memory = new Memory({
129
+ storage: new LibSQLStore({ url: 'file:./collab.db' }),
130
+ options: {
131
+ lastMessages: 20,
132
+ },
133
+ })
134
+ ```
135
+
136
+ The model reads identity from the `<turn>` tag on the current message and from prior tagged messages brought back through `lastMessages`.
137
+
138
+ ### With observational memory (recommended)
139
+
140
+ [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.`
141
+
142
+ 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:
143
+
144
+ ```typescript
145
+ import { Memory } from '@mastra/memory'
146
+ import { LibSQLStore } from '@mastra/libsql'
147
+
148
+ const memory = new Memory({
149
+ storage: new LibSQLStore({ url: 'file:./collab.db' }),
150
+ options: {
151
+ lastMessages: 20,
152
+ observationalMemory: true,
153
+ },
154
+ })
155
+ ```
156
+
157
+ OM requires a storage adapter that supports it: `@mastra/libsql`, `@mastra/pg`, or `@mastra/mongodb`.
158
+
159
+ > **Note:** If you switch the Observer to a weaker model and see facts collapse to a generic `User`, use [`observation.instruction`](https://mastra.ai/reference/memory/observational-memory) to teach the Observer how to read the `<turn>` tag.
160
+
161
+ ### With working memory
162
+
163
+ 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.
164
+
165
+ 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:
166
+
167
+ ```typescript
168
+ import { Memory } from '@mastra/memory'
169
+ import { LibSQLStore } from '@mastra/libsql'
170
+
171
+ const memory = new Memory({
172
+ storage: new LibSQLStore({ url: 'file:./collab.db' }),
173
+ options: {
174
+ lastMessages: 20,
175
+ workingMemory: {
176
+ enabled: true,
177
+ scope: 'thread',
178
+ template: `# Document Collaboration State
179
+
180
+ ## Participants
181
+ <!-- One entry per known collaborator. Use author_id as the stable key. -->
182
+ <!-- - **<author_name>** (<author_id>, <functional_role>): <their position> -->
183
+
184
+ ## Open Questions
185
+
186
+ ## Decisions
187
+ `,
188
+ },
189
+ },
190
+ })
191
+ ```
192
+
193
+ Set `scope: 'thread'` so the participants list belongs to the document, not to any individual user. Add one instruction telling the agent to append new participants to the list whenever a new `author_id` shows up in a `<turn>`.
194
+
195
+ For more on templates, see [Custom templates](https://mastra.ai/docs/memory/working-memory).
196
+
197
+ ## Security
198
+
199
+ Set the `speaker` from your authenticated request context, never from the request body. If a client can choose its own `author_id`, one user can impersonate another. Use [Request Context](https://mastra.ai/docs/server/request-context) to read the verified user from your auth layer and build the `<turn>` tag on the server before calling the agent.
200
+
201
+ ## Related
202
+
203
+ - [Working memory](https://mastra.ai/docs/memory/working-memory)
204
+ - [Observational memory](https://mastra.ai/docs/memory/observational-memory)
205
+ - [Share memory between agents](https://mastra.ai/docs/memory/overview)
206
+ - [`Memory` reference](https://mastra.ai/reference/memory/memory-class)
@@ -565,6 +565,7 @@ No manual migration needed. OM reads existing messages and observes them lazily
565
565
  - **[Message history](https://mastra.ai/docs/memory/message-history)**: High-fidelity record of the current conversation
566
566
  - **[Working memory](https://mastra.ai/docs/memory/working-memory)**: Small, structured state (JSON or markdown) for user preferences, names, goals
567
567
  - **[Semantic Recall](https://mastra.ai/docs/memory/semantic-recall)**: RAG-based retrieval of relevant past messages
568
+ - **[Multi-user threads](https://mastra.ai/docs/memory/multi-user-threads)**: How OM attributes facts to individual users when several people share a single thread
568
569
 
569
570
  If you're using working memory to store conversation summaries or ongoing state that grows over time, OM is a better fit. Working memory is for small, structured data; OM is for long-running event logs. OM also manages message history automatically—the `messageTokens` setting controls how much raw history remains before observation runs.
570
571
 
@@ -7,6 +7,7 @@ Mastra agents can be configured to store [message history](https://mastra.ai/doc
7
7
  - [Observational Memory](https://mastra.ai/docs/memory/observational-memory) (Recommended): Uses background agents to maintain a dense observation log that replaces raw message history as it grows. This keeps the context window small while preserving long-term memory.
8
8
  - [Working memory](https://mastra.ai/docs/memory/working-memory): Stores persistent, structured user data such as names, preferences, and goals.
9
9
  - [Semantic recall](https://mastra.ai/docs/memory/semantic-recall): Retrieves relevant past messages based on semantic meaning rather than exact keywords.
10
+ - [Multi-user threads](https://mastra.ai/docs/memory/multi-user-threads): Share one thread between multiple users.
10
11
 
11
12
  If the combined memory exceeds the model's context limit, [memory processors](https://mastra.ai/docs/memory/memory-processors) can filter, trim, or prioritize content so the most relevant information is preserved.
12
13
 
@@ -130,7 +130,7 @@ Resource-scoped working memory requires specific storage adapters that support t
130
130
 
131
131
  ## Custom templates
132
132
 
133
- Templates guide the agent on what information to track and update in working memory. While a default template is used if none is provided, you'll typically want to define a custom template tailored to your agent's specific use case to ensure it remembers the most relevant information.
133
+ Templates guide the agent on what information to track and update in working memory. While a default template is used if none is provided, you'll typically want to define a custom template tailored to your agent's specific use case to ensure it remembers the most relevant information. For threads shared by multiple users, see [Multi-user threads](https://mastra.ai/docs/memory/multi-user-threads).
134
134
 
135
135
  Here's an example of a custom template. In this example the agent will store the users name, location, timezone, etc as soon as the user sends a message containing any of the info:
136
136
 
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk5IJQOXJM_cjs = require('./chunk-5IJQOXJM.cjs');
3
+ var chunkIRMLP4QH_cjs = require('./chunk-IRMLP4QH.cjs');
4
4
  var v3 = require('zod/v3');
5
5
  var zod = require('zod');
6
6
  var z4 = require('zod/v4');
@@ -16056,7 +16056,7 @@ function formatTimestamp(date) {
16056
16056
  }
16057
16057
  function truncateByTokens(text4, maxTokens, hint) {
16058
16058
  if (tokenx.estimateTokenCount(text4) <= maxTokens) return { text: text4, wasTruncated: false };
16059
- const truncated = chunk5IJQOXJM_cjs.truncateStringByTokens(text4, maxTokens);
16059
+ const truncated = chunkIRMLP4QH_cjs.truncateStringByTokens(text4, maxTokens);
16060
16060
  const suffix = hint ? ` [${hint} for more]` : "";
16061
16061
  return { text: truncated + suffix, wasTruncated: true };
16062
16062
  }
@@ -16108,11 +16108,11 @@ ${JSON.stringify(inv.args, null, 2)}`;
16108
16108
  });
16109
16109
  }
16110
16110
  if (inv.state === "result") {
16111
- const { value: resultValue } = chunk5IJQOXJM_cjs.resolveToolResultValue(
16111
+ const { value: resultValue } = chunkIRMLP4QH_cjs.resolveToolResultValue(
16112
16112
  part,
16113
16113
  inv.result
16114
16114
  );
16115
- const resultStr = chunk5IJQOXJM_cjs.formatToolResultForObserver(resultValue, { maxTokens: HIGH_DETAIL_TOOL_RESULT_TOKENS });
16115
+ const resultStr = chunkIRMLP4QH_cjs.formatToolResultForObserver(resultValue, { maxTokens: HIGH_DETAIL_TOOL_RESULT_TOKENS });
16116
16116
  const fullText = `[Tool Result: ${inv.toolName}]
16117
16117
  ${resultStr}`;
16118
16118
  parts.push(makePart(msg, i, "tool-result", fullText, detail, inv.toolName));
@@ -16139,7 +16139,7 @@ ${typeof rawArgs === "string" ? rawArgs : JSON.stringify(rawArgs, null, 2)}`;
16139
16139
  const toolName = part.toolName;
16140
16140
  if (toolName) {
16141
16141
  const rawResult = part.output ?? part.result;
16142
- const resultStr = chunk5IJQOXJM_cjs.formatToolResultForObserver(rawResult, { maxTokens: HIGH_DETAIL_TOOL_RESULT_TOKENS });
16142
+ const resultStr = chunkIRMLP4QH_cjs.formatToolResultForObserver(rawResult, { maxTokens: HIGH_DETAIL_TOOL_RESULT_TOKENS });
16143
16143
  const fullText = `[Tool Result: ${toolName}]
16144
16144
  ${resultStr}`;
16145
16145
  parts.push(makePart(msg, i, "tool-result", fullText, detail, toolName));
@@ -16218,7 +16218,7 @@ function renderFormattedParts(parts, timestamps, options) {
16218
16218
  const text4 = buildRenderedText(parts, timestamps);
16219
16219
  let totalTokens = tokenx.estimateTokenCount(text4);
16220
16220
  if (totalTokens > options.maxTokens) {
16221
- const truncated = chunk5IJQOXJM_cjs.truncateStringByTokens(text4, options.maxTokens);
16221
+ const truncated = chunkIRMLP4QH_cjs.truncateStringByTokens(text4, options.maxTokens);
16222
16222
  return { text: truncated, truncated: true, tokenOffset: totalTokens - options.maxTokens };
16223
16223
  }
16224
16224
  const truncatedIndices = parts.map((p, i) => ({ part: p, index: i })).filter(({ part }) => part.text !== part.fullText).sort((a, b) => expandPriority(a.part) - expandPriority(b.part));
@@ -16251,7 +16251,7 @@ function renderFormattedParts(parts, timestamps, options) {
16251
16251
  if (expandedTokens <= options.maxTokens) {
16252
16252
  return { text: expanded, truncated: false, tokenOffset: 0 };
16253
16253
  }
16254
- const hardTruncated = chunk5IJQOXJM_cjs.truncateStringByTokens(expanded, options.maxTokens);
16254
+ const hardTruncated = chunkIRMLP4QH_cjs.truncateStringByTokens(expanded, options.maxTokens);
16255
16255
  return { text: hardTruncated, truncated: true, tokenOffset: expandedTokens - options.maxTokens };
16256
16256
  }
16257
16257
  async function recallPart({
@@ -16302,7 +16302,7 @@ async function recallPart({
16302
16302
 
16303
16303
  `;
16304
16304
  const fallbackText = `${fallbackNote}${firstNextPart.text}`;
16305
- const truncatedText2 = chunk5IJQOXJM_cjs.truncateStringByTokens(fallbackText, maxTokens);
16305
+ const truncatedText2 = chunkIRMLP4QH_cjs.truncateStringByTokens(fallbackText, maxTokens);
16306
16306
  const wasTruncated2 = truncatedText2 !== fallbackText;
16307
16307
  return {
16308
16308
  text: truncatedText2,
@@ -16317,7 +16317,7 @@ async function recallPart({
16317
16317
  }
16318
16318
  throw new Error(`Part index ${partIndex} not found in message ${cursor}. Available indices: ${availableIndices}`);
16319
16319
  }
16320
- const truncatedText = chunk5IJQOXJM_cjs.truncateStringByTokens(target.text, maxTokens);
16320
+ const truncatedText = chunkIRMLP4QH_cjs.truncateStringByTokens(target.text, maxTokens);
16321
16321
  const wasTruncated = truncatedText !== target.text;
16322
16322
  return {
16323
16323
  text: truncatedText,
@@ -18079,7 +18079,7 @@ ${workingMemory}`;
18079
18079
  "Observational memory requires @mastra/core support for request-response-id-rotation. Please bump @mastra/core to a newer version."
18080
18080
  );
18081
18081
  }
18082
- const { ObservationalMemory: OMClass } = await import('./observational-memory-V2APY3TO.cjs');
18082
+ const { ObservationalMemory: OMClass } = await import('./observational-memory-RWXMKXJP.cjs');
18083
18083
  const onIndexObservations = this.hasRetrievalSearch(omConfig.retrieval) ? async (observation) => {
18084
18084
  await this.indexObservation(observation);
18085
18085
  } : void 0;
@@ -19005,7 +19005,7 @@ Notes:
19005
19005
  if (!effectiveConfig) return null;
19006
19006
  const engine = await this.omEngine;
19007
19007
  if (!engine) return null;
19008
- const { ObservationalMemoryProcessor } = await import('./observational-memory-V2APY3TO.cjs');
19008
+ const { ObservationalMemoryProcessor } = await import('./observational-memory-RWXMKXJP.cjs');
19009
19009
  return new ObservationalMemoryProcessor(engine, this, {
19010
19010
  temporalMarkers: effectiveConfig.temporalMarkers
19011
19011
  });
@@ -19014,11 +19014,11 @@ Notes:
19014
19014
 
19015
19015
  Object.defineProperty(exports, "ModelByInputTokens", {
19016
19016
  enumerable: true,
19017
- get: function () { return chunk5IJQOXJM_cjs.ModelByInputTokens; }
19017
+ get: function () { return chunkIRMLP4QH_cjs.ModelByInputTokens; }
19018
19018
  });
19019
19019
  Object.defineProperty(exports, "getObservationsAsOf", {
19020
19020
  enumerable: true,
19021
- get: function () { return chunk5IJQOXJM_cjs.getObservationsAsOf; }
19021
+ get: function () { return chunkIRMLP4QH_cjs.getObservationsAsOf; }
19022
19022
  });
19023
19023
  Object.defineProperty(exports, "MessageHistory", {
19024
19024
  enumerable: true,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { embedMany } from './_types/@internal_ai-sdk-v4/dist/index.js';
1
+ import { embedMany } from './_types/@internal_ai-sdk-v4/dist/index.d.ts';
2
2
  import type { MastraDBMessage } from '@mastra/core/agent';
3
3
  import type { Mastra } from '@mastra/core/mastra';
4
4
  import { MastraMemory } from '@mastra/core/memory';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { truncateStringByTokens, resolveToolResultValue, formatToolResultForObserver } from './chunk-NZXH5WER.js';
2
- export { ModelByInputTokens, getObservationsAsOf } from './chunk-NZXH5WER.js';
1
+ import { truncateStringByTokens, resolveToolResultValue, formatToolResultForObserver } from './chunk-BCYGTJF2.js';
2
+ export { ModelByInputTokens, getObservationsAsOf } from './chunk-BCYGTJF2.js';
3
3
  import { ZodFirstPartyTypeKind } from 'zod/v3';
4
4
  import { z } from 'zod';
5
5
  import * as z4 from 'zod/v4';
@@ -18056,7 +18056,7 @@ ${workingMemory}`;
18056
18056
  "Observational memory requires @mastra/core support for request-response-id-rotation. Please bump @mastra/core to a newer version."
18057
18057
  );
18058
18058
  }
18059
- const { ObservationalMemory: OMClass } = await import('./observational-memory-KFKHBTCB.js');
18059
+ const { ObservationalMemory: OMClass } = await import('./observational-memory-CSI3OZXQ.js');
18060
18060
  const onIndexObservations = this.hasRetrievalSearch(omConfig.retrieval) ? async (observation) => {
18061
18061
  await this.indexObservation(observation);
18062
18062
  } : void 0;
@@ -18982,7 +18982,7 @@ Notes:
18982
18982
  if (!effectiveConfig) return null;
18983
18983
  const engine = await this.omEngine;
18984
18984
  if (!engine) return null;
18985
- const { ObservationalMemoryProcessor } = await import('./observational-memory-KFKHBTCB.js');
18985
+ const { ObservationalMemoryProcessor } = await import('./observational-memory-CSI3OZXQ.js');
18986
18986
  return new ObservationalMemoryProcessor(engine, this, {
18987
18987
  temporalMarkers: effectiveConfig.temporalMarkers
18988
18988
  });
@@ -1,4 +1,4 @@
1
- export { ModelByInputTokens, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, ObservationalMemoryProcessor, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, combineObservationGroupRanges, deriveObservationGroupProvenance, extractCurrentTask, formatMessagesForObserver, getObservationsAsOf, hasCurrentTaskSection, injectAnchorIds, optimizeObservationsForContext, parseAnchorId, parseObservationGroups, parseObserverOutput, reconcileObservationGroupsFromReflection, renderObservationGroupsForReflection, stripEphemeralAnchorIds, stripObservationGroups, wrapInObservationGroup } from './chunk-NZXH5WER.js';
1
+ export { ModelByInputTokens, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, ObservationalMemoryProcessor, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, combineObservationGroupRanges, deriveObservationGroupProvenance, extractCurrentTask, formatMessagesForObserver, getObservationsAsOf, hasCurrentTaskSection, injectAnchorIds, optimizeObservationsForContext, parseAnchorId, parseObservationGroups, parseObserverOutput, reconcileObservationGroupsFromReflection, renderObservationGroupsForReflection, stripEphemeralAnchorIds, stripObservationGroups, wrapInObservationGroup } from './chunk-BCYGTJF2.js';
2
2
  export { OBSERVATIONAL_MEMORY_DEFAULTS, OBSERVATION_CONTEXT_INSTRUCTIONS, OBSERVATION_CONTEXT_PROMPT, OBSERVATION_CONTINUATION_HINT } from './chunk-LSJJAJAF.js';
3
- //# sourceMappingURL=observational-memory-KFKHBTCB.js.map
4
- //# sourceMappingURL=observational-memory-KFKHBTCB.js.map
3
+ //# sourceMappingURL=observational-memory-CSI3OZXQ.js.map
4
+ //# sourceMappingURL=observational-memory-CSI3OZXQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"observational-memory-KFKHBTCB.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"observational-memory-CSI3OZXQ.js"}