@mastra/memory 1.36.0 → 1.37.0-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.
Files changed (28) hide show
  1. package/dist/docs/SKILL.md +1 -1
  2. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  3. package/dist/docs/references/docs-agents-human-in-the-loop.md +5 -3
  4. package/dist/docs/references/docs-memory-memory-processors.md +2 -5
  5. package/dist/docs/references/docs-memory-semantic-recall.md +2 -2
  6. package/dist/docs/references/docs-memory-working-memory.md +16 -0
  7. package/dist/docs/references/docs-subagents.md +10 -8
  8. package/dist/docs/references/reference-memory-memory-class.md +2 -2
  9. package/dist/docs/references/reference-memory-summarizeConversation.md +1 -1
  10. package/dist/docs/references/reference-migrations-upgrade-to-v1-memory.md +1 -1
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.d.ts +18 -31
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/processors/index.cjs +1 -1
  16. package/dist/processors/index.js +1 -1
  17. package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
  18. package/dist/processors/observational-memory/observation-turn/turn.d.ts +1 -1
  19. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  20. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  21. package/dist/{src-DwfyRK9Q.cjs → src-BYiaPbIt.cjs} +110 -20
  22. package/dist/src-BYiaPbIt.cjs.map +1 -0
  23. package/dist/{src-yhShj4JX.js → src-BaiJf9y9.js} +110 -20
  24. package/dist/{src-yhShj4JX.js.map → src-BaiJf9y9.js.map} +1 -1
  25. package/dist/tools/working-memory.d.ts +4 -0
  26. package/dist/tools/working-memory.d.ts.map +1 -1
  27. package/package.json +3 -3
  28. package/dist/src-DwfyRK9Q.cjs.map +0 -1
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import * as z4 from "zod/v4";
4
4
  import { z as z$1 } from "zod/v4";
5
5
  import { Agent, MessageList } from "@mastra/core/agent";
6
+ import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
6
7
  import { coreFeatures } from "@mastra/core/features";
7
8
  import { MastraMemory, getMemoryTokenBoundary, getThreadOMMetadata, isAfterMemoryTokenBoundary, isThreadTitlePinned, loadMessageHistory, normalizeMessageHistoryConfig, parseMemoryRequestContext, setThreadOMMetadata } from "@mastra/core/memory";
8
9
  import { EntityType, SpanType, createObservabilityContext, getOrCreateSpan } from "@mastra/core/observability";
@@ -22,7 +23,6 @@ import { AsyncLocalStorage } from "async_hooks";
22
23
  import { createHash } from "crypto";
23
24
  import probeImageSizeSync from "probe-image-size/sync.js";
24
25
  import { isStandardSchemaWithJSON as isStandardSchemaWithJSON$1, toStandardSchema as toStandardSchema$1 } from "@mastra/core/schema";
25
- import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
26
26
  import { structuredPatch } from "diff";
27
27
  import { inspect } from "util";
28
28
  //#region \0rolldown/runtime.js
@@ -23598,6 +23598,18 @@ const SET_WORKING_MEMORY_TOOL_NAME = "setWorkingMemory";
23598
23598
  * - Primitive values are overwritten
23599
23599
  * - The returned object is always newly constructed and never aliases `update`
23600
23600
  */
23601
+ /**
23602
+ * Parses stored JSON working memory. Invalid or empty data yields `null` (start fresh).
23603
+ */
23604
+ function parseWorkingMemoryJson(raw) {
23605
+ if (!raw) return null;
23606
+ if (typeof raw !== "string") return raw;
23607
+ try {
23608
+ return JSON.parse(raw);
23609
+ } catch {
23610
+ return null;
23611
+ }
23612
+ }
23601
23613
  function deepMergeWorkingMemory(existing, update) {
23602
23614
  if (!update || typeof update !== "object" || Object.keys(update).length === 0) return existing && typeof existing === "object" ? { ...existing } : {};
23603
23615
  const result = { ...existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {} };
@@ -23704,17 +23716,6 @@ const updateWorkingMemoryTool = (memoryConfig) => {
23704
23716
  }
23705
23717
  let workingMemory;
23706
23718
  if (usesMergeSemantics) {
23707
- const existingRaw = await memory.getWorkingMemory({
23708
- threadId,
23709
- resourceId,
23710
- memoryConfig
23711
- });
23712
- let existingData = null;
23713
- if (existingRaw) try {
23714
- existingData = typeof existingRaw === "string" ? JSON.parse(existingRaw) : existingRaw;
23715
- } catch {
23716
- existingData = null;
23717
- }
23718
23719
  const memoryInput = workingMemoryInput.memory;
23719
23720
  if (memoryInput === void 0 || memoryInput === null) return {
23720
23721
  success: true,
@@ -23728,7 +23729,20 @@ const updateWorkingMemoryTool = (memoryConfig) => {
23728
23729
  throw new Error(`Failed to parse working memory input as JSON: ${errorMessage}. Raw input: ${memoryInput.length > 500 ? memoryInput.slice(0, 500) + "..." : memoryInput}`);
23729
23730
  }
23730
23731
  else newData = memoryInput;
23731
- const mergedData = deepMergeWorkingMemory(existingData, newData);
23732
+ if (scope === "resource" && typeof memory.supportsAtomicWorkingMemoryMerge === "function" && await memory.supportsAtomicWorkingMemoryMerge()) {
23733
+ await memory.mergeWorkingMemory({
23734
+ threadId,
23735
+ resourceId,
23736
+ workingMemory: newData,
23737
+ memoryConfig
23738
+ });
23739
+ return { success: true };
23740
+ }
23741
+ const mergedData = deepMergeWorkingMemory(parseWorkingMemoryJson(await memory.getWorkingMemory({
23742
+ threadId,
23743
+ resourceId,
23744
+ memoryConfig
23745
+ })), newData);
23732
23746
  workingMemory = JSON.stringify(mergedData);
23733
23747
  } else {
23734
23748
  const memoryInput = workingMemoryInput.memory;
@@ -28093,8 +28107,14 @@ var ObservationStep = class {
28093
28107
  const newOutput = messageList.clear.response.db();
28094
28108
  const messagesToSave = [...newInput, ...newOutput];
28095
28109
  if (messagesToSave.length > 0) {
28096
- await om.persistMessages(messagesToSave, threadId, resourceId);
28097
28110
  for (const msg of messagesToSave) messageList.add(msg, "memory");
28111
+ try {
28112
+ await om.persistMessages(messagesToSave, threadId, resourceId);
28113
+ } catch (error) {
28114
+ restoreDrainedMessages(messageList, newInput, "input");
28115
+ restoreDrainedMessages(messageList, newOutput, "response");
28116
+ throw error;
28117
+ }
28098
28118
  }
28099
28119
  } else {
28100
28120
  const pending = [...messageList.get.input.db(), ...messageList.get.response.db()];
@@ -28321,6 +28341,18 @@ var ObservationStep = class {
28321
28341
  };
28322
28342
  }
28323
28343
  };
28344
+ /**
28345
+ * Move messages drained into the memory bucket back to `source` after a failed save. Messages that
28346
+ * have already left the memory bucket (a background tool result moves its message to `response`)
28347
+ * are left where they are. The list's current copy is re-added so changes made during the save
28348
+ * are kept.
28349
+ */
28350
+ function restoreDrainedMessages(messageList, drained, source) {
28351
+ if (drained.length === 0) return;
28352
+ const stillQueued = new Set([...messageList.get.input.db(), ...messageList.get.response.db()].map((msg) => msg.id));
28353
+ const ids = drained.map((msg) => msg.id).filter((id) => !stillQueued.has(id));
28354
+ for (const msg of messageList.removeByIds(ids)) messageList.add(msg, source, { merge: false });
28355
+ }
28324
28356
  //#endregion
28325
28357
  //#region src/processors/observational-memory/observation-turn/turn.ts
28326
28358
  /**
@@ -28479,17 +28511,17 @@ var ObservationTurn = class {
28479
28511
  * proactively while the agent is idle, rather than waiting for the next turn.
28480
28512
  * The returned record does not wait for that background buffering pass to finish.
28481
28513
  */
28482
- async end() {
28514
+ async end(messageList = this.messageList) {
28483
28515
  if (this._ended) throw new Error("Turn already ended");
28484
28516
  this._ended = true;
28485
- const unsavedInput = this.messageList.get.input.db();
28486
- const unsavedOutput = this.messageList.get.response.db();
28517
+ const unsavedInput = messageList.get.input.db();
28518
+ const unsavedOutput = messageList.get.response.db();
28487
28519
  const unsavedMessages = [...unsavedInput, ...unsavedOutput];
28488
28520
  if (unsavedMessages.length > 0) await this.om.persistMessages(unsavedMessages, this.threadId, this.resourceId);
28489
28521
  const asyncObservationEnabled = this.om.buffering.isAsyncObservationEnabled();
28490
28522
  const bufferOnIdle = this.om.getObservationConfig().bufferOnIdle;
28491
28523
  if (asyncObservationEnabled && bufferOnIdle) {
28492
- const allMessages = getObservableMessages(this.messageList);
28524
+ const allMessages = getObservableMessages(messageList);
28493
28525
  const record = this._record;
28494
28526
  const idleMessages = selectSafeBufferPrefix(this.om.getUnobservedMessages(allMessages, record));
28495
28527
  if (idleMessages.length > 0) this.om.trackBackgroundWork(this.om.buffer({
@@ -32825,6 +32857,10 @@ var ObservationalMemoryProcessor = class {
32825
32857
  } catch (error) {
32826
32858
  const err = error instanceof Error ? error : new Error(String(error));
32827
32859
  const abortMessage = abortSignal?.aborted ? "Agent execution was aborted" : `Encountered error during memory observation: ${err.message}`;
32860
+ const failedTurn = this.turn;
32861
+ await failedTurn.end().catch(() => {});
32862
+ if (this.turn === failedTurn) this.turn = void 0;
32863
+ state.__omTurn = void 0;
32828
32864
  if (typeof abort === "function") abort(abortMessage);
32829
32865
  throw err;
32830
32866
  }
@@ -32884,7 +32920,7 @@ var ObservationalMemoryProcessor = class {
32884
32920
  if (parseMemoryRequestContext(requestContext)?.memoryConfig?.readOnly) return messageList;
32885
32921
  const turn = asLiveTurn(state.__omTurn) ?? this.turn;
32886
32922
  const liveTurn = turn && !turn.ended ? turn : void 0;
32887
- if (liveTurn) await liveTurn.end();
32923
+ if (liveTurn) await liveTurn.end(messageList);
32888
32924
  this.turn = void 0;
32889
32925
  state.__omTurn = void 0;
32890
32926
  if (!liveTurn) {
@@ -33156,6 +33192,12 @@ const DEFAULT_EMBEDDING_CACHE_MAX_SIZE = 1e3;
33156
33192
  * @see [Memory documentation](https://mastra.ai/docs/memory/overview)
33157
33193
  * if packaged docs are unavailable.
33158
33194
  */
33195
+ const invalidMerge = (text) => new MastraError({
33196
+ id: "MEMORY_WORKING_MEMORY_MERGE_INVALID",
33197
+ domain: ErrorDomain.MASTRA_MEMORY,
33198
+ category: ErrorCategory.USER,
33199
+ text
33200
+ });
33159
33201
  var Memory = class Memory extends MastraMemory {
33160
33202
  createMemoryTokenCounter() {
33161
33203
  return new TokenCounter();
@@ -33685,6 +33727,54 @@ var Memory = class Memory extends MastraMemory {
33685
33727
  throw error;
33686
33728
  }
33687
33729
  }
33730
+ /**
33731
+ * Whether the configured storage can merge resource working memory atomically across processes.
33732
+ */
33733
+ async supportsAtomicWorkingMemoryMerge() {
33734
+ return (await this.getMemoryStore()).supportsAtomicWorkingMemoryMerge === true;
33735
+ }
33736
+ /**
33737
+ * Deep-merges a partial JSON update into resource-scoped working memory as one
33738
+ * atomic storage operation, so concurrent writers (even in other processes) never
33739
+ * lose each other's fields. `null` values delete fields; arrays are replaced.
33740
+ * Requires schema-based working memory, resource scope, and a storage adapter
33741
+ * that supports atomic merges (e.g. PostgreSQL); otherwise throws.
33742
+ */
33743
+ async mergeWorkingMemory({ threadId, resourceId, workingMemory, memoryConfig, observabilityContext }) {
33744
+ const config = this.getMergedThreadConfig(memoryConfig || {});
33745
+ if (!config.workingMemory?.enabled) throw invalidMerge("Working memory is not enabled for this memory instance");
33746
+ if (!config.workingMemory.schema) throw invalidMerge("Working memory merge requires schema-based (JSON) working memory");
33747
+ if ((config.workingMemory.scope || "resource") !== "resource" || !resourceId) throw invalidMerge("Working memory merge requires resource-scoped working memory and a resourceId");
33748
+ let patch = workingMemory;
33749
+ if (typeof workingMemory === "string") try {
33750
+ patch = JSON.parse(workingMemory);
33751
+ } catch {
33752
+ throw invalidMerge("Working memory merge requires a JSON object");
33753
+ }
33754
+ if (!patch || typeof patch !== "object" || Array.isArray(patch)) throw invalidMerge("Working memory merge requires a JSON object");
33755
+ const memoryStore = await this.getMemoryStore();
33756
+ if (!memoryStore.supportsAtomicWorkingMemoryMerge) throw invalidMerge(`Atomic working memory merge is not supported by this storage adapter (${memoryStore.constructor.name}).`);
33757
+ const span = this.createMemorySpan("update", observabilityContext, {
33758
+ threadId,
33759
+ resourceId
33760
+ }, { workingMemoryEnabled: true });
33761
+ try {
33762
+ await memoryStore.mergeResourceWorkingMemory({
33763
+ resourceId,
33764
+ merge: (existing) => {
33765
+ const parsed = parseWorkingMemoryJson(existing);
33766
+ return JSON.stringify(deepMergeWorkingMemory(parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null, patch));
33767
+ }
33768
+ });
33769
+ span?.end({ output: { success: true } });
33770
+ } catch (error) {
33771
+ span?.error({
33772
+ error,
33773
+ endSpan: true
33774
+ });
33775
+ throw error;
33776
+ }
33777
+ }
33688
33778
  updateWorkingMemoryMutexes = /* @__PURE__ */ new Map();
33689
33779
  /**
33690
33780
  * @warning experimental! can be removed or changed at any time
@@ -35386,4 +35476,4 @@ Notes:
35386
35476
  //#endregion
35387
35477
  export { formatMessagesForObserver as A, OBSERVATION_CONTEXT_PROMPT_THREAD as B, SUMMARIZE_THREAD_DEFAULTS as C, buildObserverPrompt as D, OBSERVER_SYSTEM_PROMPT as E, parseAnchorId as F, StaleKnowledgeSemanticIndexError as G, getObservationContextPrompt as H, stripEphemeralAnchorIds as I, SUBCONSCIOUS_ACTIVITY_STATE_ID as J, SubconsciousRemindExtractor 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, deepMergeWorkingMemory as S, TokenCounter as T, Subconscious as U, OBSERVATION_CONTINUATION_HINT as V, KnowledgeSemanticIndexCoordinator as W, publishSubconsciousActivity as X, buildSubconsciousActivitySnapshot as Y, renderSubconsciousActivity as Z, reconcileObservationGroupsFromReflection as _, extractWorkingMemoryContent as a, wrapInObservationGroup as b, getObservationsAsOf as c, WORKING_MEMORY_STATE_ID as d, WORKING_MEMORY_STATE_PROCESSOR_ID as f, parseObservationGroups as g, deriveObservationGroupProvenance as h, WorkingMemory as i, hasCurrentTaskSection as j, extractCurrentTask as k, ObservationalMemoryProcessor as l, combineObservationGroupRanges as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, WorkingMemoryStateProcessor as p, ModelByInputTokens as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, ObservationalMemory as u, renderObservationGroupsForReflection as v, summarizeConversation as w, WorkingMemoryExtractor as x, stripObservationGroups as y, OBSERVATION_CONTEXT_PROMPT as z };
35388
35478
 
35389
- //# sourceMappingURL=src-yhShj4JX.js.map
35479
+ //# sourceMappingURL=src-BaiJf9y9.js.map