@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
@@ -14835,6 +14835,7 @@ var Extractor = class Extractor {
14835
14835
  includePreviousExtraction;
14836
14836
  metadataKeyPath;
14837
14837
  onExtracted;
14838
+ retryStructuredExtractionOnEmptyObject;
14838
14839
  /** @internal */
14839
14840
  internal;
14840
14841
  instructionsConfig;
@@ -14857,6 +14858,7 @@ var Extractor = class Extractor {
14857
14858
  this.includePreviousExtraction = config.includePreviousExtraction ?? true;
14858
14859
  this.metadataKeyPath = config.metadataKeyPath ?? `extracted.${slug}`;
14859
14860
  this.onExtracted = config.onExtracted;
14861
+ this.retryStructuredExtractionOnEmptyObject = config.retryStructuredExtractionOnEmptyObject ?? false;
14860
14862
  this.internal = internal;
14861
14863
  }
14862
14864
  async resolve(context) {
@@ -14869,7 +14871,8 @@ var Extractor = class Extractor {
14869
14871
  ...schema ? { schema } : {},
14870
14872
  includePreviousExtraction: this.includePreviousExtraction,
14871
14873
  metadataKeyPath: this.metadataKeyPath,
14872
- onExtracted: this.onExtracted
14874
+ onExtracted: this.onExtracted,
14875
+ retryStructuredExtractionOnEmptyObject: this.retryStructuredExtractionOnEmptyObject
14873
14876
  }, this.internal);
14874
14877
  }
14875
14878
  };
@@ -15194,6 +15197,9 @@ if (OM_DEBUG_LOG) {
15194
15197
  function isAbortError$1(error, abortSignal) {
15195
15198
  return abortSignal?.aborted === true || error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
15196
15199
  }
15200
+ function shouldRetryEmptyStructuredObject(object, extractors) {
15201
+ return Object.keys(object).length === 0 && extractors.some((extractor) => extractor.retryStructuredExtractionOnEmptyObject);
15202
+ }
15197
15203
  async function extractStructuredValues(opts) {
15198
15204
  const structuredExtractors = (opts.extractors ?? []).filter((extractor) => extractor.mode === "structured");
15199
15205
  if (structuredExtractors.length === 0) return {
@@ -15230,8 +15236,10 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
15230
15236
  return output.object;
15231
15237
  };
15232
15238
  let object;
15239
+ let retryEmptyObject = false;
15233
15240
  try {
15234
15241
  object = await generateWithStructuredOutput();
15242
+ retryEmptyObject = shouldRetryEmptyStructuredObject(object, structuredExtractors);
15235
15243
  } catch (error) {
15236
15244
  if (isAbortError$1(error, opts.abortSignal)) throw error;
15237
15245
  try {
@@ -15248,6 +15256,19 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
15248
15256
  };
15249
15257
  }
15250
15258
  }
15259
+ if (retryEmptyObject) try {
15260
+ object = await generateWithStructuredOutput(coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
15261
+ } catch (fallbackError) {
15262
+ if (isAbortError$1(fallbackError, opts.abortSignal)) throw fallbackError;
15263
+ const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
15264
+ return {
15265
+ values,
15266
+ failures: structuredExtractors.map((extractor) => ({
15267
+ slug: extractor.slug,
15268
+ error: message
15269
+ }))
15270
+ };
15271
+ }
15251
15272
  for (const extractor of structuredExtractors) {
15252
15273
  const value = object[extractor.slug];
15253
15274
  if (value === void 0 || value === null || value === "") continue;
@@ -18553,6 +18574,17 @@ var TokenCounter = class TokenCounter {
18553
18574
  toolResultDelta
18554
18575
  };
18555
18576
  }
18577
+ if (invocation.state === "output-error") {
18578
+ toolResultDelta++;
18579
+ const errorText = invocation.errorText;
18580
+ const errorMessage = typeof errorText === "string" ? errorText : "Tool execution failed";
18581
+ tokens += this.readOrPersistPartEstimate(part, "tool-result-error", errorMessage);
18582
+ return {
18583
+ tokens,
18584
+ overheadDelta,
18585
+ toolResultDelta
18586
+ };
18587
+ }
18556
18588
  throw new Error(`Unhandled tool-invocation state '${part.toolInvocation?.state}' in token counting for part type '${part.type}'`);
18557
18589
  }
18558
18590
  if (typeof part.type === "string" && part.type.startsWith("data-")) return {
@@ -18778,6 +18810,7 @@ var WorkingMemoryExtractor = class extends Extractor {
18778
18810
  name: "Working Memory",
18779
18811
  includePreviousExtraction: false,
18780
18812
  metadataKeyPath: false,
18813
+ retryStructuredExtractionOnEmptyObject: true,
18781
18814
  instructions: async (context) => buildWorkingMemoryInstructions(await getWorkingMemoryDetails(context)),
18782
18815
  schema: async (context) => {
18783
18816
  return (await getWorkingMemoryDetails(context)).usesSchema ? z.union([z.record(z.string(), z.unknown()), z.null()]) : void 0;
@@ -20969,7 +21002,7 @@ var ObservationStrategy = class ObservationStrategy {
20969
21002
  transient: true
20970
21003
  }).catch(() => {});
20971
21004
  const markerThreadId = marker.data?.threadId ?? this.opts.threadId;
20972
- await this.persistMarkerToStorage(marker, markerThreadId, this.opts.resourceId);
21005
+ if (!await this.persistMarkerToMessage(marker, this.opts.messageList, markerThreadId, this.opts.resourceId)) await this.persistMarkerToStorage(marker, markerThreadId, this.opts.resourceId);
20973
21006
  }
20974
21007
  getObservationMarkerConfig() {
20975
21008
  return {
@@ -21097,9 +21130,13 @@ var ObservationStrategy = class ObservationStrategy {
21097
21130
  /**
21098
21131
  * Persist a marker part on the last assistant message in a MessageList
21099
21132
  * AND save the updated message to the DB.
21133
+ *
21134
+ * @returns true when a marker was placed on an assistant message, false when
21135
+ * no list was provided or the list contains no assistant message (caller
21136
+ * should fall back to `persistMarkerToStorage`).
21100
21137
  */
21101
21138
  async persistMarkerToMessage(marker, messageList, threadId, resourceId) {
21102
- if (!messageList) return;
21139
+ if (!messageList) return false;
21103
21140
  const allMsgs = messageList.get.all.db();
21104
21141
  for (let i = allMsgs.length - 1; i >= 0; i--) {
21105
21142
  const msg = allMsgs[i];
@@ -21115,9 +21152,10 @@ var ObservationStrategy = class ObservationStrategy {
21115
21152
  } catch (e) {
21116
21153
  omDebug(`[OM:persistMarker] failed to save marker to DB: ${e}`);
21117
21154
  }
21118
- return;
21155
+ return true;
21119
21156
  }
21120
21157
  }
21158
+ return false;
21121
21159
  }
21122
21160
  };
21123
21161
  //#endregion
@@ -21926,6 +21964,12 @@ var ObservationStep = class {
21926
21964
  stepNumber;
21927
21965
  _prepared = false;
21928
21966
  _context;
21967
+ /**
21968
+ * True when this step seeded an empty assistant response message for a step-0
21969
+ * observation. While set, the response-id rotation hook must NOT run — rotating
21970
+ * would orphan the seed (markers would sit on a message the agent never streams into).
21971
+ */
21972
+ seededResponseMessage = false;
21929
21973
  constructor(turn, stepNumber) {
21930
21974
  this.turn = turn;
21931
21975
  this.stepNumber = stepNumber;
@@ -22054,15 +22098,41 @@ var ObservationStep = class {
22054
22098
  });
22055
22099
  buffered = true;
22056
22100
  }
22057
- if (this.stepNumber > 0) {
22058
- const newInput = messageList.clear.input.db();
22059
- const newOutput = messageList.clear.response.db();
22060
- const messagesToSave = [...newInput, ...newOutput];
22061
- if (messagesToSave.length > 0) {
22062
- await om.persistMessages(messagesToSave, threadId, resourceId);
22063
- for (const msg of messagesToSave) messageList.add(msg, "memory");
22101
+ const willObserveNow = statusSnapshot.shouldObserve && !hasIncompleteToolCalls;
22102
+ /** In-flight message ids the step-0 cleanup must never remove from live context. */
22103
+ let step0PreserveIds;
22104
+ if (this.stepNumber > 0 || willObserveNow) {
22105
+ if (this.stepNumber > 0) {
22106
+ const newInput = messageList.clear.input.db();
22107
+ const newOutput = messageList.clear.response.db();
22108
+ const messagesToSave = [...newInput, ...newOutput];
22109
+ if (messagesToSave.length > 0) {
22110
+ await om.persistMessages(messagesToSave, threadId, resourceId);
22111
+ for (const msg of messagesToSave) messageList.add(msg, "memory");
22112
+ }
22113
+ } else {
22114
+ const pending = [...messageList.get.input.db(), ...messageList.get.response.db()];
22115
+ if (pending.length > 0) await om.persistMessages(pending, threadId, resourceId);
22116
+ step0PreserveIds = pending.map((msg) => msg.id);
22117
+ }
22118
+ if (this.stepNumber === 0 && willObserveNow && this.turn.responseMessageId) {
22119
+ const seed = {
22120
+ id: this.turn.responseMessageId,
22121
+ role: "assistant",
22122
+ content: {
22123
+ format: 2,
22124
+ parts: []
22125
+ },
22126
+ type: "text",
22127
+ createdAt: /* @__PURE__ */ new Date(),
22128
+ threadId,
22129
+ resourceId
22130
+ };
22131
+ messageList.add(seed, "response");
22132
+ this.seededResponseMessage = true;
22133
+ omDebug(`[OM:step0] seeded response message ${seed.id} for step-0 observation markers`);
22064
22134
  }
22065
- if (statusSnapshot.shouldObserve && !hasIncompleteToolCalls) {
22135
+ if (willObserveNow) {
22066
22136
  const preObsGeneration = this.turn.record.generationCount;
22067
22137
  const obsResult = await this.runThresholdObservation();
22068
22138
  observerExchange = obsResult.observerExchange;
@@ -22076,7 +22146,8 @@ var ObservationStep = class {
22076
22146
  resourceId,
22077
22147
  messages: messageList,
22078
22148
  observedMessageIds: observedIds,
22079
- retentionFloor: minRemaining
22149
+ retentionFloor: minRemaining,
22150
+ preserveMessageIds: step0PreserveIds
22080
22151
  });
22081
22152
  if (statusSnapshot.asyncObservationEnabled) await om.resetBufferingState({
22082
22153
  threadId,
@@ -22140,10 +22211,11 @@ var ObservationStep = class {
22140
22211
  const { threadId, resourceId, messageList } = this.turn;
22141
22212
  const om = this.turn.om;
22142
22213
  await om.waitForBuffering(threadId, resourceId);
22214
+ const observableMessages = this.seededResponseMessage ? messageList.get.all.db().filter((msg) => msg.id !== this.turn.responseMessageId) : messageList.get.all.db();
22143
22215
  const freshStatus = await om.getStatus({
22144
22216
  threadId,
22145
22217
  resourceId,
22146
- messages: messageList.get.all.db()
22218
+ messages: observableMessages
22147
22219
  });
22148
22220
  if (!freshStatus.shouldObserve) return {
22149
22221
  succeeded: false,
@@ -22153,7 +22225,7 @@ var ObservationStep = class {
22153
22225
  const activation = await om.activate({
22154
22226
  threadId,
22155
22227
  resourceId,
22156
- messages: messageList.get.all.db(),
22228
+ messages: observableMessages,
22157
22229
  currentModel: this.turn.actorModelContext,
22158
22230
  writer: this.turn.writer,
22159
22231
  messageList
@@ -22186,7 +22258,8 @@ var ObservationStep = class {
22186
22258
  const obsResult = await om.observe({
22187
22259
  threadId,
22188
22260
  resourceId,
22189
- messages: messageList.get.all.db(),
22261
+ messages: observableMessages,
22262
+ messageList,
22190
22263
  trigger: "turn-sync",
22191
22264
  requestContext: this.turn.requestContext,
22192
22265
  writer: this.turn.writer,
@@ -22203,10 +22276,12 @@ var ObservationStep = class {
22203
22276
  break;
22204
22277
  }
22205
22278
  }
22206
- const messageToSeal = latestObservedIndex >= 0 ? liveMessages[latestObservedIndex] : void 0;
22279
+ let messageToSeal = latestObservedIndex >= 0 ? liveMessages[latestObservedIndex] : void 0;
22280
+ if (this.stepNumber === 0 && messageToSeal?.role !== "assistant") messageToSeal = void 0;
22207
22281
  const messagesToSeal = messageToSeal ? [messageToSeal] : [];
22208
22282
  om.sealMessagesForBuffering(messagesToSeal);
22209
- try {
22283
+ if (this.seededResponseMessage) omDebug("[OM:observe] skipping response-id rotation — step-0 seeded response message holds the active id");
22284
+ else try {
22210
22285
  await this.turn.hooks?.onSyncObservationComplete?.();
22211
22286
  } catch (error) {
22212
22287
  omDebug(`[OM:observe] onSyncObservationComplete hook failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -22267,6 +22342,8 @@ var ObservationTurn = class {
22267
22342
  sendSignal;
22268
22343
  /** Current actor model for this step. Updated by the processor before prepare(). */
22269
22344
  actorModelContext;
22345
+ /** The active assistant response message ID for this step. Updated by the processor before prepare(). */
22346
+ responseMessageId;
22270
22347
  /** Processor-provided hooks for turn/step lifecycle integration. */
22271
22348
  hooks;
22272
22349
  constructor(opts) {
@@ -24935,6 +25012,7 @@ ${formattedMessages}
24935
25012
  */
24936
25013
  async getObservedMessageIdsForCleanup(opts) {
24937
25014
  const { threadId, resourceId, messages, observedMessageIds, retentionFloor } = opts;
25015
+ const preserveSet = opts.preserveMessageIds?.length ? new Set(opts.preserveMessageIds) : null;
24938
25016
  const record = await this.getOrCreateRecord(threadId, resourceId);
24939
25017
  const effectiveObservedIds = observedMessageIds && observedMessageIds.length > 0 ? observedMessageIds : Array.isArray(record.observedMessageIds) ? record.observedMessageIds : [];
24940
25018
  if (effectiveObservedIds.length === 0) return [];
@@ -24946,6 +25024,10 @@ ${formattedMessages}
24946
25024
  const retentionCounter = typeof retentionFloor === "number" ? new TokenCounter() : null;
24947
25025
  for (const msg of messages) {
24948
25026
  if (!msg?.id || msg.id === "om-continuation" || !observedSet.has(msg.id)) continue;
25027
+ if (preserveSet?.has(msg.id)) {
25028
+ skipped += 1;
25029
+ continue;
25030
+ }
24949
25031
  const unobservedParts = getUnobservedParts(msg);
24950
25032
  const totalParts = msg.content?.parts?.length ?? 0;
24951
25033
  if (unobservedParts.length > 0 && unobservedParts.length < totalParts) {
@@ -24990,7 +25072,7 @@ ${formattedMessages}
24990
25072
  */
24991
25073
  /** @internal Used by ObservationStep. */
24992
25074
  async cleanupMessages(opts) {
24993
- const { threadId, resourceId, observedMessageIds, retentionFloor } = opts;
25075
+ const { threadId, resourceId, observedMessageIds, retentionFloor, preserveMessageIds } = opts;
24994
25076
  const messageList = this.isMessageList(opts.messages) ? opts.messages : void 0;
24995
25077
  const allMsgs = messageList ? messageList.get.all.db() : opts.messages;
24996
25078
  let markerIdx = -1;
@@ -25011,7 +25093,8 @@ ${formattedMessages}
25011
25093
  resourceId,
25012
25094
  messages: allMsgs,
25013
25095
  observedMessageIds,
25014
- retentionFloor
25096
+ retentionFloor,
25097
+ preserveMessageIds
25015
25098
  });
25016
25099
  if (messageList) {
25017
25100
  if (idsToRemoveList.length > 0) messageList.removeByIds(idsToRemoveList);
@@ -25023,18 +25106,21 @@ ${formattedMessages}
25023
25106
  if (markerMsg && markerIdx !== -1) {
25024
25107
  const idsToRemove = [];
25025
25108
  const messagesToSave = [];
25109
+ const preserveSet = preserveMessageIds?.length ? new Set(preserveMessageIds) : null;
25026
25110
  for (let i = 0; i < markerIdx; i++) {
25027
25111
  const msg = allMsgs[i];
25028
- if (msg?.id && msg.id !== "om-continuation") {
25112
+ if (msg?.id && msg.id !== "om-continuation" && !preserveSet?.has(msg.id)) {
25029
25113
  idsToRemove.push(msg.id);
25030
25114
  messagesToSave.push(msg);
25031
25115
  }
25032
25116
  }
25033
25117
  messagesToSave.push(markerMsg);
25034
- const unobservedParts = getUnobservedParts(markerMsg);
25035
- if (unobservedParts.length === 0) {
25036
- if (markerMsg.id) idsToRemove.push(markerMsg.id);
25037
- } else if (unobservedParts.length < (markerMsg.content?.parts?.length ?? 0)) markerMsg.content.parts = unobservedParts;
25118
+ if (!Boolean(markerMsg.id && preserveSet?.has(markerMsg.id))) {
25119
+ const unobservedParts = getUnobservedParts(markerMsg);
25120
+ if (unobservedParts.length === 0) {
25121
+ if (markerMsg.id) idsToRemove.push(markerMsg.id);
25122
+ } else if (unobservedParts.length < (markerMsg.content?.parts?.length ?? 0)) markerMsg.content.parts = unobservedParts;
25123
+ }
25038
25124
  if (messageList) {
25039
25125
  if (idsToRemove.length > 0) messageList.removeByIds(idsToRemove);
25040
25126
  if (messagesToSave.length > 0) await this.persistMessages(messagesToSave, threadId, resourceId);
@@ -25821,6 +25907,7 @@ ${formattedMessages}
25821
25907
  threadId,
25822
25908
  resourceId,
25823
25909
  messages: unobservedMessages,
25910
+ messageList: opts.messageList,
25824
25911
  reflectionHooks,
25825
25912
  agent: opts.agent,
25826
25913
  requestContext,
@@ -26210,7 +26297,7 @@ var ObservationalMemoryProcessor = class {
26210
26297
  this.temporalMarkers = options?.temporalMarkers ?? false;
26211
26298
  }
26212
26299
  async processInputStep(args) {
26213
- const { messageList, requestContext, stepNumber, state: _state, writer, model, abortSignal, abort, rotateResponseMessageId } = args;
26300
+ const { messageList, requestContext, stepNumber, state: _state, writer, model, abortSignal, abort, messageId, rotateResponseMessageId } = args;
26214
26301
  const state = _state ?? {};
26215
26302
  omDebug(`[OM:processInputStep:ENTER] step=${stepNumber}, hasMastraMemory=${!!requestContext?.get("MastraMemory")}, hasMemoryInfo=${!!messageList?.serialize()?.memoryInfo?.threadId}`);
26216
26303
  const context = this.engine.getThreadContext(requestContext, messageList);
@@ -26296,6 +26383,7 @@ var ObservationalMemoryProcessor = class {
26296
26383
  state.__omObservabilityContext = observabilityContext;
26297
26384
  this.turn.observabilityContext = observabilityContext;
26298
26385
  this.turn.actorModelContext = actorModelContext;
26386
+ this.turn.responseMessageId = messageId;
26299
26387
  {
26300
26388
  const step = this.turn.step(stepNumber);
26301
26389
  let ctx;
@@ -28443,4 +28531,4 @@ Notes:
28443
28531
  //#endregion
28444
28532
  export { extractCurrentTask as A, OBSERVATION_CONTEXT_INSTRUCTIONS as B, WorkingMemoryExtractor as C, OBSERVER_SYSTEM_PROMPT as D, TokenCounter as E, injectAnchorIds as F, OBSERVATION_CONTINUATION_HINT as H, parseAnchorId as I, stripEphemeralAnchorIds as L, hasCurrentTaskSection as M, optimizeObservationsForContext as N, buildObserverPrompt as O, parseObserverOutput as P, Extractor as R, deepMergeWorkingMemory as S, summarizeConversation as T, OBSERVATION_CONTEXT_PROMPT as V, 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, formatMessagesForObserver as j, buildObserverSystemPrompt as k, WORKING_MEMORY_STATE_PROCESSOR_ID as l, combineObservationGroupRanges as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, ObservationalMemory as p, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, WorkingMemoryStateProcessor as u, renderObservationGroupsForReflection as v, SUMMARIZE_THREAD_DEFAULTS as w, ModelByInputTokens as x, stripObservationGroups as y, OBSERVATIONAL_MEMORY_DEFAULTS as z };
28445
28533
 
28446
- //# sourceMappingURL=src-Cwt9gefz.js.map
28534
+ //# sourceMappingURL=src-Iw-V5CfD.js.map