@mastra/memory 1.5.2 → 1.6.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 +54 -0
- package/LICENSE.md +15 -0
- package/dist/{chunk-PVFLHAZX.cjs → chunk-5UYAHJVJ.cjs} +77 -73
- package/dist/chunk-5UYAHJVJ.cjs.map +1 -0
- package/dist/{chunk-HNPAIFCZ.js → chunk-A62BQK35.js} +77 -73
- package/dist/chunk-A62BQK35.js.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +16 -16
- package/dist/docs/references/docs-memory-observational-memory.md +3 -5
- package/dist/docs/references/reference-memory-cloneThread.md +13 -1
- package/dist/docs/references/reference-memory-observational-memory.md +4 -2
- package/dist/index.cjs +106 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +106 -7
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-Q47HN5YL.cjs → observational-memory-MXI54VC7.cjs} +17 -17
- package/dist/{observational-memory-Q47HN5YL.cjs.map → observational-memory-MXI54VC7.cjs.map} +1 -1
- package/dist/{observational-memory-KAFD4QZK.js → observational-memory-SR6G4HN5.js} +3 -3
- package/dist/{observational-memory-KAFD4QZK.js.map → observational-memory-SR6G4HN5.js.map} +1 -1
- package/dist/processors/index.cjs +15 -15
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +3 -4
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +8 -2
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/chunk-HNPAIFCZ.js.map +0 -1
- package/dist/chunk-PVFLHAZX.cjs.map +0 -1
package/dist/docs/SKILL.md
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.6.0",
|
|
3
3
|
"package": "@mastra/memory",
|
|
4
4
|
"exports": {
|
|
5
5
|
"OBSERVATIONAL_MEMORY_DEFAULTS": {
|
|
6
6
|
"types": "dist/processors/index.d.ts",
|
|
7
|
-
"implementation": "dist/chunk-
|
|
7
|
+
"implementation": "dist/chunk-A62BQK35.js"
|
|
8
8
|
},
|
|
9
9
|
"OBSERVATION_CONTEXT_INSTRUCTIONS": {
|
|
10
10
|
"types": "dist/processors/index.d.ts",
|
|
11
|
-
"implementation": "dist/chunk-
|
|
11
|
+
"implementation": "dist/chunk-A62BQK35.js"
|
|
12
12
|
},
|
|
13
13
|
"OBSERVATION_CONTEXT_PROMPT": {
|
|
14
14
|
"types": "dist/processors/index.d.ts",
|
|
15
|
-
"implementation": "dist/chunk-
|
|
15
|
+
"implementation": "dist/chunk-A62BQK35.js"
|
|
16
16
|
},
|
|
17
17
|
"OBSERVATION_CONTINUATION_HINT": {
|
|
18
18
|
"types": "dist/processors/index.d.ts",
|
|
19
|
-
"implementation": "dist/chunk-
|
|
19
|
+
"implementation": "dist/chunk-A62BQK35.js"
|
|
20
20
|
},
|
|
21
21
|
"OBSERVER_SYSTEM_PROMPT": {
|
|
22
22
|
"types": "dist/processors/index.d.ts",
|
|
23
|
-
"implementation": "dist/chunk-
|
|
23
|
+
"implementation": "dist/chunk-A62BQK35.js"
|
|
24
24
|
},
|
|
25
25
|
"ObservationalMemory": {
|
|
26
26
|
"types": "dist/processors/index.d.ts",
|
|
27
|
-
"implementation": "dist/chunk-
|
|
27
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
28
28
|
"line": 1258
|
|
29
29
|
},
|
|
30
30
|
"TokenCounter": {
|
|
31
31
|
"types": "dist/processors/index.d.ts",
|
|
32
|
-
"implementation": "dist/chunk-
|
|
32
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
33
33
|
"line": 919
|
|
34
34
|
},
|
|
35
35
|
"buildObserverPrompt": {
|
|
36
36
|
"types": "dist/processors/index.d.ts",
|
|
37
|
-
"implementation": "dist/chunk-
|
|
37
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
38
38
|
"line": 533
|
|
39
39
|
},
|
|
40
40
|
"buildObserverSystemPrompt": {
|
|
41
41
|
"types": "dist/processors/index.d.ts",
|
|
42
|
-
"implementation": "dist/chunk-
|
|
42
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
43
43
|
"line": 281
|
|
44
44
|
},
|
|
45
45
|
"extractCurrentTask": {
|
|
46
46
|
"types": "dist/processors/index.d.ts",
|
|
47
|
-
"implementation": "dist/chunk-
|
|
47
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
48
48
|
"line": 662
|
|
49
49
|
},
|
|
50
50
|
"formatMessagesForObserver": {
|
|
51
51
|
"types": "dist/processors/index.d.ts",
|
|
52
|
-
"implementation": "dist/chunk-
|
|
52
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
53
53
|
"line": 376
|
|
54
54
|
},
|
|
55
55
|
"hasCurrentTaskSection": {
|
|
56
56
|
"types": "dist/processors/index.d.ts",
|
|
57
|
-
"implementation": "dist/chunk-
|
|
57
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
58
58
|
"line": 650
|
|
59
59
|
},
|
|
60
60
|
"optimizeObservationsForContext": {
|
|
61
61
|
"types": "dist/processors/index.d.ts",
|
|
62
|
-
"implementation": "dist/chunk-
|
|
62
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
63
63
|
"line": 673
|
|
64
64
|
},
|
|
65
65
|
"parseObserverOutput": {
|
|
66
66
|
"types": "dist/processors/index.d.ts",
|
|
67
|
-
"implementation": "dist/chunk-
|
|
67
|
+
"implementation": "dist/chunk-A62BQK35.js",
|
|
68
68
|
"line": 564
|
|
69
69
|
},
|
|
70
70
|
"extractWorkingMemoryContent": {
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"processors": {
|
|
97
97
|
"index": "dist/processors/index.js",
|
|
98
98
|
"chunks": [
|
|
99
|
-
"chunk-
|
|
99
|
+
"chunk-A62BQK35.js"
|
|
100
100
|
]
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -85,13 +85,11 @@ The result is a three-tier system:
|
|
|
85
85
|
|
|
86
86
|
## Models
|
|
87
87
|
|
|
88
|
-
The Observer and Reflector run in the background. Any model that works with Mastra's model routing (
|
|
88
|
+
The Observer and Reflector run in the background. Any model that works with Mastra's [model routing](https://mastra.ai/models) (`provider/model`) can be used. When using `observationalMemory: true`, the default model is `google/gemini-2.5-flash`. When passing a config object, a `model` must be explicitly set.
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Generally speaking, we recommend using a model that has a large context window (128K+ tokens) and is fast enough to run in the background without slowing down your actions.
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
We've also tested `deepseek`, `qwen3`, and `glm-4.7` for the Observer. For the Reflector, make sure the model's context window can fit all observations. Note that Claude 4.5 models currently don't work well as observer or reflector.
|
|
92
|
+
If you're unsure which model to use, start with the default `google/gemini-2.5-flash`. We've also successfully tested `openai/gpt-5-mini`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-reasoner`, `qwen3`, and `glm-4.7`.
|
|
95
93
|
|
|
96
94
|
```typescript
|
|
97
95
|
const memory = new Memory({
|
|
@@ -44,6 +44,8 @@ const { thread, clonedMessages } = await memory.cloneThread({
|
|
|
44
44
|
|
|
45
45
|
**clonedMessages:** (`MastraDBMessage[]`): Array of the cloned messages with new IDs assigned to the new thread.
|
|
46
46
|
|
|
47
|
+
**messageIdMap?:** (`Record<string, string>`): A mapping from source message IDs to their corresponding cloned message IDs.
|
|
48
|
+
|
|
47
49
|
### Clone Metadata
|
|
48
50
|
|
|
49
51
|
The cloned thread's metadata includes a `clone` property with:
|
|
@@ -127,4 +129,14 @@ const results = await memory.recall({
|
|
|
127
129
|
threadId: thread.id,
|
|
128
130
|
vectorSearchString: 'search query',
|
|
129
131
|
})
|
|
130
|
-
```
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Observational Memory
|
|
135
|
+
|
|
136
|
+
When [Observational Memory](https://mastra.ai/docs/memory/observational-memory) is enabled, `cloneThread()` automatically clones the OM records associated with the source thread. The behavior depends on the OM scope:
|
|
137
|
+
|
|
138
|
+
- **Thread-scoped OM**: The OM record is cloned to the new thread. All internal message ID references are remapped to point to the cloned messages.
|
|
139
|
+
- **Resource-scoped OM (same `resourceId`)**: The OM record is shared between the source and cloned threads since they belong to the same resource. No duplication occurs.
|
|
140
|
+
- **Resource-scoped OM (different `resourceId`)**: The OM record is cloned to the new resource. Message IDs are remapped and any thread-identifying tags within observations are updated to reference the cloned thread.
|
|
141
|
+
|
|
142
|
+
Only the current (most recent) OM generation is cloned — older history generations are not copied. Transient processing state (observation/reflection in-progress flags) is reset on the cloned record.
|
|
@@ -107,6 +107,8 @@ export const agent = new Agent({
|
|
|
107
107
|
|
|
108
108
|
### Shared token budget
|
|
109
109
|
|
|
110
|
+
When `shareTokenBudget` is enabled, the total budget is `observation.messageTokens + reflection.observationTokens` (100k in this example). If observations only use 30k tokens, messages can expand to use up to 70k. If messages are short, observations have more room before triggering reflection.
|
|
111
|
+
|
|
110
112
|
```typescript
|
|
111
113
|
import { Memory } from '@mastra/memory'
|
|
112
114
|
import { Agent } from '@mastra/core/agent'
|
|
@@ -132,10 +134,10 @@ export const agent = new Agent({
|
|
|
132
134
|
})
|
|
133
135
|
```
|
|
134
136
|
|
|
135
|
-
When `shareTokenBudget` is enabled, the total budget is `observation.messageTokens + reflection.observationTokens` (100k in this example). If observations only use 30k tokens, messages can expand to use up to 70k. If messages are short, observations have more room before triggering reflection.
|
|
136
|
-
|
|
137
137
|
### Custom model
|
|
138
138
|
|
|
139
|
+
By passing a `model` in the config, you can use any model from Mastra's model router.
|
|
140
|
+
|
|
139
141
|
```typescript
|
|
140
142
|
import { Memory } from '@mastra/memory'
|
|
141
143
|
import { Agent } from '@mastra/core/agent'
|
package/dist/index.cjs
CHANGED
|
@@ -15364,7 +15364,8 @@ var Memory = class extends memory.MastraMemory {
|
|
|
15364
15364
|
const rawMessages = shouldGetNewestAndReverse ? paginatedResult.messages.reverse() : paginatedResult.messages;
|
|
15365
15365
|
const list = new agent.MessageList({ threadId, resourceId }).add(rawMessages, "memory");
|
|
15366
15366
|
const messages = list.get.all.db();
|
|
15367
|
-
|
|
15367
|
+
const { total, page: resultPage, perPage: resultPerPage, hasMore } = paginatedResult;
|
|
15368
|
+
return { messages, usage, total, page: resultPage, perPage: resultPerPage, hasMore };
|
|
15368
15369
|
}
|
|
15369
15370
|
async getThreadById({ threadId }) {
|
|
15370
15371
|
const memoryStore = await this.getMemoryStore();
|
|
@@ -16119,13 +16120,10 @@ Notes:
|
|
|
16119
16120
|
const memoryStore = await this.getMemoryStore();
|
|
16120
16121
|
const result = await memoryStore.cloneThread(args);
|
|
16121
16122
|
const config = this.getMergedThreadConfig(memoryConfig);
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
}
|
|
16123
|
+
const sourceThread = await this.getThreadById({ threadId: args.sourceThreadId });
|
|
16124
|
+
const sourceResourceId = sourceThread?.resourceId;
|
|
16125
16125
|
if (config.workingMemory?.enabled) {
|
|
16126
16126
|
const scope = config.workingMemory.scope || "resource";
|
|
16127
|
-
const sourceThread = await this.getThreadById({ threadId: args.sourceThreadId });
|
|
16128
|
-
const sourceResourceId = sourceThread?.resourceId;
|
|
16129
16127
|
const shouldCopy = scope === "thread" || scope === "resource" && args.resourceId && args.resourceId !== sourceResourceId;
|
|
16130
16128
|
if (shouldCopy) {
|
|
16131
16129
|
const sourceWm = await this.getWorkingMemory({
|
|
@@ -16143,8 +16141,109 @@ Notes:
|
|
|
16143
16141
|
}
|
|
16144
16142
|
}
|
|
16145
16143
|
}
|
|
16144
|
+
if (memoryStore.supportsObservationalMemory && sourceResourceId) {
|
|
16145
|
+
try {
|
|
16146
|
+
await this.cloneObservationalMemory(memoryStore, args.sourceThreadId, sourceResourceId, result);
|
|
16147
|
+
} catch (error) {
|
|
16148
|
+
try {
|
|
16149
|
+
await memoryStore.deleteThread({ threadId: result.thread.id });
|
|
16150
|
+
} catch (rollbackError) {
|
|
16151
|
+
this.logger.error("Failed to rollback cloned thread after OM clone failure", rollbackError);
|
|
16152
|
+
}
|
|
16153
|
+
throw error;
|
|
16154
|
+
}
|
|
16155
|
+
}
|
|
16156
|
+
if (this.vector && config.semanticRecall && result.clonedMessages.length > 0) {
|
|
16157
|
+
await this.embedClonedMessages(result.clonedMessages, config);
|
|
16158
|
+
}
|
|
16146
16159
|
return result;
|
|
16147
16160
|
}
|
|
16161
|
+
/**
|
|
16162
|
+
* Clone observational memory records when cloning a thread.
|
|
16163
|
+
* Thread-scoped: always cloned to the new thread.
|
|
16164
|
+
* Resource-scoped: cloned only when the resourceId changes (same resourceId shares OM naturally).
|
|
16165
|
+
* All stored message/thread IDs are remapped to the cloned IDs.
|
|
16166
|
+
*/
|
|
16167
|
+
async cloneObservationalMemory(memoryStore, sourceThreadId, sourceResourceId, result) {
|
|
16168
|
+
let sourceOM = await memoryStore.getObservationalMemory(sourceThreadId, sourceResourceId);
|
|
16169
|
+
if (!sourceOM) {
|
|
16170
|
+
sourceOM = await memoryStore.getObservationalMemory(null, sourceResourceId);
|
|
16171
|
+
}
|
|
16172
|
+
if (!sourceOM) return;
|
|
16173
|
+
const clonedThreadId = result.thread.id;
|
|
16174
|
+
const clonedResourceId = result.thread.resourceId;
|
|
16175
|
+
const resourceChanged = clonedResourceId !== sourceResourceId;
|
|
16176
|
+
if (sourceOM.scope === "resource" && !resourceChanged) return;
|
|
16177
|
+
const messageIdMap = result.messageIdMap ?? {};
|
|
16178
|
+
const hasher = await this.hasher;
|
|
16179
|
+
const cloned = this.remapObservationalMemoryRecord(sourceOM, {
|
|
16180
|
+
newThreadId: sourceOM.scope === "thread" ? clonedThreadId : null,
|
|
16181
|
+
newResourceId: clonedResourceId,
|
|
16182
|
+
messageIdMap,
|
|
16183
|
+
sourceThreadId: resourceChanged ? sourceThreadId : void 0,
|
|
16184
|
+
clonedThreadId: resourceChanged ? clonedThreadId : void 0,
|
|
16185
|
+
hasher: resourceChanged ? hasher : void 0
|
|
16186
|
+
});
|
|
16187
|
+
const now = /* @__PURE__ */ new Date();
|
|
16188
|
+
cloned.id = crypto.randomUUID();
|
|
16189
|
+
cloned.createdAt = now;
|
|
16190
|
+
cloned.updatedAt = now;
|
|
16191
|
+
await memoryStore.insertObservationalMemoryRecord(cloned);
|
|
16192
|
+
}
|
|
16193
|
+
/**
|
|
16194
|
+
* Create a remapped copy of an OM record with new thread/message IDs.
|
|
16195
|
+
*/
|
|
16196
|
+
remapObservationalMemoryRecord(record, opts) {
|
|
16197
|
+
const { newThreadId, newResourceId, messageIdMap, sourceThreadId, clonedThreadId, hasher } = opts;
|
|
16198
|
+
const cloned = { ...record };
|
|
16199
|
+
cloned.threadId = newThreadId;
|
|
16200
|
+
cloned.resourceId = newResourceId;
|
|
16201
|
+
if (Array.isArray(cloned.observedMessageIds)) {
|
|
16202
|
+
cloned.observedMessageIds = cloned.observedMessageIds.map((id) => messageIdMap[id]).filter((id) => Boolean(id));
|
|
16203
|
+
} else {
|
|
16204
|
+
cloned.observedMessageIds = void 0;
|
|
16205
|
+
}
|
|
16206
|
+
if (Array.isArray(cloned.bufferedMessageIds)) {
|
|
16207
|
+
cloned.bufferedMessageIds = cloned.bufferedMessageIds.map((id) => messageIdMap[id]).filter((id) => Boolean(id));
|
|
16208
|
+
} else {
|
|
16209
|
+
cloned.bufferedMessageIds = void 0;
|
|
16210
|
+
}
|
|
16211
|
+
if (Array.isArray(cloned.bufferedObservationChunks)) {
|
|
16212
|
+
cloned.bufferedObservationChunks = cloned.bufferedObservationChunks.map(
|
|
16213
|
+
(chunk) => ({
|
|
16214
|
+
...chunk,
|
|
16215
|
+
messageIds: Array.isArray(chunk.messageIds) ? chunk.messageIds.map((id) => messageIdMap[id]).filter((id) => Boolean(id)) : []
|
|
16216
|
+
})
|
|
16217
|
+
);
|
|
16218
|
+
} else {
|
|
16219
|
+
cloned.bufferedObservationChunks = void 0;
|
|
16220
|
+
}
|
|
16221
|
+
if (sourceThreadId && clonedThreadId && hasher) {
|
|
16222
|
+
const sourceObscured = hasher.h32ToString(sourceThreadId);
|
|
16223
|
+
const clonedObscured = hasher.h32ToString(clonedThreadId);
|
|
16224
|
+
if (sourceObscured !== clonedObscured) {
|
|
16225
|
+
const replaceThreadTags = (text4) => {
|
|
16226
|
+
if (!text4) return text4;
|
|
16227
|
+
return text4.replaceAll(`<thread id="${sourceObscured}">`, `<thread id="${clonedObscured}">`);
|
|
16228
|
+
};
|
|
16229
|
+
cloned.activeObservations = replaceThreadTags(cloned.activeObservations) ?? "";
|
|
16230
|
+
cloned.bufferedReflection = replaceThreadTags(cloned.bufferedReflection);
|
|
16231
|
+
if (cloned.bufferedObservationChunks) {
|
|
16232
|
+
cloned.bufferedObservationChunks = cloned.bufferedObservationChunks.map(
|
|
16233
|
+
(chunk) => ({
|
|
16234
|
+
...chunk,
|
|
16235
|
+
observations: replaceThreadTags(chunk.observations) ?? chunk.observations
|
|
16236
|
+
})
|
|
16237
|
+
);
|
|
16238
|
+
}
|
|
16239
|
+
}
|
|
16240
|
+
}
|
|
16241
|
+
cloned.isObserving = false;
|
|
16242
|
+
cloned.isReflecting = false;
|
|
16243
|
+
cloned.isBufferingObservation = false;
|
|
16244
|
+
cloned.isBufferingReflection = false;
|
|
16245
|
+
return cloned;
|
|
16246
|
+
}
|
|
16148
16247
|
/**
|
|
16149
16248
|
* Embed cloned messages for semantic recall.
|
|
16150
16249
|
* This is similar to the embedding logic in saveMessages but operates on already-saved messages.
|
|
@@ -16377,7 +16476,7 @@ Notes:
|
|
|
16377
16476
|
"Observational memory async buffering is enabled by default but the installed version of @mastra/core does not support it. Either upgrade @mastra/core, @mastra/memory, and your storage adapter (@mastra/libsql, @mastra/pg, or @mastra/mongodb) to the latest version, or explicitly disable async buffering by setting `observation: { bufferTokens: false }` in your observationalMemory config."
|
|
16378
16477
|
);
|
|
16379
16478
|
}
|
|
16380
|
-
const { ObservationalMemory } = await import('./observational-memory-
|
|
16479
|
+
const { ObservationalMemory } = await import('./observational-memory-MXI54VC7.cjs');
|
|
16381
16480
|
return new ObservationalMemory({
|
|
16382
16481
|
storage: memoryStore,
|
|
16383
16482
|
scope: omConfig.scope,
|