@mastra/memory 1.26.0 → 1.26.1-alpha.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 (29) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/docs/SKILL.md +1 -1
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-capabilities-subagents.md +23 -5
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/processors/index.cjs +1 -1
  8. package/dist/processors/index.js +1 -1
  9. package/dist/processors/observational-memory/extraction-runner.d.ts.map +1 -1
  10. package/dist/processors/observational-memory/extractor.d.ts +3 -0
  11. package/dist/processors/observational-memory/extractor.d.ts.map +1 -1
  12. package/dist/processors/observational-memory/observation-strategies/base.d.ts +5 -1
  13. package/dist/processors/observational-memory/observation-strategies/base.d.ts.map +1 -1
  14. package/dist/processors/observational-memory/observation-strategies/types.d.ts +7 -1
  15. package/dist/processors/observational-memory/observation-strategies/types.d.ts.map +1 -1
  16. package/dist/processors/observational-memory/observation-turn/step.d.ts +6 -0
  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 +2 -0
  19. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  20. package/dist/processors/observational-memory/observational-memory.d.ts +6 -0
  21. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  22. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  23. package/dist/processors/observational-memory/token-counter.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/working-memory-extractor.d.ts.map +1 -1
  25. package/dist/{src-Cwt9gefz.js → src-Iw-V5CfD.js} +115 -27
  26. package/dist/{src-Cwt9gefz.js.map → src-Iw-V5CfD.js.map} +1 -1
  27. package/dist/{src-CjTEWCUF.cjs → src-x5iu_K3X.cjs} +115 -27
  28. package/dist/{src-CjTEWCUF.cjs.map → src-x5iu_K3X.cjs.map} +1 -1
  29. package/package.json +5 -5
@@ -14856,6 +14856,7 @@ var Extractor = class Extractor {
14856
14856
  includePreviousExtraction;
14857
14857
  metadataKeyPath;
14858
14858
  onExtracted;
14859
+ retryStructuredExtractionOnEmptyObject;
14859
14860
  /** @internal */
14860
14861
  internal;
14861
14862
  instructionsConfig;
@@ -14878,6 +14879,7 @@ var Extractor = class Extractor {
14878
14879
  this.includePreviousExtraction = config.includePreviousExtraction ?? true;
14879
14880
  this.metadataKeyPath = config.metadataKeyPath ?? `extracted.${slug}`;
14880
14881
  this.onExtracted = config.onExtracted;
14882
+ this.retryStructuredExtractionOnEmptyObject = config.retryStructuredExtractionOnEmptyObject ?? false;
14881
14883
  this.internal = internal;
14882
14884
  }
14883
14885
  async resolve(context) {
@@ -14890,7 +14892,8 @@ var Extractor = class Extractor {
14890
14892
  ...schema ? { schema } : {},
14891
14893
  includePreviousExtraction: this.includePreviousExtraction,
14892
14894
  metadataKeyPath: this.metadataKeyPath,
14893
- onExtracted: this.onExtracted
14895
+ onExtracted: this.onExtracted,
14896
+ retryStructuredExtractionOnEmptyObject: this.retryStructuredExtractionOnEmptyObject
14894
14897
  }, this.internal);
14895
14898
  }
14896
14899
  };
@@ -15215,6 +15218,9 @@ if (OM_DEBUG_LOG) {
15215
15218
  function isAbortError$1(error, abortSignal) {
15216
15219
  return abortSignal?.aborted === true || error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
15217
15220
  }
15221
+ function shouldRetryEmptyStructuredObject(object, extractors) {
15222
+ return Object.keys(object).length === 0 && extractors.some((extractor) => extractor.retryStructuredExtractionOnEmptyObject);
15223
+ }
15218
15224
  async function extractStructuredValues(opts) {
15219
15225
  const structuredExtractors = (opts.extractors ?? []).filter((extractor) => extractor.mode === "structured");
15220
15226
  if (structuredExtractors.length === 0) return {
@@ -15251,8 +15257,10 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
15251
15257
  return output.object;
15252
15258
  };
15253
15259
  let object;
15260
+ let retryEmptyObject = false;
15254
15261
  try {
15255
15262
  object = await generateWithStructuredOutput();
15263
+ retryEmptyObject = shouldRetryEmptyStructuredObject(object, structuredExtractors);
15256
15264
  } catch (error) {
15257
15265
  if (isAbortError$1(error, opts.abortSignal)) throw error;
15258
15266
  try {
@@ -15269,6 +15277,19 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
15269
15277
  };
15270
15278
  }
15271
15279
  }
15280
+ if (retryEmptyObject) try {
15281
+ object = await generateWithStructuredOutput(_mastra_core_features.coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
15282
+ } catch (fallbackError) {
15283
+ if (isAbortError$1(fallbackError, opts.abortSignal)) throw fallbackError;
15284
+ const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
15285
+ return {
15286
+ values,
15287
+ failures: structuredExtractors.map((extractor) => ({
15288
+ slug: extractor.slug,
15289
+ error: message
15290
+ }))
15291
+ };
15292
+ }
15272
15293
  for (const extractor of structuredExtractors) {
15273
15294
  const value = object[extractor.slug];
15274
15295
  if (value === void 0 || value === null || value === "") continue;
@@ -18574,6 +18595,17 @@ var TokenCounter = class TokenCounter {
18574
18595
  toolResultDelta
18575
18596
  };
18576
18597
  }
18598
+ if (invocation.state === "output-error") {
18599
+ toolResultDelta++;
18600
+ const errorText = invocation.errorText;
18601
+ const errorMessage = typeof errorText === "string" ? errorText : "Tool execution failed";
18602
+ tokens += this.readOrPersistPartEstimate(part, "tool-result-error", errorMessage);
18603
+ return {
18604
+ tokens,
18605
+ overheadDelta,
18606
+ toolResultDelta
18607
+ };
18608
+ }
18577
18609
  throw new Error(`Unhandled tool-invocation state '${part.toolInvocation?.state}' in token counting for part type '${part.type}'`);
18578
18610
  }
18579
18611
  if (typeof part.type === "string" && part.type.startsWith("data-")) return {
@@ -18799,6 +18831,7 @@ var WorkingMemoryExtractor = class extends Extractor {
18799
18831
  name: "Working Memory",
18800
18832
  includePreviousExtraction: false,
18801
18833
  metadataKeyPath: false,
18834
+ retryStructuredExtractionOnEmptyObject: true,
18802
18835
  instructions: async (context) => buildWorkingMemoryInstructions(await getWorkingMemoryDetails(context)),
18803
18836
  schema: async (context) => {
18804
18837
  return (await getWorkingMemoryDetails(context)).usesSchema ? zod.z.union([zod.z.record(zod.z.string(), zod.z.unknown()), zod.z.null()]) : void 0;
@@ -20990,7 +21023,7 @@ var ObservationStrategy = class ObservationStrategy {
20990
21023
  transient: true
20991
21024
  }).catch(() => {});
20992
21025
  const markerThreadId = marker.data?.threadId ?? this.opts.threadId;
20993
- await this.persistMarkerToStorage(marker, markerThreadId, this.opts.resourceId);
21026
+ if (!await this.persistMarkerToMessage(marker, this.opts.messageList, markerThreadId, this.opts.resourceId)) await this.persistMarkerToStorage(marker, markerThreadId, this.opts.resourceId);
20994
21027
  }
20995
21028
  getObservationMarkerConfig() {
20996
21029
  return {
@@ -21118,9 +21151,13 @@ var ObservationStrategy = class ObservationStrategy {
21118
21151
  /**
21119
21152
  * Persist a marker part on the last assistant message in a MessageList
21120
21153
  * AND save the updated message to the DB.
21154
+ *
21155
+ * @returns true when a marker was placed on an assistant message, false when
21156
+ * no list was provided or the list contains no assistant message (caller
21157
+ * should fall back to `persistMarkerToStorage`).
21121
21158
  */
21122
21159
  async persistMarkerToMessage(marker, messageList, threadId, resourceId) {
21123
- if (!messageList) return;
21160
+ if (!messageList) return false;
21124
21161
  const allMsgs = messageList.get.all.db();
21125
21162
  for (let i = allMsgs.length - 1; i >= 0; i--) {
21126
21163
  const msg = allMsgs[i];
@@ -21136,9 +21173,10 @@ var ObservationStrategy = class ObservationStrategy {
21136
21173
  } catch (e) {
21137
21174
  omDebug(`[OM:persistMarker] failed to save marker to DB: ${e}`);
21138
21175
  }
21139
- return;
21176
+ return true;
21140
21177
  }
21141
21178
  }
21179
+ return false;
21142
21180
  }
21143
21181
  };
21144
21182
  //#endregion
@@ -21947,6 +21985,12 @@ var ObservationStep = class {
21947
21985
  stepNumber;
21948
21986
  _prepared = false;
21949
21987
  _context;
21988
+ /**
21989
+ * True when this step seeded an empty assistant response message for a step-0
21990
+ * observation. While set, the response-id rotation hook must NOT run — rotating
21991
+ * would orphan the seed (markers would sit on a message the agent never streams into).
21992
+ */
21993
+ seededResponseMessage = false;
21950
21994
  constructor(turn, stepNumber) {
21951
21995
  this.turn = turn;
21952
21996
  this.stepNumber = stepNumber;
@@ -22075,15 +22119,41 @@ var ObservationStep = class {
22075
22119
  });
22076
22120
  buffered = true;
22077
22121
  }
22078
- if (this.stepNumber > 0) {
22079
- const newInput = messageList.clear.input.db();
22080
- const newOutput = messageList.clear.response.db();
22081
- const messagesToSave = [...newInput, ...newOutput];
22082
- if (messagesToSave.length > 0) {
22083
- await om.persistMessages(messagesToSave, threadId, resourceId);
22084
- for (const msg of messagesToSave) messageList.add(msg, "memory");
22122
+ const willObserveNow = statusSnapshot.shouldObserve && !hasIncompleteToolCalls;
22123
+ /** In-flight message ids the step-0 cleanup must never remove from live context. */
22124
+ let step0PreserveIds;
22125
+ if (this.stepNumber > 0 || willObserveNow) {
22126
+ if (this.stepNumber > 0) {
22127
+ const newInput = messageList.clear.input.db();
22128
+ const newOutput = messageList.clear.response.db();
22129
+ const messagesToSave = [...newInput, ...newOutput];
22130
+ if (messagesToSave.length > 0) {
22131
+ await om.persistMessages(messagesToSave, threadId, resourceId);
22132
+ for (const msg of messagesToSave) messageList.add(msg, "memory");
22133
+ }
22134
+ } else {
22135
+ const pending = [...messageList.get.input.db(), ...messageList.get.response.db()];
22136
+ if (pending.length > 0) await om.persistMessages(pending, threadId, resourceId);
22137
+ step0PreserveIds = pending.map((msg) => msg.id);
22138
+ }
22139
+ if (this.stepNumber === 0 && willObserveNow && this.turn.responseMessageId) {
22140
+ const seed = {
22141
+ id: this.turn.responseMessageId,
22142
+ role: "assistant",
22143
+ content: {
22144
+ format: 2,
22145
+ parts: []
22146
+ },
22147
+ type: "text",
22148
+ createdAt: /* @__PURE__ */ new Date(),
22149
+ threadId,
22150
+ resourceId
22151
+ };
22152
+ messageList.add(seed, "response");
22153
+ this.seededResponseMessage = true;
22154
+ omDebug(`[OM:step0] seeded response message ${seed.id} for step-0 observation markers`);
22085
22155
  }
22086
- if (statusSnapshot.shouldObserve && !hasIncompleteToolCalls) {
22156
+ if (willObserveNow) {
22087
22157
  const preObsGeneration = this.turn.record.generationCount;
22088
22158
  const obsResult = await this.runThresholdObservation();
22089
22159
  observerExchange = obsResult.observerExchange;
@@ -22097,7 +22167,8 @@ var ObservationStep = class {
22097
22167
  resourceId,
22098
22168
  messages: messageList,
22099
22169
  observedMessageIds: observedIds,
22100
- retentionFloor: minRemaining
22170
+ retentionFloor: minRemaining,
22171
+ preserveMessageIds: step0PreserveIds
22101
22172
  });
22102
22173
  if (statusSnapshot.asyncObservationEnabled) await om.resetBufferingState({
22103
22174
  threadId,
@@ -22161,10 +22232,11 @@ var ObservationStep = class {
22161
22232
  const { threadId, resourceId, messageList } = this.turn;
22162
22233
  const om = this.turn.om;
22163
22234
  await om.waitForBuffering(threadId, resourceId);
22235
+ const observableMessages = this.seededResponseMessage ? messageList.get.all.db().filter((msg) => msg.id !== this.turn.responseMessageId) : messageList.get.all.db();
22164
22236
  const freshStatus = await om.getStatus({
22165
22237
  threadId,
22166
22238
  resourceId,
22167
- messages: messageList.get.all.db()
22239
+ messages: observableMessages
22168
22240
  });
22169
22241
  if (!freshStatus.shouldObserve) return {
22170
22242
  succeeded: false,
@@ -22174,7 +22246,7 @@ var ObservationStep = class {
22174
22246
  const activation = await om.activate({
22175
22247
  threadId,
22176
22248
  resourceId,
22177
- messages: messageList.get.all.db(),
22249
+ messages: observableMessages,
22178
22250
  currentModel: this.turn.actorModelContext,
22179
22251
  writer: this.turn.writer,
22180
22252
  messageList
@@ -22207,7 +22279,8 @@ var ObservationStep = class {
22207
22279
  const obsResult = await om.observe({
22208
22280
  threadId,
22209
22281
  resourceId,
22210
- messages: messageList.get.all.db(),
22282
+ messages: observableMessages,
22283
+ messageList,
22211
22284
  trigger: "turn-sync",
22212
22285
  requestContext: this.turn.requestContext,
22213
22286
  writer: this.turn.writer,
@@ -22224,10 +22297,12 @@ var ObservationStep = class {
22224
22297
  break;
22225
22298
  }
22226
22299
  }
22227
- const messageToSeal = latestObservedIndex >= 0 ? liveMessages[latestObservedIndex] : void 0;
22300
+ let messageToSeal = latestObservedIndex >= 0 ? liveMessages[latestObservedIndex] : void 0;
22301
+ if (this.stepNumber === 0 && messageToSeal?.role !== "assistant") messageToSeal = void 0;
22228
22302
  const messagesToSeal = messageToSeal ? [messageToSeal] : [];
22229
22303
  om.sealMessagesForBuffering(messagesToSeal);
22230
- try {
22304
+ if (this.seededResponseMessage) omDebug("[OM:observe] skipping response-id rotation — step-0 seeded response message holds the active id");
22305
+ else try {
22231
22306
  await this.turn.hooks?.onSyncObservationComplete?.();
22232
22307
  } catch (error) {
22233
22308
  omDebug(`[OM:observe] onSyncObservationComplete hook failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -22288,6 +22363,8 @@ var ObservationTurn = class {
22288
22363
  sendSignal;
22289
22364
  /** Current actor model for this step. Updated by the processor before prepare(). */
22290
22365
  actorModelContext;
22366
+ /** The active assistant response message ID for this step. Updated by the processor before prepare(). */
22367
+ responseMessageId;
22291
22368
  /** Processor-provided hooks for turn/step lifecycle integration. */
22292
22369
  hooks;
22293
22370
  constructor(opts) {
@@ -24956,6 +25033,7 @@ ${formattedMessages}
24956
25033
  */
24957
25034
  async getObservedMessageIdsForCleanup(opts) {
24958
25035
  const { threadId, resourceId, messages, observedMessageIds, retentionFloor } = opts;
25036
+ const preserveSet = opts.preserveMessageIds?.length ? new Set(opts.preserveMessageIds) : null;
24959
25037
  const record = await this.getOrCreateRecord(threadId, resourceId);
24960
25038
  const effectiveObservedIds = observedMessageIds && observedMessageIds.length > 0 ? observedMessageIds : Array.isArray(record.observedMessageIds) ? record.observedMessageIds : [];
24961
25039
  if (effectiveObservedIds.length === 0) return [];
@@ -24967,6 +25045,10 @@ ${formattedMessages}
24967
25045
  const retentionCounter = typeof retentionFloor === "number" ? new TokenCounter() : null;
24968
25046
  for (const msg of messages) {
24969
25047
  if (!msg?.id || msg.id === "om-continuation" || !observedSet.has(msg.id)) continue;
25048
+ if (preserveSet?.has(msg.id)) {
25049
+ skipped += 1;
25050
+ continue;
25051
+ }
24970
25052
  const unobservedParts = getUnobservedParts(msg);
24971
25053
  const totalParts = msg.content?.parts?.length ?? 0;
24972
25054
  if (unobservedParts.length > 0 && unobservedParts.length < totalParts) {
@@ -25011,7 +25093,7 @@ ${formattedMessages}
25011
25093
  */
25012
25094
  /** @internal Used by ObservationStep. */
25013
25095
  async cleanupMessages(opts) {
25014
- const { threadId, resourceId, observedMessageIds, retentionFloor } = opts;
25096
+ const { threadId, resourceId, observedMessageIds, retentionFloor, preserveMessageIds } = opts;
25015
25097
  const messageList = this.isMessageList(opts.messages) ? opts.messages : void 0;
25016
25098
  const allMsgs = messageList ? messageList.get.all.db() : opts.messages;
25017
25099
  let markerIdx = -1;
@@ -25032,7 +25114,8 @@ ${formattedMessages}
25032
25114
  resourceId,
25033
25115
  messages: allMsgs,
25034
25116
  observedMessageIds,
25035
- retentionFloor
25117
+ retentionFloor,
25118
+ preserveMessageIds
25036
25119
  });
25037
25120
  if (messageList) {
25038
25121
  if (idsToRemoveList.length > 0) messageList.removeByIds(idsToRemoveList);
@@ -25044,18 +25127,21 @@ ${formattedMessages}
25044
25127
  if (markerMsg && markerIdx !== -1) {
25045
25128
  const idsToRemove = [];
25046
25129
  const messagesToSave = [];
25130
+ const preserveSet = preserveMessageIds?.length ? new Set(preserveMessageIds) : null;
25047
25131
  for (let i = 0; i < markerIdx; i++) {
25048
25132
  const msg = allMsgs[i];
25049
- if (msg?.id && msg.id !== "om-continuation") {
25133
+ if (msg?.id && msg.id !== "om-continuation" && !preserveSet?.has(msg.id)) {
25050
25134
  idsToRemove.push(msg.id);
25051
25135
  messagesToSave.push(msg);
25052
25136
  }
25053
25137
  }
25054
25138
  messagesToSave.push(markerMsg);
25055
- const unobservedParts = getUnobservedParts(markerMsg);
25056
- if (unobservedParts.length === 0) {
25057
- if (markerMsg.id) idsToRemove.push(markerMsg.id);
25058
- } else if (unobservedParts.length < (markerMsg.content?.parts?.length ?? 0)) markerMsg.content.parts = unobservedParts;
25139
+ if (!Boolean(markerMsg.id && preserveSet?.has(markerMsg.id))) {
25140
+ const unobservedParts = getUnobservedParts(markerMsg);
25141
+ if (unobservedParts.length === 0) {
25142
+ if (markerMsg.id) idsToRemove.push(markerMsg.id);
25143
+ } else if (unobservedParts.length < (markerMsg.content?.parts?.length ?? 0)) markerMsg.content.parts = unobservedParts;
25144
+ }
25059
25145
  if (messageList) {
25060
25146
  if (idsToRemove.length > 0) messageList.removeByIds(idsToRemove);
25061
25147
  if (messagesToSave.length > 0) await this.persistMessages(messagesToSave, threadId, resourceId);
@@ -25842,6 +25928,7 @@ ${formattedMessages}
25842
25928
  threadId,
25843
25929
  resourceId,
25844
25930
  messages: unobservedMessages,
25931
+ messageList: opts.messageList,
25845
25932
  reflectionHooks,
25846
25933
  agent: opts.agent,
25847
25934
  requestContext,
@@ -26231,7 +26318,7 @@ var ObservationalMemoryProcessor = class {
26231
26318
  this.temporalMarkers = options?.temporalMarkers ?? false;
26232
26319
  }
26233
26320
  async processInputStep(args) {
26234
- const { messageList, requestContext, stepNumber, state: _state, writer, model, abortSignal, abort, rotateResponseMessageId } = args;
26321
+ const { messageList, requestContext, stepNumber, state: _state, writer, model, abortSignal, abort, messageId, rotateResponseMessageId } = args;
26235
26322
  const state = _state ?? {};
26236
26323
  omDebug(`[OM:processInputStep:ENTER] step=${stepNumber}, hasMastraMemory=${!!requestContext?.get("MastraMemory")}, hasMemoryInfo=${!!messageList?.serialize()?.memoryInfo?.threadId}`);
26237
26324
  const context = this.engine.getThreadContext(requestContext, messageList);
@@ -26317,6 +26404,7 @@ var ObservationalMemoryProcessor = class {
26317
26404
  state.__omObservabilityContext = observabilityContext;
26318
26405
  this.turn.observabilityContext = observabilityContext;
26319
26406
  this.turn.actorModelContext = actorModelContext;
26407
+ this.turn.responseMessageId = messageId;
26320
26408
  {
26321
26409
  const step = this.turn.step(stepNumber);
26322
26410
  let ctx;
@@ -28697,4 +28785,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
28697
28785
  }
28698
28786
  });
28699
28787
 
28700
- //# sourceMappingURL=src-CjTEWCUF.cjs.map
28788
+ //# sourceMappingURL=src-x5iu_K3X.cjs.map