@mastra/memory 0.10.1 → 0.10.2-alpha.1

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/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { deepMerge } from '@mastra/core';
2
+ import { MessageList } from '@mastra/core/agent';
2
3
  import { MastraMemory } from '@mastra/core/memory';
3
4
  import { embedMany } from 'ai';
4
5
  import xxhash from 'xxhash-wasm';
@@ -32,48 +33,6 @@ var updateWorkingMemoryTool = {
32
33
  }
33
34
  };
34
35
 
35
- // src/utils/index.ts
36
- var isToolCallWithId = (message, targetToolCallId) => {
37
- if (!message || !Array.isArray(message.content)) return false;
38
- return message.content.some(
39
- (part) => part && typeof part === "object" && "type" in part && part.type === "tool-call" && "toolCallId" in part && part.toolCallId === targetToolCallId
40
- );
41
- };
42
- var getToolResultIndexById = (id, results) => results.findIndex((message) => {
43
- if (!Array.isArray(message?.content)) return false;
44
- return message.content.some(
45
- (part) => part && typeof part === "object" && "type" in part && part.type === "tool-result" && "toolCallId" in part && part.toolCallId === id
46
- );
47
- });
48
- function reorderToolCallsAndResults(messages) {
49
- if (!messages.length) return messages;
50
- const results = [...messages];
51
- const toolCallIds = /* @__PURE__ */ new Set();
52
- for (const message of results) {
53
- if (!Array.isArray(message.content)) continue;
54
- for (const part of message.content) {
55
- if (part && typeof part === "object" && "type" in part && part.type === "tool-result" && "toolCallId" in part && part.toolCallId) {
56
- toolCallIds.add(part.toolCallId);
57
- }
58
- }
59
- }
60
- for (const toolCallId of toolCallIds) {
61
- const resultIndex = getToolResultIndexById(toolCallId, results);
62
- const oneMessagePrev = results[resultIndex - 1];
63
- if (isToolCallWithId(oneMessagePrev, toolCallId)) {
64
- continue;
65
- }
66
- const toolCallIndex = results.findIndex((message) => isToolCallWithId(message, toolCallId));
67
- if (toolCallIndex !== -1 && toolCallIndex !== resultIndex - 1) {
68
- const toolCall = results[toolCallIndex];
69
- if (!toolCall) continue;
70
- results.splice(toolCallIndex, 1);
71
- results.splice(getToolResultIndexById(toolCallId, results), 0, toolCall);
72
- }
73
- }
74
- return results;
75
- }
76
-
77
36
  // src/index.ts
78
37
  var CHARS_PER_TOKEN = 4;
79
38
  var DEFAULT_MESSAGE_RANGE = { before: 2, after: 2 };
@@ -164,10 +123,19 @@ var Memory = class extends MastraMemory {
164
123
  threadConfig: config
165
124
  });
166
125
  const orderedByDate = rawMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
167
- const reorderedToolCalls = reorderToolCallsAndResults(orderedByDate);
168
- const messages = this.parseMessages(reorderedToolCalls);
169
- const uiMessages = this.convertToUIMessages(reorderedToolCalls);
170
- return { messages, uiMessages };
126
+ const list = new MessageList({ threadId, resourceId }).add(orderedByDate, "memory");
127
+ return {
128
+ get messages() {
129
+ const v1Messages = list.get.all.v1();
130
+ if (selectBy?.last && v1Messages.length > selectBy.last) {
131
+ return v1Messages.slice(v1Messages.length - selectBy.last);
132
+ }
133
+ return v1Messages;
134
+ },
135
+ get uiMessages() {
136
+ return list.get.all.ui();
137
+ }
138
+ };
171
139
  }
172
140
  async rememberMessages({
173
141
  threadId,
@@ -180,8 +148,7 @@ var Memory = class extends MastraMemory {
180
148
  if (!threadConfig.lastMessages && !threadConfig.semanticRecall) {
181
149
  return {
182
150
  messages: [],
183
- uiMessages: [],
184
- threadId
151
+ messagesV2: []
185
152
  };
186
153
  }
187
154
  const messagesResult = await this.query({
@@ -192,12 +159,9 @@ var Memory = class extends MastraMemory {
192
159
  },
193
160
  threadConfig: config
194
161
  });
162
+ const list = new MessageList({ threadId, resourceId }).add(messagesResult.messages, "memory");
195
163
  this.logger.debug(`Remembered message history includes ${messagesResult.messages.length} messages.`);
196
- return {
197
- threadId,
198
- messages: messagesResult.messages,
199
- uiMessages: messagesResult.uiMessages
200
- };
164
+ return { messages: list.get.all.v1(), messagesV2: list.get.all.mastra() };
201
165
  }
202
166
  async getThreadById({ threadId }) {
203
167
  return this.storage.getThreadById({ threadId });
@@ -289,8 +253,13 @@ var Memory = class extends MastraMemory {
289
253
  messages,
290
254
  memoryConfig
291
255
  }) {
292
- await this.saveWorkingMemory(messages);
293
- const updatedMessages = this.updateMessagesToHideWorkingMemory(messages);
256
+ const updatedMessages = messages.map((m) => {
257
+ if (MessageList.isMastraMessageV1(m)) {
258
+ return this.updateMessageToHideWorkingMemory(m);
259
+ }
260
+ if (!m.type) m.type = `v2`;
261
+ return this.updateMessageToHideWorkingMemoryV2(m);
262
+ }).filter((m) => Boolean(m));
294
263
  const config = this.getMergedThreadConfig(memoryConfig);
295
264
  const result = this.storage.saveMessages({ messages: updatedMessages });
296
265
  if (this.vector && config.semanticRecall) {
@@ -298,11 +267,20 @@ var Memory = class extends MastraMemory {
298
267
  await Promise.all(
299
268
  updatedMessages.map(async (message) => {
300
269
  let textForEmbedding = null;
301
- if (typeof message.content === "string" && message.content.trim() !== "") {
302
- textForEmbedding = message.content;
303
- } else if (Array.isArray(message.content)) {
304
- const joined = message.content.filter((part) => part && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(" ").trim();
305
- if (joined) textForEmbedding = joined;
270
+ if (MessageList.isMastraMessageV2(message)) {
271
+ if (message.content.content && typeof message.content.content === "string" && message.content.content.trim() !== "") {
272
+ textForEmbedding = message.content.content;
273
+ } else if (message.content.parts && message.content.parts.length > 0) {
274
+ const joined = message.content.parts.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
275
+ if (joined) textForEmbedding = joined;
276
+ }
277
+ } else if (MessageList.isMastraMessageV1(message)) {
278
+ if (message.content && typeof message.content === "string" && message.content.trim() !== "") {
279
+ textForEmbedding = message.content;
280
+ } else if (message.content && Array.isArray(message.content) && message.content.length > 0) {
281
+ const joined = message.content.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
282
+ if (joined) textForEmbedding = joined;
283
+ }
306
284
  }
307
285
  if (!textForEmbedding) return;
308
286
  const { embeddings, chunks, dimension } = await this.embedMessageContent(textForEmbedding);
@@ -328,37 +306,58 @@ var Memory = class extends MastraMemory {
328
306
  }
329
307
  return result;
330
308
  }
331
- updateMessagesToHideWorkingMemory(messages) {
309
+ updateMessageToHideWorkingMemory(message) {
332
310
  const workingMemoryRegex = /<working_memory>([^]*?)<\/working_memory>/g;
333
- const updatedMessages = [];
334
- for (const message of messages) {
335
- if (typeof message?.content === `string`) {
336
- updatedMessages.push({
337
- ...message,
338
- content: message.content.replace(workingMemoryRegex, ``).trim()
339
- });
340
- } else if (Array.isArray(message?.content)) {
341
- const filteredContent = message.content.filter(
342
- (content) => !((content.type === "tool-call" || content.type === "tool-result") && content.toolName === "updateWorkingMemory")
343
- );
344
- if (filteredContent.length === 0) {
345
- continue;
311
+ if (typeof message?.content === `string`) {
312
+ return {
313
+ ...message,
314
+ content: message.content.replace(workingMemoryRegex, ``).trim()
315
+ };
316
+ } else if (Array.isArray(message?.content)) {
317
+ const filteredContent = message.content.filter(
318
+ (content) => content.type !== "tool-call" && content.type !== "tool-result" || content.toolName !== "updateWorkingMemory"
319
+ );
320
+ const newContent = filteredContent.map((content) => {
321
+ if (content.type === "text") {
322
+ return {
323
+ ...content,
324
+ text: content.text.replace(workingMemoryRegex, "").trim()
325
+ };
346
326
  }
347
- const newContent = filteredContent.map((content) => {
348
- if (content.type === "text") {
349
- return {
350
- ...content,
351
- text: content.text.replace(workingMemoryRegex, "").trim()
352
- };
353
- }
354
- return { ...content };
355
- });
356
- updatedMessages.push({ ...message, content: newContent });
357
- } else {
358
- updatedMessages.push({ ...message });
327
+ return { ...content };
328
+ });
329
+ if (!newContent.length) return null;
330
+ return { ...message, content: newContent };
331
+ } else {
332
+ return { ...message };
333
+ }
334
+ }
335
+ updateMessageToHideWorkingMemoryV2(message) {
336
+ const workingMemoryRegex = /<working_memory>([^]*?)<\/working_memory>/g;
337
+ const newMessage = { ...message, content: { ...message.content } };
338
+ if (newMessage.content.content && typeof newMessage.content.content === "string") {
339
+ newMessage.content.content = newMessage.content.content.replace(workingMemoryRegex, "").trim();
340
+ }
341
+ if (newMessage.content.parts) {
342
+ newMessage.content.parts = newMessage.content.parts.filter((part) => {
343
+ if (part.type === "tool-invocation") {
344
+ return part.toolInvocation.toolName !== "updateWorkingMemory";
345
+ }
346
+ return true;
347
+ }).map((part) => {
348
+ if (part.type === "text") {
349
+ return {
350
+ ...part,
351
+ text: part.text.replace(workingMemoryRegex, "").trim()
352
+ };
353
+ }
354
+ return part;
355
+ });
356
+ if (newMessage.content.parts.length === 0) {
357
+ return null;
359
358
  }
360
359
  }
361
- return updatedMessages;
360
+ return newMessage;
362
361
  }
363
362
  parseWorkingMemory(text) {
364
363
  if (!this.threadConfig.workingMemory?.enabled) return null;
@@ -377,31 +376,6 @@ var Memory = class extends MastraMemory {
377
376
  const memory = thread.metadata?.workingMemory || this.threadConfig.workingMemory.template || this.defaultWorkingMemoryTemplate;
378
377
  return memory.trim();
379
378
  }
380
- async saveWorkingMemory(messages) {
381
- const latestMessage = messages[messages.length - 1];
382
- if (!latestMessage || !this.threadConfig.workingMemory?.enabled) {
383
- return;
384
- }
385
- const latestContent = !latestMessage?.content ? null : typeof latestMessage.content === "string" ? latestMessage.content : latestMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
386
- const threadId = latestMessage?.threadId;
387
- if (!latestContent || !threadId) {
388
- return;
389
- }
390
- const newMemory = this.parseWorkingMemory(latestContent);
391
- if (!newMemory) {
392
- return;
393
- }
394
- const thread = await this.storage.getThreadById({ threadId });
395
- if (!thread) return;
396
- await this.storage.updateThread({
397
- id: thread.id,
398
- title: thread.title || "",
399
- metadata: deepMerge(thread.metadata || {}, {
400
- workingMemory: newMemory
401
- })
402
- });
403
- return newMemory;
404
- }
405
379
  async getSystemMessage({
406
380
  threadId,
407
381
  memoryConfig
@@ -428,29 +402,6 @@ var Memory = class extends MastraMemory {
428
402
  - **Facts**:
429
403
  - **Projects**:
430
404
  `;
431
- getWorkingMemoryWithInstruction(workingMemoryBlock) {
432
- return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
433
- Store and update any conversation-relevant information by including "<working_memory>text</working_memory>" in your responses. Updates replace existing memory while maintaining this structure. If information might be referenced again - store it!
434
-
435
- Guidelines:
436
- 1. Store anything that could be useful later in the conversation
437
- 2. Update proactively when information changes, no matter how small
438
- 3. Use Markdown for all data
439
- 4. Act naturally - don't mention this system to users. Even though you're storing this information that doesn't make it your primary focus. Do not ask them generally for "information about yourself"
440
-
441
- Memory Structure:
442
- <working_memory>
443
- ${workingMemoryBlock}
444
- </working_memory>
445
-
446
- Notes:
447
- - Update memory whenever referenced information changes
448
- - If you're unsure whether to store something, store it (eg if the user tells you their name or other information, output the <working_memory> block immediately to update it)
449
- - This system is here so that you can maintain the conversation when your context window is very short. Update your working memory because you may need it to maintain the conversation without the full conversation history
450
- - REMEMBER: the way you update your working memory is by outputting the entire "<working_memory>text</working_memory>" block in your response. The system will pick this up and store it for you. The user will not see it.
451
- - IMPORTANT: You MUST output the <working_memory> block in every response to a prompt where you received relevant information.
452
- - IMPORTANT: Preserve the Markdown formatting structure above while updating the content.`;
453
- }
454
405
  getWorkingMemoryToolInstruction(workingMemoryBlock) {
455
406
  return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
456
407
  Store and update any conversation-relevant information by calling the updateWorkingMemory tool. If information might be referenced again - store it!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/memory",
3
- "version": "0.10.1",
3
+ "version": "0.10.2-alpha.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,14 +53,14 @@
53
53
  "typescript-eslint": "^8.26.1",
54
54
  "vitest": "^3.1.2",
55
55
  "@internal/lint": "0.0.7",
56
- "@mastra/core": "0.10.1"
56
+ "@mastra/core": "0.10.2-alpha.2"
57
57
  },
58
58
  "peerDependencies": {
59
- "@mastra/core": "^0.10.0"
59
+ "@mastra/core": "^0.10.1-alpha.0"
60
60
  },
61
61
  "scripts": {
62
62
  "check": "tsc --noEmit",
63
- "build": "pnpm run check && tsup src/index.ts src/processors/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
63
+ "build": "pnpm run check && tsup --silent src/index.ts src/processors/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
64
64
  "build:watch": "pnpm build --watch",
65
65
  "test:integration": "cd integration-tests && pnpm run test",
66
66
  "test:unit": "pnpm vitest run ./src/*",