@mastra/memory 0.10.5-alpha.0 → 0.10.5-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.
@@ -1,9 +1,9 @@
1
1
 
2
- > @mastra/memory@0.10.5-alpha.0 build /home/runner/work/mastra/mastra/packages/memory
2
+ > @mastra/memory@0.10.5-alpha.1 build /home/runner/work/mastra/mastra/packages/memory
3
3
  > pnpm run check && tsup --silent src/index.ts src/processors/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
 
6
- > @mastra/memory@0.10.5-alpha.0 check /home/runner/work/mastra/mastra/packages/memory
6
+ > @mastra/memory@0.10.5-alpha.1 check /home/runner/work/mastra/mastra/packages/memory
7
7
  > tsc --noEmit
8
8
 
9
9
  Analysis will use the bundled TypeScript version 5.8.3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 0.10.5-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 8e1b6e9: dependencies updates:
8
+ - Updated dependency [`zod@^3.25.67` ↗︎](https://www.npmjs.com/package/zod/v/3.25.67) (from `^3.25.57`, in `dependencies`)
9
+ - 15e9d26: Added per-resource working memory for LibSQL, Upstash, and PG
10
+ - Updated dependencies [15e9d26]
11
+ - Updated dependencies [07d6d88]
12
+ - Updated dependencies [5d74aab]
13
+ - Updated dependencies [144eb0b]
14
+ - @mastra/core@0.10.7-alpha.2
15
+
3
16
  ## 0.10.5-alpha.0
4
17
 
5
18
  ### Patch Changes
@@ -56,6 +56,12 @@ export declare class Memory extends MastraMemory {
56
56
  metadata: Record<string, unknown>;
57
57
  }): Promise<StorageThreadType>;
58
58
  deleteThread(threadId: string): Promise<void>;
59
+ updateWorkingMemory({ threadId, resourceId, workingMemory, memoryConfig, }: {
60
+ threadId: string;
61
+ resourceId?: string;
62
+ workingMemory: string;
63
+ memoryConfig?: MemoryConfig;
64
+ }): Promise<void>;
59
65
  private chunkText;
60
66
  private hasher;
61
67
  private embeddingCache;
@@ -74,17 +80,21 @@ export declare class Memory extends MastraMemory {
74
80
  protected updateMessageToHideWorkingMemory(message: MastraMessageV1): MastraMessageV1 | null;
75
81
  protected updateMessageToHideWorkingMemoryV2(message: MastraMessageV2): MastraMessageV2 | null;
76
82
  protected parseWorkingMemory(text: string): string | null;
77
- getWorkingMemory({ threadId, format, }: {
83
+ getWorkingMemory({ threadId, resourceId, memoryConfig, }: {
78
84
  threadId: string;
79
- format?: WorkingMemoryFormat;
85
+ resourceId?: string;
86
+ memoryConfig?: MemoryConfig;
80
87
  }): Promise<string | null>;
81
88
  getWorkingMemoryTemplate(): Promise<WorkingMemoryTemplate | null>;
82
- getSystemMessage({ threadId, memoryConfig, }: {
89
+ getSystemMessage({ threadId, resourceId, memoryConfig, }: {
83
90
  threadId: string;
91
+ resourceId?: string;
84
92
  memoryConfig?: MemoryConfig;
85
93
  }): Promise<string | null>;
86
- getUserContextMessage({ threadId }: {
94
+ getUserContextMessage({ threadId, resourceId, memoryConfig, }: {
87
95
  threadId: string;
96
+ resourceId?: string;
97
+ memoryConfig?: MemoryConfig;
88
98
  }): Promise<string | null>;
89
99
  defaultWorkingMemoryTemplate: string;
90
100
  private getWorkingMemoryToolInstruction;
@@ -56,6 +56,12 @@ export declare class Memory extends MastraMemory {
56
56
  metadata: Record<string, unknown>;
57
57
  }): Promise<StorageThreadType>;
58
58
  deleteThread(threadId: string): Promise<void>;
59
+ updateWorkingMemory({ threadId, resourceId, workingMemory, memoryConfig, }: {
60
+ threadId: string;
61
+ resourceId?: string;
62
+ workingMemory: string;
63
+ memoryConfig?: MemoryConfig;
64
+ }): Promise<void>;
59
65
  private chunkText;
60
66
  private hasher;
61
67
  private embeddingCache;
@@ -74,17 +80,21 @@ export declare class Memory extends MastraMemory {
74
80
  protected updateMessageToHideWorkingMemory(message: MastraMessageV1): MastraMessageV1 | null;
75
81
  protected updateMessageToHideWorkingMemoryV2(message: MastraMessageV2): MastraMessageV2 | null;
76
82
  protected parseWorkingMemory(text: string): string | null;
77
- getWorkingMemory({ threadId, format, }: {
83
+ getWorkingMemory({ threadId, resourceId, memoryConfig, }: {
78
84
  threadId: string;
79
- format?: WorkingMemoryFormat;
85
+ resourceId?: string;
86
+ memoryConfig?: MemoryConfig;
80
87
  }): Promise<string | null>;
81
88
  getWorkingMemoryTemplate(): Promise<WorkingMemoryTemplate | null>;
82
- getSystemMessage({ threadId, memoryConfig, }: {
89
+ getSystemMessage({ threadId, resourceId, memoryConfig, }: {
83
90
  threadId: string;
91
+ resourceId?: string;
84
92
  memoryConfig?: MemoryConfig;
85
93
  }): Promise<string | null>;
86
- getUserContextMessage({ threadId }: {
94
+ getUserContextMessage({ threadId, resourceId, memoryConfig, }: {
87
95
  threadId: string;
96
+ resourceId?: string;
97
+ memoryConfig?: MemoryConfig;
88
98
  }): Promise<string | null>;
89
99
  defaultWorkingMemoryTemplate: string;
90
100
  private getWorkingMemoryToolInstruction;
package/dist/index.cjs CHANGED
@@ -32,14 +32,10 @@ var updateWorkingMemoryTool = ({ format }) => ({
32
32
  throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
33
33
  }
34
34
  const workingMemory = context.memory;
35
- await memory.saveThread({
36
- thread: {
37
- ...thread,
38
- metadata: {
39
- ...thread.metadata,
40
- workingMemory
41
- }
42
- }
35
+ await memory.updateWorkingMemory({
36
+ threadId,
37
+ resourceId: resourceId || thread.resourceId,
38
+ workingMemory
43
39
  });
44
40
  return { success: true };
45
41
  }
@@ -81,6 +77,11 @@ var Memory = class extends memory.MastraMemory {
81
77
  `Memory error: Attached storage adapter "${this.storage.name || "unknown"}" doesn't support semanticRecall: { scope: "resource" } yet and currently only supports per-thread semantic recall.`
82
78
  );
83
79
  }
80
+ if (config.workingMemory?.enabled && config.workingMemory.scope === `resource` && !this.storage.supports.resourceWorkingMemory) {
81
+ throw new Error(
82
+ `Memory error: Attached storage adapter "${this.storage.name || "unknown"}" doesn't support workingMemory: { scope: "resource" } yet and currently only supports per-thread working memory. Supported adapters: LibSQL, PostgreSQL, Upstash.`
83
+ );
84
+ }
84
85
  }
85
86
  async query({
86
87
  threadId,
@@ -205,18 +206,33 @@ var Memory = class extends memory.MastraMemory {
205
206
  memoryConfig
206
207
  }) {
207
208
  const config = this.getMergedThreadConfig(memoryConfig || {});
208
- if (config.workingMemory?.enabled && !thread?.metadata?.workingMemory) {
209
- let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
210
- if (config.workingMemory.schema) {
211
- workingMemory = JSON.stringify(zodToJsonSchema__default.default(config.workingMemory.schema));
212
- }
213
- return this.storage.saveThread({
214
- thread: core.deepMerge(thread, {
215
- metadata: {
216
- workingMemory
209
+ if (config.workingMemory?.enabled) {
210
+ const scope = config.workingMemory.scope || "thread";
211
+ if (scope === "resource" && thread.resourceId) {
212
+ const existingResource = await this.storage.getResourceById({ resourceId: thread.resourceId });
213
+ if (!existingResource?.workingMemory) {
214
+ let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
215
+ if (config.workingMemory.schema) {
216
+ workingMemory = JSON.stringify(zodToJsonSchema__default.default(config.workingMemory.schema));
217
217
  }
218
- })
219
- });
218
+ await this.storage.updateResource({
219
+ resourceId: thread.resourceId,
220
+ workingMemory
221
+ });
222
+ }
223
+ } else if (scope === "thread" && !thread?.metadata?.workingMemory) {
224
+ let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
225
+ if (config.workingMemory.schema) {
226
+ workingMemory = JSON.stringify(zodToJsonSchema__default.default(config.workingMemory.schema));
227
+ }
228
+ return this.storage.saveThread({
229
+ thread: core.deepMerge(thread, {
230
+ metadata: {
231
+ workingMemory
232
+ }
233
+ })
234
+ });
235
+ }
220
236
  }
221
237
  return this.storage.saveThread({ thread });
222
238
  }
@@ -234,6 +250,37 @@ var Memory = class extends memory.MastraMemory {
234
250
  async deleteThread(threadId) {
235
251
  await this.storage.deleteThread({ threadId });
236
252
  }
253
+ async updateWorkingMemory({
254
+ threadId,
255
+ resourceId,
256
+ workingMemory,
257
+ memoryConfig
258
+ }) {
259
+ const config = this.getMergedThreadConfig(memoryConfig || {});
260
+ if (!config.workingMemory?.enabled) {
261
+ throw new Error("Working memory is not enabled for this memory instance");
262
+ }
263
+ const scope = config.workingMemory.scope || "thread";
264
+ if (scope === "resource" && resourceId) {
265
+ await this.storage.updateResource({
266
+ resourceId,
267
+ workingMemory
268
+ });
269
+ } else {
270
+ const thread = await this.storage.getThreadById({ threadId });
271
+ if (!thread) {
272
+ throw new Error(`Thread ${threadId} not found`);
273
+ }
274
+ await this.storage.updateThread({
275
+ id: threadId,
276
+ title: thread.title || "Untitled Thread",
277
+ metadata: {
278
+ ...thread.metadata,
279
+ workingMemory
280
+ }
281
+ });
282
+ }
283
+ }
237
284
  chunkText(text, tokenSize = 4096) {
238
285
  const charSize = tokenSize * CHARS_PER_TOKEN;
239
286
  const chunks = [];
@@ -411,20 +458,26 @@ var Memory = class extends memory.MastraMemory {
411
458
  }
412
459
  async getWorkingMemory({
413
460
  threadId,
414
- format
461
+ resourceId,
462
+ memoryConfig
415
463
  }) {
416
- if (!this.threadConfig.workingMemory?.enabled) {
464
+ const config = this.getMergedThreadConfig(memoryConfig || {});
465
+ if (!config.workingMemory?.enabled) {
417
466
  return null;
418
467
  }
419
- const thread = await this.storage.getThreadById({ threadId });
420
- if (format === "json") {
421
- try {
422
- return JSON.parse(thread?.metadata?.workingMemory) || null;
423
- } catch (e) {
424
- this.logger.error("Unable to parse working memory as JSON. Returning string.", e);
425
- }
468
+ const scope = config.workingMemory.scope || "thread";
469
+ let workingMemoryData = null;
470
+ if (scope === "resource" && resourceId) {
471
+ const resource = await this.storage.getResourceById({ resourceId });
472
+ workingMemoryData = resource?.workingMemory || null;
473
+ } else {
474
+ const thread = await this.storage.getThreadById({ threadId });
475
+ workingMemoryData = thread?.metadata?.workingMemory;
426
476
  }
427
- return thread?.metadata?.workingMemory ? JSON.stringify(thread?.metadata?.workingMemory) : null;
477
+ if (!workingMemoryData) {
478
+ return null;
479
+ }
480
+ return workingMemoryData;
428
481
  }
429
482
  async getWorkingMemoryTemplate() {
430
483
  if (!this.threadConfig.workingMemory?.enabled) {
@@ -447,6 +500,7 @@ var Memory = class extends memory.MastraMemory {
447
500
  }
448
501
  async getSystemMessage({
449
502
  threadId,
503
+ resourceId,
450
504
  memoryConfig
451
505
  }) {
452
506
  const config = this.getMergedThreadConfig(memoryConfig);
@@ -454,7 +508,7 @@ var Memory = class extends memory.MastraMemory {
454
508
  return null;
455
509
  }
456
510
  const workingMemoryTemplate = await this.getWorkingMemoryTemplate();
457
- const workingMemoryData = await this.getWorkingMemory({ threadId });
511
+ const workingMemoryData = await this.getWorkingMemory({ threadId, resourceId, memoryConfig: config });
458
512
  if (!workingMemoryTemplate) {
459
513
  return null;
460
514
  }
@@ -463,8 +517,12 @@ var Memory = class extends memory.MastraMemory {
463
517
  data: workingMemoryData
464
518
  });
465
519
  }
466
- async getUserContextMessage({ threadId }) {
467
- const workingMemory = await this.getWorkingMemory({ threadId });
520
+ async getUserContextMessage({
521
+ threadId,
522
+ resourceId,
523
+ memoryConfig
524
+ }) {
525
+ const workingMemory = await this.getWorkingMemory({ threadId, resourceId, memoryConfig });
468
526
  if (!workingMemory) {
469
527
  return null;
470
528
  }
package/dist/index.js CHANGED
@@ -25,14 +25,10 @@ var updateWorkingMemoryTool = ({ format }) => ({
25
25
  throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
26
26
  }
27
27
  const workingMemory = context.memory;
28
- await memory.saveThread({
29
- thread: {
30
- ...thread,
31
- metadata: {
32
- ...thread.metadata,
33
- workingMemory
34
- }
35
- }
28
+ await memory.updateWorkingMemory({
29
+ threadId,
30
+ resourceId: resourceId || thread.resourceId,
31
+ workingMemory
36
32
  });
37
33
  return { success: true };
38
34
  }
@@ -74,6 +70,11 @@ var Memory = class extends MastraMemory {
74
70
  `Memory error: Attached storage adapter "${this.storage.name || "unknown"}" doesn't support semanticRecall: { scope: "resource" } yet and currently only supports per-thread semantic recall.`
75
71
  );
76
72
  }
73
+ if (config.workingMemory?.enabled && config.workingMemory.scope === `resource` && !this.storage.supports.resourceWorkingMemory) {
74
+ throw new Error(
75
+ `Memory error: Attached storage adapter "${this.storage.name || "unknown"}" doesn't support workingMemory: { scope: "resource" } yet and currently only supports per-thread working memory. Supported adapters: LibSQL, PostgreSQL, Upstash.`
76
+ );
77
+ }
77
78
  }
78
79
  async query({
79
80
  threadId,
@@ -198,18 +199,33 @@ var Memory = class extends MastraMemory {
198
199
  memoryConfig
199
200
  }) {
200
201
  const config = this.getMergedThreadConfig(memoryConfig || {});
201
- if (config.workingMemory?.enabled && !thread?.metadata?.workingMemory) {
202
- let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
203
- if (config.workingMemory.schema) {
204
- workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
205
- }
206
- return this.storage.saveThread({
207
- thread: deepMerge(thread, {
208
- metadata: {
209
- workingMemory
202
+ if (config.workingMemory?.enabled) {
203
+ const scope = config.workingMemory.scope || "thread";
204
+ if (scope === "resource" && thread.resourceId) {
205
+ const existingResource = await this.storage.getResourceById({ resourceId: thread.resourceId });
206
+ if (!existingResource?.workingMemory) {
207
+ let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
208
+ if (config.workingMemory.schema) {
209
+ workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
210
210
  }
211
- })
212
- });
211
+ await this.storage.updateResource({
212
+ resourceId: thread.resourceId,
213
+ workingMemory
214
+ });
215
+ }
216
+ } else if (scope === "thread" && !thread?.metadata?.workingMemory) {
217
+ let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
218
+ if (config.workingMemory.schema) {
219
+ workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
220
+ }
221
+ return this.storage.saveThread({
222
+ thread: deepMerge(thread, {
223
+ metadata: {
224
+ workingMemory
225
+ }
226
+ })
227
+ });
228
+ }
213
229
  }
214
230
  return this.storage.saveThread({ thread });
215
231
  }
@@ -227,6 +243,37 @@ var Memory = class extends MastraMemory {
227
243
  async deleteThread(threadId) {
228
244
  await this.storage.deleteThread({ threadId });
229
245
  }
246
+ async updateWorkingMemory({
247
+ threadId,
248
+ resourceId,
249
+ workingMemory,
250
+ memoryConfig
251
+ }) {
252
+ const config = this.getMergedThreadConfig(memoryConfig || {});
253
+ if (!config.workingMemory?.enabled) {
254
+ throw new Error("Working memory is not enabled for this memory instance");
255
+ }
256
+ const scope = config.workingMemory.scope || "thread";
257
+ if (scope === "resource" && resourceId) {
258
+ await this.storage.updateResource({
259
+ resourceId,
260
+ workingMemory
261
+ });
262
+ } else {
263
+ const thread = await this.storage.getThreadById({ threadId });
264
+ if (!thread) {
265
+ throw new Error(`Thread ${threadId} not found`);
266
+ }
267
+ await this.storage.updateThread({
268
+ id: threadId,
269
+ title: thread.title || "Untitled Thread",
270
+ metadata: {
271
+ ...thread.metadata,
272
+ workingMemory
273
+ }
274
+ });
275
+ }
276
+ }
230
277
  chunkText(text, tokenSize = 4096) {
231
278
  const charSize = tokenSize * CHARS_PER_TOKEN;
232
279
  const chunks = [];
@@ -404,20 +451,26 @@ var Memory = class extends MastraMemory {
404
451
  }
405
452
  async getWorkingMemory({
406
453
  threadId,
407
- format
454
+ resourceId,
455
+ memoryConfig
408
456
  }) {
409
- if (!this.threadConfig.workingMemory?.enabled) {
457
+ const config = this.getMergedThreadConfig(memoryConfig || {});
458
+ if (!config.workingMemory?.enabled) {
410
459
  return null;
411
460
  }
412
- const thread = await this.storage.getThreadById({ threadId });
413
- if (format === "json") {
414
- try {
415
- return JSON.parse(thread?.metadata?.workingMemory) || null;
416
- } catch (e) {
417
- this.logger.error("Unable to parse working memory as JSON. Returning string.", e);
418
- }
461
+ const scope = config.workingMemory.scope || "thread";
462
+ let workingMemoryData = null;
463
+ if (scope === "resource" && resourceId) {
464
+ const resource = await this.storage.getResourceById({ resourceId });
465
+ workingMemoryData = resource?.workingMemory || null;
466
+ } else {
467
+ const thread = await this.storage.getThreadById({ threadId });
468
+ workingMemoryData = thread?.metadata?.workingMemory;
419
469
  }
420
- return thread?.metadata?.workingMemory ? JSON.stringify(thread?.metadata?.workingMemory) : null;
470
+ if (!workingMemoryData) {
471
+ return null;
472
+ }
473
+ return workingMemoryData;
421
474
  }
422
475
  async getWorkingMemoryTemplate() {
423
476
  if (!this.threadConfig.workingMemory?.enabled) {
@@ -440,6 +493,7 @@ var Memory = class extends MastraMemory {
440
493
  }
441
494
  async getSystemMessage({
442
495
  threadId,
496
+ resourceId,
443
497
  memoryConfig
444
498
  }) {
445
499
  const config = this.getMergedThreadConfig(memoryConfig);
@@ -447,7 +501,7 @@ var Memory = class extends MastraMemory {
447
501
  return null;
448
502
  }
449
503
  const workingMemoryTemplate = await this.getWorkingMemoryTemplate();
450
- const workingMemoryData = await this.getWorkingMemory({ threadId });
504
+ const workingMemoryData = await this.getWorkingMemory({ threadId, resourceId, memoryConfig: config });
451
505
  if (!workingMemoryTemplate) {
452
506
  return null;
453
507
  }
@@ -456,8 +510,12 @@ var Memory = class extends MastraMemory {
456
510
  data: workingMemoryData
457
511
  });
458
512
  }
459
- async getUserContextMessage({ threadId }) {
460
- const workingMemory = await this.getWorkingMemory({ threadId });
513
+ async getUserContextMessage({
514
+ threadId,
515
+ resourceId,
516
+ memoryConfig
517
+ }) {
518
+ const workingMemory = await this.getWorkingMemory({ threadId, resourceId, memoryConfig });
461
519
  if (!workingMemory) {
462
520
  return null;
463
521
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/memory",
3
- "version": "0.10.5-alpha.0",
3
+ "version": "0.10.5-alpha.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -40,7 +40,7 @@
40
40
  "postgres": "^3.4.7",
41
41
  "redis": "^4.7.1",
42
42
  "xxhash-wasm": "^1.1.0",
43
- "zod": "^3.25.57",
43
+ "zod": "^3.25.67",
44
44
  "zod-to-json-schema": "^3.24.5"
45
45
  },
46
46
  "devDependencies": {
@@ -48,13 +48,13 @@
48
48
  "@microsoft/api-extractor": "^7.52.8",
49
49
  "@types/node": "^20.19.0",
50
50
  "@types/pg": "^8.15.4",
51
- "eslint": "^9.28.0",
51
+ "eslint": "^9.29.0",
52
52
  "tsup": "^8.5.0",
53
53
  "typescript": "^5.8.3",
54
54
  "typescript-eslint": "^8.34.0",
55
55
  "vitest": "^3.2.3",
56
56
  "@internal/lint": "0.0.13",
57
- "@mastra/core": "0.10.7-alpha.0"
57
+ "@mastra/core": "0.10.7-alpha.2"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@mastra/core": ">=0.10.4-0 <0.11.0"
package/src/index.ts CHANGED
@@ -3,13 +3,7 @@ import type { CoreTool, MastraMessageV1 } from '@mastra/core';
3
3
  import { MessageList } from '@mastra/core/agent';
4
4
  import type { MastraMessageV2 } from '@mastra/core/agent';
5
5
  import { MastraMemory } from '@mastra/core/memory';
6
- import type {
7
- MemoryConfig,
8
- SharedMemoryConfig,
9
- StorageThreadType,
10
- WorkingMemoryFormat,
11
- WorkingMemoryTemplate,
12
- } from '@mastra/core/memory';
6
+ import type { MemoryConfig, SharedMemoryConfig, StorageThreadType, WorkingMemoryTemplate } from '@mastra/core/memory';
13
7
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
14
8
  import { embedMany } from 'ai';
15
9
  import type { CoreMessage, TextPart, UIMessage } from 'ai';
@@ -68,6 +62,16 @@ export class Memory extends MastraMemory {
68
62
  `Memory error: Attached storage adapter "${this.storage.name || 'unknown'}" doesn't support semanticRecall: { scope: "resource" } yet and currently only supports per-thread semantic recall.`,
69
63
  );
70
64
  }
65
+
66
+ if (
67
+ config.workingMemory?.enabled &&
68
+ config.workingMemory.scope === `resource` &&
69
+ !this.storage.supports.resourceWorkingMemory
70
+ ) {
71
+ throw new Error(
72
+ `Memory error: Attached storage adapter "${this.storage.name || 'unknown'}" doesn't support workingMemory: { scope: "resource" } yet and currently only supports per-thread working memory. Supported adapters: LibSQL, PostgreSQL, Upstash.`,
73
+ );
74
+ }
71
75
  }
72
76
 
73
77
  async query({
@@ -252,21 +256,41 @@ export class Memory extends MastraMemory {
252
256
  }): Promise<StorageThreadType> {
253
257
  const config = this.getMergedThreadConfig(memoryConfig || {});
254
258
 
255
- if (config.workingMemory?.enabled && !thread?.metadata?.workingMemory) {
256
- // if working memory is enabled but the thread doesn't have it, we need to set it
257
- let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
259
+ if (config.workingMemory?.enabled) {
260
+ const scope = config.workingMemory.scope || 'thread';
258
261
 
259
- if (config.workingMemory.schema) {
260
- workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
261
- }
262
+ if (scope === 'resource' && thread.resourceId) {
263
+ // For resource scope, initialize working memory in resource table
264
+ const existingResource = await this.storage.getResourceById({ resourceId: thread.resourceId });
265
+
266
+ if (!existingResource?.workingMemory) {
267
+ let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
262
268
 
263
- return this.storage.saveThread({
264
- thread: deepMerge(thread, {
265
- metadata: {
269
+ if (config.workingMemory.schema) {
270
+ workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
271
+ }
272
+
273
+ await this.storage.updateResource({
274
+ resourceId: thread.resourceId,
266
275
  workingMemory,
267
- },
268
- }),
269
- });
276
+ });
277
+ }
278
+ } else if (scope === 'thread' && !thread?.metadata?.workingMemory) {
279
+ // For thread scope, initialize working memory in thread metadata (existing behavior)
280
+ let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
281
+
282
+ if (config.workingMemory.schema) {
283
+ workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
284
+ }
285
+
286
+ return this.storage.saveThread({
287
+ thread: deepMerge(thread, {
288
+ metadata: {
289
+ workingMemory,
290
+ },
291
+ }),
292
+ });
293
+ }
270
294
  }
271
295
 
272
296
  return this.storage.saveThread({ thread });
@@ -292,6 +316,49 @@ export class Memory extends MastraMemory {
292
316
  await this.storage.deleteThread({ threadId });
293
317
  }
294
318
 
319
+ async updateWorkingMemory({
320
+ threadId,
321
+ resourceId,
322
+ workingMemory,
323
+ memoryConfig,
324
+ }: {
325
+ threadId: string;
326
+ resourceId?: string;
327
+ workingMemory: string;
328
+ memoryConfig?: MemoryConfig;
329
+ }): Promise<void> {
330
+ const config = this.getMergedThreadConfig(memoryConfig || {});
331
+
332
+ if (!config.workingMemory?.enabled) {
333
+ throw new Error('Working memory is not enabled for this memory instance');
334
+ }
335
+
336
+ const scope = config.workingMemory.scope || 'thread';
337
+
338
+ if (scope === 'resource' && resourceId) {
339
+ // Update working memory in resource table
340
+ await this.storage.updateResource({
341
+ resourceId,
342
+ workingMemory,
343
+ });
344
+ } else {
345
+ // Update working memory in thread metadata (existing behavior)
346
+ const thread = await this.storage.getThreadById({ threadId });
347
+ if (!thread) {
348
+ throw new Error(`Thread ${threadId} not found`);
349
+ }
350
+
351
+ await this.storage.updateThread({
352
+ id: threadId,
353
+ title: thread.title || 'Untitled Thread',
354
+ metadata: {
355
+ ...thread.metadata,
356
+ workingMemory,
357
+ },
358
+ });
359
+ }
360
+ }
361
+
295
362
  private chunkText(text: string, tokenSize = 4096) {
296
363
  // Convert token size to character size with some buffer
297
364
  const charSize = tokenSize * CHARS_PER_TOKEN;
@@ -555,26 +622,36 @@ export class Memory extends MastraMemory {
555
622
 
556
623
  public async getWorkingMemory({
557
624
  threadId,
558
- format,
625
+ resourceId,
626
+ memoryConfig,
559
627
  }: {
560
628
  threadId: string;
561
- format?: WorkingMemoryFormat;
629
+ resourceId?: string;
630
+ memoryConfig?: MemoryConfig;
562
631
  }): Promise<string | null> {
563
- if (!this.threadConfig.workingMemory?.enabled) {
632
+ const config = this.getMergedThreadConfig(memoryConfig || {});
633
+ if (!config.workingMemory?.enabled) {
564
634
  return null;
565
635
  }
566
636
 
567
- const thread = await this.storage.getThreadById({ threadId });
637
+ const scope = config.workingMemory.scope || 'thread';
638
+ let workingMemoryData: string | null = null;
568
639
 
569
- if (format === 'json') {
570
- try {
571
- return JSON.parse(thread?.metadata?.workingMemory as string) || null;
572
- } catch (e) {
573
- this.logger.error('Unable to parse working memory as JSON. Returning string.', e);
574
- }
640
+ if (scope === 'resource' && resourceId) {
641
+ // Get working memory from resource table
642
+ const resource = await this.storage.getResourceById({ resourceId });
643
+ workingMemoryData = resource?.workingMemory || null;
644
+ } else {
645
+ // Get working memory from thread metadata (default behavior)
646
+ const thread = await this.storage.getThreadById({ threadId });
647
+ workingMemoryData = thread?.metadata?.workingMemory as string;
648
+ }
649
+
650
+ if (!workingMemoryData) {
651
+ return null;
575
652
  }
576
653
 
577
- return thread?.metadata?.workingMemory ? JSON.stringify(thread?.metadata?.workingMemory) : null;
654
+ return workingMemoryData;
578
655
  }
579
656
 
580
657
  public async getWorkingMemoryTemplate(): Promise<WorkingMemoryTemplate | null> {
@@ -605,9 +682,11 @@ export class Memory extends MastraMemory {
605
682
 
606
683
  public async getSystemMessage({
607
684
  threadId,
685
+ resourceId,
608
686
  memoryConfig,
609
687
  }: {
610
688
  threadId: string;
689
+ resourceId?: string;
611
690
  memoryConfig?: MemoryConfig;
612
691
  }): Promise<string | null> {
613
692
  const config = this.getMergedThreadConfig(memoryConfig);
@@ -616,7 +695,7 @@ export class Memory extends MastraMemory {
616
695
  }
617
696
 
618
697
  const workingMemoryTemplate = await this.getWorkingMemoryTemplate();
619
- const workingMemoryData = await this.getWorkingMemory({ threadId });
698
+ const workingMemoryData = await this.getWorkingMemory({ threadId, resourceId, memoryConfig: config });
620
699
 
621
700
  if (!workingMemoryTemplate) {
622
701
  return null;
@@ -628,8 +707,16 @@ export class Memory extends MastraMemory {
628
707
  });
629
708
  }
630
709
 
631
- public async getUserContextMessage({ threadId }: { threadId: string }) {
632
- const workingMemory = await this.getWorkingMemory({ threadId });
710
+ public async getUserContextMessage({
711
+ threadId,
712
+ resourceId,
713
+ memoryConfig,
714
+ }: {
715
+ threadId: string;
716
+ resourceId?: string;
717
+ memoryConfig?: MemoryConfig;
718
+ }) {
719
+ const workingMemory = await this.getWorkingMemory({ threadId, resourceId, memoryConfig });
633
720
  if (!workingMemory) {
634
721
  return null;
635
722
  }
@@ -27,15 +27,11 @@ export const updateWorkingMemoryTool = ({ format }: { format: WorkingMemoryForma
27
27
 
28
28
  const workingMemory = context.memory;
29
29
 
30
- // Update thread metadata with new working memory
31
- await memory.saveThread({
32
- thread: {
33
- ...thread,
34
- metadata: {
35
- ...thread.metadata,
36
- workingMemory: workingMemory,
37
- },
38
- },
30
+ // Use the new updateWorkingMemory method which handles both thread and resource scope
31
+ await memory.updateWorkingMemory({
32
+ threadId,
33
+ resourceId: resourceId || thread.resourceId,
34
+ workingMemory: workingMemory,
39
35
  });
40
36
 
41
37
  return { success: true };