@mastra/memory 1.16.0 → 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.
- package/CHANGELOG.md +13 -0
- package/dist/{chunk-HAUWGR76.cjs → chunk-5MTY2UYL.cjs} +206 -10
- package/dist/chunk-5MTY2UYL.cjs.map +1 -0
- package/dist/{chunk-OOA4C7IX.js → chunk-MPBMHIAQ.js} +206 -10
- package/dist/chunk-MPBMHIAQ.js.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +47 -47
- package/dist/docs/references/docs-memory-observational-memory.md +31 -0
- package/dist/docs/references/reference-memory-observational-memory.md +2 -0
- package/dist/index.cjs +16 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-CIGODMXV.js → observational-memory-7YYZIIX4.js} +3 -3
- package/dist/{observational-memory-CIGODMXV.js.map → observational-memory-7YYZIIX4.js.map} +1 -1
- package/dist/{observational-memory-EWU3QFIE.cjs → observational-memory-UCFGVLVW.cjs} +26 -26
- package/dist/{observational-memory-EWU3QFIE.cjs.map → observational-memory-UCFGVLVW.cjs.map} +1 -1
- package/dist/processors/index.cjs +24 -24
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/date-utils.d.ts +6 -0
- package/dist/processors/observational-memory/date-utils.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/processor.d.ts +5 -1
- package/dist/processors/observational-memory/processor.d.ts.map +1 -1
- package/dist/processors/observational-memory/string-utils.d.ts +13 -0
- package/dist/processors/observational-memory/string-utils.d.ts.map +1 -0
- package/dist/processors/observational-memory/temporal-markers.d.ts +4 -0
- package/dist/processors/observational-memory/temporal-markers.d.ts.map +1 -0
- package/dist/processors/observational-memory/tool-result-helpers.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +7 -0
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-HAUWGR76.cjs.map +0 -1
- 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
|
|
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 ? `
|
|
3055
|
-
|
|
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 (
|
|
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
|
|
3212
|
-
const remaining = str.length -
|
|
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]
|
|
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-
|
|
9080
|
-
//# sourceMappingURL=chunk-
|
|
9275
|
+
//# sourceMappingURL=chunk-MPBMHIAQ.js.map
|
|
9276
|
+
//# sourceMappingURL=chunk-MPBMHIAQ.js.map
|