@mastra/memory 1.16.0 → 1.17.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 (36) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/{chunk-HAUWGR76.cjs → chunk-5MTY2UYL.cjs} +206 -10
  3. package/dist/chunk-5MTY2UYL.cjs.map +1 -0
  4. package/dist/{chunk-OOA4C7IX.js → chunk-MPBMHIAQ.js} +206 -10
  5. package/dist/chunk-MPBMHIAQ.js.map +1 -0
  6. package/dist/docs/SKILL.md +1 -1
  7. package/dist/docs/assets/SOURCE_MAP.json +47 -47
  8. package/dist/docs/references/docs-memory-observational-memory.md +31 -0
  9. package/dist/docs/references/reference-memory-observational-memory.md +2 -0
  10. package/dist/index.cjs +16 -14
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +7 -5
  15. package/dist/index.js.map +1 -1
  16. package/dist/{observational-memory-CIGODMXV.js → observational-memory-7YYZIIX4.js} +3 -3
  17. package/dist/{observational-memory-CIGODMXV.js.map → observational-memory-7YYZIIX4.js.map} +1 -1
  18. package/dist/{observational-memory-EWU3QFIE.cjs → observational-memory-UCFGVLVW.cjs} +26 -26
  19. package/dist/{observational-memory-EWU3QFIE.cjs.map → observational-memory-UCFGVLVW.cjs.map} +1 -1
  20. package/dist/processors/index.cjs +24 -24
  21. package/dist/processors/index.js +1 -1
  22. package/dist/processors/observational-memory/date-utils.d.ts +6 -0
  23. package/dist/processors/observational-memory/date-utils.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/processor.d.ts +5 -1
  26. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  27. package/dist/processors/observational-memory/string-utils.d.ts +13 -0
  28. package/dist/processors/observational-memory/string-utils.d.ts.map +1 -0
  29. package/dist/processors/observational-memory/temporal-markers.d.ts +4 -0
  30. package/dist/processors/observational-memory/temporal-markers.d.ts.map +1 -0
  31. package/dist/processors/observational-memory/tool-result-helpers.d.ts.map +1 -1
  32. package/dist/processors/observational-memory/types.d.ts +7 -0
  33. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  34. package/package.json +7 -7
  35. package/dist/chunk-HAUWGR76.cjs.map +0 -1
  36. package/dist/chunk-OOA4C7IX.js.map +0 -1
@@ -378,6 +378,67 @@ ${gap}
378
378
  result += withInlineDates.slice(lastIndex);
379
379
  return result;
380
380
  }
381
+ var MIN_TEMPORAL_GAP_MS = 10 * 60 * 1e3;
382
+ function formatTemporalGap(diffMs) {
383
+ if (diffMs < MIN_TEMPORAL_GAP_MS) return null;
384
+ const minute = 60 * 1e3;
385
+ const hour = 60 * minute;
386
+ const day = 24 * hour;
387
+ const week = 7 * day;
388
+ const month = 30 * day;
389
+ const year = 365 * day;
390
+ const formatUnit = (value, unit) => `${value} ${unit}${value === 1 ? "" : "s"}`;
391
+ if (diffMs < hour) {
392
+ const minutes = Math.max(1, Math.round(diffMs / minute));
393
+ return `${formatUnit(minutes, "minute")} later`;
394
+ }
395
+ const formatTwoUnits = (primaryMs, primaryUnit, secondaryMs, secondaryUnit) => {
396
+ const primary = Math.floor(diffMs / primaryMs);
397
+ const remainder = diffMs - primary * primaryMs;
398
+ const secondary = Math.floor(remainder / secondaryMs);
399
+ const parts = [formatUnit(primary, primaryUnit)];
400
+ if (secondary > 0) {
401
+ parts.push(formatUnit(secondary, secondaryUnit));
402
+ }
403
+ return `${parts.join(" ")} later`;
404
+ };
405
+ if (diffMs < day) {
406
+ return formatTwoUnits(hour, "hour", minute, "minute");
407
+ }
408
+ if (diffMs < week) {
409
+ return formatTwoUnits(day, "day", hour, "hour");
410
+ }
411
+ if (diffMs < month) {
412
+ return formatTwoUnits(week, "week", day, "day");
413
+ }
414
+ if (diffMs < year) {
415
+ return formatTwoUnits(month, "month", week, "week");
416
+ }
417
+ return formatTwoUnits(year, "year", month, "month");
418
+ }
419
+ function formatTemporalTimestamp(date) {
420
+ return date.toLocaleString("en-US", {
421
+ year: "numeric",
422
+ month: "2-digit",
423
+ day: "2-digit",
424
+ hour: "numeric",
425
+ minute: "2-digit",
426
+ hour12: true,
427
+ timeZoneName: "short"
428
+ });
429
+ }
430
+ function getMessagePartTimestamp(msg, position) {
431
+ const timestamps = msg.content?.parts?.map((part) => "createdAt" in part ? part.createdAt : void 0).filter((timestamp) => typeof timestamp === "number");
432
+ if (timestamps && timestamps.length > 0) {
433
+ const index = position === "first" ? 0 : timestamps.length - 1;
434
+ const timestamp = timestamps[index];
435
+ if (timestamp !== void 0) return timestamp;
436
+ }
437
+ return new Date(msg.createdAt).getTime();
438
+ }
439
+ function isTemporalGapMarker(msg) {
440
+ return msg.id.startsWith("__temporal_");
441
+ }
381
442
 
382
443
  // src/processors/observational-memory/markers.ts
383
444
  function createObservationStartMarker(params) {
@@ -2432,6 +2493,15 @@ function stripEphemeralAnchorIds(observations) {
2432
2493
  }
2433
2494
  return observations.replace(/(^|\n)([^\S\n]*)\[(O\d+(?:-N\d+)?)\][^\S\n]*/g, "$1$2");
2434
2495
  }
2496
+
2497
+ // src/processors/observational-memory/string-utils.ts
2498
+ function safeSlice(str, end) {
2499
+ if (end <= 0) return "";
2500
+ if (end >= str.length) return str;
2501
+ const code = str.charCodeAt(end - 1);
2502
+ const safeEnd = code >= 55296 && code <= 56319 ? end - 1 : end;
2503
+ return str.slice(0, safeEnd);
2504
+ }
2435
2505
  var ENCRYPTED_CONTENT_KEY = "encryptedContent";
2436
2506
  var ENCRYPTED_CONTENT_REDACTION_THRESHOLD = 256;
2437
2507
  var DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS = 1e4;
@@ -2497,7 +2567,7 @@ function truncateStringByTokens(text, maxTokens) {
2497
2567
  return text;
2498
2568
  }
2499
2569
  const buildCandidate = (sliceEnd) => {
2500
- const visible = text.slice(0, sliceEnd);
2570
+ const visible = safeSlice(text, sliceEnd);
2501
2571
  return `${visible}
2502
2572
  ... [truncated ~${totalTokens - estimateTokenCount(visible)} tokens]`;
2503
2573
  };
@@ -3051,8 +3121,11 @@ function formatObserverAttachmentPlaceholder(part, counter) {
3051
3121
  return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
3052
3122
  }
3053
3123
  function formatObserverPartLine(title, body, time, previousTime) {
3054
- const timeLabel = time && time !== previousTime ? ` (${time})` : "";
3055
- return `${title}${timeLabel}: ${body}`;
3124
+ const timeLabel = time && time !== previousTime ? `(${time})` : "";
3125
+ if (!title) {
3126
+ return timeLabel ? `${timeLabel}: ${body}` : body;
3127
+ }
3128
+ return `${title}${timeLabel ? ` ${timeLabel}` : ""}: ${body}`;
3056
3129
  }
3057
3130
  function normalizeObserverCreatedAt(createdAt) {
3058
3131
  if (createdAt instanceof Date) {
@@ -3088,6 +3161,16 @@ function formatObserverLines(lines, context = {}) {
3088
3161
  context: { previousDate, previousTime }
3089
3162
  };
3090
3163
  }
3164
+ function getTemporalGapMarkerText(msg) {
3165
+ const metadata = typeof msg.content === "object" && msg.content && "metadata" in msg.content ? msg.content.metadata : void 0;
3166
+ if (metadata?.reminderType === "temporal-gap" && typeof metadata.gapText === "string") {
3167
+ return metadata.gapText;
3168
+ }
3169
+ if (typeof metadata?.systemReminder === "object" && metadata.systemReminder && "type" in metadata.systemReminder && metadata.systemReminder.type === "temporal-gap" && "gapText" in metadata.systemReminder && typeof metadata.systemReminder.gapText === "string") {
3170
+ return metadata.systemReminder.gapText;
3171
+ }
3172
+ return void 0;
3173
+ }
3091
3174
  function formatObserverMessage(msg, counter, options) {
3092
3175
  const maxLen = options?.maxPartLength;
3093
3176
  const maxToolResultTokens = options?.maxToolResultTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
@@ -3095,6 +3178,7 @@ function formatObserverMessage(msg, counter, options) {
3095
3178
  const attachments = [];
3096
3179
  const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
3097
3180
  let lines = [];
3181
+ const temporalGapText = isTemporalGapMarker(msg) ? getTemporalGapMarkerText(msg) : void 0;
3098
3182
  const pushLine = (title, body, createdAt) => {
3099
3183
  if (!body) {
3100
3184
  return;
@@ -3107,7 +3191,9 @@ function formatObserverMessage(msg, counter, options) {
3107
3191
  body
3108
3192
  });
3109
3193
  };
3110
- if (typeof msg.content === "string") {
3194
+ if (temporalGapText) {
3195
+ pushLine("", temporalGapText, messageCreatedAt);
3196
+ } else if (typeof msg.content === "string") {
3111
3197
  pushLine(role, maybeTruncate(msg.content, maxLen), messageCreatedAt);
3112
3198
  } else if (msg.content?.parts && Array.isArray(msg.content.parts) && msg.content.parts.length > 0) {
3113
3199
  msg.content.parts.forEach((part) => {
@@ -3208,8 +3294,8 @@ function buildObserverHistoryMessage(messages, options) {
3208
3294
  }
3209
3295
  function maybeTruncate(str, maxLen) {
3210
3296
  if (!maxLen || str.length <= maxLen) return str;
3211
- const truncated = str.slice(0, maxLen);
3212
- const remaining = str.length - maxLen;
3297
+ const truncated = safeSlice(str, maxLen);
3298
+ const remaining = str.length - truncated.length;
3213
3299
  return `${truncated}
3214
3300
  ... [truncated ${remaining} characters]`;
3215
3301
  }
@@ -3513,7 +3599,7 @@ function sanitizeObservationLines(observations) {
3513
3599
  let changed = false;
3514
3600
  for (let i = 0; i < lines.length; i++) {
3515
3601
  if (lines[i].length > MAX_OBSERVATION_LINE_CHARS) {
3516
- lines[i] = lines[i].slice(0, MAX_OBSERVATION_LINE_CHARS) + " \u2026 [truncated]";
3602
+ lines[i] = safeSlice(lines[i], MAX_OBSERVATION_LINE_CHARS) + " \u2026 [truncated]";
3517
3603
  changed = true;
3518
3604
  }
3519
3605
  }
@@ -8852,6 +8938,110 @@ ${grouped}` : grouped;
8852
8938
  });
8853
8939
  }
8854
8940
  };
8941
+
8942
+ // src/processors/observational-memory/temporal-markers.ts
8943
+ var TEMPORAL_GAP_REMINDER_TYPE = "temporal-gap";
8944
+ function getTemporalGapReminderText(gapText, timestamp) {
8945
+ return `${gapText} \u2014 ${formatTemporalTimestamp(new Date(timestamp))}`;
8946
+ }
8947
+ function getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp) {
8948
+ const formattedTimestamp = formatTemporalTimestamp(new Date(timestamp));
8949
+ return {
8950
+ reminderType: TEMPORAL_GAP_REMINDER_TYPE,
8951
+ gapText,
8952
+ gapMs,
8953
+ timestamp: formattedTimestamp,
8954
+ timestampMs: timestamp,
8955
+ precedesMessageId: message.id,
8956
+ systemReminder: {
8957
+ type: TEMPORAL_GAP_REMINDER_TYPE,
8958
+ message: getTemporalGapReminderText(gapText, timestamp),
8959
+ gapText,
8960
+ gapMs,
8961
+ timestamp: formattedTimestamp,
8962
+ timestampMs: timestamp,
8963
+ precedesMessageId: message.id
8964
+ }
8965
+ };
8966
+ }
8967
+ function isTemporalGapMarkerForMessage(message, targetMessageId) {
8968
+ if (!isTemporalGapMarker(message)) {
8969
+ return false;
8970
+ }
8971
+ const metadata = message.content.metadata;
8972
+ if (metadata?.precedesMessageId === targetMessageId) {
8973
+ return true;
8974
+ }
8975
+ return metadata?.systemReminder?.type === TEMPORAL_GAP_REMINDER_TYPE && metadata.systemReminder.precedesMessageId === targetMessageId;
8976
+ }
8977
+ function createTemporalGapMarker(message, gapText, gapMs, timestamp) {
8978
+ const metadata = getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp);
8979
+ return {
8980
+ id: `__temporal_gap_${crypto.randomUUID()}`,
8981
+ role: "user",
8982
+ createdAt: new Date(timestamp - 1),
8983
+ threadId: message.threadId,
8984
+ resourceId: message.resourceId,
8985
+ content: {
8986
+ format: 2,
8987
+ parts: [
8988
+ {
8989
+ type: "text",
8990
+ text: `<system-reminder type="${TEMPORAL_GAP_REMINDER_TYPE}" precedesMessageId="${message.id}">${getTemporalGapReminderText(gapText, timestamp)}</system-reminder>`
8991
+ }
8992
+ ],
8993
+ metadata
8994
+ }
8995
+ };
8996
+ }
8997
+ async function insertTemporalGapMarkers({
8998
+ messageList,
8999
+ writer
9000
+ }) {
9001
+ const inputMessages = messageList.get.input.db().filter((message) => Boolean(message));
9002
+ const latestInputMessage = inputMessages.at(-1);
9003
+ if (!latestInputMessage || isTemporalGapMarker(latestInputMessage)) {
9004
+ return;
9005
+ }
9006
+ const allMessages = messageList.get.all.db().filter((message) => Boolean(message));
9007
+ const latestInputIndex = allMessages.findIndex((message) => message.id === latestInputMessage.id);
9008
+ if (latestInputIndex <= 0) {
9009
+ return;
9010
+ }
9011
+ if (allMessages.some((message) => isTemporalGapMarkerForMessage(message, latestInputMessage.id))) {
9012
+ return;
9013
+ }
9014
+ let previousNonMarker;
9015
+ for (let index = latestInputIndex - 1; index >= 0; index--) {
9016
+ const candidate = allMessages[index];
9017
+ if (candidate && !isTemporalGapMarker(candidate)) {
9018
+ previousNonMarker = candidate;
9019
+ break;
9020
+ }
9021
+ }
9022
+ if (!previousNonMarker) {
9023
+ return;
9024
+ }
9025
+ const timestamp = getMessagePartTimestamp(latestInputMessage, "first");
9026
+ const gapMs = timestamp - getMessagePartTimestamp(previousNonMarker, "last");
9027
+ const gapText = formatTemporalGap(gapMs);
9028
+ if (!gapText) {
9029
+ return;
9030
+ }
9031
+ const reminderMetadata = getTemporalGapReminderMetadata(latestInputMessage, gapText, gapMs, timestamp);
9032
+ await writer?.custom({
9033
+ type: "data-system-reminder",
9034
+ data: {
9035
+ message: getTemporalGapReminderText(gapText, timestamp),
9036
+ ...reminderMetadata
9037
+ },
9038
+ transient: true
9039
+ });
9040
+ const marker = createTemporalGapMarker(latestInputMessage, gapText, gapMs, timestamp);
9041
+ messageList.add(marker, "input");
9042
+ }
9043
+
9044
+ // src/processors/observational-memory/processor.ts
8855
9045
  function getOmObservabilityContext(args) {
8856
9046
  if (!args.tracing || !args.tracingContext || !args.loggerVNext || !args.metrics) {
8857
9047
  return void 0;
@@ -8874,11 +9064,14 @@ var ObservationalMemoryProcessor = class {
8874
9064
  engine;
8875
9065
  /** Memory instance for loading context. */
8876
9066
  memory;
9067
+ /** Whether temporal-gap reminder markers should be inserted. */
9068
+ temporalMarkers;
8877
9069
  /** Active turn — created on first processInputStep, ended on processOutputResult. */
8878
9070
  turn;
8879
- constructor(engine, memory) {
9071
+ constructor(engine, memory, options) {
8880
9072
  this.engine = engine;
8881
9073
  this.memory = memory;
9074
+ this.temporalMarkers = options?.temporalMarkers ?? false;
8882
9075
  }
8883
9076
  // ─── Processor lifecycle hooks ──────────────────────────────────────────
8884
9077
  async processInputStep(args) {
@@ -8937,6 +9130,9 @@ var ObservationalMemoryProcessor = class {
8937
9130
  this.turn.writer = writer;
8938
9131
  this.turn.requestContext = requestContext;
8939
9132
  await this.turn.start(this.memory);
9133
+ if (stepNumber === 0 && this.temporalMarkers) {
9134
+ await insertTemporalGapMarkers({ messageList, writer });
9135
+ }
8940
9136
  state.__omTurn = this.turn;
8941
9137
  }
8942
9138
  const observabilityContext = getOmObservabilityContext(args);
@@ -9076,5 +9272,5 @@ function getObservationsAsOf(activeObservations, asOf) {
9076
9272
  }
9077
9273
 
9078
9274
  export { ModelByInputTokens, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, ObservationalMemoryProcessor, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, combineObservationGroupRanges, deriveObservationGroupProvenance, extractCurrentTask, formatMessagesForObserver, formatToolResultForObserver, getObservationsAsOf, hasCurrentTaskSection, injectAnchorIds, optimizeObservationsForContext, parseAnchorId, parseObservationGroups, parseObserverOutput, reconcileObservationGroupsFromReflection, renderObservationGroupsForReflection, resolveToolResultValue, stripEphemeralAnchorIds, stripObservationGroups, truncateStringByTokens, wrapInObservationGroup };
9079
- //# sourceMappingURL=chunk-OOA4C7IX.js.map
9080
- //# sourceMappingURL=chunk-OOA4C7IX.js.map
9275
+ //# sourceMappingURL=chunk-MPBMHIAQ.js.map
9276
+ //# sourceMappingURL=chunk-MPBMHIAQ.js.map