@mastra/memory 1.16.0-alpha.4 → 1.17.0-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 (36) hide show
  1. package/CHANGELOG.md +60 -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 +8 -8
  35. package/dist/chunk-HAUWGR76.cjs.map +0 -1
  36. package/dist/chunk-OOA4C7IX.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 1.17.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added opt-in temporal-gap markers for observational memory. When enabled via `observationalMemory.temporalMarkers: true`, the agent receives a `<system-reminder type="temporal-gap">` before any user message that arrives more than 10 minutes after the previous one, so it can anchor responses in real elapsed time. Markers are persisted, surfaced to the observer, and rendered by the MastraCode TUI on reload. ([#15605](https://github.com/mastra-ai/mastra/pull/15605))
8
+
9
+ ### Patch Changes
10
+
11
+ - Fixed observer agent truncation that could cut UTF-16 surrogate pairs in half when formatting messages, tool results, or observation lines with emoji or other astral-plane characters. This produced lone surrogates that strict JSON parsers (including Anthropic's) reject with errors like `no low surrogate in string`, causing observer runs to fail. ([#15634](https://github.com/mastra-ai/mastra/pull/15634))
12
+
13
+ - Updated dependencies [[`0a0aa94`](https://github.com/mastra-ai/mastra/commit/0a0aa94729592e99885af2efb90c56aaada62247), [`01a7d51`](https://github.com/mastra-ai/mastra/commit/01a7d513493d21562f677f98550f7ceb165ba78c)]:
14
+ - @mastra/core@1.27.0-alpha.1
15
+
16
+ ## 1.16.0
17
+
18
+ ### Minor Changes
19
+
20
+ - Added activateAfterIdle setting for observational memory so buffered observations can activate after idle time before the next prompt. ([#15365](https://github.com/mastra-ai/mastra/pull/15365))
21
+
22
+ **Example**
23
+
24
+ Set `activateAfterIdle: 300_000` (or `"5m"`) on the `observationalMemory` config to activate buffered context after 5 minutes of inactivity.
25
+
26
+ This helps long-running threads reuse compressed context after prompt cache TTLs expire instead of sending a larger raw message window on the next request.
27
+
28
+ - Added `activateOnProviderChange` so observational memory can activate buffered observations and reflections before switching to a different provider or model. ([#15420](https://github.com/mastra-ai/mastra/pull/15420))
29
+
30
+ ```ts
31
+ const memory = new Memory({
32
+ options: {
33
+ observationalMemory: {
34
+ model: 'google/gemini-2.5-flash',
35
+ activateOnProviderChange: true,
36
+ },
37
+ },
38
+ });
39
+ ```
40
+
41
+ This helps keep prompt-cache savings when the next step cannot reuse the previous provider's cache.
42
+
43
+ ### Patch Changes
44
+
45
+ - Fixed early observational memory activations so buffered reflections are only activated when they still leave a healthy active observation set. ([#15462](https://github.com/mastra-ai/mastra/pull/15462))
46
+
47
+ Before this change, idle-timeout (`activateAfterIdle`) and model/provider-change (`activateOnProviderChange`) activations could swap in a buffered reflection too early. In bad cases, that replaced a large raw observation tail with a much smaller mostly-compressed result, which hurt reflection quality.
48
+
49
+ Early activations now stay buffered unless both of these checks pass:
50
+ - The unreflected observation tail is at least as large as the buffered reflection, so the activated result is not dominated by compressed content.
51
+ - The combined post-activation size is at least 75% of what a normal threshold activation would produce, so early activations do not cliff far below the regular target.
52
+
53
+ This update also fixes false `provider_change` activations when older persisted messages only contain a bare model id like `gpt-5.4` while newer turns use the fully qualified `provider/modelId` form.
54
+
55
+ - Fixed a security issue where several parsing and tracing paths could slow down on malformed or attacker-crafted input. Normal behavior is unchanged, and these packages now handle pathological input in linear time. ([#15566](https://github.com/mastra-ai/mastra/pull/15566))
56
+
57
+ - Fixed other-thread context filtering falling back to the observational memory record timestamp when thread metadata is missing. ([#15269](https://github.com/mastra-ai/mastra/pull/15269))
58
+
59
+ - Updated dependencies [[`20f59b8`](https://github.com/mastra-ai/mastra/commit/20f59b876cf91199efbc49a0e36b391240708f08), [`aba393e`](https://github.com/mastra-ai/mastra/commit/aba393e2da7390c69b80e516a4f153cda6f09376), [`3d83d06`](https://github.com/mastra-ai/mastra/commit/3d83d06f776f00fb5f4163dddd32a030c5c20844), [`e2687a7`](https://github.com/mastra-ai/mastra/commit/e2687a7408790c384563816a9a28ed06735684c9), [`fdd54cf`](https://github.com/mastra-ai/mastra/commit/fdd54cf612a9af876e9fdd85e534454f6e7dd518), [`6315317`](https://github.com/mastra-ai/mastra/commit/63153175fe9a7b224e5be7c209bbebc01dd9b0d5), [`a371ac5`](https://github.com/mastra-ai/mastra/commit/a371ac534aa1bb368a1acf9d8b313378dfdc787e), [`7db42a9`](https://github.com/mastra-ai/mastra/commit/7db42a9cccd3b29c44fb0731f792c51575e8421c), [`0474c2b`](https://github.com/mastra-ai/mastra/commit/0474c2b2e7c7e1ad8691dca031284841391ff1ef), [`0a5fa1d`](https://github.com/mastra-ai/mastra/commit/0a5fa1d3cb0583889d06687155f26fd7d2edc76c), [`7e0e63e`](https://github.com/mastra-ai/mastra/commit/7e0e63e2e485e84442351f4c7a79a424c83539dc), [`ea43e64`](https://github.com/mastra-ai/mastra/commit/ea43e646dd95d507694b6112b0bf1df22ad552b2), [`f607106`](https://github.com/mastra-ai/mastra/commit/f607106854c6416c4a07d4082604b9f66d047221), [`30456b6`](https://github.com/mastra-ai/mastra/commit/30456b6b08c8fd17e109dd093b73d93b65e83bc5), [`9d11a8c`](https://github.com/mastra-ai/mastra/commit/9d11a8c1c8924eb975a245a5884d40ca1b7e0491), [`9d3b24b`](https://github.com/mastra-ai/mastra/commit/9d3b24b19407ae9c09586cf7766d38dc4dff4a69), [`00d1b16`](https://github.com/mastra-ai/mastra/commit/00d1b16b401199cb294fa23f43336547db4dca9b), [`47cee3e`](https://github.com/mastra-ai/mastra/commit/47cee3e137fe39109cf7fffd2a8cf47b76dc702e), [`62919a6`](https://github.com/mastra-ai/mastra/commit/62919a6ee0fbf3779ad21a97b1ec6696515d5104), [`d246696`](https://github.com/mastra-ai/mastra/commit/d246696139a3144a5b21b042d41c532688e957e1), [`354f9ce`](https://github.com/mastra-ai/mastra/commit/354f9ce1ca6af2074b6a196a23f8ec30012dccca), [`16e34ca`](https://github.com/mastra-ai/mastra/commit/16e34caa98b9a114b17a6125e4e3fd87f169d0d0), [`7020c06`](https://github.com/mastra-ai/mastra/commit/7020c0690b199d9da337f0e805f16948e557922e), [`8786a61`](https://github.com/mastra-ai/mastra/commit/8786a61fa54ba265f85eeff9985ca39863d18bb6), [`9467ea8`](https://github.com/mastra-ai/mastra/commit/9467ea87695749a53dfc041576410ebf9ee7bb67), [`7338d94`](https://github.com/mastra-ai/mastra/commit/7338d949380cf68b095342e8e42610dc51d557c1), [`c80dc16`](https://github.com/mastra-ai/mastra/commit/c80dc16e113e6cc159f510ffde501ad4711b2189), [`af8a57e`](https://github.com/mastra-ai/mastra/commit/af8a57ed9ba9685ad8601d5b71ae3706da6222f9), [`d63ffdb`](https://github.com/mastra-ai/mastra/commit/d63ffdbb2c11e76fe5ea45faab44bc15460f010c), [`47cee3e`](https://github.com/mastra-ai/mastra/commit/47cee3e137fe39109cf7fffd2a8cf47b76dc702e), [`1bd5104`](https://github.com/mastra-ai/mastra/commit/1bd51048b6da93507276d6623e3fd96a9e1a8944), [`e9837b5`](https://github.com/mastra-ai/mastra/commit/e9837b53699e18711b09e0ca010a4106376f2653), [`c65aec3`](https://github.com/mastra-ai/mastra/commit/c65aec356cc037ee7c4b30ccea946807d4c4f443), [`8f1b280`](https://github.com/mastra-ai/mastra/commit/8f1b280b7fe6999ec654f160cb69c1a8719e7a57), [`92dcf02`](https://github.com/mastra-ai/mastra/commit/92dcf029294210ac91b090900c1a0555a425c57a), [`0fd90a2`](https://github.com/mastra-ai/mastra/commit/0fd90a215caf5fca8099c15a67ca03e4427747a3), [`8fb2405`](https://github.com/mastra-ai/mastra/commit/8fb2405138f2d208b7962ad03f121ca25bcc28c5), [`12df98c`](https://github.com/mastra-ai/mastra/commit/12df98c4904643d9481f5c78f3bed443725b4c96)]:
60
+ - @mastra/core@1.26.0
61
+ - @mastra/schema-compat@1.2.9
62
+
3
63
  ## 1.16.0-alpha.4
4
64
 
5
65
  ### Patch Changes
@@ -385,6 +385,67 @@ ${gap}
385
385
  result += withInlineDates.slice(lastIndex);
386
386
  return result;
387
387
  }
388
+ var MIN_TEMPORAL_GAP_MS = 10 * 60 * 1e3;
389
+ function formatTemporalGap(diffMs) {
390
+ if (diffMs < MIN_TEMPORAL_GAP_MS) return null;
391
+ const minute = 60 * 1e3;
392
+ const hour = 60 * minute;
393
+ const day = 24 * hour;
394
+ const week = 7 * day;
395
+ const month = 30 * day;
396
+ const year = 365 * day;
397
+ const formatUnit = (value, unit) => `${value} ${unit}${value === 1 ? "" : "s"}`;
398
+ if (diffMs < hour) {
399
+ const minutes = Math.max(1, Math.round(diffMs / minute));
400
+ return `${formatUnit(minutes, "minute")} later`;
401
+ }
402
+ const formatTwoUnits = (primaryMs, primaryUnit, secondaryMs, secondaryUnit) => {
403
+ const primary = Math.floor(diffMs / primaryMs);
404
+ const remainder = diffMs - primary * primaryMs;
405
+ const secondary = Math.floor(remainder / secondaryMs);
406
+ const parts = [formatUnit(primary, primaryUnit)];
407
+ if (secondary > 0) {
408
+ parts.push(formatUnit(secondary, secondaryUnit));
409
+ }
410
+ return `${parts.join(" ")} later`;
411
+ };
412
+ if (diffMs < day) {
413
+ return formatTwoUnits(hour, "hour", minute, "minute");
414
+ }
415
+ if (diffMs < week) {
416
+ return formatTwoUnits(day, "day", hour, "hour");
417
+ }
418
+ if (diffMs < month) {
419
+ return formatTwoUnits(week, "week", day, "day");
420
+ }
421
+ if (diffMs < year) {
422
+ return formatTwoUnits(month, "month", week, "week");
423
+ }
424
+ return formatTwoUnits(year, "year", month, "month");
425
+ }
426
+ function formatTemporalTimestamp(date) {
427
+ return date.toLocaleString("en-US", {
428
+ year: "numeric",
429
+ month: "2-digit",
430
+ day: "2-digit",
431
+ hour: "numeric",
432
+ minute: "2-digit",
433
+ hour12: true,
434
+ timeZoneName: "short"
435
+ });
436
+ }
437
+ function getMessagePartTimestamp(msg, position) {
438
+ const timestamps = msg.content?.parts?.map((part) => "createdAt" in part ? part.createdAt : void 0).filter((timestamp) => typeof timestamp === "number");
439
+ if (timestamps && timestamps.length > 0) {
440
+ const index = position === "first" ? 0 : timestamps.length - 1;
441
+ const timestamp = timestamps[index];
442
+ if (timestamp !== void 0) return timestamp;
443
+ }
444
+ return new Date(msg.createdAt).getTime();
445
+ }
446
+ function isTemporalGapMarker(msg) {
447
+ return msg.id.startsWith("__temporal_");
448
+ }
388
449
 
389
450
  // src/processors/observational-memory/markers.ts
390
451
  function createObservationStartMarker(params) {
@@ -2439,6 +2500,15 @@ function stripEphemeralAnchorIds(observations) {
2439
2500
  }
2440
2501
  return observations.replace(/(^|\n)([^\S\n]*)\[(O\d+(?:-N\d+)?)\][^\S\n]*/g, "$1$2");
2441
2502
  }
2503
+
2504
+ // src/processors/observational-memory/string-utils.ts
2505
+ function safeSlice(str, end) {
2506
+ if (end <= 0) return "";
2507
+ if (end >= str.length) return str;
2508
+ const code = str.charCodeAt(end - 1);
2509
+ const safeEnd = code >= 55296 && code <= 56319 ? end - 1 : end;
2510
+ return str.slice(0, safeEnd);
2511
+ }
2442
2512
  var ENCRYPTED_CONTENT_KEY = "encryptedContent";
2443
2513
  var ENCRYPTED_CONTENT_REDACTION_THRESHOLD = 256;
2444
2514
  var DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS = 1e4;
@@ -2504,7 +2574,7 @@ function truncateStringByTokens(text, maxTokens) {
2504
2574
  return text;
2505
2575
  }
2506
2576
  const buildCandidate = (sliceEnd) => {
2507
- const visible = text.slice(0, sliceEnd);
2577
+ const visible = safeSlice(text, sliceEnd);
2508
2578
  return `${visible}
2509
2579
  ... [truncated ~${totalTokens - tokenx.estimateTokenCount(visible)} tokens]`;
2510
2580
  };
@@ -3058,8 +3128,11 @@ function formatObserverAttachmentPlaceholder(part, counter) {
3058
3128
  return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
3059
3129
  }
3060
3130
  function formatObserverPartLine(title, body, time, previousTime) {
3061
- const timeLabel = time && time !== previousTime ? ` (${time})` : "";
3062
- return `${title}${timeLabel}: ${body}`;
3131
+ const timeLabel = time && time !== previousTime ? `(${time})` : "";
3132
+ if (!title) {
3133
+ return timeLabel ? `${timeLabel}: ${body}` : body;
3134
+ }
3135
+ return `${title}${timeLabel ? ` ${timeLabel}` : ""}: ${body}`;
3063
3136
  }
3064
3137
  function normalizeObserverCreatedAt(createdAt) {
3065
3138
  if (createdAt instanceof Date) {
@@ -3095,6 +3168,16 @@ function formatObserverLines(lines, context = {}) {
3095
3168
  context: { previousDate, previousTime }
3096
3169
  };
3097
3170
  }
3171
+ function getTemporalGapMarkerText(msg) {
3172
+ const metadata = typeof msg.content === "object" && msg.content && "metadata" in msg.content ? msg.content.metadata : void 0;
3173
+ if (metadata?.reminderType === "temporal-gap" && typeof metadata.gapText === "string") {
3174
+ return metadata.gapText;
3175
+ }
3176
+ 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") {
3177
+ return metadata.systemReminder.gapText;
3178
+ }
3179
+ return void 0;
3180
+ }
3098
3181
  function formatObserverMessage(msg, counter, options) {
3099
3182
  const maxLen = options?.maxPartLength;
3100
3183
  const maxToolResultTokens = options?.maxToolResultTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
@@ -3102,6 +3185,7 @@ function formatObserverMessage(msg, counter, options) {
3102
3185
  const attachments = [];
3103
3186
  const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
3104
3187
  let lines = [];
3188
+ const temporalGapText = isTemporalGapMarker(msg) ? getTemporalGapMarkerText(msg) : void 0;
3105
3189
  const pushLine = (title, body, createdAt) => {
3106
3190
  if (!body) {
3107
3191
  return;
@@ -3114,7 +3198,9 @@ function formatObserverMessage(msg, counter, options) {
3114
3198
  body
3115
3199
  });
3116
3200
  };
3117
- if (typeof msg.content === "string") {
3201
+ if (temporalGapText) {
3202
+ pushLine("", temporalGapText, messageCreatedAt);
3203
+ } else if (typeof msg.content === "string") {
3118
3204
  pushLine(role, maybeTruncate(msg.content, maxLen), messageCreatedAt);
3119
3205
  } else if (msg.content?.parts && Array.isArray(msg.content.parts) && msg.content.parts.length > 0) {
3120
3206
  msg.content.parts.forEach((part) => {
@@ -3215,8 +3301,8 @@ function buildObserverHistoryMessage(messages, options) {
3215
3301
  }
3216
3302
  function maybeTruncate(str, maxLen) {
3217
3303
  if (!maxLen || str.length <= maxLen) return str;
3218
- const truncated = str.slice(0, maxLen);
3219
- const remaining = str.length - maxLen;
3304
+ const truncated = safeSlice(str, maxLen);
3305
+ const remaining = str.length - truncated.length;
3220
3306
  return `${truncated}
3221
3307
  ... [truncated ${remaining} characters]`;
3222
3308
  }
@@ -3520,7 +3606,7 @@ function sanitizeObservationLines(observations) {
3520
3606
  let changed = false;
3521
3607
  for (let i = 0; i < lines.length; i++) {
3522
3608
  if (lines[i].length > MAX_OBSERVATION_LINE_CHARS) {
3523
- lines[i] = lines[i].slice(0, MAX_OBSERVATION_LINE_CHARS) + " \u2026 [truncated]";
3609
+ lines[i] = safeSlice(lines[i], MAX_OBSERVATION_LINE_CHARS) + " \u2026 [truncated]";
3524
3610
  changed = true;
3525
3611
  }
3526
3612
  }
@@ -8859,6 +8945,110 @@ ${grouped}` : grouped;
8859
8945
  });
8860
8946
  }
8861
8947
  };
8948
+
8949
+ // src/processors/observational-memory/temporal-markers.ts
8950
+ var TEMPORAL_GAP_REMINDER_TYPE = "temporal-gap";
8951
+ function getTemporalGapReminderText(gapText, timestamp) {
8952
+ return `${gapText} \u2014 ${formatTemporalTimestamp(new Date(timestamp))}`;
8953
+ }
8954
+ function getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp) {
8955
+ const formattedTimestamp = formatTemporalTimestamp(new Date(timestamp));
8956
+ return {
8957
+ reminderType: TEMPORAL_GAP_REMINDER_TYPE,
8958
+ gapText,
8959
+ gapMs,
8960
+ timestamp: formattedTimestamp,
8961
+ timestampMs: timestamp,
8962
+ precedesMessageId: message.id,
8963
+ systemReminder: {
8964
+ type: TEMPORAL_GAP_REMINDER_TYPE,
8965
+ message: getTemporalGapReminderText(gapText, timestamp),
8966
+ gapText,
8967
+ gapMs,
8968
+ timestamp: formattedTimestamp,
8969
+ timestampMs: timestamp,
8970
+ precedesMessageId: message.id
8971
+ }
8972
+ };
8973
+ }
8974
+ function isTemporalGapMarkerForMessage(message, targetMessageId) {
8975
+ if (!isTemporalGapMarker(message)) {
8976
+ return false;
8977
+ }
8978
+ const metadata = message.content.metadata;
8979
+ if (metadata?.precedesMessageId === targetMessageId) {
8980
+ return true;
8981
+ }
8982
+ return metadata?.systemReminder?.type === TEMPORAL_GAP_REMINDER_TYPE && metadata.systemReminder.precedesMessageId === targetMessageId;
8983
+ }
8984
+ function createTemporalGapMarker(message, gapText, gapMs, timestamp) {
8985
+ const metadata = getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp);
8986
+ return {
8987
+ id: `__temporal_gap_${crypto.randomUUID()}`,
8988
+ role: "user",
8989
+ createdAt: new Date(timestamp - 1),
8990
+ threadId: message.threadId,
8991
+ resourceId: message.resourceId,
8992
+ content: {
8993
+ format: 2,
8994
+ parts: [
8995
+ {
8996
+ type: "text",
8997
+ text: `<system-reminder type="${TEMPORAL_GAP_REMINDER_TYPE}" precedesMessageId="${message.id}">${getTemporalGapReminderText(gapText, timestamp)}</system-reminder>`
8998
+ }
8999
+ ],
9000
+ metadata
9001
+ }
9002
+ };
9003
+ }
9004
+ async function insertTemporalGapMarkers({
9005
+ messageList,
9006
+ writer
9007
+ }) {
9008
+ const inputMessages = messageList.get.input.db().filter((message) => Boolean(message));
9009
+ const latestInputMessage = inputMessages.at(-1);
9010
+ if (!latestInputMessage || isTemporalGapMarker(latestInputMessage)) {
9011
+ return;
9012
+ }
9013
+ const allMessages = messageList.get.all.db().filter((message) => Boolean(message));
9014
+ const latestInputIndex = allMessages.findIndex((message) => message.id === latestInputMessage.id);
9015
+ if (latestInputIndex <= 0) {
9016
+ return;
9017
+ }
9018
+ if (allMessages.some((message) => isTemporalGapMarkerForMessage(message, latestInputMessage.id))) {
9019
+ return;
9020
+ }
9021
+ let previousNonMarker;
9022
+ for (let index = latestInputIndex - 1; index >= 0; index--) {
9023
+ const candidate = allMessages[index];
9024
+ if (candidate && !isTemporalGapMarker(candidate)) {
9025
+ previousNonMarker = candidate;
9026
+ break;
9027
+ }
9028
+ }
9029
+ if (!previousNonMarker) {
9030
+ return;
9031
+ }
9032
+ const timestamp = getMessagePartTimestamp(latestInputMessage, "first");
9033
+ const gapMs = timestamp - getMessagePartTimestamp(previousNonMarker, "last");
9034
+ const gapText = formatTemporalGap(gapMs);
9035
+ if (!gapText) {
9036
+ return;
9037
+ }
9038
+ const reminderMetadata = getTemporalGapReminderMetadata(latestInputMessage, gapText, gapMs, timestamp);
9039
+ await writer?.custom({
9040
+ type: "data-system-reminder",
9041
+ data: {
9042
+ message: getTemporalGapReminderText(gapText, timestamp),
9043
+ ...reminderMetadata
9044
+ },
9045
+ transient: true
9046
+ });
9047
+ const marker = createTemporalGapMarker(latestInputMessage, gapText, gapMs, timestamp);
9048
+ messageList.add(marker, "input");
9049
+ }
9050
+
9051
+ // src/processors/observational-memory/processor.ts
8862
9052
  function getOmObservabilityContext(args) {
8863
9053
  if (!args.tracing || !args.tracingContext || !args.loggerVNext || !args.metrics) {
8864
9054
  return void 0;
@@ -8881,11 +9071,14 @@ var ObservationalMemoryProcessor = class {
8881
9071
  engine;
8882
9072
  /** Memory instance for loading context. */
8883
9073
  memory;
9074
+ /** Whether temporal-gap reminder markers should be inserted. */
9075
+ temporalMarkers;
8884
9076
  /** Active turn — created on first processInputStep, ended on processOutputResult. */
8885
9077
  turn;
8886
- constructor(engine, memory) {
9078
+ constructor(engine, memory, options) {
8887
9079
  this.engine = engine;
8888
9080
  this.memory = memory;
9081
+ this.temporalMarkers = options?.temporalMarkers ?? false;
8889
9082
  }
8890
9083
  // ─── Processor lifecycle hooks ──────────────────────────────────────────
8891
9084
  async processInputStep(args) {
@@ -8944,6 +9137,9 @@ var ObservationalMemoryProcessor = class {
8944
9137
  this.turn.writer = writer;
8945
9138
  this.turn.requestContext = requestContext;
8946
9139
  await this.turn.start(this.memory);
9140
+ if (stepNumber === 0 && this.temporalMarkers) {
9141
+ await insertTemporalGapMarkers({ messageList, writer });
9142
+ }
8947
9143
  state.__omTurn = this.turn;
8948
9144
  }
8949
9145
  const observabilityContext = getOmObservabilityContext(args);
@@ -9108,5 +9304,5 @@ exports.stripEphemeralAnchorIds = stripEphemeralAnchorIds;
9108
9304
  exports.stripObservationGroups = stripObservationGroups;
9109
9305
  exports.truncateStringByTokens = truncateStringByTokens;
9110
9306
  exports.wrapInObservationGroup = wrapInObservationGroup;
9111
- //# sourceMappingURL=chunk-HAUWGR76.cjs.map
9112
- //# sourceMappingURL=chunk-HAUWGR76.cjs.map
9307
+ //# sourceMappingURL=chunk-5MTY2UYL.cjs.map
9308
+ //# sourceMappingURL=chunk-5MTY2UYL.cjs.map