@mastra/memory 1.28.0-alpha.3 → 1.28.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.
@@ -24,8 +24,8 @@ import probeImageSizeSync from "probe-image-size/sync.js";
24
24
  import { isStandardSchemaWithJSON as isStandardSchemaWithJSON$1, toStandardSchema as toStandardSchema$1 } from "@mastra/core/schema";
25
25
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
26
26
  import { MessageHistory, MessageHistory as MessageHistory$1, SemanticRecall, WorkingMemory } from "@mastra/core/processors";
27
- import { inspect } from "util";
28
27
  import { structuredPatch } from "diff";
28
+ import { inspect } from "util";
29
29
  //#region \0rolldown/runtime.js
30
30
  var __defProp$3 = Object.defineProperty;
31
31
  var __exportAll = (all, no_symbols) => {
@@ -22415,6 +22415,151 @@ function createWorkingMemoryTool(config, options = {}) {
22415
22415
  };
22416
22416
  }
22417
22417
  //#endregion
22418
+ //#region src/processors/working-memory-state/processor.ts
22419
+ /**
22420
+ * WorkingMemoryStateProcessor
22421
+ *
22422
+ * Experimental: delivers working memory to the model as a state signal instead
22423
+ * of folding it into the system message. Storage and the `setWorkingMemory`
22424
+ * tool are unchanged — this processor only changes the delivery path.
22425
+ *
22426
+ * Pattern matches `BrowserContextProcessor` in `@mastra/core/browser`:
22427
+ * - `stateId` namespaces the state lane on the thread.
22428
+ * - `cacheKey` is derived from the rendered payload so dedup is automatic.
22429
+ * - `contextWindow.hasSnapshot` re-injection ensures the model still sees the
22430
+ * current snapshot after older messages drop out of the window.
22431
+ *
22432
+ * Delta emission (markdown mode only): when a prior snapshot exists in the
22433
+ * context window, the processor emits a unified-diff delta against that
22434
+ * snapshot's contents. Schema mode and the snapshot fallback always emit a
22435
+ * full snapshot.
22436
+ *
22437
+ * @example
22438
+ * ```ts
22439
+ * new Memory({
22440
+ * options: {
22441
+ * workingMemory: {
22442
+ * enabled: true,
22443
+ * template: '...',
22444
+ * useStateSignals: true, // auto-attaches this processor
22445
+ * },
22446
+ * },
22447
+ * });
22448
+ * ```
22449
+ */
22450
+ const WORKING_MEMORY_STATE_ID = "working-memory";
22451
+ const WORKING_MEMORY_STATE_PROCESSOR_ID = "working-memory-state";
22452
+ var WorkingMemoryStateProcessor = class {
22453
+ memory;
22454
+ memoryConfig;
22455
+ id = WORKING_MEMORY_STATE_PROCESSOR_ID;
22456
+ stateId = WORKING_MEMORY_STATE_ID;
22457
+ constructor(memory, memoryConfig) {
22458
+ this.memory = memory;
22459
+ this.memoryConfig = memoryConfig;
22460
+ }
22461
+ async computeStateSignal(args) {
22462
+ const template = await this.memory.getWorkingMemoryTemplate({ memoryConfig: this.memoryConfig });
22463
+ if (!template) return;
22464
+ const contents = (await this.memory.getWorkingMemory({
22465
+ threadId: args.threadId,
22466
+ resourceId: args.resourceId,
22467
+ memoryConfig: this.memoryConfig
22468
+ }))?.trim();
22469
+ if (!contents) return;
22470
+ const cacheKey = stableWorkingMemoryCacheKey({
22471
+ format: template.format,
22472
+ data: contents
22473
+ });
22474
+ const shouldMakeSnapshot = !args.contextWindow.hasSnapshot;
22475
+ if (args.tracking?.currentCacheKey === cacheKey && !shouldMakeSnapshot) return;
22476
+ const scope = this.memory.getMergedThreadConfig(this.memoryConfig).workingMemory?.scope ?? "resource";
22477
+ const deltaCandidate = template.format === "markdown" && !shouldMakeSnapshot ? buildMarkdownDelta({
22478
+ lastSnapshot: args.lastSnapshot,
22479
+ deltasSinceSnapshot: args.deltasSinceSnapshot,
22480
+ nextContents: contents
22481
+ }) : void 0;
22482
+ if (deltaCandidate) return {
22483
+ id: WORKING_MEMORY_STATE_ID,
22484
+ mode: "delta",
22485
+ cacheKey,
22486
+ tagName: "working-memory",
22487
+ contents: deltaCandidate.contents,
22488
+ delta: deltaCandidate.contents,
22489
+ value: contents,
22490
+ attributes: {
22491
+ format: template.format,
22492
+ scope,
22493
+ patch: "unified-diff"
22494
+ }
22495
+ };
22496
+ return {
22497
+ id: WORKING_MEMORY_STATE_ID,
22498
+ mode: "snapshot",
22499
+ cacheKey,
22500
+ tagName: "working-memory",
22501
+ contents,
22502
+ value: contents,
22503
+ attributes: {
22504
+ format: template.format,
22505
+ scope
22506
+ }
22507
+ };
22508
+ }
22509
+ };
22510
+ /**
22511
+ * Stable cache key for the rendered working memory payload. Returns a SHA-256
22512
+ * digest so dedup metadata stays compact regardless of payload size (working
22513
+ * memory blobs can grow arbitrarily long).
22514
+ */
22515
+ function stableWorkingMemoryCacheKey(input) {
22516
+ const hash = createHash("sha256");
22517
+ hash.update(input.format);
22518
+ hash.update("\0");
22519
+ hash.update(input.data ?? "");
22520
+ return `sha256:${hash.digest("hex")}`;
22521
+ }
22522
+ /**
22523
+ * Build a unified-diff delta against the most recently emitted state. Prefers
22524
+ * the latest delta's `value` (the post-edit full text) when available, falling
22525
+ * back to the snapshot's `value` and finally the snapshot's `contents`. This
22526
+ * keeps deltas incremental (B→C) instead of cumulative against a stale
22527
+ * snapshot (A→C), which matters when many small edits land between snapshots.
22528
+ *
22529
+ * Returns undefined when:
22530
+ * - there's no prior state to diff against
22531
+ * - the prior state isn't a plain string (multimodal signal)
22532
+ *
22533
+ * In either case the caller falls back to emitting a full snapshot.
22534
+ */
22535
+ function buildMarkdownDelta(args) {
22536
+ const { lastSnapshot, deltasSinceSnapshot, nextContents } = args;
22537
+ const prior = pickStringValue(readSignalValue(deltasSinceSnapshot.at(-1))) ?? pickStringValue(readSignalValue(lastSnapshot)) ?? (typeof lastSnapshot?.contents === "string" ? lastSnapshot.contents : void 0);
22538
+ if (!prior) return;
22539
+ return { contents: renderHunksOnly(prior, nextContents) };
22540
+ }
22541
+ function pickStringValue(value) {
22542
+ return typeof value === "string" ? value : void 0;
22543
+ }
22544
+ function readSignalValue(signal) {
22545
+ return (signal?.metadata)?.value;
22546
+ }
22547
+ /**
22548
+ * Render a unified-diff-style patch body containing only `@@` hunks and their
22549
+ * lines — dropping the filename preamble (`Index:` / `===` / `---` / `+++`)
22550
+ * that `createPatch` emits and the `` trailer.
22551
+ * The preamble exists for tooling like `patch -p1` to know which file to
22552
+ * apply to; we only ever diff a single working-memory blob. The newline
22553
+ * trailer is semantically meaningless to the model and adds noise to the
22554
+ * state signal.
22555
+ */
22556
+ function renderHunksOnly(prior, next) {
22557
+ const { hunks } = structuredPatch("", "", prior, next, "", "", { context: 0 });
22558
+ return hunks.map((hunk) => {
22559
+ return [`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`, ...hunk.lines.filter((line) => !line.startsWith("\"))].join("\n");
22560
+ }).join("\n");
22561
+ }
22562
+ //#endregion
22418
22563
  //#region src/processors/observational-memory/activation-ttl.ts
22419
22564
  const MINUTE = 6e4;
22420
22565
  const HOUR = 60 * MINUTE;
@@ -26433,10 +26578,24 @@ function getLatestStepParts(parts) {
26433
26578
  return parts;
26434
26579
  }
26435
26580
  /**
26436
- * Returns true when a message contains at least one part with visible user/assistant
26437
- * content (text, tool-invocation, reasoning, image, file). Messages that only carry
26438
- * internal `data-*` parts (buffering markers, observation markers, etc.) return false.
26581
+ * Returns true for persisted Working Memory state signals (`role: 'signal'`,
26582
+ * `signal.type: 'state'`, `state.id: 'working-memory'`). OM must not observe these:
26583
+ * they mirror memory OM itself manages, and re-observing them creates a self-feedback
26584
+ * loop that can overwrite stored working memory (#21961). Only the working-memory lane
26585
+ * is filtered — other state lanes are outside OM's write path, so re-observing them
26586
+ * cannot corrupt OM-managed state; excluding them broadly is a separate decision.
26439
26587
  */
26588
+ function isWorkingMemoryStateSignal(message) {
26589
+ if (message.role !== "signal") return false;
26590
+ const signal = message.content.metadata?.signal;
26591
+ if (!signal || typeof signal !== "object" || Array.isArray(signal)) return false;
26592
+ const signalRecord = signal;
26593
+ if ((signalRecord.type ?? message.type) !== "state") return false;
26594
+ const metadata = signalRecord.metadata;
26595
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return false;
26596
+ const state = metadata.state;
26597
+ return !!state && typeof state === "object" && !Array.isArray(state) && state.id === "working-memory";
26598
+ }
26440
26599
  function messageHasVisibleContent(msg) {
26441
26600
  const content = msg.content;
26442
26601
  if (content?.parts && Array.isArray(content.parts)) return content.parts.some((p) => {
@@ -27207,7 +27366,7 @@ var ObservationalMemory = class ObservationalMemory {
27207
27366
  }
27208
27367
  const result = [];
27209
27368
  for (const msg of allMessages) {
27210
- if (msg.role === "system") continue;
27369
+ if (msg.role === "system" || isWorkingMemoryStateSignal(msg)) continue;
27211
27370
  if (observedMessageIds?.has(msg.id)) continue;
27212
27371
  const endMarkerIndex = findLastCompletedObservationBoundary(msg);
27213
27372
  if (this.hasInProgressObservation(msg)) result.push(msg);
@@ -27429,7 +27588,7 @@ var ObservationalMemory = class ObservationalMemory {
27429
27588
  },
27430
27589
  filter: startDate ? { dateRange: { start: startDate } } : void 0
27431
27590
  });
27432
- return result.messages.filter((msg) => msg.role !== "system");
27591
+ return result.messages.filter((msg) => msg.role !== "system" && !isWorkingMemoryStateSignal(msg));
27433
27592
  }
27434
27593
  /**
27435
27594
  * Format unobserved messages from other threads as <unobserved-context> blocks.
@@ -27888,7 +28047,7 @@ ${formattedMessages}
27888
28047
  direction: "ASC"
27889
28048
  },
27890
28049
  filter: startDate ? { dateRange: { start: startDate } } : void 0
27891
- })).messages.filter((m) => !this.observedMessageIds.has(m.id));
28050
+ })).messages.filter((message) => !this.observedMessageIds.has(message.id) && !isWorkingMemoryStateSignal(message));
27892
28051
  if (filtered.length > 0) messagesByThread.set(thread.id, filtered);
27893
28052
  }
27894
28053
  if (messagesByThread.size === 0) return void 0;
@@ -29314,151 +29473,6 @@ var observational_memory_exports = /* @__PURE__ */ __exportAll({
29314
29473
  wrapInObservationGroup: () => wrapInObservationGroup
29315
29474
  });
29316
29475
  //#endregion
29317
- //#region src/processors/working-memory-state/processor.ts
29318
- /**
29319
- * WorkingMemoryStateProcessor
29320
- *
29321
- * Experimental: delivers working memory to the model as a state signal instead
29322
- * of folding it into the system message. Storage and the `setWorkingMemory`
29323
- * tool are unchanged — this processor only changes the delivery path.
29324
- *
29325
- * Pattern matches `BrowserContextProcessor` in `@mastra/core/browser`:
29326
- * - `stateId` namespaces the state lane on the thread.
29327
- * - `cacheKey` is derived from the rendered payload so dedup is automatic.
29328
- * - `contextWindow.hasSnapshot` re-injection ensures the model still sees the
29329
- * current snapshot after older messages drop out of the window.
29330
- *
29331
- * Delta emission (markdown mode only): when a prior snapshot exists in the
29332
- * context window, the processor emits a unified-diff delta against that
29333
- * snapshot's contents. Schema mode and the snapshot fallback always emit a
29334
- * full snapshot.
29335
- *
29336
- * @example
29337
- * ```ts
29338
- * new Memory({
29339
- * options: {
29340
- * workingMemory: {
29341
- * enabled: true,
29342
- * template: '...',
29343
- * useStateSignals: true, // auto-attaches this processor
29344
- * },
29345
- * },
29346
- * });
29347
- * ```
29348
- */
29349
- const WORKING_MEMORY_STATE_ID = "working-memory";
29350
- const WORKING_MEMORY_STATE_PROCESSOR_ID = "working-memory-state";
29351
- var WorkingMemoryStateProcessor = class {
29352
- memory;
29353
- memoryConfig;
29354
- id = WORKING_MEMORY_STATE_PROCESSOR_ID;
29355
- stateId = WORKING_MEMORY_STATE_ID;
29356
- constructor(memory, memoryConfig) {
29357
- this.memory = memory;
29358
- this.memoryConfig = memoryConfig;
29359
- }
29360
- async computeStateSignal(args) {
29361
- const template = await this.memory.getWorkingMemoryTemplate({ memoryConfig: this.memoryConfig });
29362
- if (!template) return;
29363
- const contents = (await this.memory.getWorkingMemory({
29364
- threadId: args.threadId,
29365
- resourceId: args.resourceId,
29366
- memoryConfig: this.memoryConfig
29367
- }))?.trim();
29368
- if (!contents) return;
29369
- const cacheKey = stableWorkingMemoryCacheKey({
29370
- format: template.format,
29371
- data: contents
29372
- });
29373
- const shouldMakeSnapshot = !args.contextWindow.hasSnapshot;
29374
- if (args.tracking?.currentCacheKey === cacheKey && !shouldMakeSnapshot) return;
29375
- const scope = this.memory.getMergedThreadConfig(this.memoryConfig).workingMemory?.scope ?? "resource";
29376
- const deltaCandidate = template.format === "markdown" && !shouldMakeSnapshot ? buildMarkdownDelta({
29377
- lastSnapshot: args.lastSnapshot,
29378
- deltasSinceSnapshot: args.deltasSinceSnapshot,
29379
- nextContents: contents
29380
- }) : void 0;
29381
- if (deltaCandidate) return {
29382
- id: WORKING_MEMORY_STATE_ID,
29383
- mode: "delta",
29384
- cacheKey,
29385
- tagName: "working-memory",
29386
- contents: deltaCandidate.contents,
29387
- delta: deltaCandidate.contents,
29388
- value: contents,
29389
- attributes: {
29390
- format: template.format,
29391
- scope,
29392
- patch: "unified-diff"
29393
- }
29394
- };
29395
- return {
29396
- id: WORKING_MEMORY_STATE_ID,
29397
- mode: "snapshot",
29398
- cacheKey,
29399
- tagName: "working-memory",
29400
- contents,
29401
- value: contents,
29402
- attributes: {
29403
- format: template.format,
29404
- scope
29405
- }
29406
- };
29407
- }
29408
- };
29409
- /**
29410
- * Stable cache key for the rendered working memory payload. Returns a SHA-256
29411
- * digest so dedup metadata stays compact regardless of payload size (working
29412
- * memory blobs can grow arbitrarily long).
29413
- */
29414
- function stableWorkingMemoryCacheKey(input) {
29415
- const hash = createHash("sha256");
29416
- hash.update(input.format);
29417
- hash.update("\0");
29418
- hash.update(input.data ?? "");
29419
- return `sha256:${hash.digest("hex")}`;
29420
- }
29421
- /**
29422
- * Build a unified-diff delta against the most recently emitted state. Prefers
29423
- * the latest delta's `value` (the post-edit full text) when available, falling
29424
- * back to the snapshot's `value` and finally the snapshot's `contents`. This
29425
- * keeps deltas incremental (B→C) instead of cumulative against a stale
29426
- * snapshot (A→C), which matters when many small edits land between snapshots.
29427
- *
29428
- * Returns undefined when:
29429
- * - there's no prior state to diff against
29430
- * - the prior state isn't a plain string (multimodal signal)
29431
- *
29432
- * In either case the caller falls back to emitting a full snapshot.
29433
- */
29434
- function buildMarkdownDelta(args) {
29435
- const { lastSnapshot, deltasSinceSnapshot, nextContents } = args;
29436
- const prior = pickStringValue(readSignalValue(deltasSinceSnapshot.at(-1))) ?? pickStringValue(readSignalValue(lastSnapshot)) ?? (typeof lastSnapshot?.contents === "string" ? lastSnapshot.contents : void 0);
29437
- if (!prior) return;
29438
- return { contents: renderHunksOnly(prior, nextContents) };
29439
- }
29440
- function pickStringValue(value) {
29441
- return typeof value === "string" ? value : void 0;
29442
- }
29443
- function readSignalValue(signal) {
29444
- return (signal?.metadata)?.value;
29445
- }
29446
- /**
29447
- * Render a unified-diff-style patch body containing only `@@` hunks and their
29448
- * lines — dropping the filename preamble (`Index:` / `===` / `---` / `+++`)
29449
- * that `createPatch` emits and the `` trailer.
29450
- * The preamble exists for tooling like `patch -p1` to know which file to
29451
- * apply to; we only ever diff a single working-memory blob. The newline
29452
- * trailer is semantically meaningless to the model and adds noise to the
29453
- * state signal.
29454
- */
29455
- function renderHunksOnly(prior, next) {
29456
- const { hunks } = structuredPatch("", "", prior, next, "", "", { context: 0 });
29457
- return hunks.map((hunk) => {
29458
- return [`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`, ...hunk.lines.filter((line) => !line.startsWith("\"))].join("\n");
29459
- }).join("\n");
29460
- }
29461
- //#endregion
29462
29476
  //#region src/processors/working-memory-state/index.ts
29463
29477
  var working_memory_state_exports = /* @__PURE__ */ __exportAll({
29464
29478
  WORKING_MEMORY_STATE_ID: () => WORKING_MEMORY_STATE_ID,
@@ -31479,6 +31493,6 @@ Notes:
31479
31493
  }
31480
31494
  };
31481
31495
  //#endregion
31482
- export { formatMessagesForObserver as A, OBSERVATION_CONTINUATION_HINT as B, SUMMARIZE_THREAD_DEFAULTS as C, buildObserverPrompt as D, OBSERVER_SYSTEM_PROMPT as E, parseAnchorId as F, ModelByInputTokens as G, KnowledgeSemanticIndexCoordinator as H, stripEphemeralAnchorIds as I, SUBCONSCIOUS_ACTIVITY_STATE_ID as J, SubconsciousCaptureExtractor as K, OBSERVATIONAL_MEMORY_DEFAULTS as L, optimizeObservationsForContext as M, parseObserverOutput as N, buildObserverSystemPrompt as O, injectAnchorIds as P, Extractor as Q, OBSERVATION_CONTEXT_INSTRUCTIONS as R, WorkingMemoryExtractor as S, TokenCounter as T, StaleKnowledgeSemanticIndexError as U, Subconscious as V, SubconsciousRemindExtractor as W, publishSubconsciousActivity as X, buildSubconsciousActivitySnapshot as Y, renderSubconsciousActivity as Z, reconcileObservationGroupsFromReflection as _, extractWorkingMemoryContent as a, wrapInObservationGroup as b, WORKING_MEMORY_STATE_ID as c, getObservationsAsOf as d, ObservationalMemoryProcessor as f, parseObservationGroups as g, deriveObservationGroupProvenance as h, WorkingMemory as i, hasCurrentTaskSection as j, extractCurrentTask as k, WORKING_MEMORY_STATE_PROCESSOR_ID as l, combineObservationGroupRanges as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, ObservationalMemory as p, subconsciousCaptureSchema as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, WorkingMemoryStateProcessor as u, renderObservationGroupsForReflection as v, summarizeConversation as w, deepMergeWorkingMemory as x, stripObservationGroups as y, OBSERVATION_CONTEXT_PROMPT as z };
31496
+ export { formatMessagesForObserver as A, OBSERVATION_CONTINUATION_HINT as B, SUMMARIZE_THREAD_DEFAULTS as C, buildObserverPrompt as D, OBSERVER_SYSTEM_PROMPT as E, parseAnchorId as F, ModelByInputTokens as G, KnowledgeSemanticIndexCoordinator as H, stripEphemeralAnchorIds as I, SUBCONSCIOUS_ACTIVITY_STATE_ID as J, SubconsciousCaptureExtractor as K, OBSERVATIONAL_MEMORY_DEFAULTS as L, optimizeObservationsForContext as M, parseObserverOutput as N, buildObserverSystemPrompt as O, injectAnchorIds as P, Extractor as Q, OBSERVATION_CONTEXT_INSTRUCTIONS as R, WorkingMemoryExtractor as S, TokenCounter as T, StaleKnowledgeSemanticIndexError as U, Subconscious as V, SubconsciousRemindExtractor as W, publishSubconsciousActivity as X, buildSubconsciousActivitySnapshot as Y, renderSubconsciousActivity as Z, wrapInObservationGroup as _, extractWorkingMemoryContent as a, WorkingMemoryStateProcessor as b, getObservationsAsOf as c, combineObservationGroupRanges as d, deriveObservationGroupProvenance as f, stripObservationGroups as g, renderObservationGroupsForReflection as h, WorkingMemory as i, hasCurrentTaskSection as j, extractCurrentTask as k, ObservationalMemoryProcessor as l, reconcileObservationGroupsFromReflection as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, parseObservationGroups as p, subconsciousCaptureSchema as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, ObservationalMemory as u, WORKING_MEMORY_STATE_ID as v, summarizeConversation as w, deepMergeWorkingMemory as x, WORKING_MEMORY_STATE_PROCESSOR_ID as y, OBSERVATION_CONTEXT_PROMPT as z };
31483
31497
 
31484
- //# sourceMappingURL=src-BTHCV1ov.js.map
31498
+ //# sourceMappingURL=src-BATJenuZ.js.map