@mastra/memory 0.15.11 → 1.0.0-beta.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/dist/index.cjs CHANGED
@@ -33,8 +33,10 @@ var updateWorkingMemoryTool = (memoryConfig) => {
33
33
  id: "update-working-memory",
34
34
  description: `Update the working memory with new information. Any data not included will be overwritten.${schema ? " Always pass data as string to the memory field. Never pass an object." : ""}`,
35
35
  inputSchema,
36
- execute: async (params) => {
37
- const { context, threadId, memory, resourceId } = params;
36
+ execute: async (inputData, context) => {
37
+ const threadId = context?.agent?.threadId;
38
+ const resourceId = context?.agent?.resourceId;
39
+ const memory = context?.memory;
38
40
  if (!threadId || !memory || !resourceId) {
39
41
  throw new Error("Thread ID, Memory instance, and resourceId are required for working memory updates");
40
42
  }
@@ -49,7 +51,7 @@ var updateWorkingMemoryTool = (memoryConfig) => {
49
51
  if (thread.resourceId && thread.resourceId !== resourceId) {
50
52
  throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
51
53
  }
52
- const workingMemory = typeof context.memory === "string" ? context.memory : JSON.stringify(context.memory);
54
+ const workingMemory = typeof inputData.memory === "string" ? inputData.memory : JSON.stringify(inputData.memory);
53
55
  await memory.updateWorkingMemory({
54
56
  threadId,
55
57
  resourceId,
@@ -75,8 +77,10 @@ var __experimental_updateWorkingMemoryToolVNext = (config) => {
75
77
  "The reason you're updating working memory. Passing any value other than 'append-new-memory' requires a searchString to be provided. Defaults to append-new-memory"
76
78
  )
77
79
  }),
78
- execute: async (params) => {
79
- const { context, threadId, memory, resourceId } = params;
80
+ execute: async (inputData, context) => {
81
+ const threadId = context?.agent?.threadId;
82
+ const resourceId = context?.agent?.resourceId;
83
+ const memory = context?.memory;
80
84
  if (!threadId || !memory || !resourceId) {
81
85
  throw new Error("Thread ID, Memory instance, and resourceId are required for working memory updates");
82
86
  }
@@ -91,25 +95,25 @@ var __experimental_updateWorkingMemoryToolVNext = (config) => {
91
95
  if (thread.resourceId && thread.resourceId !== resourceId) {
92
96
  throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
93
97
  }
94
- const workingMemory = context.newMemory || "";
95
- if (!context.updateReason) context.updateReason = `append-new-memory`;
96
- if (context.searchString && config.workingMemory?.scope === `resource` && context.updateReason === `replace-irrelevant-memory`) {
97
- context.searchString = void 0;
98
+ const workingMemory = inputData.newMemory || "";
99
+ if (!inputData.updateReason) inputData.updateReason = `append-new-memory`;
100
+ if (inputData.searchString && config.workingMemory?.scope === `resource` && inputData.updateReason === `replace-irrelevant-memory`) {
101
+ inputData.searchString = void 0;
98
102
  }
99
- if (context.updateReason === `append-new-memory` && context.searchString) {
100
- context.searchString = void 0;
103
+ if (inputData.updateReason === `append-new-memory` && inputData.searchString) {
104
+ inputData.searchString = void 0;
101
105
  }
102
- if (context.updateReason !== `append-new-memory` && !context.searchString) {
106
+ if (inputData.updateReason !== `append-new-memory` && !inputData.searchString) {
103
107
  return {
104
108
  success: false,
105
- reason: `updateReason was ${context.updateReason} but no searchString was provided. Unable to replace undefined with "${context.newMemory}"`
109
+ reason: `updateReason was ${inputData.updateReason} but no searchString was provided. Unable to replace undefined with "${inputData.newMemory}"`
106
110
  };
107
111
  }
108
112
  const result = await memory.__experimental_updateWorkingMemoryVNext({
109
113
  threadId,
110
114
  resourceId,
111
115
  workingMemory,
112
- searchString: context.searchString,
116
+ searchString: inputData.searchString,
113
117
  memoryConfig: config
114
118
  });
115
119
  if (result) {
@@ -122,8 +126,8 @@ var __experimental_updateWorkingMemoryToolVNext = (config) => {
122
126
 
123
127
  // src/index.ts
124
128
  var CHARS_PER_TOKEN = 4;
125
- var DEFAULT_MESSAGE_RANGE = { before: 2, after: 2 };
126
- var DEFAULT_TOP_K = 2;
129
+ var DEFAULT_MESSAGE_RANGE = { before: 1, after: 1 };
130
+ var DEFAULT_TOP_K = 4;
127
131
  var isZodObject = (v) => v instanceof zod.ZodObject;
128
132
  var Memory = class extends memory.MastraMemory {
129
133
  constructor(config = {}) {
@@ -140,7 +144,7 @@ var Memory = class extends memory.MastraMemory {
140
144
  this.threadConfig = mergedConfig;
141
145
  }
142
146
  async validateThreadIsOwnedByResource(threadId, resourceId, config) {
143
- const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope === "resource";
147
+ const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope !== `thread` || config.semanticRecall === true;
144
148
  const thread = await this.storage.getThreadById({ threadId });
145
149
  if (!thread && !resourceScope) {
146
150
  throw new Error(`No thread found with id ${threadId}`);
@@ -152,7 +156,9 @@ var Memory = class extends memory.MastraMemory {
152
156
  }
153
157
  }
154
158
  checkStorageFeatureSupport(config) {
155
- if (typeof config.semanticRecall === `object` && config.semanticRecall.scope === `resource` && !this.storage.supports.selectByIncludeResourceScope) {
159
+ const resourceScope = typeof config.semanticRecall === "object" && config.semanticRecall.scope !== "thread" || // resource scope is now default
160
+ config.semanticRecall === true;
161
+ if (resourceScope && !this.storage.supports.selectByIncludeResourceScope) {
156
162
  throw new Error(
157
163
  `Memory error: Attached storage adapter "${this.storage.name || "unknown"}" doesn't support semanticRecall: { scope: "resource" } yet and currently only supports per-thread semantic recall.`
158
164
  );
@@ -163,18 +169,17 @@ var Memory = class extends memory.MastraMemory {
163
169
  );
164
170
  }
165
171
  }
166
- async query({
167
- threadId,
168
- resourceId,
169
- selectBy,
170
- threadConfig
171
- }) {
172
+ async recall(args) {
173
+ const { threadId, resourceId, perPage: perPageArg, page, orderBy, threadConfig, vectorSearchString, filter } = args;
172
174
  const config = this.getMergedThreadConfig(threadConfig || {});
173
175
  if (resourceId) await this.validateThreadIsOwnedByResource(threadId, resourceId, config);
176
+ const perPage = perPageArg !== void 0 ? perPageArg : config.lastMessages;
174
177
  const vectorResults = [];
175
- this.logger.debug(`Memory query() with:`, {
178
+ this.logger.debug(`Memory recall() with:`, {
176
179
  threadId,
177
- selectBy,
180
+ perPage,
181
+ page,
182
+ orderBy,
178
183
  threadConfig
179
184
  });
180
185
  this.checkStorageFeatureSupport(config);
@@ -187,9 +192,14 @@ var Memory = class extends memory.MastraMemory {
187
192
  topK: config?.semanticRecall?.topK ?? defaultTopK,
188
193
  messageRange: config?.semanticRecall?.messageRange ?? defaultRange
189
194
  };
190
- const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope === `resource`;
191
- if (config?.semanticRecall && selectBy?.vectorSearchString && this.vector) {
192
- const { embeddings, dimension } = await this.embedMessageContent(selectBy.vectorSearchString);
195
+ const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope !== `thread` || config.semanticRecall === true;
196
+ if (resourceScope && !resourceId && config?.semanticRecall && vectorSearchString) {
197
+ throw new Error(
198
+ `Memory error: Resource-scoped semantic recall is enabled but no resourceId was provided. Either provide a resourceId or explicitly set semanticRecall.scope to 'thread'.`
199
+ );
200
+ }
201
+ if (config?.semanticRecall && vectorSearchString && this.vector) {
202
+ const { embeddings, dimension } = await this.embedMessageContent(vectorSearchString);
193
203
  const { indexName } = await this.createEmbeddingIndex(dimension, config);
194
204
  await Promise.all(
195
205
  embeddings.map(async (embedding) => {
@@ -213,114 +223,32 @@ var Memory = class extends memory.MastraMemory {
213
223
  })
214
224
  );
215
225
  }
216
- let rawMessages;
217
- if (selectBy?.pagination) {
218
- const paginatedResult = await this.storage.getMessagesPaginated({
219
- threadId,
220
- resourceId,
221
- format: "v2",
222
- selectBy: {
223
- ...selectBy,
224
- ...vectorResults?.length ? {
225
- include: vectorResults.map((r) => ({
226
- id: r.metadata?.message_id,
227
- threadId: r.metadata?.thread_id,
228
- withNextMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.after,
229
- withPreviousMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.before
230
- }))
231
- } : {}
232
- },
233
- threadConfig: config
234
- });
235
- rawMessages = paginatedResult.messages;
236
- } else {
237
- rawMessages = await this.storage.getMessages({
238
- threadId,
239
- resourceId,
240
- format: "v2",
241
- selectBy: {
242
- ...selectBy,
243
- ...vectorResults?.length ? {
244
- include: vectorResults.map((r) => ({
245
- id: r.metadata?.message_id,
246
- threadId: r.metadata?.thread_id,
247
- withNextMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.after,
248
- withPreviousMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.before
249
- }))
250
- } : {}
251
- },
252
- threadConfig: config
253
- });
254
- }
255
- const list = new agent.MessageList({ threadId, resourceId }).add(rawMessages, "memory");
256
- return {
257
- get messages() {
258
- const v1Messages = list.get.all.v1();
259
- if (selectBy?.last && v1Messages.length > selectBy.last) {
260
- return v1Messages.slice(v1Messages.length - selectBy.last);
261
- }
262
- return v1Messages;
263
- },
264
- get uiMessages() {
265
- return list.get.all.ui();
266
- },
267
- get messagesV2() {
268
- return list.get.all.v2();
269
- }
270
- };
271
- }
272
- async rememberMessages({
273
- threadId,
274
- resourceId,
275
- vectorMessageSearch,
276
- config
277
- }) {
278
- const threadConfig = this.getMergedThreadConfig(config || {});
279
- if (resourceId) await this.validateThreadIsOwnedByResource(threadId, resourceId, threadConfig);
280
- if (!threadConfig.lastMessages && !threadConfig.semanticRecall) {
281
- return {
282
- messages: [],
283
- messagesV2: []
284
- };
285
- }
286
- const messagesResult = await this.query({
287
- resourceId,
226
+ const paginatedResult = await this.storage.listMessages({
288
227
  threadId,
289
- selectBy: {
290
- last: threadConfig.lastMessages,
291
- vectorSearchString: threadConfig.semanticRecall && vectorMessageSearch ? vectorMessageSearch : void 0
292
- },
293
- threadConfig: config,
294
- format: "v2"
228
+ resourceId,
229
+ perPage,
230
+ page,
231
+ orderBy,
232
+ filter,
233
+ ...vectorResults?.length ? {
234
+ include: vectorResults.map((r) => ({
235
+ id: r.metadata?.message_id,
236
+ threadId: r.metadata?.thread_id,
237
+ withNextMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.after,
238
+ withPreviousMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.before
239
+ }))
240
+ } : {}
295
241
  });
296
- const list = new agent.MessageList({ threadId, resourceId }).add(messagesResult.messagesV2, "memory");
297
- this.logger.debug(`Remembered message history includes ${messagesResult.messages.length} messages.`);
298
- return { messages: list.get.all.v1(), messagesV2: list.get.all.v2() };
242
+ const rawMessages = paginatedResult.messages;
243
+ const list = new agent.MessageList({ threadId, resourceId }).add(rawMessages, "memory");
244
+ const messages = list.get.all.db();
245
+ return { messages };
299
246
  }
300
247
  async getThreadById({ threadId }) {
301
248
  return this.storage.getThreadById({ threadId });
302
249
  }
303
- async getThreadsByResourceId({
304
- resourceId,
305
- orderBy,
306
- sortDirection
307
- }) {
308
- return this.storage.getThreadsByResourceId({ resourceId, orderBy, sortDirection });
309
- }
310
- async getThreadsByResourceIdPaginated({
311
- resourceId,
312
- page,
313
- perPage,
314
- orderBy,
315
- sortDirection
316
- }) {
317
- return this.storage.getThreadsByResourceIdPaginated({
318
- resourceId,
319
- page,
320
- perPage,
321
- orderBy,
322
- sortDirection
323
- });
250
+ async listThreadsByResourceId(args) {
251
+ return this.storage.listThreadsByResourceId(args);
324
252
  }
325
253
  async handleWorkingMemoryFromMetadata({
326
254
  workingMemory,
@@ -330,7 +258,7 @@ var Memory = class extends memory.MastraMemory {
330
258
  const config = this.getMergedThreadConfig(memoryConfig || {});
331
259
  if (config.workingMemory?.enabled) {
332
260
  this.checkStorageFeatureSupport(config);
333
- const scope = config.workingMemory.scope || "thread";
261
+ const scope = config.workingMemory.scope || "resource";
334
262
  if (scope === "resource" && resourceId) {
335
263
  await this.storage.updateResource({
336
264
  resourceId,
@@ -387,7 +315,12 @@ var Memory = class extends memory.MastraMemory {
387
315
  throw new Error("Working memory is not enabled for this memory instance");
388
316
  }
389
317
  this.checkStorageFeatureSupport(config);
390
- const scope = config.workingMemory.scope || "thread";
318
+ const scope = config.workingMemory.scope || "resource";
319
+ if (scope === "resource" && !resourceId) {
320
+ throw new Error(
321
+ `Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.`
322
+ );
323
+ }
391
324
  if (scope === "resource" && resourceId) {
392
325
  await this.storage.updateResource({
393
326
  resourceId,
@@ -459,7 +392,12 @@ ${workingMemory}`;
459
392
  reason = `started new working memory`;
460
393
  }
461
394
  workingMemory = template?.content ? workingMemory.replaceAll(template?.content, "") : workingMemory;
462
- const scope = config.workingMemory.scope || "thread";
395
+ const scope = config.workingMemory.scope || "resource";
396
+ if (scope === "resource" && !resourceId) {
397
+ throw new Error(
398
+ `Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.`
399
+ );
400
+ }
463
401
  if (scope === "resource" && resourceId) {
464
402
  await this.storage.updateResource({
465
403
  resourceId,
@@ -543,40 +481,28 @@ ${workingMemory}`;
543
481
  }
544
482
  async saveMessages({
545
483
  messages,
546
- memoryConfig,
547
- format = `v1`
484
+ memoryConfig
548
485
  }) {
549
486
  const updatedMessages = messages.map((m) => {
550
- if (agent.MessageList.isMastraMessageV1(m)) {
551
- return this.updateMessageToHideWorkingMemory(m);
552
- }
553
- if (!m.type) m.type = `v2`;
554
487
  return this.updateMessageToHideWorkingMemoryV2(m);
555
488
  }).filter((m) => Boolean(m));
556
489
  const config = this.getMergedThreadConfig(memoryConfig);
557
- const result = this.storage.saveMessages({
558
- messages: new agent.MessageList().add(updatedMessages, "memory").get.all.v2(),
559
- format: "v2"
490
+ const dbMessages = new agent.MessageList({
491
+ generateMessageId: () => this.generateId()
492
+ }).add(updatedMessages, "memory").get.all.db();
493
+ const result = await this.storage.saveMessages({
494
+ messages: dbMessages
560
495
  });
561
496
  if (this.vector && config.semanticRecall) {
562
497
  let indexName;
563
498
  await Promise.all(
564
499
  updatedMessages.map(async (message) => {
565
500
  let textForEmbedding = null;
566
- if (agent.MessageList.isMastraMessageV2(message)) {
567
- if (message.content.content && typeof message.content.content === "string" && message.content.content.trim() !== "") {
568
- textForEmbedding = message.content.content;
569
- } else if (message.content.parts && message.content.parts.length > 0) {
570
- const joined = message.content.parts.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
571
- if (joined) textForEmbedding = joined;
572
- }
573
- } else if (agent.MessageList.isMastraMessageV1(message)) {
574
- if (message.content && typeof message.content === "string" && message.content.trim() !== "") {
575
- textForEmbedding = message.content;
576
- } else if (message.content && Array.isArray(message.content) && message.content.length > 0) {
577
- const joined = message.content.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
578
- if (joined) textForEmbedding = joined;
579
- }
501
+ if (message.content.content && typeof message.content.content === "string" && message.content.content.trim() !== "") {
502
+ textForEmbedding = message.content.content;
503
+ } else if (message.content.parts && message.content.parts.length > 0) {
504
+ const joined = message.content.parts.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
505
+ if (joined) textForEmbedding = joined;
580
506
  }
581
507
  if (!textForEmbedding) return;
582
508
  const { embeddings, chunks, dimension } = await this.embedMessageContent(textForEmbedding);
@@ -600,7 +526,6 @@ ${workingMemory}`;
600
526
  })
601
527
  );
602
528
  }
603
- if (format === `v1`) return new agent.MessageList().add(await result, "memory").get.all.v1();
604
529
  return result;
605
530
  }
606
531
  updateMessageToHideWorkingMemory(message) {
@@ -676,8 +601,13 @@ ${workingMemory}`;
676
601
  return null;
677
602
  }
678
603
  this.checkStorageFeatureSupport(config);
679
- const scope = config.workingMemory.scope || "thread";
604
+ const scope = config.workingMemory.scope || "resource";
680
605
  let workingMemoryData = null;
606
+ if (scope === "resource" && !resourceId) {
607
+ throw new Error(
608
+ `Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.`
609
+ );
610
+ }
681
611
  if (scope === "resource" && resourceId) {
682
612
  const resource = await this.storage.getResourceById({ resourceId });
683
613
  workingMemoryData = resource?.workingMemory || null;
@@ -833,7 +763,7 @@ ${template.content !== this.defaultWorkingMemoryTemplate ? `- Only store informa
833
763
  const isMDWorkingMemory = !(`schema` in config.workingMemory) && (typeof config.workingMemory.template === `string` || config.workingMemory.template) && config.workingMemory;
834
764
  return Boolean(isMDWorkingMemory && isMDWorkingMemory.version === `vnext`);
835
765
  }
836
- getTools(config) {
766
+ listTools(config) {
837
767
  const mergedConfig = this.getMergedThreadConfig(config);
838
768
  if (mergedConfig.workingMemory?.enabled) {
839
769
  return {