@mastra/memory 0.15.11 → 1.0.0-beta.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
@@ -27,8 +27,10 @@ var updateWorkingMemoryTool = (memoryConfig) => {
27
27
  id: "update-working-memory",
28
28
  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." : ""}`,
29
29
  inputSchema,
30
- execute: async (params) => {
31
- const { context, threadId, memory, resourceId } = params;
30
+ execute: async (inputData, context) => {
31
+ const threadId = context?.agent?.threadId;
32
+ const resourceId = context?.agent?.resourceId;
33
+ const memory = context?.memory;
32
34
  if (!threadId || !memory || !resourceId) {
33
35
  throw new Error("Thread ID, Memory instance, and resourceId are required for working memory updates");
34
36
  }
@@ -43,7 +45,7 @@ var updateWorkingMemoryTool = (memoryConfig) => {
43
45
  if (thread.resourceId && thread.resourceId !== resourceId) {
44
46
  throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
45
47
  }
46
- const workingMemory = typeof context.memory === "string" ? context.memory : JSON.stringify(context.memory);
48
+ const workingMemory = typeof inputData.memory === "string" ? inputData.memory : JSON.stringify(inputData.memory);
47
49
  await memory.updateWorkingMemory({
48
50
  threadId,
49
51
  resourceId,
@@ -69,8 +71,10 @@ var __experimental_updateWorkingMemoryToolVNext = (config) => {
69
71
  "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"
70
72
  )
71
73
  }),
72
- execute: async (params) => {
73
- const { context, threadId, memory, resourceId } = params;
74
+ execute: async (inputData, context) => {
75
+ const threadId = context?.agent?.threadId;
76
+ const resourceId = context?.agent?.resourceId;
77
+ const memory = context?.memory;
74
78
  if (!threadId || !memory || !resourceId) {
75
79
  throw new Error("Thread ID, Memory instance, and resourceId are required for working memory updates");
76
80
  }
@@ -85,25 +89,25 @@ var __experimental_updateWorkingMemoryToolVNext = (config) => {
85
89
  if (thread.resourceId && thread.resourceId !== resourceId) {
86
90
  throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
87
91
  }
88
- const workingMemory = context.newMemory || "";
89
- if (!context.updateReason) context.updateReason = `append-new-memory`;
90
- if (context.searchString && config.workingMemory?.scope === `resource` && context.updateReason === `replace-irrelevant-memory`) {
91
- context.searchString = void 0;
92
+ const workingMemory = inputData.newMemory || "";
93
+ if (!inputData.updateReason) inputData.updateReason = `append-new-memory`;
94
+ if (inputData.searchString && config.workingMemory?.scope === `resource` && inputData.updateReason === `replace-irrelevant-memory`) {
95
+ inputData.searchString = void 0;
92
96
  }
93
- if (context.updateReason === `append-new-memory` && context.searchString) {
94
- context.searchString = void 0;
97
+ if (inputData.updateReason === `append-new-memory` && inputData.searchString) {
98
+ inputData.searchString = void 0;
95
99
  }
96
- if (context.updateReason !== `append-new-memory` && !context.searchString) {
100
+ if (inputData.updateReason !== `append-new-memory` && !inputData.searchString) {
97
101
  return {
98
102
  success: false,
99
- reason: `updateReason was ${context.updateReason} but no searchString was provided. Unable to replace undefined with "${context.newMemory}"`
103
+ reason: `updateReason was ${inputData.updateReason} but no searchString was provided. Unable to replace undefined with "${inputData.newMemory}"`
100
104
  };
101
105
  }
102
106
  const result = await memory.__experimental_updateWorkingMemoryVNext({
103
107
  threadId,
104
108
  resourceId,
105
109
  workingMemory,
106
- searchString: context.searchString,
110
+ searchString: inputData.searchString,
107
111
  memoryConfig: config
108
112
  });
109
113
  if (result) {
@@ -116,8 +120,8 @@ var __experimental_updateWorkingMemoryToolVNext = (config) => {
116
120
 
117
121
  // src/index.ts
118
122
  var CHARS_PER_TOKEN = 4;
119
- var DEFAULT_MESSAGE_RANGE = { before: 2, after: 2 };
120
- var DEFAULT_TOP_K = 2;
123
+ var DEFAULT_MESSAGE_RANGE = { before: 1, after: 1 };
124
+ var DEFAULT_TOP_K = 4;
121
125
  var isZodObject = (v) => v instanceof ZodObject;
122
126
  var Memory = class extends MastraMemory {
123
127
  constructor(config = {}) {
@@ -134,7 +138,7 @@ var Memory = class extends MastraMemory {
134
138
  this.threadConfig = mergedConfig;
135
139
  }
136
140
  async validateThreadIsOwnedByResource(threadId, resourceId, config) {
137
- const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope === "resource";
141
+ const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope !== `thread` || config.semanticRecall === true;
138
142
  const thread = await this.storage.getThreadById({ threadId });
139
143
  if (!thread && !resourceScope) {
140
144
  throw new Error(`No thread found with id ${threadId}`);
@@ -146,7 +150,9 @@ var Memory = class extends MastraMemory {
146
150
  }
147
151
  }
148
152
  checkStorageFeatureSupport(config) {
149
- if (typeof config.semanticRecall === `object` && config.semanticRecall.scope === `resource` && !this.storage.supports.selectByIncludeResourceScope) {
153
+ const resourceScope = typeof config.semanticRecall === "object" && config.semanticRecall.scope !== "thread" || // resource scope is now default
154
+ config.semanticRecall === true;
155
+ if (resourceScope && !this.storage.supports.selectByIncludeResourceScope) {
150
156
  throw new Error(
151
157
  `Memory error: Attached storage adapter "${this.storage.name || "unknown"}" doesn't support semanticRecall: { scope: "resource" } yet and currently only supports per-thread semantic recall.`
152
158
  );
@@ -157,18 +163,17 @@ var Memory = class extends MastraMemory {
157
163
  );
158
164
  }
159
165
  }
160
- async query({
161
- threadId,
162
- resourceId,
163
- selectBy,
164
- threadConfig
165
- }) {
166
+ async recall(args) {
167
+ const { threadId, resourceId, perPage: perPageArg, page, orderBy, threadConfig, vectorSearchString, filter } = args;
166
168
  const config = this.getMergedThreadConfig(threadConfig || {});
167
169
  if (resourceId) await this.validateThreadIsOwnedByResource(threadId, resourceId, config);
170
+ const perPage = perPageArg !== void 0 ? perPageArg : config.lastMessages;
168
171
  const vectorResults = [];
169
- this.logger.debug(`Memory query() with:`, {
172
+ this.logger.debug(`Memory recall() with:`, {
170
173
  threadId,
171
- selectBy,
174
+ perPage,
175
+ page,
176
+ orderBy,
172
177
  threadConfig
173
178
  });
174
179
  this.checkStorageFeatureSupport(config);
@@ -181,9 +186,14 @@ var Memory = class extends MastraMemory {
181
186
  topK: config?.semanticRecall?.topK ?? defaultTopK,
182
187
  messageRange: config?.semanticRecall?.messageRange ?? defaultRange
183
188
  };
184
- const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope === `resource`;
185
- if (config?.semanticRecall && selectBy?.vectorSearchString && this.vector) {
186
- const { embeddings, dimension } = await this.embedMessageContent(selectBy.vectorSearchString);
189
+ const resourceScope = typeof config?.semanticRecall === "object" && config?.semanticRecall?.scope !== `thread` || config.semanticRecall === true;
190
+ if (resourceScope && !resourceId && config?.semanticRecall && vectorSearchString) {
191
+ throw new Error(
192
+ `Memory error: Resource-scoped semantic recall is enabled but no resourceId was provided. Either provide a resourceId or explicitly set semanticRecall.scope to 'thread'.`
193
+ );
194
+ }
195
+ if (config?.semanticRecall && vectorSearchString && this.vector) {
196
+ const { embeddings, dimension } = await this.embedMessageContent(vectorSearchString);
187
197
  const { indexName } = await this.createEmbeddingIndex(dimension, config);
188
198
  await Promise.all(
189
199
  embeddings.map(async (embedding) => {
@@ -207,114 +217,32 @@ var Memory = class extends MastraMemory {
207
217
  })
208
218
  );
209
219
  }
210
- let rawMessages;
211
- if (selectBy?.pagination) {
212
- const paginatedResult = await this.storage.getMessagesPaginated({
213
- threadId,
214
- resourceId,
215
- format: "v2",
216
- selectBy: {
217
- ...selectBy,
218
- ...vectorResults?.length ? {
219
- include: vectorResults.map((r) => ({
220
- id: r.metadata?.message_id,
221
- threadId: r.metadata?.thread_id,
222
- withNextMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.after,
223
- withPreviousMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.before
224
- }))
225
- } : {}
226
- },
227
- threadConfig: config
228
- });
229
- rawMessages = paginatedResult.messages;
230
- } else {
231
- rawMessages = await this.storage.getMessages({
232
- threadId,
233
- resourceId,
234
- format: "v2",
235
- selectBy: {
236
- ...selectBy,
237
- ...vectorResults?.length ? {
238
- include: vectorResults.map((r) => ({
239
- id: r.metadata?.message_id,
240
- threadId: r.metadata?.thread_id,
241
- withNextMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.after,
242
- withPreviousMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.before
243
- }))
244
- } : {}
245
- },
246
- threadConfig: config
247
- });
248
- }
249
- const list = new MessageList({ threadId, resourceId }).add(rawMessages, "memory");
250
- return {
251
- get messages() {
252
- const v1Messages = list.get.all.v1();
253
- if (selectBy?.last && v1Messages.length > selectBy.last) {
254
- return v1Messages.slice(v1Messages.length - selectBy.last);
255
- }
256
- return v1Messages;
257
- },
258
- get uiMessages() {
259
- return list.get.all.ui();
260
- },
261
- get messagesV2() {
262
- return list.get.all.v2();
263
- }
264
- };
265
- }
266
- async rememberMessages({
267
- threadId,
268
- resourceId,
269
- vectorMessageSearch,
270
- config
271
- }) {
272
- const threadConfig = this.getMergedThreadConfig(config || {});
273
- if (resourceId) await this.validateThreadIsOwnedByResource(threadId, resourceId, threadConfig);
274
- if (!threadConfig.lastMessages && !threadConfig.semanticRecall) {
275
- return {
276
- messages: [],
277
- messagesV2: []
278
- };
279
- }
280
- const messagesResult = await this.query({
281
- resourceId,
220
+ const paginatedResult = await this.storage.listMessages({
282
221
  threadId,
283
- selectBy: {
284
- last: threadConfig.lastMessages,
285
- vectorSearchString: threadConfig.semanticRecall && vectorMessageSearch ? vectorMessageSearch : void 0
286
- },
287
- threadConfig: config,
288
- format: "v2"
222
+ resourceId,
223
+ perPage,
224
+ page,
225
+ orderBy,
226
+ filter,
227
+ ...vectorResults?.length ? {
228
+ include: vectorResults.map((r) => ({
229
+ id: r.metadata?.message_id,
230
+ threadId: r.metadata?.thread_id,
231
+ withNextMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.after,
232
+ withPreviousMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.before
233
+ }))
234
+ } : {}
289
235
  });
290
- const list = new MessageList({ threadId, resourceId }).add(messagesResult.messagesV2, "memory");
291
- this.logger.debug(`Remembered message history includes ${messagesResult.messages.length} messages.`);
292
- return { messages: list.get.all.v1(), messagesV2: list.get.all.v2() };
236
+ const rawMessages = paginatedResult.messages;
237
+ const list = new MessageList({ threadId, resourceId }).add(rawMessages, "memory");
238
+ const messages = list.get.all.db();
239
+ return { messages };
293
240
  }
294
241
  async getThreadById({ threadId }) {
295
242
  return this.storage.getThreadById({ threadId });
296
243
  }
297
- async getThreadsByResourceId({
298
- resourceId,
299
- orderBy,
300
- sortDirection
301
- }) {
302
- return this.storage.getThreadsByResourceId({ resourceId, orderBy, sortDirection });
303
- }
304
- async getThreadsByResourceIdPaginated({
305
- resourceId,
306
- page,
307
- perPage,
308
- orderBy,
309
- sortDirection
310
- }) {
311
- return this.storage.getThreadsByResourceIdPaginated({
312
- resourceId,
313
- page,
314
- perPage,
315
- orderBy,
316
- sortDirection
317
- });
244
+ async listThreadsByResourceId(args) {
245
+ return this.storage.listThreadsByResourceId(args);
318
246
  }
319
247
  async handleWorkingMemoryFromMetadata({
320
248
  workingMemory,
@@ -324,7 +252,7 @@ var Memory = class extends MastraMemory {
324
252
  const config = this.getMergedThreadConfig(memoryConfig || {});
325
253
  if (config.workingMemory?.enabled) {
326
254
  this.checkStorageFeatureSupport(config);
327
- const scope = config.workingMemory.scope || "thread";
255
+ const scope = config.workingMemory.scope || "resource";
328
256
  if (scope === "resource" && resourceId) {
329
257
  await this.storage.updateResource({
330
258
  resourceId,
@@ -381,7 +309,12 @@ var Memory = class extends MastraMemory {
381
309
  throw new Error("Working memory is not enabled for this memory instance");
382
310
  }
383
311
  this.checkStorageFeatureSupport(config);
384
- const scope = config.workingMemory.scope || "thread";
312
+ const scope = config.workingMemory.scope || "resource";
313
+ if (scope === "resource" && !resourceId) {
314
+ throw new Error(
315
+ `Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.`
316
+ );
317
+ }
385
318
  if (scope === "resource" && resourceId) {
386
319
  await this.storage.updateResource({
387
320
  resourceId,
@@ -453,7 +386,12 @@ ${workingMemory}`;
453
386
  reason = `started new working memory`;
454
387
  }
455
388
  workingMemory = template?.content ? workingMemory.replaceAll(template?.content, "") : workingMemory;
456
- const scope = config.workingMemory.scope || "thread";
389
+ const scope = config.workingMemory.scope || "resource";
390
+ if (scope === "resource" && !resourceId) {
391
+ throw new Error(
392
+ `Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.`
393
+ );
394
+ }
457
395
  if (scope === "resource" && resourceId) {
458
396
  await this.storage.updateResource({
459
397
  resourceId,
@@ -537,40 +475,28 @@ ${workingMemory}`;
537
475
  }
538
476
  async saveMessages({
539
477
  messages,
540
- memoryConfig,
541
- format = `v1`
478
+ memoryConfig
542
479
  }) {
543
480
  const updatedMessages = messages.map((m) => {
544
- if (MessageList.isMastraMessageV1(m)) {
545
- return this.updateMessageToHideWorkingMemory(m);
546
- }
547
- if (!m.type) m.type = `v2`;
548
481
  return this.updateMessageToHideWorkingMemoryV2(m);
549
482
  }).filter((m) => Boolean(m));
550
483
  const config = this.getMergedThreadConfig(memoryConfig);
551
- const result = this.storage.saveMessages({
552
- messages: new MessageList().add(updatedMessages, "memory").get.all.v2(),
553
- format: "v2"
484
+ const dbMessages = new MessageList({
485
+ generateMessageId: () => this.generateId()
486
+ }).add(updatedMessages, "memory").get.all.db();
487
+ const result = await this.storage.saveMessages({
488
+ messages: dbMessages
554
489
  });
555
490
  if (this.vector && config.semanticRecall) {
556
491
  let indexName;
557
492
  await Promise.all(
558
493
  updatedMessages.map(async (message) => {
559
494
  let textForEmbedding = null;
560
- if (MessageList.isMastraMessageV2(message)) {
561
- if (message.content.content && typeof message.content.content === "string" && message.content.content.trim() !== "") {
562
- textForEmbedding = message.content.content;
563
- } else if (message.content.parts && message.content.parts.length > 0) {
564
- const joined = message.content.parts.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
565
- if (joined) textForEmbedding = joined;
566
- }
567
- } else if (MessageList.isMastraMessageV1(message)) {
568
- if (message.content && typeof message.content === "string" && message.content.trim() !== "") {
569
- textForEmbedding = message.content;
570
- } else if (message.content && Array.isArray(message.content) && message.content.length > 0) {
571
- const joined = message.content.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
572
- if (joined) textForEmbedding = joined;
573
- }
495
+ if (message.content.content && typeof message.content.content === "string" && message.content.content.trim() !== "") {
496
+ textForEmbedding = message.content.content;
497
+ } else if (message.content.parts && message.content.parts.length > 0) {
498
+ const joined = message.content.parts.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
499
+ if (joined) textForEmbedding = joined;
574
500
  }
575
501
  if (!textForEmbedding) return;
576
502
  const { embeddings, chunks, dimension } = await this.embedMessageContent(textForEmbedding);
@@ -594,7 +520,6 @@ ${workingMemory}`;
594
520
  })
595
521
  );
596
522
  }
597
- if (format === `v1`) return new MessageList().add(await result, "memory").get.all.v1();
598
523
  return result;
599
524
  }
600
525
  updateMessageToHideWorkingMemory(message) {
@@ -670,8 +595,13 @@ ${workingMemory}`;
670
595
  return null;
671
596
  }
672
597
  this.checkStorageFeatureSupport(config);
673
- const scope = config.workingMemory.scope || "thread";
598
+ const scope = config.workingMemory.scope || "resource";
674
599
  let workingMemoryData = null;
600
+ if (scope === "resource" && !resourceId) {
601
+ throw new Error(
602
+ `Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.`
603
+ );
604
+ }
675
605
  if (scope === "resource" && resourceId) {
676
606
  const resource = await this.storage.getResourceById({ resourceId });
677
607
  workingMemoryData = resource?.workingMemory || null;
@@ -827,7 +757,7 @@ ${template.content !== this.defaultWorkingMemoryTemplate ? `- Only store informa
827
757
  const isMDWorkingMemory = !(`schema` in config.workingMemory) && (typeof config.workingMemory.template === `string` || config.workingMemory.template) && config.workingMemory;
828
758
  return Boolean(isMDWorkingMemory && isMDWorkingMemory.version === `vnext`);
829
759
  }
830
- getTools(config) {
760
+ listTools(config) {
831
761
  const mergedConfig = this.getMergedThreadConfig(config);
832
762
  if (mergedConfig.workingMemory?.enabled) {
833
763
  return {