@mastra/memory 1.30.0-alpha.3 → 1.30.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.
Files changed (26) 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-guides-context-engineering.md +1 -1
  4. package/dist/docs/references/docs-memory-observational-memory.md +2 -2
  5. package/dist/docs/references/reference-memory-observational-memory.md +3 -2
  6. package/dist/index.cjs +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/processors/index.cjs +1 -1
  10. package/dist/processors/index.js +1 -1
  11. package/dist/processors/observational-memory/observation-turn/load-memory-context.d.ts.map +1 -1
  12. package/dist/processors/observational-memory/observation-turn/safe-buffer-prefix.d.ts +35 -0
  13. package/dist/processors/observational-memory/observation-turn/safe-buffer-prefix.d.ts.map +1 -0
  14. package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
  15. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  16. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  17. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  18. package/dist/processors/observational-memory/types.d.ts +6 -0
  19. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  20. package/dist/{src-t3Mhge0m.js → src-Dt8oPiQN.js} +101 -33
  21. package/dist/src-Dt8oPiQN.js.map +1 -0
  22. package/dist/{src-CJgJb_MC.cjs → src-oL_dDHN9.cjs} +101 -33
  23. package/dist/src-oL_dDHN9.cjs.map +1 -0
  24. package/package.json +8 -8
  25. package/dist/src-CJgJb_MC.cjs.map +0 -1
  26. package/dist/src-t3Mhge0m.js.map +0 -1
@@ -25266,10 +25266,61 @@ async function loadMemoryContextMessages({ memory, messageList, threadId, resour
25266
25266
  resourceId,
25267
25267
  runState
25268
25268
  });
25269
- for (const msg of ctx.messages) if (msg.role !== "system") messageList.add(msg, "memory");
25269
+ const existingIds = new Set(messageList.get.all.db().map((message) => message.id));
25270
+ for (const msg of ctx.messages) if (msg.role !== "system" && !existingIds.has(msg.id)) messageList.add(msg, "memory");
25270
25271
  return ctx;
25271
25272
  }
25272
25273
  //#endregion
25274
+ //#region src/processors/observational-memory/observation-turn/safe-buffer-prefix.ts
25275
+ function hasPendingToolCall(message) {
25276
+ return message.content.parts?.some((part) => part.type === "tool-invocation" && part.toolInvocation.state === "call") ?? false;
25277
+ }
25278
+ /**
25279
+ * Selects the unobserved messages that are safe to buffer while a tool call
25280
+ * may still be in flight.
25281
+ *
25282
+ * Only the chronologically last candidate can hold a live pending call: a
25283
+ * `tool-invocation` still in state `call` (client- or provider-executed) on the
25284
+ * newest message means the request ended waiting for its result. Any earlier
25285
+ * `call` is an orphan — the conversation already continued past it, and core's
25286
+ * output converter drops or placeholder-pairs it before the prompt reaches a
25287
+ * provider — so it is buffered like any other message.
25288
+ *
25289
+ * When the last candidate is pending, the prefix before it is buffered, but only
25290
+ * when the cut is clean. The cut is unsafe — and the whole attempt is deferred —
25291
+ * when:
25292
+ *
25293
+ * - Cursor collision: buffering advances the persisted cursor to
25294
+ * `max(buffered.createdAt) + 1ms`, and later candidate selection requires
25295
+ * `createdAt > cursor`. If the retained message sits at the same or +1ms
25296
+ * timestamp, the cursor would permanently hide it even after its result
25297
+ * arrives.
25298
+ * - Split tool exchange: a `toolCallId` appears on both sides of the cut, so
25299
+ * the observer would see half of a tool exchange.
25300
+ * - Ambiguous tail: several candidates share the newest timestamp and one of
25301
+ * them holds a pending call. `createdAt` alone cannot say which is really
25302
+ * last, so the pending call is treated as the tail and the attempt deferred.
25303
+ *
25304
+ * The result is always in chronological order. When the tail has no pending
25305
+ * call, every message is returned. An empty result means "defer this buffering
25306
+ * attempt" — callers must skip buffering entirely. Raw message persistence is
25307
+ * unaffected and happens elsewhere; the retained message becomes eligible again
25308
+ * once its tool call completes.
25309
+ */
25310
+ function selectSafeBufferPrefix(messages) {
25311
+ const chronological = [...messages].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
25312
+ const last = chronological[chronological.length - 1];
25313
+ if (!last) return chronological;
25314
+ const newestTime = new Date(last.createdAt).getTime();
25315
+ const newestGroup = chronological.filter((message) => new Date(message.createdAt).getTime() === newestTime);
25316
+ if (!newestGroup.some(hasPendingToolCall)) return chronological;
25317
+ if (newestGroup.length > 1) return [];
25318
+ const prefix = chronological.slice(0, -1);
25319
+ const retainedTime = new Date(last.createdAt).getTime();
25320
+ const retainedToolIds = new Set((last.content.parts ?? []).flatMap((part) => part.type === "tool-invocation" ? [part.toolInvocation.toolCallId] : []));
25321
+ return prefix.some((message) => new Date(message.createdAt).getTime() + 1 >= retainedTime || message.content.parts?.some((part) => part.type === "tool-invocation" && retainedToolIds.has(part.toolInvocation.toolCallId))) ? [] : prefix;
25322
+ }
25323
+ //#endregion
25273
25324
  //#region src/processors/observational-memory/observation-turn/step.ts
25274
25325
  /**
25275
25326
  * Represents a single step in the agentic loop within an observation turn.
@@ -25392,37 +25443,41 @@ var ObservationStep = class {
25392
25443
  record: this.turn.record,
25393
25444
  messages: getObservableMessages(messageList)
25394
25445
  });
25395
- if (statusSnapshot.shouldBuffer && !hasIncompleteToolCalls) {
25446
+ if (statusSnapshot.shouldBuffer) {
25396
25447
  const allMessages = getObservableMessages(messageList);
25397
25448
  const unobservedMessages = om.getUnobservedMessages(allMessages, statusSnapshot.record);
25398
25449
  const candidates = om.getUnobservedMessages(unobservedMessages, statusSnapshot.record, { excludeBuffered: true });
25399
- if (candidates.length > 0) {
25400
- om.sealMessagesForBuffering(candidates);
25450
+ const safeCandidates = selectSafeBufferPrefix(candidates);
25451
+ const deferred = candidates.length > 0 && safeCandidates.length === 0;
25452
+ if (safeCandidates.length > 0) {
25453
+ om.sealMessagesForBuffering(safeCandidates);
25401
25454
  try {
25402
25455
  await this.turn.hooks?.onBufferChunkSealed?.();
25403
25456
  } catch (error) {
25404
25457
  omDebug(`[OM:buffer] onBufferChunkSealed hook failed: ${error instanceof Error ? error.message : String(error)}`);
25405
25458
  }
25406
- if (this.turn.memory) await this.turn.memory.persistMessages(candidates);
25407
- messageList.removeByIds(candidates.map((msg) => msg.id));
25408
- for (const msg of candidates) messageList.add(msg, "memory");
25459
+ if (this.turn.memory) await this.turn.memory.persistMessages(safeCandidates);
25460
+ messageList.removeByIds(safeCandidates.map((msg) => msg.id));
25461
+ for (const msg of safeCandidates) messageList.add(msg, "memory");
25462
+ }
25463
+ if (!deferred) {
25464
+ om.trackBackgroundWork(om.buffer({
25465
+ threadId,
25466
+ resourceId,
25467
+ messages: safeCandidates,
25468
+ pendingTokens: statusSnapshot.pendingTokens,
25469
+ record: statusSnapshot.record,
25470
+ writer: this.turn.writer,
25471
+ agent: this.turn.agent,
25472
+ sendSignal: this.turn.sendSignal,
25473
+ sendStateSignal: this.turn.sendStateSignal,
25474
+ requestContext: this.turn.requestContext,
25475
+ observabilityContext: this.turn.observabilityContext
25476
+ }).catch((err) => {
25477
+ omDebug(`[OM:buffer] fire-and-forget buffer failed: ${err?.message}`);
25478
+ }));
25479
+ buffered = true;
25409
25480
  }
25410
- om.trackBackgroundWork(om.buffer({
25411
- threadId,
25412
- resourceId,
25413
- messages: unobservedMessages,
25414
- pendingTokens: statusSnapshot.pendingTokens,
25415
- record: statusSnapshot.record,
25416
- writer: this.turn.writer,
25417
- agent: this.turn.agent,
25418
- sendSignal: this.turn.sendSignal,
25419
- sendStateSignal: this.turn.sendStateSignal,
25420
- requestContext: this.turn.requestContext,
25421
- observabilityContext: this.turn.observabilityContext
25422
- }).catch((err) => {
25423
- omDebug(`[OM:buffer] fire-and-forget buffer failed: ${err?.message}`);
25424
- }));
25425
- buffered = true;
25426
25481
  }
25427
25482
  const willObserveNow = statusSnapshot.shouldObserve && !hasIncompleteToolCalls;
25428
25483
  /** In-flight message ids the step-0 cleanup must never remove from live context. */
@@ -25797,11 +25852,11 @@ var ObservationTurn = class {
25797
25852
  if (asyncObservationEnabled && bufferOnIdle) {
25798
25853
  const allMessages = getObservableMessages(this.messageList);
25799
25854
  const record = this._record;
25800
- const unobservedMessages = this.om.getUnobservedMessages(allMessages, record);
25801
- if (unobservedMessages.length > 0) this.om.trackBackgroundWork(this.om.buffer({
25855
+ const idleMessages = selectSafeBufferPrefix(this.om.getUnobservedMessages(allMessages, record));
25856
+ if (idleMessages.length > 0) this.om.trackBackgroundWork(this.om.buffer({
25802
25857
  threadId: this.threadId,
25803
25858
  resourceId: this.resourceId,
25804
- messages: unobservedMessages,
25859
+ messages: idleMessages,
25805
25860
  record,
25806
25861
  writer: this.writer,
25807
25862
  agent: this.agent,
@@ -27529,15 +27584,18 @@ var ObservationalMemory = class ObservationalMemory {
27529
27584
  this.hookExecution = config.hookExecution ?? "non-blocking";
27530
27585
  this.mastra = config.mastra;
27531
27586
  this.memory = config.memory;
27587
+ const topLevelModel = config.model;
27588
+ const observationConfigModel = config.observation?.model;
27589
+ const reflectionConfigModel = config.reflection?.model;
27532
27590
  const resolveModel = (model, defaultModel) => model === "default" ? defaultModel : model;
27533
- const observationModel = resolveModel(config.model, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? resolveModel(config.observation?.model, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? resolveModel(config.reflection?.model, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.model;
27534
- const reflectionModel = resolveModel(config.model, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? resolveModel(config.reflection?.model, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? resolveModel(config.observation?.model, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model;
27591
+ const observationModel = resolveModel(topLevelModel, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? resolveModel(observationConfigModel, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? resolveModel(reflectionConfigModel, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.model;
27592
+ const reflectionModel = resolveModel(topLevelModel, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? resolveModel(reflectionConfigModel, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? resolveModel(observationConfigModel, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model;
27535
27593
  const messageTokens = config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens;
27536
27594
  const observationTokens = config.reflection?.observationTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens;
27537
27595
  const isSharedBudget = config.shareTokenBudget ?? false;
27538
27596
  const isDefaultModelSelection = (model) => model === void 0 || model === "default" || model instanceof ModelByInputTokens;
27539
- const observationSelectedModel = config.model ?? config.observation?.model ?? config.reflection?.model;
27540
- const reflectionSelectedModel = config.model ?? config.reflection?.model ?? config.observation?.model;
27597
+ const observationSelectedModel = topLevelModel ?? observationConfigModel ?? reflectionConfigModel;
27598
+ const reflectionSelectedModel = topLevelModel ?? reflectionConfigModel ?? observationConfigModel;
27541
27599
  const observationDefaultMaxOutputTokens = config.observation?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(observationSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.maxOutputTokens : void 0);
27542
27600
  const reflectionDefaultMaxOutputTokens = config.reflection?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(reflectionSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.maxOutputTokens : void 0);
27543
27601
  const totalBudget = messageTokens + observationTokens;
@@ -30257,6 +30315,16 @@ function normalizeObservationalMemoryConfig(config) {
30257
30315
  if (typeof config === "object" && config.enabled === false) return void 0;
30258
30316
  return config;
30259
30317
  }
30318
+ /**
30319
+ * Observer model selection (`observation.model`, else top-level `model`), read into the widened
30320
+ * model type first: combining values of the public type makes TS subtype-reduce the model-id
30321
+ * literal union, which fails with TS2590 once the provider registry is large enough.
30322
+ */
30323
+ function selectObserverModel(omConfig) {
30324
+ const observationModel = omConfig.observation?.model;
30325
+ const topLevelModel = omConfig.model;
30326
+ return observationModel ?? topLevelModel;
30327
+ }
30260
30328
  function hasWorkingMemoryExtractor(extractors) {
30261
30329
  return !!extractors?.some((extractor) => extractor.slug === "working-memory");
30262
30330
  }
@@ -30374,7 +30442,7 @@ var Memory = class Memory extends MastraMemory {
30374
30442
  const extract = observation.extract ?? [];
30375
30443
  const existingSlugs = new Set(extract.map((extractor) => extractor.slug));
30376
30444
  let curatorMemory;
30377
- const subconsciousExtractors = omConfig.experimental_subconscious.createObservationExtractors(observation.model ?? omConfig.model, () => curatorMemory ??= new Memory({
30445
+ const subconsciousExtractors = omConfig.experimental_subconscious.createObservationExtractors(selectObserverModel(omConfig), () => curatorMemory ??= new Memory({
30378
30446
  storage: this.storage,
30379
30447
  options: { observationalMemory: false }
30380
30448
  })).filter((extractor) => !existingSlugs.has(extractor.slug));
@@ -31680,7 +31748,7 @@ Notes:
31680
31748
  if (remind && "builtIn" in remind) tools.ask_memory = createAskMemoryTool({
31681
31749
  memory: this,
31682
31750
  config: remind,
31683
- omModel: omConfig.observation?.model ?? omConfig.model,
31751
+ omModel: selectObserverModel(omConfig),
31684
31752
  getParentAgent: (agentId) => this._mastraInstance?.getAgentById(agentId)
31685
31753
  });
31686
31754
  }
@@ -32359,4 +32427,4 @@ Notes:
32359
32427
  //#endregion
32360
32428
  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, publishSubconsciousActivity as J, SUBCONSCIOUS_ACTIVITY_STATE_ID as K, OBSERVATIONAL_MEMORY_DEFAULTS as L, optimizeObservationsForContext as M, parseObserverOutput as N, buildObserverSystemPrompt as O, injectAnchorIds as P, OBSERVATION_CONTEXT_INSTRUCTIONS as R, WorkingMemoryExtractor as S, TokenCounter as T, StaleKnowledgeSemanticIndexError as U, Subconscious as V, SubconsciousRemindExtractor as W, Extractor as X, renderSubconsciousActivity as Y, 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, buildSubconsciousActivitySnapshot 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 };
32361
32429
 
32362
- //# sourceMappingURL=src-t3Mhge0m.js.map
32430
+ //# sourceMappingURL=src-Dt8oPiQN.js.map