@mastra/memory 1.27.0-alpha.0 → 1.27.0-alpha.2

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 (38) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/docs/SKILL.md +7 -6
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/{docs-agents-agent-approval.md → docs-agents-human-in-the-loop.md} +5 -5
  5. package/dist/docs/references/docs-agents-networks.md +3 -3
  6. package/dist/docs/references/{docs-long-running-agents-background-tasks.md → docs-harness-background-tasks.md} +5 -5
  7. package/dist/docs/references/{docs-long-running-agents-goals.md → docs-harness-goals.md} +6 -6
  8. package/dist/docs/references/docs-memory-message-history.md +1 -1
  9. package/dist/docs/references/docs-memory-overview.md +4 -4
  10. package/dist/docs/references/docs-memory-working-memory.md +1 -1
  11. package/dist/docs/references/{docs-storage-overview.md → docs-storage.md} +1 -1
  12. package/dist/docs/references/{docs-capabilities-subagents.md → docs-subagents.md} +35 -5
  13. package/dist/docs/references/integrations-channels-github.md +1 -1
  14. package/dist/docs/references/integrations-databases-postgresql.md +1 -0
  15. package/dist/docs/references/reference-file-based-agents-memory.md +2 -2
  16. package/dist/docs/references/reference-memory-memory-class.md +1 -0
  17. package/dist/docs/references/reference-memory-settled.md +57 -0
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.ts +15 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/processors/index.cjs +1 -1
  23. package/dist/processors/index.js +1 -1
  24. package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  26. package/dist/processors/observational-memory/observational-memory.d.ts +19 -0
  27. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  28. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  29. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
  30. package/dist/processors/observational-memory/types.d.ts +2 -0
  31. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  32. package/dist/{src-BUlG9vaB.js → src-C-nmWgoF.js} +74 -11
  33. package/dist/{src-BUlG9vaB.js.map → src-C-nmWgoF.js.map} +1 -1
  34. package/dist/{src-MvtBOFYO.cjs → src-Drm6l_ZN.cjs} +74 -11
  35. package/dist/{src-MvtBOFYO.cjs.map → src-Drm6l_ZN.cjs.map} +1 -1
  36. package/dist/tools/working-memory.d.ts +3 -1
  37. package/dist/tools/working-memory.d.ts.map +1 -1
  38. package/package.json +3 -3
@@ -19903,21 +19903,22 @@ const SET_WORKING_MEMORY_TOOL_NAME = "setWorkingMemory";
19903
19903
  /**
19904
19904
  * Deep merges two objects, with special handling for null values (delete) and arrays (replace).
19905
19905
  * - Object properties are recursively merged
19906
- * - null values in the update will delete the corresponding property
19906
+ * - null values in the update will delete the corresponding property, even when the property
19907
+ * or its parent object does not exist yet (so padded nulls never get stored literally)
19907
19908
  * - Arrays are replaced entirely (not merged element-by-element)
19908
19909
  * - Primitive values are overwritten
19910
+ * - The returned object is always newly constructed and never aliases `update`
19909
19911
  */
19910
19912
  function deepMergeWorkingMemory(existing, update) {
19911
19913
  if (!update || typeof update !== "object" || Object.keys(update).length === 0) return existing && typeof existing === "object" ? { ...existing } : {};
19912
- if (!existing || typeof existing !== "object") return update;
19913
- const result = { ...existing };
19914
+ const result = { ...existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {} };
19914
19915
  for (const key of Object.keys(update)) {
19915
19916
  const updateValue = update[key];
19916
19917
  const existingValue = result[key];
19917
19918
  if (updateValue === void 0) continue;
19918
19919
  if (updateValue === null) delete result[key];
19919
19920
  else if (Array.isArray(updateValue)) result[key] = updateValue;
19920
- else if (typeof updateValue === "object" && updateValue !== null && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) result[key] = deepMergeWorkingMemory(existingValue, updateValue);
19921
+ else if (typeof updateValue === "object") result[key] = deepMergeWorkingMemory(existingValue && typeof existingValue === "object" && !Array.isArray(existingValue) ? existingValue : void 0, updateValue);
19921
19922
  else result[key] = updateValue;
19922
19923
  }
19923
19924
  return result;
@@ -22294,7 +22295,7 @@ var ObservationStep = class {
22294
22295
  messageList.removeByIds(candidates.map((msg) => msg.id));
22295
22296
  for (const msg of candidates) messageList.add(msg, "memory");
22296
22297
  }
22297
- om.buffer({
22298
+ om.trackBackgroundWork(om.buffer({
22298
22299
  threadId,
22299
22300
  resourceId,
22300
22301
  messages: unobservedMessages,
@@ -22305,7 +22306,7 @@ var ObservationStep = class {
22305
22306
  observabilityContext: this.turn.observabilityContext
22306
22307
  }).catch((err) => {
22307
22308
  omDebug(`[OM:buffer] fire-and-forget buffer failed: ${err?.message}`);
22308
- });
22309
+ }));
22309
22310
  buffered = true;
22310
22311
  }
22311
22312
  const willObserveNow = statusSnapshot.shouldObserve && !hasIncompleteToolCalls;
@@ -22673,7 +22674,7 @@ var ObservationTurn = class {
22673
22674
  const allMessages = getObservableMessages(this.messageList);
22674
22675
  const record = this._record;
22675
22676
  const unobservedMessages = this.om.getUnobservedMessages(allMessages, record);
22676
- if (unobservedMessages.length > 0) this.om.buffer({
22677
+ if (unobservedMessages.length > 0) this.om.trackBackgroundWork(this.om.buffer({
22677
22678
  threadId: this.threadId,
22678
22679
  resourceId: this.resourceId,
22679
22680
  messages: unobservedMessages,
@@ -22687,7 +22688,7 @@ var ObservationTurn = class {
22687
22688
  skipMinimumTokenCheck: true
22688
22689
  }).catch((err) => {
22689
22690
  omDebug(`[OM:turn.end] idle buffer failed: ${err?.message}`);
22690
- });
22691
+ }));
22691
22692
  }
22692
22693
  return { record: this._record };
22693
22694
  }
@@ -23171,6 +23172,8 @@ var ReflectorRunner = class {
23171
23172
  };
23172
23173
  let reflectedTokens = 0;
23173
23174
  let attemptNumber = 0;
23175
+ /** Observations from the previous attempt, used to detect a no-progress ladder. */
23176
+ let previousObservations;
23174
23177
  while (currentLevel <= maxLevel) {
23175
23178
  attemptNumber++;
23176
23179
  const isRetry = attemptNumber > 1;
@@ -23244,6 +23247,11 @@ var ReflectorRunner = class {
23244
23247
  omDebug(`[OM:callReflector] degenerate output persists at maxLevel=${maxLevel}, breaking`);
23245
23248
  break;
23246
23249
  }
23250
+ if (!parsed.degenerate && previousObservations !== void 0 && parsed.observations === previousObservations) {
23251
+ omDebug(`[OM:callReflector] attempt #${attemptNumber} returned output identical to the previous attempt; escalating cannot help, stopping the ladder`);
23252
+ break;
23253
+ }
23254
+ previousObservations = parsed.observations;
23247
23255
  if (streamContext?.writer) {
23248
23256
  const failedMarker = createObservationFailedMarker({
23249
23257
  cycleId: streamContext.cycleId,
@@ -24222,6 +24230,39 @@ var ObservationalMemory = class ObservationalMemory {
24222
24230
  */
24223
24231
  recordInitializations = /* @__PURE__ */ new Map();
24224
24232
  /**
24233
+ * In-flight fire-and-forget background cycles (buffered observation, reflection).
24234
+ * Tracked so callers can join them via `settled()` before closing a storage
24235
+ * connection they own — otherwise a background cycle's tail statements race the close.
24236
+ */
24237
+ pendingBackgroundWork = /* @__PURE__ */ new Set();
24238
+ /**
24239
+ * Register fire-and-forget background work so `settled()` can join it.
24240
+ * Returns the original promise so callers keep their own rejection handling.
24241
+ */
24242
+ trackBackgroundWork(work) {
24243
+ const tracked = work.catch(() => {}).finally(() => {
24244
+ this.pendingBackgroundWork.delete(tracked);
24245
+ });
24246
+ this.pendingBackgroundWork.add(tracked);
24247
+ return work;
24248
+ }
24249
+ /**
24250
+ * Resolve once all background observational-memory work started so far has finished.
24251
+ *
24252
+ * Background cycles enqueue further background work (a buffered observation can
24253
+ * trigger a reflection, which runs nested agent streams), so this drains repeatedly
24254
+ * until nothing is left rather than joining a single snapshot.
24255
+ */
24256
+ async settled() {
24257
+ const maxDrainRounds = 100;
24258
+ for (let round = 0; round < maxDrainRounds; round++) {
24259
+ if (this.pendingBackgroundWork.size === 0) return;
24260
+ await Promise.allSettled([...this.pendingBackgroundWork]);
24261
+ }
24262
+ if (this.pendingBackgroundWork.size === 0) return;
24263
+ omError(`[OM:settled] background work still pending after ${maxDrainRounds} drain rounds; giving up waiting`);
24264
+ }
24265
+ /**
24225
24266
  * Acquire a lock for the given key, execute the callback, then release.
24226
24267
  * If a lock is already held, waits for it to be released before acquiring.
24227
24268
  */
@@ -25222,7 +25263,7 @@ ${formattedMessages}
25222
25263
  if (!this.buffering.isAsyncObservationEnabled()) return false;
25223
25264
  const lockKey = this.buffering.getLockKey(opts.threadId, opts.resourceId);
25224
25265
  const shouldTrigger = this.buffering.shouldTriggerAsyncObservation(opts.pendingTokens, lockKey, opts.record, this.storage, opts.threshold);
25225
- if (shouldTrigger) this.startAsyncBufferedObservation(opts.record, opts.threadId, opts.unobservedMessages, lockKey, opts.writer, opts.unbufferedPendingTokens, opts.requestContext);
25266
+ if (shouldTrigger) this.trackBackgroundWork(this.startAsyncBufferedObservation(opts.record, opts.threadId, opts.unobservedMessages, lockKey, opts.writer, opts.unbufferedPendingTokens, opts.requestContext));
25226
25267
  return shouldTrigger;
25227
25268
  }
25228
25269
  isMessageList(value) {
@@ -25535,7 +25576,10 @@ ${formattedMessages}
25535
25576
  }
25536
25577
  };
25537
25578
  omDebug(`[OM:status] step=${stepNumber} msgs=${pendingTokens}/${threshold} obs=${currentObservationTokens}/${effectiveObservationTokensThreshold} gen=${record.generationCount}`);
25538
- await writer.custom(statusPart).catch(() => {});
25579
+ await writer.custom({
25580
+ ...statusPart,
25581
+ transient: true
25582
+ }).catch(() => {});
25539
25583
  }
25540
25584
  }
25541
25585
  /**
@@ -27049,6 +27093,25 @@ var Memory = class extends _mastra_core_memory.MastraMemory {
27049
27093
  trackVectorCleanup(cleanup) {
27050
27094
  this.pendingVectorCleanup = Promise.allSettled([this.pendingVectorCleanup, cleanup]).then(() => void 0);
27051
27095
  }
27096
+ /**
27097
+ * Resolve once all background work this Memory started has finished: observational-memory
27098
+ * cycles (buffered observation and reflection, including the nested agent runs they spawn)
27099
+ * and vector cleanup from `deleteThread` / `deleteMessages`.
27100
+ *
27101
+ * Callers that own the storage connection should await this before closing it, otherwise
27102
+ * background statements can race the close.
27103
+ *
27104
+ * ```ts
27105
+ * await agent.generate('hello', { memory: { thread, resource } });
27106
+ * await memory.settled();
27107
+ * await store.close();
27108
+ * ```
27109
+ */
27110
+ async settled() {
27111
+ await this.pendingVectorCleanup;
27112
+ await (this._omEngine ? await this._omEngine : this._omEngineInstance)?.settled();
27113
+ await this.pendingVectorCleanup;
27114
+ }
27052
27115
  /** The shared ObservationalMemory engine. Lazily created on first access. */
27053
27116
  get omEngine() {
27054
27117
  if (!this._omEngine) this._omEngine = this._initOMEngine().then((engine) => {
@@ -29046,4 +29109,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
29046
29109
  }
29047
29110
  });
29048
29111
 
29049
- //# sourceMappingURL=src-MvtBOFYO.cjs.map
29112
+ //# sourceMappingURL=src-Drm6l_ZN.cjs.map