@mastra/memory 0.0.0-studio-deploy-20260404182525 → 0.0.0-studio-cli-20260504022012
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 +461 -3
- package/dist/_types/@internal_ai-sdk-v4/dist/index.d.ts +51 -5
- package/dist/{chunk-4FMHSWZD.cjs → chunk-6ODYOMHN.cjs} +1279 -492
- package/dist/chunk-6ODYOMHN.cjs.map +1 -0
- package/dist/{chunk-C7PARRAD.js → chunk-Z5GWA7LB.js} +1280 -493
- package/dist/chunk-Z5GWA7LB.js.map +1 -0
- package/dist/{constants-HXOCZPB7.cjs → constants-PJXB7ZIS.cjs} +2 -2
- package/dist/{constants-HXOCZPB7.cjs.map → constants-PJXB7ZIS.cjs.map} +1 -1
- package/dist/{constants-BDOITAO3.js → constants-XEHPYRDJ.js} +2 -2
- package/dist/{constants-BDOITAO3.js.map → constants-XEHPYRDJ.js.map} +1 -1
- package/dist/docs/SKILL.md +3 -1
- package/dist/docs/assets/SOURCE_MAP.json +47 -59
- package/dist/docs/references/docs-agents-background-tasks.md +242 -0
- package/dist/docs/references/docs-agents-supervisor-agents.md +65 -0
- package/dist/docs/references/docs-memory-memory-processors.md +3 -3
- package/dist/docs/references/docs-memory-message-history.md +8 -2
- package/dist/docs/references/docs-memory-observational-memory.md +65 -8
- package/dist/docs/references/docs-memory-storage.md +1 -2
- package/dist/docs/references/docs-memory-working-memory.md +2 -2
- package/dist/docs/references/reference-memory-clone-utilities.md +4 -2
- package/dist/docs/references/reference-memory-cloneThread.md +4 -2
- package/dist/docs/references/reference-memory-observational-memory.md +4 -0
- package/dist/docs/references/reference-storage-redis.md +266 -0
- package/dist/docs/references/reference-vectors-mongodb.md +0 -2
- package/dist/index.cjs +1168 -553
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +35 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1158 -535
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-5YDQLKHE.cjs → observational-memory-OVSEK47Z.cjs} +26 -26
- package/dist/{observational-memory-5YDQLKHE.cjs.map → observational-memory-OVSEK47Z.cjs.map} +1 -1
- package/dist/{observational-memory-B7AUSTEY.js → observational-memory-UMDC4VC2.js} +3 -3
- package/dist/{observational-memory-B7AUSTEY.js.map → observational-memory-UMDC4VC2.js.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/index.d.ts +1 -1
- package/dist/processors/observational-memory/index.d.ts.map +1 -1
- package/dist/processors/observational-memory/markers.d.ts +5 -0
- package/dist/processors/observational-memory/markers.d.ts.map +1 -1
- package/dist/processors/observational-memory/message-utils.d.ts.map +1 -1
- package/dist/processors/observational-memory/model-context.d.ts +10 -0
- package/dist/processors/observational-memory/model-context.d.ts.map +1 -0
- package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/base.d.ts +3 -3
- package/dist/processors/observational-memory/observation-strategies/base.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/types.d.ts +9 -0
- package/dist/processors/observational-memory/observation-strategies/types.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/turn.d.ts +6 -2
- package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/types.d.ts +1 -0
- package/dist/processors/observational-memory/observation-turn/types.d.ts.map +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +54 -3
- package/dist/processors/observational-memory/observational-memory.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/reflector-runner.d.ts +14 -2
- package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/repro-capture.d.ts +8 -0
- package/dist/processors/observational-memory/repro-capture.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 +56 -2
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/dist/tools/om-tools.d.ts +14 -2
- package/dist/tools/om-tools.d.ts.map +1 -1
- package/dist/tools/working-memory.d.ts.map +1 -1
- package/package.json +15 -15
- package/dist/chunk-3PUO6DLX.js +0 -439
- package/dist/chunk-3PUO6DLX.js.map +0 -1
- package/dist/chunk-4FMHSWZD.cjs.map +0 -1
- package/dist/chunk-C7PARRAD.js.map +0 -1
- package/dist/chunk-CK4U3AYR.cjs +0 -84
- package/dist/chunk-CK4U3AYR.cjs.map +0 -1
- package/dist/chunk-DAJJSFVA.cjs +0 -441
- package/dist/chunk-DAJJSFVA.cjs.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -10
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-EQ4M72KU.js +0 -439
- package/dist/chunk-EQ4M72KU.js.map +0 -1
- package/dist/chunk-IDRQZVB4.cjs +0 -84
- package/dist/chunk-IDRQZVB4.cjs.map +0 -1
- package/dist/chunk-JEQ2X3Z6.cjs +0 -12
- package/dist/chunk-JEQ2X3Z6.cjs.map +0 -1
- package/dist/chunk-RC6RZVYE.js +0 -79
- package/dist/chunk-RC6RZVYE.js.map +0 -1
- package/dist/chunk-SVPZMV27.js +0 -79
- package/dist/chunk-SVPZMV27.js.map +0 -1
- package/dist/chunk-ZD3BKU5O.cjs +0 -441
- package/dist/chunk-ZD3BKU5O.cjs.map +0 -1
- package/dist/token-APYSY3BW-2DN6RAUY.js +0 -61
- package/dist/token-APYSY3BW-2DN6RAUY.js.map +0 -1
- package/dist/token-APYSY3BW-POD4OUWN.cjs +0 -63
- package/dist/token-APYSY3BW-POD4OUWN.cjs.map +0 -1
- package/dist/token-APYSY3BW-YTVQELJT.js +0 -61
- package/dist/token-APYSY3BW-YTVQELJT.js.map +0 -1
- package/dist/token-APYSY3BW-ZQ7TMBY7.cjs +0 -63
- package/dist/token-APYSY3BW-ZQ7TMBY7.cjs.map +0 -1
- package/dist/token-util-RMHT2CPJ-6TGPE335.cjs +0 -10
- package/dist/token-util-RMHT2CPJ-6TGPE335.cjs.map +0 -1
- package/dist/token-util-RMHT2CPJ-77HHGIQN.cjs +0 -10
- package/dist/token-util-RMHT2CPJ-77HHGIQN.cjs.map +0 -1
- package/dist/token-util-RMHT2CPJ-RJEA3FAN.js +0 -8
- package/dist/token-util-RMHT2CPJ-RJEA3FAN.js.map +0 -1
- package/dist/token-util-RMHT2CPJ-WJZ2SYAR.js +0 -8
- package/dist/token-util-RMHT2CPJ-WJZ2SYAR.js.map +0 -1
|
@@ -12,9 +12,9 @@ var crypto$1 = require('crypto');
|
|
|
12
12
|
var tokenx = require('tokenx');
|
|
13
13
|
var agent = require('@mastra/core/agent');
|
|
14
14
|
var observability = require('@mastra/core/observability');
|
|
15
|
+
var util = require('util');
|
|
15
16
|
var async_hooks = require('async_hooks');
|
|
16
17
|
var imageSize = require('image-size');
|
|
17
|
-
var util = require('util');
|
|
18
18
|
|
|
19
19
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
20
|
|
|
@@ -208,6 +208,19 @@ var BufferingCoordinator = class _BufferingCoordinator {
|
|
|
208
208
|
}
|
|
209
209
|
};
|
|
210
210
|
|
|
211
|
+
// src/processors/observational-memory/model-context.ts
|
|
212
|
+
function didProviderChange(actorModel, lastModel) {
|
|
213
|
+
if (actorModel === void 0 || lastModel === void 0) return false;
|
|
214
|
+
const actorHasSlash = actorModel.includes("/");
|
|
215
|
+
const lastHasSlash = lastModel.includes("/");
|
|
216
|
+
if (actorHasSlash && lastHasSlash) {
|
|
217
|
+
return actorModel !== lastModel;
|
|
218
|
+
}
|
|
219
|
+
const actorModelId = actorHasSlash ? actorModel.slice(actorModel.indexOf("/") + 1) : actorModel;
|
|
220
|
+
const lastModelId = lastHasSlash ? lastModel.slice(lastModel.indexOf("/") + 1) : lastModel;
|
|
221
|
+
return actorModelId !== lastModelId;
|
|
222
|
+
}
|
|
223
|
+
|
|
211
224
|
// src/processors/observational-memory/date-utils.ts
|
|
212
225
|
function formatRelativeTime(date, currentDate) {
|
|
213
226
|
const diffMs = currentDate.getTime() - date.getTime();
|
|
@@ -372,6 +385,67 @@ ${gap}
|
|
|
372
385
|
result += withInlineDates.slice(lastIndex);
|
|
373
386
|
return result;
|
|
374
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
|
+
}
|
|
375
449
|
|
|
376
450
|
// src/processors/observational-memory/markers.ts
|
|
377
451
|
function createObservationStartMarker(params) {
|
|
@@ -491,7 +565,12 @@ function createActivationMarker(params) {
|
|
|
491
565
|
threadId: params.threadId,
|
|
492
566
|
generationCount: params.generationCount,
|
|
493
567
|
config: params.config,
|
|
494
|
-
observations: params.observations
|
|
568
|
+
observations: params.observations,
|
|
569
|
+
triggeredBy: params.triggeredBy,
|
|
570
|
+
lastActivityAt: params.lastActivityAt,
|
|
571
|
+
ttlExpiredMs: params.ttlExpiredMs,
|
|
572
|
+
previousModel: params.previousModel,
|
|
573
|
+
currentModel: params.currentModel
|
|
495
574
|
}
|
|
496
575
|
};
|
|
497
576
|
}
|
|
@@ -641,7 +720,7 @@ function sortThreadsByOldestMessage(messagesByThread) {
|
|
|
641
720
|
})).sort((a, b) => a.oldestTimestamp - b.oldestTimestamp).map((t) => t.threadId);
|
|
642
721
|
}
|
|
643
722
|
function stripThreadTags(observations) {
|
|
644
|
-
return observations.replace(
|
|
723
|
+
return observations.replace(/<\/?thread\b[^>]{0,1024}>/gi, "").trim();
|
|
645
724
|
}
|
|
646
725
|
|
|
647
726
|
// src/processors/observational-memory/model-by-input-tokens.ts
|
|
@@ -941,6 +1020,8 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
941
1020
|
this.scope = deps.scope;
|
|
942
1021
|
this.retrieval = deps.retrieval;
|
|
943
1022
|
}
|
|
1023
|
+
deps;
|
|
1024
|
+
opts;
|
|
944
1025
|
storage;
|
|
945
1026
|
messageHistory;
|
|
946
1027
|
tokenCounter;
|
|
@@ -952,7 +1033,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
952
1033
|
static create;
|
|
953
1034
|
/**
|
|
954
1035
|
* Run the full observation lifecycle.
|
|
955
|
-
* @returns `
|
|
1036
|
+
* @returns Result with `observed` flag and optional `usage` from the observer LLM call.
|
|
956
1037
|
* @throws On sync/resource-scoped observer failure after failed markers (same as pre–Option-A contract).
|
|
957
1038
|
*/
|
|
958
1039
|
async run() {
|
|
@@ -962,7 +1043,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
962
1043
|
if (this.needsLock) {
|
|
963
1044
|
const fresh = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
964
1045
|
if (fresh?.lastObservedAt && record.lastObservedAt && fresh.lastObservedAt > record.lastObservedAt) {
|
|
965
|
-
return false;
|
|
1046
|
+
return { observed: false };
|
|
966
1047
|
}
|
|
967
1048
|
}
|
|
968
1049
|
const { messages, existingObservations } = await this.prepare();
|
|
@@ -983,7 +1064,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
983
1064
|
observabilityContext: this.opts.observabilityContext
|
|
984
1065
|
});
|
|
985
1066
|
}
|
|
986
|
-
return true;
|
|
1067
|
+
return { observed: true, usage: output.usage };
|
|
987
1068
|
} catch (error) {
|
|
988
1069
|
await this.emitFailedMarkers(cycleId, error);
|
|
989
1070
|
if (!this.rethrowOnFailure) {
|
|
@@ -1002,7 +1083,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
1002
1083
|
});
|
|
1003
1084
|
if (abortSignal?.aborted) throw error;
|
|
1004
1085
|
omError("[OM] Observation failed", error);
|
|
1005
|
-
return false;
|
|
1086
|
+
return { observed: false };
|
|
1006
1087
|
}
|
|
1007
1088
|
omError("[OM] Observation failed", error);
|
|
1008
1089
|
throw error;
|
|
@@ -1014,9 +1095,11 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
1014
1095
|
}
|
|
1015
1096
|
async streamMarker(marker) {
|
|
1016
1097
|
if (this.opts.writer) {
|
|
1017
|
-
await this.opts.writer.custom(marker).catch(() => {
|
|
1098
|
+
await this.opts.writer.custom({ ...marker, transient: true }).catch(() => {
|
|
1018
1099
|
});
|
|
1019
1100
|
}
|
|
1101
|
+
const markerThreadId = marker.data?.threadId ?? this.opts.threadId;
|
|
1102
|
+
await this.persistMarkerToStorage(marker, markerThreadId, this.opts.resourceId);
|
|
1020
1103
|
}
|
|
1021
1104
|
getObservationMarkerConfig() {
|
|
1022
1105
|
return {
|
|
@@ -1522,7 +1605,7 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
1522
1605
|
}
|
|
1523
1606
|
}
|
|
1524
1607
|
async emitEndMarkers(_cycleId, processed) {
|
|
1525
|
-
if (!processed.observations
|
|
1608
|
+
if (!processed.observations) return;
|
|
1526
1609
|
const { record, threadId, messages } = this.opts;
|
|
1527
1610
|
const tokensBuffered = await this.tokenCounter.countMessagesAsync(messages);
|
|
1528
1611
|
const updatedRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
@@ -1538,12 +1621,13 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
1538
1621
|
threadId,
|
|
1539
1622
|
observations: processed.observations
|
|
1540
1623
|
});
|
|
1541
|
-
|
|
1542
|
-
|
|
1624
|
+
if (this.opts.writer) {
|
|
1625
|
+
void this.opts.writer.custom({ ...endMarker, transient: true }).catch(() => {
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1543
1628
|
await this.persistMarkerToStorage(endMarker, threadId, record.resourceId ?? void 0);
|
|
1544
1629
|
}
|
|
1545
1630
|
async emitFailedMarkers(_cycleId, error) {
|
|
1546
|
-
if (!this.opts.writer) return;
|
|
1547
1631
|
const { record, threadId, messages } = this.opts;
|
|
1548
1632
|
const tokensAttempted = await this.tokenCounter.countMessagesAsync(messages);
|
|
1549
1633
|
const failedMarker = createBufferingFailedMarker({
|
|
@@ -1555,8 +1639,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
1555
1639
|
recordId: record.id,
|
|
1556
1640
|
threadId
|
|
1557
1641
|
});
|
|
1558
|
-
|
|
1559
|
-
|
|
1642
|
+
if (this.opts.writer) {
|
|
1643
|
+
void this.opts.writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1560
1646
|
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
|
|
1561
1647
|
}
|
|
1562
1648
|
};
|
|
@@ -1947,6 +2033,8 @@ var ObservationStep = class {
|
|
|
1947
2033
|
this.turn = turn;
|
|
1948
2034
|
this.stepNumber = stepNumber;
|
|
1949
2035
|
}
|
|
2036
|
+
turn;
|
|
2037
|
+
stepNumber;
|
|
1950
2038
|
_prepared = false;
|
|
1951
2039
|
_context;
|
|
1952
2040
|
/** Whether this step has been prepared. */
|
|
@@ -1982,6 +2070,7 @@ var ObservationStep = class {
|
|
|
1982
2070
|
resourceId,
|
|
1983
2071
|
checkThreshold: true,
|
|
1984
2072
|
messages: step0Messages,
|
|
2073
|
+
currentModel: this.turn.actorModelContext,
|
|
1985
2074
|
writer: this.turn.writer,
|
|
1986
2075
|
messageList
|
|
1987
2076
|
});
|
|
@@ -2005,8 +2094,11 @@ var ObservationStep = class {
|
|
|
2005
2094
|
observationTokens: obsTokens,
|
|
2006
2095
|
threadId,
|
|
2007
2096
|
writer: this.turn.writer,
|
|
2097
|
+
messageList,
|
|
2098
|
+
currentModel: this.turn.actorModelContext,
|
|
2008
2099
|
requestContext: this.turn.requestContext,
|
|
2009
|
-
observabilityContext: this.turn.observabilityContext
|
|
2100
|
+
observabilityContext: this.turn.observabilityContext,
|
|
2101
|
+
lastActivityAt: getLastActivityFromMessages(messageList.get.all.db())
|
|
2010
2102
|
});
|
|
2011
2103
|
await this.turn.refreshRecord();
|
|
2012
2104
|
if (this.turn.record.generationCount > preReflectGeneration) {
|
|
@@ -2015,7 +2107,11 @@ var ObservationStep = class {
|
|
|
2015
2107
|
}
|
|
2016
2108
|
const allMsgsForToolCheck = messageList.get.all.db();
|
|
2017
2109
|
const lastMessage = allMsgsForToolCheck[allMsgsForToolCheck.length - 1];
|
|
2018
|
-
const
|
|
2110
|
+
const pendingStepMessages = [...messageList.get.input.db(), ...messageList.get.response.db()];
|
|
2111
|
+
const latestStepParts = [
|
|
2112
|
+
...getLatestStepParts(lastMessage?.content?.parts ?? []),
|
|
2113
|
+
...pendingStepMessages.flatMap((msg) => getLatestStepParts(msg.content?.parts ?? []))
|
|
2114
|
+
];
|
|
2019
2115
|
const hasIncompleteToolCalls = latestStepParts.some(
|
|
2020
2116
|
(part) => part?.type === "tool-invocation" && part.toolInvocation?.state === "call"
|
|
2021
2117
|
);
|
|
@@ -2170,6 +2266,7 @@ var ObservationStep = class {
|
|
|
2170
2266
|
threadId,
|
|
2171
2267
|
resourceId,
|
|
2172
2268
|
messages: messageList.get.all.db(),
|
|
2269
|
+
currentModel: this.turn.actorModelContext,
|
|
2173
2270
|
writer: this.turn.writer,
|
|
2174
2271
|
messageList
|
|
2175
2272
|
});
|
|
@@ -2181,8 +2278,10 @@ var ObservationStep = class {
|
|
|
2181
2278
|
threadId,
|
|
2182
2279
|
writer: this.turn.writer,
|
|
2183
2280
|
messageList,
|
|
2281
|
+
currentModel: this.turn.actorModelContext,
|
|
2184
2282
|
requestContext: this.turn.requestContext,
|
|
2185
|
-
observabilityContext: this.turn.observabilityContext
|
|
2283
|
+
observabilityContext: this.turn.observabilityContext,
|
|
2284
|
+
lastActivityAt: getLastActivityFromMessages(messageList.get.all.db())
|
|
2186
2285
|
});
|
|
2187
2286
|
return {
|
|
2188
2287
|
succeeded: true,
|
|
@@ -2199,6 +2298,31 @@ var ObservationStep = class {
|
|
|
2199
2298
|
writer: this.turn.writer,
|
|
2200
2299
|
observabilityContext: this.turn.observabilityContext
|
|
2201
2300
|
});
|
|
2301
|
+
if (obsResult.observed) {
|
|
2302
|
+
const observedMessageIds = new Set(obsResult.record.observedMessageIds ?? []);
|
|
2303
|
+
const liveMessages = messageList.get.all.db();
|
|
2304
|
+
let latestObservedIndex = -1;
|
|
2305
|
+
for (let i = liveMessages.length - 1; i >= 0; i--) {
|
|
2306
|
+
const message = liveMessages[i];
|
|
2307
|
+
if (message && observedMessageIds.has(message.id)) {
|
|
2308
|
+
latestObservedIndex = i;
|
|
2309
|
+
break;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
const messageToSeal = latestObservedIndex >= 0 ? liveMessages[latestObservedIndex] : void 0;
|
|
2313
|
+
const messagesToSeal = messageToSeal ? [messageToSeal] : [];
|
|
2314
|
+
om.sealMessagesForBuffering(messagesToSeal);
|
|
2315
|
+
try {
|
|
2316
|
+
await this.turn.hooks?.onSyncObservationComplete?.();
|
|
2317
|
+
} catch (error) {
|
|
2318
|
+
omDebug(
|
|
2319
|
+
`[OM:observe] onSyncObservationComplete hook failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
if (messagesToSeal.length > 0) {
|
|
2323
|
+
await om.persistMessages(messagesToSeal, threadId, resourceId);
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2202
2326
|
return {
|
|
2203
2327
|
succeeded: obsResult.observed,
|
|
2204
2328
|
record: obsResult.record,
|
|
@@ -2224,7 +2348,9 @@ var ObservationTurn = class {
|
|
|
2224
2348
|
requestContext;
|
|
2225
2349
|
/** Optional observability context for nested OM spans. */
|
|
2226
2350
|
observabilityContext;
|
|
2227
|
-
/**
|
|
2351
|
+
/** Current actor model for this step. Updated by the processor before prepare(). */
|
|
2352
|
+
actorModelContext;
|
|
2353
|
+
/** Processor-provided hooks for turn/step lifecycle integration. */
|
|
2228
2354
|
hooks;
|
|
2229
2355
|
constructor(opts) {
|
|
2230
2356
|
this.om = opts.om;
|
|
@@ -2232,7 +2358,7 @@ var ObservationTurn = class {
|
|
|
2232
2358
|
this.resourceId = opts.resourceId;
|
|
2233
2359
|
this.messageList = opts.messageList;
|
|
2234
2360
|
this.observabilityContext = opts.observabilityContext;
|
|
2235
|
-
this.hooks = opts.hooks;
|
|
2361
|
+
this.hooks = opts.hooks ?? {};
|
|
2236
2362
|
}
|
|
2237
2363
|
om;
|
|
2238
2364
|
threadId;
|
|
@@ -2252,6 +2378,10 @@ var ObservationTurn = class {
|
|
|
2252
2378
|
get currentStep() {
|
|
2253
2379
|
return this._currentStep;
|
|
2254
2380
|
}
|
|
2381
|
+
addHooks(hooks) {
|
|
2382
|
+
if (!hooks) return;
|
|
2383
|
+
Object.assign(this.hooks, hooks);
|
|
2384
|
+
}
|
|
2255
2385
|
/**
|
|
2256
2386
|
* Load context and cache the record. Call once at the start of the turn.
|
|
2257
2387
|
*
|
|
@@ -2408,6 +2538,15 @@ function stripEphemeralAnchorIds(observations) {
|
|
|
2408
2538
|
}
|
|
2409
2539
|
return observations.replace(/(^|\n)([^\S\n]*)\[(O\d+(?:-N\d+)?)\][^\S\n]*/g, "$1$2");
|
|
2410
2540
|
}
|
|
2541
|
+
|
|
2542
|
+
// src/processors/observational-memory/string-utils.ts
|
|
2543
|
+
function safeSlice(str, end) {
|
|
2544
|
+
if (end <= 0) return "";
|
|
2545
|
+
if (end >= str.length) return str;
|
|
2546
|
+
const code = str.charCodeAt(end - 1);
|
|
2547
|
+
const safeEnd = code >= 55296 && code <= 56319 ? end - 1 : end;
|
|
2548
|
+
return str.slice(0, safeEnd);
|
|
2549
|
+
}
|
|
2411
2550
|
var ENCRYPTED_CONTENT_KEY = "encryptedContent";
|
|
2412
2551
|
var ENCRYPTED_CONTENT_REDACTION_THRESHOLD = 256;
|
|
2413
2552
|
var DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS = 1e4;
|
|
@@ -2473,7 +2612,7 @@ function truncateStringByTokens(text, maxTokens) {
|
|
|
2473
2612
|
return text;
|
|
2474
2613
|
}
|
|
2475
2614
|
const buildCandidate = (sliceEnd) => {
|
|
2476
|
-
const visible = text
|
|
2615
|
+
const visible = safeSlice(text, sliceEnd);
|
|
2477
2616
|
return `${visible}
|
|
2478
2617
|
... [truncated ~${totalTokens - tokenx.estimateTokenCount(visible)} tokens]`;
|
|
2479
2618
|
};
|
|
@@ -2929,11 +3068,13 @@ var OBSERVER_IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
2929
3068
|
"heif",
|
|
2930
3069
|
"avif"
|
|
2931
3070
|
]);
|
|
2932
|
-
function
|
|
2933
|
-
return createdAt ?
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
3071
|
+
function formatObserverDate(createdAt) {
|
|
3072
|
+
return createdAt ? `${createdAt.toLocaleDateString("en-US", {
|
|
3073
|
+
month: "short"
|
|
3074
|
+
})} ${createdAt.getDate()} ${createdAt.getFullYear()}` : "";
|
|
3075
|
+
}
|
|
3076
|
+
function formatObserverTime(createdAt) {
|
|
3077
|
+
return createdAt ? createdAt.toLocaleTimeString("en-US", {
|
|
2937
3078
|
hour: "numeric",
|
|
2938
3079
|
minute: "2-digit",
|
|
2939
3080
|
hour12: true
|
|
@@ -3024,19 +3165,88 @@ function formatObserverAttachmentPlaceholder(part, counter) {
|
|
|
3024
3165
|
const label = resolveObserverAttachmentLabel(part);
|
|
3025
3166
|
return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
|
|
3026
3167
|
}
|
|
3168
|
+
function formatObserverPartLine(title, body, time, previousTime) {
|
|
3169
|
+
const timeLabel = time && time !== previousTime ? `(${time})` : "";
|
|
3170
|
+
if (!title) {
|
|
3171
|
+
return timeLabel ? `${timeLabel}: ${body}` : body;
|
|
3172
|
+
}
|
|
3173
|
+
return `${title}${timeLabel ? ` ${timeLabel}` : ""}: ${body}`;
|
|
3174
|
+
}
|
|
3175
|
+
function normalizeObserverCreatedAt(createdAt) {
|
|
3176
|
+
if (createdAt instanceof Date) {
|
|
3177
|
+
if (Number.isNaN(createdAt.getTime())) {
|
|
3178
|
+
return void 0;
|
|
3179
|
+
}
|
|
3180
|
+
return createdAt;
|
|
3181
|
+
}
|
|
3182
|
+
if (typeof createdAt === "number" || typeof createdAt === "string") {
|
|
3183
|
+
const date = new Date(createdAt);
|
|
3184
|
+
if (Number.isNaN(date.getTime())) {
|
|
3185
|
+
return void 0;
|
|
3186
|
+
}
|
|
3187
|
+
return date;
|
|
3188
|
+
}
|
|
3189
|
+
return void 0;
|
|
3190
|
+
}
|
|
3191
|
+
function formatObserverLines(lines, context = {}) {
|
|
3192
|
+
const output = [];
|
|
3193
|
+
let previousDate = context.previousDate;
|
|
3194
|
+
let previousTime = context.previousTime;
|
|
3195
|
+
for (const line of lines) {
|
|
3196
|
+
if (line.date && line.date !== previousDate) {
|
|
3197
|
+
output.push(`${line.date}:`);
|
|
3198
|
+
previousDate = line.date;
|
|
3199
|
+
previousTime = void 0;
|
|
3200
|
+
}
|
|
3201
|
+
output.push(formatObserverPartLine(line.title, line.body, line.time, previousTime));
|
|
3202
|
+
previousTime = line.time || previousTime;
|
|
3203
|
+
}
|
|
3204
|
+
return {
|
|
3205
|
+
text: output.join("\n"),
|
|
3206
|
+
context: { previousDate, previousTime }
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
function getTemporalGapMarkerText(msg) {
|
|
3210
|
+
const metadata = typeof msg.content === "object" && msg.content && "metadata" in msg.content ? msg.content.metadata : void 0;
|
|
3211
|
+
if (metadata?.reminderType === "temporal-gap" && typeof metadata.gapText === "string") {
|
|
3212
|
+
return metadata.gapText;
|
|
3213
|
+
}
|
|
3214
|
+
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") {
|
|
3215
|
+
return metadata.systemReminder.gapText;
|
|
3216
|
+
}
|
|
3217
|
+
return void 0;
|
|
3218
|
+
}
|
|
3027
3219
|
function formatObserverMessage(msg, counter, options) {
|
|
3028
3220
|
const maxLen = options?.maxPartLength;
|
|
3029
3221
|
const maxToolResultTokens = options?.maxToolResultTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
|
|
3030
|
-
const timestamp = formatObserverTimestamp(msg.createdAt);
|
|
3031
3222
|
const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
|
|
3032
|
-
const timestampStr = timestamp ? ` (${timestamp})` : "";
|
|
3033
3223
|
const attachments = [];
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3224
|
+
const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
|
|
3225
|
+
let lines = [];
|
|
3226
|
+
const temporalGapText = isTemporalGapMarker(msg) ? getTemporalGapMarkerText(msg) : void 0;
|
|
3227
|
+
const pushLine = (title, body, createdAt) => {
|
|
3228
|
+
if (!body) {
|
|
3229
|
+
return;
|
|
3230
|
+
}
|
|
3231
|
+
const normalizedCreatedAt = normalizeObserverCreatedAt(createdAt) ?? messageCreatedAt;
|
|
3232
|
+
lines.push({
|
|
3233
|
+
date: formatObserverDate(normalizedCreatedAt),
|
|
3234
|
+
time: formatObserverTime(normalizedCreatedAt),
|
|
3235
|
+
title,
|
|
3236
|
+
body
|
|
3237
|
+
});
|
|
3238
|
+
};
|
|
3239
|
+
if (temporalGapText) {
|
|
3240
|
+
pushLine("", temporalGapText, messageCreatedAt);
|
|
3241
|
+
} else if (typeof msg.content === "string") {
|
|
3242
|
+
pushLine(role, maybeTruncate(msg.content, maxLen), messageCreatedAt);
|
|
3037
3243
|
} else if (msg.content?.parts && Array.isArray(msg.content.parts) && msg.content.parts.length > 0) {
|
|
3038
|
-
|
|
3039
|
-
|
|
3244
|
+
msg.content.parts.forEach((part) => {
|
|
3245
|
+
const partCreatedAt = normalizeObserverCreatedAt(part.createdAt) ?? messageCreatedAt;
|
|
3246
|
+
if (part.type === "text") {
|
|
3247
|
+
pushLine(role, maybeTruncate(part.text, maxLen), partCreatedAt);
|
|
3248
|
+
return;
|
|
3249
|
+
}
|
|
3040
3250
|
if (part.type === "tool-invocation") {
|
|
3041
3251
|
const inv = part.toolInvocation;
|
|
3042
3252
|
if (inv.state === "result") {
|
|
@@ -3044,19 +3254,24 @@ function formatObserverMessage(msg, counter, options) {
|
|
|
3044
3254
|
part,
|
|
3045
3255
|
inv.result
|
|
3046
3256
|
);
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3257
|
+
pushLine(
|
|
3258
|
+
`Tool Result ${inv.toolName}`,
|
|
3259
|
+
maybeTruncate(formatToolResultForObserver(resultForObserver, { maxTokens: maxToolResultTokens }), maxLen),
|
|
3260
|
+
partCreatedAt
|
|
3261
|
+
);
|
|
3262
|
+
return;
|
|
3050
3263
|
}
|
|
3051
|
-
|
|
3052
|
-
return
|
|
3053
|
-
${maybeTruncate(argsStr, maxLen)}`;
|
|
3264
|
+
pushLine(`Tool Call ${inv.toolName}`, maybeTruncate(JSON.stringify(inv.args, null, 2), maxLen), partCreatedAt);
|
|
3265
|
+
return;
|
|
3054
3266
|
}
|
|
3055
3267
|
const partType = part.type;
|
|
3056
3268
|
if (partType === "reasoning") {
|
|
3057
3269
|
const reasoning = part.reasoning;
|
|
3058
|
-
if (reasoning)
|
|
3059
|
-
|
|
3270
|
+
if (!reasoning) {
|
|
3271
|
+
return;
|
|
3272
|
+
}
|
|
3273
|
+
pushLine("Reasoning", maybeTruncate(reasoning, maxLen), partCreatedAt);
|
|
3274
|
+
return;
|
|
3060
3275
|
}
|
|
3061
3276
|
if (partType === "image" || partType === "file") {
|
|
3062
3277
|
const attachment = part;
|
|
@@ -3064,40 +3279,58 @@ ${maybeTruncate(argsStr, maxLen)}`;
|
|
|
3064
3279
|
if (inputAttachment) {
|
|
3065
3280
|
attachments.push(inputAttachment);
|
|
3066
3281
|
}
|
|
3067
|
-
|
|
3282
|
+
pushLine(
|
|
3283
|
+
partType === "image" ? "Image" : "File",
|
|
3284
|
+
formatObserverAttachmentPlaceholder(attachment, counter),
|
|
3285
|
+
partCreatedAt
|
|
3286
|
+
);
|
|
3068
3287
|
}
|
|
3069
|
-
|
|
3070
|
-
return "";
|
|
3071
|
-
}).filter(Boolean).join("\n");
|
|
3288
|
+
});
|
|
3072
3289
|
} else if (msg.content?.content) {
|
|
3073
|
-
|
|
3290
|
+
pushLine(role, maybeTruncate(msg.content.content, maxLen), messageCreatedAt);
|
|
3074
3291
|
}
|
|
3075
|
-
if (
|
|
3076
|
-
return {
|
|
3292
|
+
if (lines.length === 0 && attachments.length === 0) {
|
|
3293
|
+
return { lines: [], attachments };
|
|
3077
3294
|
}
|
|
3078
3295
|
return {
|
|
3079
|
-
|
|
3080
|
-
${content}`,
|
|
3296
|
+
lines,
|
|
3081
3297
|
attachments
|
|
3082
3298
|
};
|
|
3083
3299
|
}
|
|
3084
3300
|
function formatMessagesForObserver(messages, options) {
|
|
3085
3301
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
3086
|
-
|
|
3302
|
+
const sections = [];
|
|
3303
|
+
let context = {};
|
|
3304
|
+
for (const message of messages) {
|
|
3305
|
+
const formatted = formatObserverMessage(message, counter, options);
|
|
3306
|
+
if (formatted.lines.length === 0) {
|
|
3307
|
+
continue;
|
|
3308
|
+
}
|
|
3309
|
+
const rendered = formatObserverLines(formatted.lines, context);
|
|
3310
|
+
if (!rendered.text) {
|
|
3311
|
+
continue;
|
|
3312
|
+
}
|
|
3313
|
+
sections.push(rendered.text);
|
|
3314
|
+
context = rendered.context;
|
|
3315
|
+
}
|
|
3316
|
+
return sections.join("\n");
|
|
3317
|
+
}
|
|
3318
|
+
function appendFormattedObserverMessage(content, formatted, context) {
|
|
3319
|
+
const rendered = formatObserverLines(formatted.lines, context);
|
|
3320
|
+
if (rendered.text) {
|
|
3321
|
+
content.push({ type: "text", text: rendered.text });
|
|
3322
|
+
}
|
|
3323
|
+
content.push(...formatted.attachments);
|
|
3324
|
+
return rendered.context;
|
|
3087
3325
|
}
|
|
3088
3326
|
function buildObserverHistoryMessage(messages, options) {
|
|
3089
3327
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
3090
3328
|
const content = [{ type: "text", text: "## New Message History to Observe\n\n" }];
|
|
3091
|
-
let
|
|
3329
|
+
let context = {};
|
|
3092
3330
|
messages.forEach((message) => {
|
|
3093
3331
|
const formatted = formatObserverMessage(message, counter, options);
|
|
3094
|
-
if (
|
|
3095
|
-
|
|
3096
|
-
content.push({ type: "text", text: "\n\n---\n\n" });
|
|
3097
|
-
}
|
|
3098
|
-
content.push({ type: "text", text: formatted.text });
|
|
3099
|
-
content.push(...formatted.attachments);
|
|
3100
|
-
visibleCount++;
|
|
3332
|
+
if (formatted.lines.length === 0 && formatted.attachments.length === 0) return;
|
|
3333
|
+
context = appendFormattedObserverMessage(content, formatted, context);
|
|
3101
3334
|
});
|
|
3102
3335
|
return {
|
|
3103
3336
|
role: "user",
|
|
@@ -3106,8 +3339,8 @@ function buildObserverHistoryMessage(messages, options) {
|
|
|
3106
3339
|
}
|
|
3107
3340
|
function maybeTruncate(str, maxLen) {
|
|
3108
3341
|
if (!maxLen || str.length <= maxLen) return str;
|
|
3109
|
-
const truncated = str
|
|
3110
|
-
const remaining = str.length -
|
|
3342
|
+
const truncated = safeSlice(str, maxLen);
|
|
3343
|
+
const remaining = str.length - truncated.length;
|
|
3111
3344
|
return `${truncated}
|
|
3112
3345
|
... [truncated ${remaining} characters]`;
|
|
3113
3346
|
}
|
|
@@ -3127,18 +3360,15 @@ The following messages are from ${threadOrder.length} different conversation thr
|
|
|
3127
3360
|
const messages = messagesByThread.get(threadId);
|
|
3128
3361
|
if (!messages || messages.length === 0) return;
|
|
3129
3362
|
const threadContent = [];
|
|
3130
|
-
let
|
|
3363
|
+
let context = {};
|
|
3364
|
+
let hasVisibleContent = false;
|
|
3131
3365
|
messages.forEach((message) => {
|
|
3132
3366
|
const formatted = formatObserverMessage(message, counter, options);
|
|
3133
|
-
if (
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
}
|
|
3137
|
-
threadContent.push({ type: "text", text: formatted.text });
|
|
3138
|
-
threadContent.push(...formatted.attachments);
|
|
3139
|
-
visibleCount++;
|
|
3367
|
+
if (formatted.lines.length === 0 && formatted.attachments.length === 0) return;
|
|
3368
|
+
context = appendFormattedObserverMessage(threadContent, formatted, context);
|
|
3369
|
+
hasVisibleContent = true;
|
|
3140
3370
|
});
|
|
3141
|
-
if (
|
|
3371
|
+
if (!hasVisibleContent) return;
|
|
3142
3372
|
content.push({ type: "text", text: `<thread id="${threadId}">
|
|
3143
3373
|
` });
|
|
3144
3374
|
content.push(...threadContent);
|
|
@@ -3414,7 +3644,7 @@ function sanitizeObservationLines(observations) {
|
|
|
3414
3644
|
let changed = false;
|
|
3415
3645
|
for (let i = 0; i < lines.length; i++) {
|
|
3416
3646
|
if (lines[i].length > MAX_OBSERVATION_LINE_CHARS) {
|
|
3417
|
-
lines[i] = lines[i]
|
|
3647
|
+
lines[i] = safeSlice(lines[i], MAX_OBSERVATION_LINE_CHARS) + " \u2026 [truncated]";
|
|
3418
3648
|
changed = true;
|
|
3419
3649
|
}
|
|
3420
3650
|
}
|
|
@@ -4017,12 +4247,44 @@ function validateCompression(reflectedTokens, targetThreshold) {
|
|
|
4017
4247
|
}
|
|
4018
4248
|
|
|
4019
4249
|
// src/processors/observational-memory/reflector-runner.ts
|
|
4250
|
+
function formatModelContext(provider, modelId) {
|
|
4251
|
+
if (provider && modelId) {
|
|
4252
|
+
return `${provider}/${modelId}`;
|
|
4253
|
+
}
|
|
4254
|
+
return modelId;
|
|
4255
|
+
}
|
|
4256
|
+
function getCurrentModel(model) {
|
|
4257
|
+
return formatModelContext(model?.provider, model?.modelId);
|
|
4258
|
+
}
|
|
4259
|
+
function getLastModelFromMessageList(messageList) {
|
|
4260
|
+
const messages = messageList?.get.all.db();
|
|
4261
|
+
if (!messages) return void 0;
|
|
4262
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
4263
|
+
const message = messages[i];
|
|
4264
|
+
if (!message || message.role !== "assistant" || !message.content || typeof message.content === "string") {
|
|
4265
|
+
continue;
|
|
4266
|
+
}
|
|
4267
|
+
for (let j = message.content.parts.length - 1; j >= 0; j--) {
|
|
4268
|
+
const part = message.content.parts[j];
|
|
4269
|
+
if (part?.type === "step-start" && typeof part.model === "string" && part.model.length > 0) {
|
|
4270
|
+
return part.model;
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
const metadata = message.content.metadata;
|
|
4274
|
+
const model = formatModelContext(metadata?.provider, metadata?.modelId);
|
|
4275
|
+
if (model) {
|
|
4276
|
+
return model;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
return void 0;
|
|
4280
|
+
}
|
|
4020
4281
|
async function withAbortCheck(fn, abortSignal) {
|
|
4021
4282
|
if (abortSignal?.aborted) throw new Error("The operation was aborted.");
|
|
4022
4283
|
const result = await fn();
|
|
4023
4284
|
if (abortSignal?.aborted) throw new Error("The operation was aborted.");
|
|
4024
4285
|
return result;
|
|
4025
4286
|
}
|
|
4287
|
+
var EARLY_ACTIVATION_SIZE_FLOOR_RATIO = 0.75;
|
|
4026
4288
|
var ReflectorRunner = class {
|
|
4027
4289
|
reflectionConfig;
|
|
4028
4290
|
observationConfig;
|
|
@@ -4056,13 +4318,30 @@ var ReflectorRunner = class {
|
|
|
4056
4318
|
model
|
|
4057
4319
|
});
|
|
4058
4320
|
}
|
|
4059
|
-
getObservationMarkerConfig() {
|
|
4321
|
+
getObservationMarkerConfig(record) {
|
|
4060
4322
|
return {
|
|
4061
4323
|
messageTokens: getMaxThreshold(this.observationConfig.messageTokens),
|
|
4062
|
-
observationTokens: getMaxThreshold(
|
|
4063
|
-
|
|
4324
|
+
observationTokens: getMaxThreshold(
|
|
4325
|
+
record ? this.getEffectiveReflectionTokens(record) : this.reflectionConfig.observationTokens
|
|
4326
|
+
),
|
|
4327
|
+
scope: this.scope,
|
|
4328
|
+
activateAfterIdle: this.reflectionConfig.activateAfterIdle
|
|
4064
4329
|
};
|
|
4065
4330
|
}
|
|
4331
|
+
/**
|
|
4332
|
+
* Resolve the effective reflection observationTokens for a record.
|
|
4333
|
+
* Only explicit per-record overrides (stored under `_overrides`) win;
|
|
4334
|
+
* the initial config snapshot is ignored so instance-level changes
|
|
4335
|
+
* still take effect for existing records.
|
|
4336
|
+
*/
|
|
4337
|
+
getEffectiveReflectionTokens(record) {
|
|
4338
|
+
const overrides = record.config?._overrides;
|
|
4339
|
+
const recordTokens = overrides?.reflection?.observationTokens;
|
|
4340
|
+
if (recordTokens) {
|
|
4341
|
+
return recordTokens;
|
|
4342
|
+
}
|
|
4343
|
+
return this.reflectionConfig.observationTokens;
|
|
4344
|
+
}
|
|
4066
4345
|
/**
|
|
4067
4346
|
* Call the Reflector agent with escalating compression levels.
|
|
4068
4347
|
*/
|
|
@@ -4172,8 +4451,9 @@ var ReflectorRunner = class {
|
|
|
4172
4451
|
recordId: streamContext.recordId,
|
|
4173
4452
|
threadId: streamContext.threadId
|
|
4174
4453
|
});
|
|
4175
|
-
await streamContext.writer.custom(failedMarker).catch(() => {
|
|
4454
|
+
await streamContext.writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
4176
4455
|
});
|
|
4456
|
+
await this.persistMarkerToStorage(failedMarker, streamContext.threadId, streamContext.resourceId);
|
|
4177
4457
|
const retryCycleId = crypto.randomUUID();
|
|
4178
4458
|
streamContext.cycleId = retryCycleId;
|
|
4179
4459
|
const startMarker = createObservationStartMarker({
|
|
@@ -4186,8 +4466,9 @@ var ReflectorRunner = class {
|
|
|
4186
4466
|
config: this.getObservationMarkerConfig()
|
|
4187
4467
|
});
|
|
4188
4468
|
streamContext.startedAt = startMarker.data.startedAt;
|
|
4189
|
-
await streamContext.writer.custom(startMarker).catch(() => {
|
|
4469
|
+
await streamContext.writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
4190
4470
|
});
|
|
4471
|
+
await this.persistMarkerToStorage(startMarker, streamContext.threadId, streamContext.resourceId);
|
|
4191
4472
|
}
|
|
4192
4473
|
currentLevel = Math.min(currentLevel + 1, maxLevel);
|
|
4193
4474
|
}
|
|
@@ -4200,7 +4481,7 @@ var ReflectorRunner = class {
|
|
|
4200
4481
|
/**
|
|
4201
4482
|
* Start an async buffered reflection in the background.
|
|
4202
4483
|
*/
|
|
4203
|
-
startAsyncBufferedReflection(record, observationTokens, lockKey, writer, requestContext, observabilityContext) {
|
|
4484
|
+
startAsyncBufferedReflection(record, observationTokens, lockKey, writer, requestContext, observabilityContext, reflectionHooks) {
|
|
4204
4485
|
const bufferKey = this.buffering.getReflectionBufferKey(lockKey);
|
|
4205
4486
|
if (this.buffering.isAsyncBufferingInProgress(bufferKey)) {
|
|
4206
4487
|
return;
|
|
@@ -4210,7 +4491,10 @@ var ReflectorRunner = class {
|
|
|
4210
4491
|
this.storage.setBufferingReflectionFlag(record.id, true).catch((err) => {
|
|
4211
4492
|
omError("[OM] Failed to set buffering reflection flag", err);
|
|
4212
4493
|
});
|
|
4213
|
-
|
|
4494
|
+
reflectionHooks?.onReflectionStart?.();
|
|
4495
|
+
const asyncOp = this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext, observabilityContext).then((usage) => {
|
|
4496
|
+
reflectionHooks?.onReflectionEnd?.({ usage });
|
|
4497
|
+
}).catch(async (error) => {
|
|
4214
4498
|
if (writer) {
|
|
4215
4499
|
const failedMarker = createBufferingFailedMarker({
|
|
4216
4500
|
cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
|
|
@@ -4221,11 +4505,15 @@ var ReflectorRunner = class {
|
|
|
4221
4505
|
recordId: record.id,
|
|
4222
4506
|
threadId: record.threadId ?? ""
|
|
4223
4507
|
});
|
|
4224
|
-
void writer.custom(failedMarker).catch(() => {
|
|
4508
|
+
void writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
4225
4509
|
});
|
|
4226
4510
|
await this.persistMarkerToStorage(failedMarker, record.threadId ?? "", record.resourceId ?? void 0);
|
|
4227
4511
|
}
|
|
4228
4512
|
omError("[OM] Async buffered reflection failed", error);
|
|
4513
|
+
reflectionHooks?.onReflectionEnd?.({
|
|
4514
|
+
usage: void 0,
|
|
4515
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
4516
|
+
});
|
|
4229
4517
|
BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
|
|
4230
4518
|
}).finally(() => {
|
|
4231
4519
|
BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
|
|
@@ -4244,7 +4532,7 @@ var ReflectorRunner = class {
|
|
|
4244
4532
|
const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
4245
4533
|
const currentRecord = freshRecord ?? record;
|
|
4246
4534
|
const observationTokens = currentRecord.observationTokenCount ?? 0;
|
|
4247
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
4535
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(currentRecord));
|
|
4248
4536
|
const bufferActivation = this.reflectionConfig.bufferActivation ?? 0.5;
|
|
4249
4537
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4250
4538
|
const cycleId = `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
@@ -4273,10 +4561,15 @@ var ReflectorRunner = class {
|
|
|
4273
4561
|
recordId: record.id,
|
|
4274
4562
|
threadId: record.threadId ?? "",
|
|
4275
4563
|
threadIds: record.threadId ? [record.threadId] : [],
|
|
4276
|
-
config: this.getObservationMarkerConfig()
|
|
4564
|
+
config: this.getObservationMarkerConfig(currentRecord)
|
|
4277
4565
|
});
|
|
4278
|
-
void writer.custom(startMarker).catch(() => {
|
|
4566
|
+
void writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
4279
4567
|
});
|
|
4568
|
+
await this.persistMarkerToStorage(
|
|
4569
|
+
startMarker,
|
|
4570
|
+
currentRecord.threadId ?? "",
|
|
4571
|
+
currentRecord.resourceId ?? void 0
|
|
4572
|
+
);
|
|
4280
4573
|
}
|
|
4281
4574
|
const compressionStartLevel = await this.getCompressionStartLevel(requestContext);
|
|
4282
4575
|
const reflectResult = await this.call(
|
|
@@ -4315,26 +4608,35 @@ var ReflectorRunner = class {
|
|
|
4315
4608
|
threadId: currentRecord.threadId ?? "",
|
|
4316
4609
|
observations: reflectResult.observations
|
|
4317
4610
|
});
|
|
4318
|
-
void writer.custom(endMarker).catch(() => {
|
|
4611
|
+
void writer.custom({ ...endMarker, transient: true }).catch(() => {
|
|
4319
4612
|
});
|
|
4320
4613
|
await this.persistMarkerToStorage(endMarker, currentRecord.threadId ?? "", currentRecord.resourceId ?? void 0);
|
|
4321
4614
|
}
|
|
4615
|
+
return reflectResult.usage;
|
|
4322
4616
|
}
|
|
4323
4617
|
/**
|
|
4324
4618
|
* Try to activate buffered reflection when threshold is reached.
|
|
4325
|
-
* Returns
|
|
4619
|
+
* Returns a discriminated result so the caller can distinguish between
|
|
4620
|
+
* "activated", "no buffer present", and "suppressed by overshoot guard"
|
|
4621
|
+
* without re-deriving that state.
|
|
4326
4622
|
*/
|
|
4327
|
-
async tryActivateBufferedReflection(record, lockKey, writer, messageList) {
|
|
4623
|
+
async tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata) {
|
|
4328
4624
|
const bufferKey = this.buffering.getReflectionBufferKey(lockKey);
|
|
4329
4625
|
const asyncOp = BufferingCoordinator.asyncBufferingOps.get(bufferKey);
|
|
4330
4626
|
if (asyncOp) {
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
]);
|
|
4337
|
-
|
|
4627
|
+
if (activationMetadata?.triggeredBy === "ttl" || activationMetadata?.triggeredBy === "provider_change") {
|
|
4628
|
+
omDebug(
|
|
4629
|
+
`[OM:reflect] tryActivateBufferedReflection: async op in progress, not blocking for ${activationMetadata.triggeredBy} trigger`
|
|
4630
|
+
);
|
|
4631
|
+
} else {
|
|
4632
|
+
omDebug(`[OM:reflect] tryActivateBufferedReflection: waiting for in-progress op...`);
|
|
4633
|
+
try {
|
|
4634
|
+
await Promise.race([
|
|
4635
|
+
asyncOp,
|
|
4636
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 5e3))
|
|
4637
|
+
]);
|
|
4638
|
+
} catch {
|
|
4639
|
+
}
|
|
4338
4640
|
}
|
|
4339
4641
|
}
|
|
4340
4642
|
const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
@@ -4345,8 +4647,8 @@ var ReflectorRunner = class {
|
|
|
4345
4647
|
`[OM:reflect] tryActivateBufferedReflection: freshRecord.id=${freshRecord?.id}, freshBufferedReflection=${freshRecord?.bufferedReflection ? "present (" + freshRecord.bufferedReflection.length + " chars)" : "empty"}, freshObsTokens=${freshRecord?.observationTokenCount}`
|
|
4346
4648
|
);
|
|
4347
4649
|
if (!freshRecord?.bufferedReflection) {
|
|
4348
|
-
omDebug(`[OM:reflect] tryActivateBufferedReflection: no buffered reflection after re-fetch
|
|
4349
|
-
return
|
|
4650
|
+
omDebug(`[OM:reflect] tryActivateBufferedReflection: no buffered reflection after re-fetch`);
|
|
4651
|
+
return { status: "no-buffer" };
|
|
4350
4652
|
}
|
|
4351
4653
|
const beforeTokens = freshRecord.observationTokenCount ?? 0;
|
|
4352
4654
|
const reflectedLineCount = freshRecord.reflectedObservationLineCount ?? 0;
|
|
@@ -4358,6 +4660,26 @@ var ReflectorRunner = class {
|
|
|
4358
4660
|
|
|
4359
4661
|
${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
4360
4662
|
const combinedTokenCount = this.tokenCounter.countObservations(combinedObservations);
|
|
4663
|
+
if (activationMetadata?.triggeredBy === "ttl" || activationMetadata?.triggeredBy === "provider_change") {
|
|
4664
|
+
const unreflectedTailTokens = unreflectedContent ? this.tokenCounter.countObservations(unreflectedContent) : 0;
|
|
4665
|
+
const bufferedReflectionTokens = freshRecord.bufferedReflectionTokens ?? 0;
|
|
4666
|
+
if (unreflectedTailTokens < bufferedReflectionTokens) {
|
|
4667
|
+
omDebug(
|
|
4668
|
+
`[OM:reflect] tryActivateBufferedReflection: suppressing early ${activationMetadata.triggeredBy} activation \u2014 unreflectedTailTokens=${unreflectedTailTokens} < bufferedReflectionTokens=${bufferedReflectionTokens}; keeping buffer for threshold activation`
|
|
4669
|
+
);
|
|
4670
|
+
return { status: "suppressed", reason: "composition" };
|
|
4671
|
+
}
|
|
4672
|
+
const bufferActivation = this.reflectionConfig.bufferActivation;
|
|
4673
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(freshRecord));
|
|
4674
|
+
const regularActivationTarget = reflectThreshold * (1 - bufferActivation);
|
|
4675
|
+
const minCombinedTokens = Math.round(regularActivationTarget * EARLY_ACTIVATION_SIZE_FLOOR_RATIO);
|
|
4676
|
+
if (combinedTokenCount < minCombinedTokens) {
|
|
4677
|
+
omDebug(
|
|
4678
|
+
`[OM:reflect] tryActivateBufferedReflection: suppressing early ${activationMetadata.triggeredBy} activation \u2014 combinedTokenCount=${combinedTokenCount} < minCombinedTokens=${minCombinedTokens} (${EARLY_ACTIVATION_SIZE_FLOOR_RATIO * 100}% of regular activation target ${Math.round(regularActivationTarget)}, threshold=${reflectThreshold}, bufferActivation=${bufferActivation}); keeping buffer for threshold activation`
|
|
4679
|
+
);
|
|
4680
|
+
return { status: "suppressed", reason: "size" };
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4361
4683
|
omDebug(
|
|
4362
4684
|
`[OM:reflect] tryActivateBufferedReflection: activating, beforeTokens=${beforeTokens}, combinedTokenCount=${combinedTokenCount}, reflectedLineCount=${reflectedLineCount}, unreflectedLines=${unreflectedLines.length}`
|
|
4363
4685
|
);
|
|
@@ -4384,9 +4706,14 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4384
4706
|
threadId: freshRecord.threadId ?? "",
|
|
4385
4707
|
generationCount: afterRecord?.generationCount ?? freshRecord.generationCount ?? 0,
|
|
4386
4708
|
observations: afterRecord?.activeObservations,
|
|
4387
|
-
|
|
4709
|
+
triggeredBy: activationMetadata?.triggeredBy,
|
|
4710
|
+
lastActivityAt: activationMetadata?.lastActivityAt,
|
|
4711
|
+
ttlExpiredMs: activationMetadata?.ttlExpiredMs,
|
|
4712
|
+
previousModel: activationMetadata?.previousModel,
|
|
4713
|
+
currentModel: activationMetadata?.currentModel,
|
|
4714
|
+
config: this.getObservationMarkerConfig(freshRecord)
|
|
4388
4715
|
});
|
|
4389
|
-
void writer.custom(activationMarker).catch(() => {
|
|
4716
|
+
void writer.custom({ ...activationMarker, transient: true }).catch(() => {
|
|
4390
4717
|
});
|
|
4391
4718
|
await this.persistMarkerToMessage(
|
|
4392
4719
|
activationMarker,
|
|
@@ -4396,7 +4723,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4396
4723
|
);
|
|
4397
4724
|
}
|
|
4398
4725
|
BufferingCoordinator.reflectionBufferCycleIds.delete(bufferKey);
|
|
4399
|
-
return
|
|
4726
|
+
return { status: "activated" };
|
|
4400
4727
|
}
|
|
4401
4728
|
/**
|
|
4402
4729
|
* Check if reflection needed and trigger if so.
|
|
@@ -4410,12 +4737,15 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4410
4737
|
writer,
|
|
4411
4738
|
abortSignal,
|
|
4412
4739
|
messageList,
|
|
4740
|
+
currentModel,
|
|
4413
4741
|
reflectionHooks,
|
|
4414
4742
|
requestContext,
|
|
4415
|
-
observabilityContext
|
|
4743
|
+
observabilityContext,
|
|
4744
|
+
lastActivityAt,
|
|
4745
|
+
threadId: requestedThreadId
|
|
4416
4746
|
} = opts;
|
|
4417
4747
|
const lockKey = this.buffering.getLockKey(record.threadId, record.resourceId);
|
|
4418
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
4748
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
4419
4749
|
if (this.buffering.isAsyncReflectionEnabled() && observationTokens < reflectThreshold) {
|
|
4420
4750
|
const shouldTrigger = (() => {
|
|
4421
4751
|
if (!this.buffering.isAsyncReflectionEnabled()) return false;
|
|
@@ -4439,13 +4769,28 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4439
4769
|
lockKey,
|
|
4440
4770
|
writer,
|
|
4441
4771
|
requestContext,
|
|
4442
|
-
observabilityContext
|
|
4772
|
+
observabilityContext,
|
|
4773
|
+
reflectionHooks
|
|
4443
4774
|
);
|
|
4444
4775
|
}
|
|
4445
4776
|
}
|
|
4446
|
-
|
|
4777
|
+
const activateAfterIdle = this.reflectionConfig.activateAfterIdle;
|
|
4778
|
+
const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
|
|
4779
|
+
const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
|
|
4780
|
+
const actorModel = getCurrentModel(currentModel);
|
|
4781
|
+
const lastModel = getLastModelFromMessageList(messageList);
|
|
4782
|
+
const providerChanged = this.reflectionConfig.activateOnProviderChange === true && didProviderChange(actorModel, lastModel);
|
|
4783
|
+
if (observationTokens < reflectThreshold && !ttlExpired && !providerChanged) {
|
|
4447
4784
|
return;
|
|
4448
4785
|
}
|
|
4786
|
+
const activationTriggeredBy = observationTokens >= reflectThreshold ? "threshold" : providerChanged ? "provider_change" : "ttl";
|
|
4787
|
+
const activationMetadata = {
|
|
4788
|
+
triggeredBy: activationTriggeredBy,
|
|
4789
|
+
lastActivityAt: activationTriggeredBy === "ttl" ? lastActivityAt : void 0,
|
|
4790
|
+
ttlExpiredMs: activationTriggeredBy === "ttl" ? ttlExpiredMs : void 0,
|
|
4791
|
+
previousModel: activationTriggeredBy === "provider_change" ? lastModel : void 0,
|
|
4792
|
+
currentModel: activationTriggeredBy === "provider_change" ? actorModel : void 0
|
|
4793
|
+
};
|
|
4449
4794
|
if (record.isReflecting) {
|
|
4450
4795
|
if (isOpActiveInProcess(record.id, "reflecting")) {
|
|
4451
4796
|
omDebug(`[OM:reflect] isReflecting=true and active in this process, skipping`);
|
|
@@ -4455,8 +4800,20 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4455
4800
|
await this.storage.setReflectingFlag(record.id, false);
|
|
4456
4801
|
}
|
|
4457
4802
|
if (this.buffering.isAsyncReflectionEnabled()) {
|
|
4458
|
-
const
|
|
4459
|
-
|
|
4803
|
+
const activationResult = await this.tryActivateBufferedReflection(
|
|
4804
|
+
record,
|
|
4805
|
+
lockKey,
|
|
4806
|
+
writer,
|
|
4807
|
+
messageList,
|
|
4808
|
+
activationMetadata
|
|
4809
|
+
);
|
|
4810
|
+
if (activationResult.status === "activated") {
|
|
4811
|
+
return;
|
|
4812
|
+
}
|
|
4813
|
+
if (activationResult.status === "suppressed") {
|
|
4814
|
+
omDebug(
|
|
4815
|
+
`[OM:reflect] skipping sync fallback / re-buffer after suppressed early ${activationMetadata.triggeredBy} activation (reason=${activationResult.reason})`
|
|
4816
|
+
);
|
|
4460
4817
|
return;
|
|
4461
4818
|
}
|
|
4462
4819
|
if (this.reflectionConfig.blockAfter && observationTokens >= this.reflectionConfig.blockAfter) {
|
|
@@ -4464,6 +4821,13 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4464
4821
|
`[OM:reflect] blockAfter exceeded (${observationTokens} >= ${this.reflectionConfig.blockAfter}), falling through to sync reflection`
|
|
4465
4822
|
);
|
|
4466
4823
|
} else {
|
|
4824
|
+
const activationPoint = reflectThreshold * this.reflectionConfig.bufferActivation;
|
|
4825
|
+
if (observationTokens < activationPoint) {
|
|
4826
|
+
omDebug(
|
|
4827
|
+
`[OM:reflect] skipping async reflection \u2014 observationTokens (${observationTokens}) below activation point (${activationPoint}), triggered by ${activationTriggeredBy}`
|
|
4828
|
+
);
|
|
4829
|
+
return;
|
|
4830
|
+
}
|
|
4467
4831
|
omDebug(
|
|
4468
4832
|
`[OM:reflect] async activation failed, no blockAfter or below it (obsTokens=${observationTokens}, blockAfter=${this.reflectionConfig.blockAfter}) \u2014 starting background reflection`
|
|
4469
4833
|
);
|
|
@@ -4473,7 +4837,8 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4473
4837
|
lockKey,
|
|
4474
4838
|
writer,
|
|
4475
4839
|
requestContext,
|
|
4476
|
-
observabilityContext
|
|
4840
|
+
observabilityContext,
|
|
4841
|
+
reflectionHooks
|
|
4477
4842
|
);
|
|
4478
4843
|
return;
|
|
4479
4844
|
}
|
|
@@ -4483,7 +4848,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4483
4848
|
registerOp(record.id, "reflecting");
|
|
4484
4849
|
const cycleId = crypto.randomUUID();
|
|
4485
4850
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4486
|
-
const threadId =
|
|
4851
|
+
const threadId = requestedThreadId ?? "unknown";
|
|
4487
4852
|
if (writer) {
|
|
4488
4853
|
const startMarker = createObservationStartMarker({
|
|
4489
4854
|
cycleId,
|
|
@@ -4492,10 +4857,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4492
4857
|
recordId: record.id,
|
|
4493
4858
|
threadId,
|
|
4494
4859
|
threadIds: [threadId],
|
|
4495
|
-
config: this.getObservationMarkerConfig()
|
|
4860
|
+
config: this.getObservationMarkerConfig(record)
|
|
4496
4861
|
});
|
|
4497
|
-
await writer.custom(startMarker).catch(() => {
|
|
4862
|
+
await writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
4498
4863
|
});
|
|
4864
|
+
await this.persistMarkerToStorage(startMarker, threadId, record.resourceId ?? void 0);
|
|
4499
4865
|
}
|
|
4500
4866
|
this.emitDebugEvent({
|
|
4501
4867
|
type: "reflection_triggered",
|
|
@@ -4510,8 +4876,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4510
4876
|
cycleId,
|
|
4511
4877
|
startedAt,
|
|
4512
4878
|
recordId: record.id,
|
|
4513
|
-
threadId
|
|
4879
|
+
threadId,
|
|
4880
|
+
resourceId: record.resourceId ?? void 0
|
|
4514
4881
|
} : void 0;
|
|
4882
|
+
let reflectionUsage;
|
|
4883
|
+
let reflectionError;
|
|
4515
4884
|
try {
|
|
4516
4885
|
const compressionStartLevel = await this.getCompressionStartLevel(requestContext);
|
|
4517
4886
|
const reflectResult = await this.call(
|
|
@@ -4525,6 +4894,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4525
4894
|
requestContext,
|
|
4526
4895
|
observabilityContext
|
|
4527
4896
|
);
|
|
4897
|
+
reflectionUsage = reflectResult.usage;
|
|
4528
4898
|
const reflectionTokenCount = this.tokenCounter.countObservations(reflectResult.observations);
|
|
4529
4899
|
await this.storage.createReflectionGeneration({
|
|
4530
4900
|
currentRecord: record,
|
|
@@ -4542,8 +4912,9 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4542
4912
|
recordId: record.id,
|
|
4543
4913
|
threadId
|
|
4544
4914
|
});
|
|
4545
|
-
await writer.custom(endMarker).catch(() => {
|
|
4915
|
+
await writer.custom({ ...endMarker, transient: true }).catch(() => {
|
|
4546
4916
|
});
|
|
4917
|
+
await this.persistMarkerToStorage(endMarker, threadId, record.resourceId ?? void 0);
|
|
4547
4918
|
}
|
|
4548
4919
|
this.emitDebugEvent({
|
|
4549
4920
|
type: "reflection_complete",
|
|
@@ -4566,89 +4937,413 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4566
4937
|
recordId: record.id,
|
|
4567
4938
|
threadId
|
|
4568
4939
|
});
|
|
4569
|
-
await writer.custom(failedMarker).catch(() => {
|
|
4940
|
+
await writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
4570
4941
|
});
|
|
4942
|
+
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
|
|
4571
4943
|
}
|
|
4944
|
+
reflectionError = error instanceof Error ? error : new Error(String(error));
|
|
4572
4945
|
if (abortSignal?.aborted) {
|
|
4573
4946
|
throw error;
|
|
4574
4947
|
}
|
|
4575
4948
|
omError("[OM] Reflection failed", error);
|
|
4576
4949
|
} finally {
|
|
4577
4950
|
await this.storage.setReflectingFlag(record.id, false);
|
|
4578
|
-
reflectionHooks?.onReflectionEnd?.();
|
|
4951
|
+
reflectionHooks?.onReflectionEnd?.({ usage: reflectionUsage, error: reflectionError });
|
|
4579
4952
|
unregisterOp(record.id, "reflecting");
|
|
4580
4953
|
}
|
|
4581
4954
|
}
|
|
4582
4955
|
};
|
|
4583
|
-
|
|
4584
|
-
"
|
|
4585
|
-
"jpg",
|
|
4586
|
-
"jpeg",
|
|
4587
|
-
"webp",
|
|
4588
|
-
"gif",
|
|
4589
|
-
"bmp",
|
|
4590
|
-
"tiff",
|
|
4591
|
-
"tif",
|
|
4592
|
-
"heic",
|
|
4593
|
-
"heif",
|
|
4594
|
-
"avif"
|
|
4595
|
-
]);
|
|
4596
|
-
var TOKEN_ESTIMATE_CACHE_VERSION = 6;
|
|
4597
|
-
var DEFAULT_IMAGE_ESTIMATOR = {
|
|
4598
|
-
baseTokens: 85,
|
|
4599
|
-
tileTokens: 170,
|
|
4600
|
-
fallbackTiles: 4
|
|
4601
|
-
};
|
|
4602
|
-
var GOOGLE_LEGACY_IMAGE_TOKENS_PER_TILE = 258;
|
|
4603
|
-
var GOOGLE_GEMINI_3_IMAGE_TOKENS_BY_RESOLUTION = {
|
|
4604
|
-
low: 280,
|
|
4605
|
-
medium: 560,
|
|
4606
|
-
high: 1120,
|
|
4607
|
-
ultra_high: 2240,
|
|
4608
|
-
unspecified: 1120
|
|
4609
|
-
};
|
|
4610
|
-
var ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
4611
|
-
var ANTHROPIC_IMAGE_MAX_LONG_EDGE = 1568;
|
|
4612
|
-
var GOOGLE_MEDIA_RESOLUTION_VALUES = /* @__PURE__ */ new Set([
|
|
4613
|
-
"low",
|
|
4614
|
-
"medium",
|
|
4615
|
-
"high",
|
|
4616
|
-
"ultra_high",
|
|
4617
|
-
"unspecified"
|
|
4618
|
-
]);
|
|
4619
|
-
var ATTACHMENT_COUNT_TIMEOUT_MS = 2e4;
|
|
4620
|
-
var REMOTE_IMAGE_PROBE_TIMEOUT_MS = 2500;
|
|
4621
|
-
var PROVIDER_API_KEY_ENV_VARS = {
|
|
4622
|
-
openai: ["OPENAI_API_KEY"],
|
|
4623
|
-
google: ["GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY"],
|
|
4624
|
-
anthropic: ["ANTHROPIC_API_KEY"]
|
|
4625
|
-
};
|
|
4626
|
-
function getPartMastraMetadata(part) {
|
|
4627
|
-
return part.providerMetadata?.mastra;
|
|
4956
|
+
function getOmReproCaptureDir() {
|
|
4957
|
+
return process.env.OM_REPRO_CAPTURE_DIR ?? ".mastra-om-repro";
|
|
4628
4958
|
}
|
|
4629
|
-
function
|
|
4630
|
-
const
|
|
4631
|
-
|
|
4632
|
-
typedPart.providerMetadata.mastra ??= {};
|
|
4633
|
-
return typedPart.providerMetadata.mastra;
|
|
4959
|
+
function sanitizeCapturePathSegment(value) {
|
|
4960
|
+
const sanitized = value.replace(/[\\/]+/g, "_").replace(/\.{2,}/g, "_").trim();
|
|
4961
|
+
return sanitized.length > 0 ? sanitized : "unknown-thread";
|
|
4634
4962
|
}
|
|
4635
|
-
function
|
|
4636
|
-
|
|
4637
|
-
|
|
4963
|
+
function isOmReproCaptureEnabled() {
|
|
4964
|
+
return process.env.OM_REPRO_CAPTURE === "1";
|
|
4965
|
+
}
|
|
4966
|
+
function safeCaptureJson(value) {
|
|
4967
|
+
return JSON.parse(
|
|
4968
|
+
JSON.stringify(value, (_key, current) => {
|
|
4969
|
+
if (typeof current === "bigint") return current.toString();
|
|
4970
|
+
if (typeof current === "function") return "[function]";
|
|
4971
|
+
if (typeof current === "symbol") return current.toString();
|
|
4972
|
+
if (current instanceof Error) return { name: current.name, message: current.message, stack: current.stack };
|
|
4973
|
+
if (current instanceof Set) return { __type: "Set", values: Array.from(current.values()) };
|
|
4974
|
+
if (current instanceof Map) return { __type: "Map", entries: Array.from(current.entries()) };
|
|
4975
|
+
return current;
|
|
4976
|
+
})
|
|
4977
|
+
);
|
|
4978
|
+
}
|
|
4979
|
+
function safeCaptureJsonOrError(value) {
|
|
4980
|
+
try {
|
|
4981
|
+
return { ok: true, value: safeCaptureJson(value) };
|
|
4982
|
+
} catch (error) {
|
|
4983
|
+
return {
|
|
4984
|
+
ok: false,
|
|
4985
|
+
error: safeCaptureJson({
|
|
4986
|
+
message: error instanceof Error ? error.message : String(error),
|
|
4987
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
4988
|
+
inspected: util.inspect(value, { depth: 3, maxArrayLength: 20, breakLength: 120 })
|
|
4989
|
+
})
|
|
4990
|
+
};
|
|
4638
4991
|
}
|
|
4639
|
-
return content.metadata?.mastra;
|
|
4640
4992
|
}
|
|
4641
|
-
function
|
|
4642
|
-
if (!
|
|
4993
|
+
function formatCaptureDate(value) {
|
|
4994
|
+
if (!value) return void 0;
|
|
4995
|
+
if (value instanceof Date) return value.toISOString();
|
|
4996
|
+
try {
|
|
4997
|
+
return new Date(value).toISOString();
|
|
4998
|
+
} catch {
|
|
4643
4999
|
return void 0;
|
|
4644
5000
|
}
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
}
|
|
4650
|
-
|
|
4651
|
-
return
|
|
5001
|
+
}
|
|
5002
|
+
function summarizeOmTurn(value) {
|
|
5003
|
+
if (!value || typeof value !== "object") {
|
|
5004
|
+
return value;
|
|
5005
|
+
}
|
|
5006
|
+
const turn = value;
|
|
5007
|
+
return {
|
|
5008
|
+
__type: "ObservationTurn",
|
|
5009
|
+
threadId: turn.threadId,
|
|
5010
|
+
resourceId: turn.resourceId,
|
|
5011
|
+
started: turn._started,
|
|
5012
|
+
ended: turn._ended,
|
|
5013
|
+
generationCountAtStart: turn._generationCountAtStart,
|
|
5014
|
+
record: turn._record ? {
|
|
5015
|
+
id: turn._record.id,
|
|
5016
|
+
scope: turn._record.scope,
|
|
5017
|
+
threadId: turn._record.threadId,
|
|
5018
|
+
resourceId: turn._record.resourceId,
|
|
5019
|
+
createdAt: formatCaptureDate(turn._record.createdAt),
|
|
5020
|
+
updatedAt: formatCaptureDate(turn._record.updatedAt),
|
|
5021
|
+
lastObservedAt: formatCaptureDate(turn._record.lastObservedAt),
|
|
5022
|
+
generationCount: turn._record.generationCount,
|
|
5023
|
+
observationTokenCount: turn._record.observationTokenCount,
|
|
5024
|
+
pendingMessageTokens: turn._record.pendingMessageTokens,
|
|
5025
|
+
isBufferingObservation: turn._record.isBufferingObservation,
|
|
5026
|
+
isBufferingReflection: turn._record.isBufferingReflection
|
|
5027
|
+
} : void 0,
|
|
5028
|
+
context: turn._context ? {
|
|
5029
|
+
messageCount: Array.isArray(turn._context.messages) ? turn._context.messages.length : void 0,
|
|
5030
|
+
hasSystemMessage: Array.isArray(turn._context.systemMessage) ? turn._context.systemMessage.length > 0 : Boolean(turn._context.systemMessage),
|
|
5031
|
+
continuationId: turn._context.continuation?.id,
|
|
5032
|
+
hasOtherThreadsContext: Boolean(turn._context.otherThreadsContext),
|
|
5033
|
+
recordId: turn._context.record?.id
|
|
5034
|
+
} : void 0,
|
|
5035
|
+
currentStep: turn._currentStep ? {
|
|
5036
|
+
stepNumber: turn._currentStep.stepNumber,
|
|
5037
|
+
prepared: turn._currentStep._prepared,
|
|
5038
|
+
context: turn._currentStep._context ? {
|
|
5039
|
+
activated: turn._currentStep._context.activated,
|
|
5040
|
+
observed: turn._currentStep._context.observed,
|
|
5041
|
+
buffered: turn._currentStep._context.buffered,
|
|
5042
|
+
reflected: turn._currentStep._context.reflected,
|
|
5043
|
+
didThresholdCleanup: turn._currentStep._context.didThresholdCleanup,
|
|
5044
|
+
messageCount: Array.isArray(turn._currentStep._context.messages) ? turn._currentStep._context.messages.length : void 0,
|
|
5045
|
+
systemMessageCount: Array.isArray(turn._currentStep._context.systemMessage) ? turn._currentStep._context.systemMessage.length : void 0
|
|
5046
|
+
} : void 0
|
|
5047
|
+
} : void 0
|
|
5048
|
+
};
|
|
5049
|
+
}
|
|
5050
|
+
function sanitizeCaptureState(rawState) {
|
|
5051
|
+
return Object.fromEntries(
|
|
5052
|
+
Object.entries(rawState).map(([key, value]) => {
|
|
5053
|
+
if (key === "__omTurn") {
|
|
5054
|
+
return [key, summarizeOmTurn(value)];
|
|
5055
|
+
}
|
|
5056
|
+
return [key, value];
|
|
5057
|
+
})
|
|
5058
|
+
);
|
|
5059
|
+
}
|
|
5060
|
+
function buildReproMessageFingerprint(message) {
|
|
5061
|
+
const createdAt = message.createdAt instanceof Date ? message.createdAt.toISOString() : message.createdAt ? new Date(message.createdAt).toISOString() : "";
|
|
5062
|
+
return JSON.stringify({
|
|
5063
|
+
role: message.role,
|
|
5064
|
+
createdAt,
|
|
5065
|
+
content: message.content
|
|
5066
|
+
});
|
|
5067
|
+
}
|
|
5068
|
+
function inferReproIdRemap(preMessages, postMessages) {
|
|
5069
|
+
const preByFingerprint = /* @__PURE__ */ new Map();
|
|
5070
|
+
const postByFingerprint = /* @__PURE__ */ new Map();
|
|
5071
|
+
for (const message of preMessages) {
|
|
5072
|
+
if (!message.id) continue;
|
|
5073
|
+
const fingerprint = buildReproMessageFingerprint(message);
|
|
5074
|
+
const list = preByFingerprint.get(fingerprint) ?? [];
|
|
5075
|
+
list.push(message.id);
|
|
5076
|
+
preByFingerprint.set(fingerprint, list);
|
|
5077
|
+
}
|
|
5078
|
+
for (const message of postMessages) {
|
|
5079
|
+
if (!message.id) continue;
|
|
5080
|
+
const fingerprint = buildReproMessageFingerprint(message);
|
|
5081
|
+
const list = postByFingerprint.get(fingerprint) ?? [];
|
|
5082
|
+
list.push(message.id);
|
|
5083
|
+
postByFingerprint.set(fingerprint, list);
|
|
5084
|
+
}
|
|
5085
|
+
const remap = [];
|
|
5086
|
+
for (const [fingerprint, preIds] of preByFingerprint.entries()) {
|
|
5087
|
+
const postIds = postByFingerprint.get(fingerprint);
|
|
5088
|
+
if (!postIds || preIds.length !== 1 || postIds.length !== 1) continue;
|
|
5089
|
+
const fromId = preIds[0];
|
|
5090
|
+
const toId = postIds[0];
|
|
5091
|
+
if (!fromId || !toId || fromId === toId) {
|
|
5092
|
+
continue;
|
|
5093
|
+
}
|
|
5094
|
+
remap.push({ fromId, toId, fingerprint });
|
|
5095
|
+
}
|
|
5096
|
+
return remap;
|
|
5097
|
+
}
|
|
5098
|
+
function createOmReproCaptureDir(threadId, label) {
|
|
5099
|
+
const sanitizedThreadId = sanitizeCapturePathSegment(threadId);
|
|
5100
|
+
const captureDir = path.join(
|
|
5101
|
+
process.cwd(),
|
|
5102
|
+
getOmReproCaptureDir(),
|
|
5103
|
+
sanitizedThreadId,
|
|
5104
|
+
`${Date.now()}-${label}-${crypto$1.randomUUID()}`
|
|
5105
|
+
);
|
|
5106
|
+
fs.mkdirSync(captureDir, { recursive: true });
|
|
5107
|
+
return captureDir;
|
|
5108
|
+
}
|
|
5109
|
+
function writeObserverExchangeReproCapture(params) {
|
|
5110
|
+
if (!isOmReproCaptureEnabled() || !params.observerExchange) {
|
|
5111
|
+
return;
|
|
5112
|
+
}
|
|
5113
|
+
try {
|
|
5114
|
+
const captureDir = createOmReproCaptureDir(params.threadId, params.label);
|
|
5115
|
+
const payloads = [
|
|
5116
|
+
{
|
|
5117
|
+
fileName: "input.json",
|
|
5118
|
+
data: {
|
|
5119
|
+
threadId: params.threadId,
|
|
5120
|
+
resourceId: params.resourceId,
|
|
5121
|
+
label: params.label
|
|
5122
|
+
}
|
|
5123
|
+
},
|
|
5124
|
+
{
|
|
5125
|
+
fileName: "output.json",
|
|
5126
|
+
data: {
|
|
5127
|
+
details: params.details ?? {}
|
|
5128
|
+
}
|
|
5129
|
+
},
|
|
5130
|
+
{
|
|
5131
|
+
fileName: "observer-exchange.json",
|
|
5132
|
+
data: params.observerExchange
|
|
5133
|
+
}
|
|
5134
|
+
];
|
|
5135
|
+
const captureErrors = [];
|
|
5136
|
+
for (const payload of payloads) {
|
|
5137
|
+
const serialized = safeCaptureJsonOrError(payload.data);
|
|
5138
|
+
if (serialized.ok) {
|
|
5139
|
+
fs.writeFileSync(path.join(captureDir, payload.fileName), `${JSON.stringify(serialized.value, null, 2)}
|
|
5140
|
+
`);
|
|
5141
|
+
continue;
|
|
5142
|
+
}
|
|
5143
|
+
captureErrors.push({ fileName: payload.fileName, error: serialized.error });
|
|
5144
|
+
fs.writeFileSync(
|
|
5145
|
+
path.join(captureDir, payload.fileName),
|
|
5146
|
+
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
5147
|
+
`
|
|
5148
|
+
);
|
|
5149
|
+
}
|
|
5150
|
+
if (captureErrors.length > 0) {
|
|
5151
|
+
fs.writeFileSync(path.join(captureDir, "capture-error.json"), `${JSON.stringify(captureErrors, null, 2)}
|
|
5152
|
+
`);
|
|
5153
|
+
params.debug?.(
|
|
5154
|
+
`[OM:repro-capture] wrote ${params.label} capture with ${captureErrors.length} serialization error(s) to ${captureDir}`
|
|
5155
|
+
);
|
|
5156
|
+
return;
|
|
5157
|
+
}
|
|
5158
|
+
params.debug?.(`[OM:repro-capture] wrote ${params.label} capture to ${captureDir}`);
|
|
5159
|
+
} catch (error) {
|
|
5160
|
+
params.debug?.(`[OM:repro-capture] failed to write ${params.label} capture: ${String(error)}`);
|
|
5161
|
+
}
|
|
5162
|
+
}
|
|
5163
|
+
function writeProcessInputStepReproCapture(params) {
|
|
5164
|
+
if (!isOmReproCaptureEnabled()) {
|
|
5165
|
+
return;
|
|
5166
|
+
}
|
|
5167
|
+
try {
|
|
5168
|
+
const captureDir = createOmReproCaptureDir(params.threadId, `step-${params.stepNumber}`);
|
|
5169
|
+
const contextMessages = params.messageList.get.all.db();
|
|
5170
|
+
const memoryContext = memory.parseMemoryRequestContext(params.args.requestContext);
|
|
5171
|
+
const preMessageIds = new Set(params.preMessages.map((message) => message.id));
|
|
5172
|
+
const postMessageIds = new Set(contextMessages.map((message) => message.id));
|
|
5173
|
+
const removedMessageIds = params.preMessages.map((message) => message.id).filter((id) => Boolean(id) && !postMessageIds.has(id));
|
|
5174
|
+
const addedMessageIds = contextMessages.map((message) => message.id).filter((id) => Boolean(id) && !preMessageIds.has(id));
|
|
5175
|
+
const idRemap = inferReproIdRemap(params.preMessages, contextMessages);
|
|
5176
|
+
const rawState = params.args.state ?? {};
|
|
5177
|
+
const sanitizedState = sanitizeCaptureState(rawState);
|
|
5178
|
+
const payloads = [
|
|
5179
|
+
{
|
|
5180
|
+
fileName: "input.json",
|
|
5181
|
+
data: {
|
|
5182
|
+
stepNumber: params.stepNumber,
|
|
5183
|
+
threadId: params.threadId,
|
|
5184
|
+
resourceId: params.resourceId,
|
|
5185
|
+
readOnly: memoryContext?.memoryConfig?.readOnly,
|
|
5186
|
+
messageCount: contextMessages.length,
|
|
5187
|
+
messageIds: contextMessages.map((message) => message.id),
|
|
5188
|
+
stateKeys: Object.keys(rawState),
|
|
5189
|
+
state: sanitizedState,
|
|
5190
|
+
args: {
|
|
5191
|
+
messages: params.args.messages,
|
|
5192
|
+
steps: params.args.steps,
|
|
5193
|
+
systemMessages: params.args.systemMessages,
|
|
5194
|
+
retryCount: params.args.retryCount,
|
|
5195
|
+
toolChoice: params.args.toolChoice,
|
|
5196
|
+
activeTools: params.args.activeTools,
|
|
5197
|
+
modelSettings: params.args.modelSettings,
|
|
5198
|
+
structuredOutput: params.args.structuredOutput
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
},
|
|
5202
|
+
{
|
|
5203
|
+
fileName: "pre-state.json",
|
|
5204
|
+
data: {
|
|
5205
|
+
record: params.preRecord,
|
|
5206
|
+
bufferedChunks: params.preBufferedChunks,
|
|
5207
|
+
contextTokenCount: params.preContextTokenCount,
|
|
5208
|
+
messages: params.preMessages,
|
|
5209
|
+
messageList: params.preSerializedMessageList
|
|
5210
|
+
}
|
|
5211
|
+
},
|
|
5212
|
+
{
|
|
5213
|
+
fileName: "output.json",
|
|
5214
|
+
data: {
|
|
5215
|
+
details: params.details,
|
|
5216
|
+
messageDiff: {
|
|
5217
|
+
removedMessageIds,
|
|
5218
|
+
addedMessageIds,
|
|
5219
|
+
idRemap
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
},
|
|
5223
|
+
{
|
|
5224
|
+
fileName: "post-state.json",
|
|
5225
|
+
data: {
|
|
5226
|
+
record: params.postRecord,
|
|
5227
|
+
bufferedChunks: params.postBufferedChunks,
|
|
5228
|
+
contextTokenCount: params.postContextTokenCount,
|
|
5229
|
+
messageCount: contextMessages.length,
|
|
5230
|
+
messageIds: contextMessages.map((message) => message.id),
|
|
5231
|
+
messages: contextMessages,
|
|
5232
|
+
messageList: params.messageList.serialize()
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
];
|
|
5236
|
+
const captureErrors = [];
|
|
5237
|
+
for (const payload of payloads) {
|
|
5238
|
+
const serialized = safeCaptureJsonOrError(payload.data);
|
|
5239
|
+
if (serialized.ok) {
|
|
5240
|
+
fs.writeFileSync(path.join(captureDir, payload.fileName), `${JSON.stringify(serialized.value, null, 2)}
|
|
5241
|
+
`);
|
|
5242
|
+
continue;
|
|
5243
|
+
}
|
|
5244
|
+
captureErrors.push({ fileName: payload.fileName, error: serialized.error });
|
|
5245
|
+
fs.writeFileSync(
|
|
5246
|
+
path.join(captureDir, payload.fileName),
|
|
5247
|
+
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
5248
|
+
`
|
|
5249
|
+
);
|
|
5250
|
+
}
|
|
5251
|
+
if (params.observerExchange) {
|
|
5252
|
+
const serialized = safeCaptureJsonOrError(params.observerExchange);
|
|
5253
|
+
if (serialized.ok) {
|
|
5254
|
+
fs.writeFileSync(path.join(captureDir, "observer-exchange.json"), `${JSON.stringify(serialized.value, null, 2)}
|
|
5255
|
+
`);
|
|
5256
|
+
} else {
|
|
5257
|
+
captureErrors.push({ fileName: "observer-exchange.json", error: serialized.error });
|
|
5258
|
+
fs.writeFileSync(
|
|
5259
|
+
path.join(captureDir, "observer-exchange.json"),
|
|
5260
|
+
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
5261
|
+
`
|
|
5262
|
+
);
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
if (captureErrors.length > 0) {
|
|
5266
|
+
fs.writeFileSync(path.join(captureDir, "capture-error.json"), `${JSON.stringify(captureErrors, null, 2)}
|
|
5267
|
+
`);
|
|
5268
|
+
params.debug?.(
|
|
5269
|
+
`[OM:repro-capture] wrote processInputStep capture with ${captureErrors.length} serialization error(s) to ${captureDir}`
|
|
5270
|
+
);
|
|
5271
|
+
return;
|
|
5272
|
+
}
|
|
5273
|
+
params.debug?.(`[OM:repro-capture] wrote processInputStep capture to ${captureDir}`);
|
|
5274
|
+
} catch (error) {
|
|
5275
|
+
params.debug?.(`[OM:repro-capture] failed to write processInputStep capture: ${String(error)}`);
|
|
5276
|
+
}
|
|
5277
|
+
}
|
|
5278
|
+
var IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5279
|
+
"png",
|
|
5280
|
+
"jpg",
|
|
5281
|
+
"jpeg",
|
|
5282
|
+
"webp",
|
|
5283
|
+
"gif",
|
|
5284
|
+
"bmp",
|
|
5285
|
+
"tiff",
|
|
5286
|
+
"tif",
|
|
5287
|
+
"heic",
|
|
5288
|
+
"heif",
|
|
5289
|
+
"avif"
|
|
5290
|
+
]);
|
|
5291
|
+
var TOKEN_ESTIMATE_CACHE_VERSION = 6;
|
|
5292
|
+
var DEFAULT_IMAGE_ESTIMATOR = {
|
|
5293
|
+
baseTokens: 85,
|
|
5294
|
+
tileTokens: 170,
|
|
5295
|
+
fallbackTiles: 4
|
|
5296
|
+
};
|
|
5297
|
+
var GOOGLE_LEGACY_IMAGE_TOKENS_PER_TILE = 258;
|
|
5298
|
+
var GOOGLE_GEMINI_3_IMAGE_TOKENS_BY_RESOLUTION = {
|
|
5299
|
+
low: 280,
|
|
5300
|
+
medium: 560,
|
|
5301
|
+
high: 1120,
|
|
5302
|
+
ultra_high: 2240,
|
|
5303
|
+
unspecified: 1120
|
|
5304
|
+
};
|
|
5305
|
+
var ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
5306
|
+
var ANTHROPIC_IMAGE_MAX_LONG_EDGE = 1568;
|
|
5307
|
+
var GOOGLE_MEDIA_RESOLUTION_VALUES = /* @__PURE__ */ new Set([
|
|
5308
|
+
"low",
|
|
5309
|
+
"medium",
|
|
5310
|
+
"high",
|
|
5311
|
+
"ultra_high",
|
|
5312
|
+
"unspecified"
|
|
5313
|
+
]);
|
|
5314
|
+
var ATTACHMENT_COUNT_TIMEOUT_MS = 2e4;
|
|
5315
|
+
var REMOTE_IMAGE_PROBE_TIMEOUT_MS = 2500;
|
|
5316
|
+
var PROVIDER_API_KEY_ENV_VARS = {
|
|
5317
|
+
openai: ["OPENAI_API_KEY"],
|
|
5318
|
+
google: ["GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY"],
|
|
5319
|
+
anthropic: ["ANTHROPIC_API_KEY"]
|
|
5320
|
+
};
|
|
5321
|
+
function getPartMastraMetadata(part) {
|
|
5322
|
+
return part.providerMetadata?.mastra;
|
|
5323
|
+
}
|
|
5324
|
+
function ensurePartMastraMetadata(part) {
|
|
5325
|
+
const typedPart = part;
|
|
5326
|
+
typedPart.providerMetadata ??= {};
|
|
5327
|
+
typedPart.providerMetadata.mastra ??= {};
|
|
5328
|
+
return typedPart.providerMetadata.mastra;
|
|
5329
|
+
}
|
|
5330
|
+
function getContentMastraMetadata(content) {
|
|
5331
|
+
if (!content || typeof content !== "object") {
|
|
5332
|
+
return void 0;
|
|
5333
|
+
}
|
|
5334
|
+
return content.metadata?.mastra;
|
|
5335
|
+
}
|
|
5336
|
+
function ensureContentMastraMetadata(content) {
|
|
5337
|
+
if (!content || typeof content !== "object") {
|
|
5338
|
+
return void 0;
|
|
5339
|
+
}
|
|
5340
|
+
const typedContent = content;
|
|
5341
|
+
typedContent.metadata ??= {};
|
|
5342
|
+
typedContent.metadata.mastra ??= {};
|
|
5343
|
+
return typedContent.metadata.mastra;
|
|
5344
|
+
}
|
|
5345
|
+
function getMessageMastraMetadata(message) {
|
|
5346
|
+
return message.metadata?.mastra;
|
|
4652
5347
|
}
|
|
4653
5348
|
function ensureMessageMastraMetadata(message) {
|
|
4654
5349
|
const typedMessage = message;
|
|
@@ -5807,6 +6502,88 @@ function buildMessageRange(messages) {
|
|
|
5807
6502
|
const last = [...messages].reverse().find(messageHasVisibleContent) ?? messages[messages.length - 1];
|
|
5808
6503
|
return `${first.id}:${last.id}`;
|
|
5809
6504
|
}
|
|
6505
|
+
function getLastActivityFromMessages(messages) {
|
|
6506
|
+
if (!messages) return void 0;
|
|
6507
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6508
|
+
const message = messages[i];
|
|
6509
|
+
if (!message || message.role !== "assistant") {
|
|
6510
|
+
continue;
|
|
6511
|
+
}
|
|
6512
|
+
if (!message.content || typeof message.content === "string") {
|
|
6513
|
+
return message.createdAt ? new Date(message.createdAt).getTime() : void 0;
|
|
6514
|
+
}
|
|
6515
|
+
for (let j = message.content.parts.length - 1; j >= 0; j--) {
|
|
6516
|
+
const part = message.content.parts[j];
|
|
6517
|
+
if (!part || part.type?.startsWith("data-")) {
|
|
6518
|
+
continue;
|
|
6519
|
+
}
|
|
6520
|
+
if (part.createdAt !== void 0) {
|
|
6521
|
+
return part.createdAt;
|
|
6522
|
+
}
|
|
6523
|
+
}
|
|
6524
|
+
return message.createdAt ? new Date(message.createdAt).getTime() : void 0;
|
|
6525
|
+
}
|
|
6526
|
+
return void 0;
|
|
6527
|
+
}
|
|
6528
|
+
function formatModelContext2(provider, modelId) {
|
|
6529
|
+
if (provider && modelId) {
|
|
6530
|
+
return `${provider}/${modelId}`;
|
|
6531
|
+
}
|
|
6532
|
+
return modelId;
|
|
6533
|
+
}
|
|
6534
|
+
function getLastModelFromMessages(messages) {
|
|
6535
|
+
if (!messages) return void 0;
|
|
6536
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6537
|
+
const message = messages[i];
|
|
6538
|
+
if (!message || message.role !== "assistant" || !message.content || typeof message.content === "string") {
|
|
6539
|
+
continue;
|
|
6540
|
+
}
|
|
6541
|
+
for (let j = message.content.parts.length - 1; j >= 0; j--) {
|
|
6542
|
+
const part = message.content.parts[j];
|
|
6543
|
+
if (part?.type === "step-start" && typeof part.model === "string" && part.model.length > 0) {
|
|
6544
|
+
return part.model;
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
const metadata = message.content.metadata;
|
|
6548
|
+
const model = formatModelContext2(metadata?.provider, metadata?.modelId);
|
|
6549
|
+
if (model) {
|
|
6550
|
+
return model;
|
|
6551
|
+
}
|
|
6552
|
+
}
|
|
6553
|
+
return void 0;
|
|
6554
|
+
}
|
|
6555
|
+
function getCurrentModel2(model) {
|
|
6556
|
+
return formatModelContext2(model?.provider, model?.modelId);
|
|
6557
|
+
}
|
|
6558
|
+
function parseActivationTTL(value, fieldPath) {
|
|
6559
|
+
if (value === void 0) {
|
|
6560
|
+
return void 0;
|
|
6561
|
+
}
|
|
6562
|
+
if (typeof value === "number") {
|
|
6563
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
6564
|
+
throw new Error(`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".`);
|
|
6565
|
+
}
|
|
6566
|
+
return value;
|
|
6567
|
+
}
|
|
6568
|
+
const trimmed = value.trim();
|
|
6569
|
+
const match = trimmed.match(
|
|
6570
|
+
/^(\d+(?:\.\d+)?)\s*(ms|msec|msecs|millisecond|milliseconds|s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours)$/i
|
|
6571
|
+
);
|
|
6572
|
+
if (!match) {
|
|
6573
|
+
throw new Error(
|
|
6574
|
+
`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m" or "1hr".`
|
|
6575
|
+
);
|
|
6576
|
+
}
|
|
6577
|
+
const rawAmount = match[1];
|
|
6578
|
+
const rawUnit = match[2];
|
|
6579
|
+
const amount = Number(rawAmount);
|
|
6580
|
+
const unit = rawUnit.toLowerCase();
|
|
6581
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
6582
|
+
throw new Error(`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".`);
|
|
6583
|
+
}
|
|
6584
|
+
const multiplier = unit === "ms" || unit === "msec" || unit === "msecs" || unit === "millisecond" || unit === "milliseconds" ? 1 : unit === "s" || unit === "sec" || unit === "secs" || unit === "second" || unit === "seconds" ? 1e3 : unit === "m" || unit === "min" || unit === "mins" || unit === "minute" || unit === "minutes" ? 6e4 : 36e5;
|
|
6585
|
+
return amount * multiplier;
|
|
6586
|
+
}
|
|
5810
6587
|
var ObservationalMemory = class _ObservationalMemory {
|
|
5811
6588
|
storage;
|
|
5812
6589
|
tokenCounter;
|
|
@@ -5953,6 +6730,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
5953
6730
|
config.observation?.messageTokens ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
|
|
5954
6731
|
),
|
|
5955
6732
|
bufferActivation: asyncBufferingDisabled ? void 0 : config.observation?.bufferActivation ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferActivation,
|
|
6733
|
+
activateAfterIdle: parseActivationTTL(config.activateAfterIdle, "activateAfterIdle"),
|
|
6734
|
+
activateOnProviderChange: config.activateOnProviderChange ?? false,
|
|
5956
6735
|
blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
|
|
5957
6736
|
config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0),
|
|
5958
6737
|
config.observation?.messageTokens ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
|
|
@@ -5971,6 +6750,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
5971
6750
|
},
|
|
5972
6751
|
providerOptions: config.reflection?.providerOptions ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.reflection.providerOptions,
|
|
5973
6752
|
bufferActivation: asyncBufferingDisabled ? void 0 : config?.reflection?.bufferActivation ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation,
|
|
6753
|
+
activateAfterIdle: parseActivationTTL(config.activateAfterIdle, "activateAfterIdle"),
|
|
6754
|
+
activateOnProviderChange: config.activateOnProviderChange ?? false,
|
|
5974
6755
|
blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
|
|
5975
6756
|
config.reflection?.blockAfter ?? (config.reflection?.bufferActivation ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation ? 1.2 : void 0),
|
|
5976
6757
|
config.reflection?.observationTokens ?? chunkD4J4XPGM_cjs.OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens
|
|
@@ -6251,6 +7032,47 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
6251
7032
|
}
|
|
6252
7033
|
}
|
|
6253
7034
|
}
|
|
7035
|
+
/**
|
|
7036
|
+
* Resolve the effective messageTokens for a record.
|
|
7037
|
+
* Only explicit per-record overrides (stored under `_overrides`) win;
|
|
7038
|
+
* the initial config snapshot written by getOrCreateRecord() is ignored
|
|
7039
|
+
* so that later instance-level changes still take effect.
|
|
7040
|
+
*
|
|
7041
|
+
* Overrides that fall below the instance-level buffering floor
|
|
7042
|
+
* (bufferTokens / absolute bufferActivation) are clamped to the
|
|
7043
|
+
* instance threshold to preserve buffering invariants.
|
|
7044
|
+
*/
|
|
7045
|
+
getEffectiveMessageTokens(record) {
|
|
7046
|
+
const overrides = record.config?._overrides;
|
|
7047
|
+
const recordTokens = overrides?.observation?.messageTokens;
|
|
7048
|
+
if (recordTokens) {
|
|
7049
|
+
const maxOverride = getMaxThreshold(recordTokens);
|
|
7050
|
+
const bufferTokens = this.observationConfig.bufferTokens;
|
|
7051
|
+
if (bufferTokens && maxOverride <= bufferTokens) {
|
|
7052
|
+
return this.observationConfig.messageTokens;
|
|
7053
|
+
}
|
|
7054
|
+
const bufferActivation = this.observationConfig.bufferActivation;
|
|
7055
|
+
if (bufferActivation && bufferActivation >= 1e3 && maxOverride <= bufferActivation) {
|
|
7056
|
+
return this.observationConfig.messageTokens;
|
|
7057
|
+
}
|
|
7058
|
+
return recordTokens;
|
|
7059
|
+
}
|
|
7060
|
+
return this.observationConfig.messageTokens;
|
|
7061
|
+
}
|
|
7062
|
+
/**
|
|
7063
|
+
* Resolve the effective reflection observationTokens for a record.
|
|
7064
|
+
* Only explicit per-record overrides (stored under `_overrides`) win;
|
|
7065
|
+
* the initial config snapshot is ignored so instance-level changes
|
|
7066
|
+
* still take effect for existing records.
|
|
7067
|
+
*/
|
|
7068
|
+
getEffectiveReflectionTokens(record) {
|
|
7069
|
+
const overrides = record.config?._overrides;
|
|
7070
|
+
const recordTokens = overrides?.reflection?.observationTokens;
|
|
7071
|
+
if (recordTokens) {
|
|
7072
|
+
return recordTokens;
|
|
7073
|
+
}
|
|
7074
|
+
return this.reflectionConfig.observationTokens;
|
|
7075
|
+
}
|
|
6254
7076
|
/**
|
|
6255
7077
|
* Check whether the unobserved message tokens meet the observation threshold.
|
|
6256
7078
|
*/
|
|
@@ -6258,7 +7080,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
6258
7080
|
const { record, unobservedTokens, extraTokens = 0 } = opts;
|
|
6259
7081
|
const pendingTokens = (record.pendingMessageTokens ?? 0) + unobservedTokens + extraTokens;
|
|
6260
7082
|
const currentObservationTokens = record.observationTokenCount ?? 0;
|
|
6261
|
-
const threshold = calculateDynamicThreshold(this.
|
|
7083
|
+
const threshold = calculateDynamicThreshold(this.getEffectiveMessageTokens(record), currentObservationTokens);
|
|
6262
7084
|
return pendingTokens >= threshold;
|
|
6263
7085
|
}
|
|
6264
7086
|
/**
|
|
@@ -6324,7 +7146,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
6324
7146
|
return {
|
|
6325
7147
|
messageTokens: getMaxThreshold(this.observationConfig.messageTokens),
|
|
6326
7148
|
observationTokens: getMaxThreshold(this.reflectionConfig.observationTokens),
|
|
6327
|
-
scope: this.scope
|
|
7149
|
+
scope: this.scope,
|
|
7150
|
+
activateAfterIdle: this.observationConfig.activateAfterIdle
|
|
6328
7151
|
};
|
|
6329
7152
|
}
|
|
6330
7153
|
/**
|
|
@@ -7045,17 +7868,18 @@ ${grouped}` : grouped;
|
|
|
7045
7868
|
const cycleId = `buffer-obs-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
7046
7869
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7047
7870
|
const tokensToBuffer = await this.tokenCounter.countMessagesAsync(messagesToBuffer);
|
|
7871
|
+
const startMarker = createBufferingStartMarker({
|
|
7872
|
+
cycleId,
|
|
7873
|
+
operationType: "observation",
|
|
7874
|
+
tokensToBuffer,
|
|
7875
|
+
recordId: freshRecord.id,
|
|
7876
|
+
threadId,
|
|
7877
|
+
threadIds: [threadId],
|
|
7878
|
+
config: this.getObservationMarkerConfig()
|
|
7879
|
+
});
|
|
7880
|
+
await this.persistMarkerToStorage(startMarker, threadId, freshRecord.resourceId ?? void 0);
|
|
7048
7881
|
if (writer) {
|
|
7049
|
-
|
|
7050
|
-
cycleId,
|
|
7051
|
-
operationType: "observation",
|
|
7052
|
-
tokensToBuffer,
|
|
7053
|
-
recordId: freshRecord.id,
|
|
7054
|
-
threadId,
|
|
7055
|
-
threadIds: [threadId],
|
|
7056
|
-
config: this.getObservationMarkerConfig()
|
|
7057
|
-
});
|
|
7058
|
-
void writer.custom(startMarker).catch(() => {
|
|
7882
|
+
void writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
7059
7883
|
});
|
|
7060
7884
|
}
|
|
7061
7885
|
omDebug(
|
|
@@ -7351,10 +8175,12 @@ ${grouped}` : grouped;
|
|
|
7351
8175
|
async getOtherThreadsContext(resourceId, currentThreadId) {
|
|
7352
8176
|
const { threads: allThreads } = await this.storage.listThreads({ filter: { resourceId } });
|
|
7353
8177
|
const messagesByThread = /* @__PURE__ */ new Map();
|
|
8178
|
+
const record = await this.getRecord(currentThreadId, resourceId);
|
|
8179
|
+
const recordLastObservedAt = record?.lastObservedAt;
|
|
7354
8180
|
for (const thread of allThreads) {
|
|
7355
8181
|
if (thread.id === currentThreadId) continue;
|
|
7356
8182
|
const omMetadata = memory.getThreadOMMetadata(thread.metadata);
|
|
7357
|
-
const threadLastObservedAt = omMetadata?.lastObservedAt;
|
|
8183
|
+
const threadLastObservedAt = omMetadata?.lastObservedAt ?? recordLastObservedAt;
|
|
7358
8184
|
const startDate = threadLastObservedAt ? new Date(new Date(threadLastObservedAt).getTime() + 1) : void 0;
|
|
7359
8185
|
const result = await this.storage.listMessages({
|
|
7360
8186
|
threadId: thread.id,
|
|
@@ -7407,7 +8233,7 @@ ${grouped}` : grouped;
|
|
|
7407
8233
|
const projectedMessageRemoval = calculateProjectedMessageRemoval(
|
|
7408
8234
|
bufferedChunks,
|
|
7409
8235
|
this.observationConfig.bufferActivation ?? 1,
|
|
7410
|
-
getMaxThreshold(this.
|
|
8236
|
+
getMaxThreshold(this.getEffectiveMessageTokens(record)),
|
|
7411
8237
|
pendingTokens
|
|
7412
8238
|
);
|
|
7413
8239
|
let obsBufferStatus = "idle";
|
|
@@ -7496,7 +8322,7 @@ ${grouped}` : grouped;
|
|
|
7496
8322
|
otherThreadTokens = otherContext ? this.tokenCounter.countString(otherContext) : 0;
|
|
7497
8323
|
}
|
|
7498
8324
|
const pendingTokens = Math.max(0, contextWindowTokens + otherThreadTokens);
|
|
7499
|
-
const threshold = calculateDynamicThreshold(this.
|
|
8325
|
+
const threshold = calculateDynamicThreshold(this.getEffectiveMessageTokens(record), currentObservationTokens);
|
|
7500
8326
|
const bufferedChunks = getBufferedChunks(record);
|
|
7501
8327
|
const bufferedChunkCount = bufferedChunks.length;
|
|
7502
8328
|
const bufferedChunkTokens = bufferedChunks.reduce((sum, chunk) => sum + (chunk.messageTokens ?? 0), 0);
|
|
@@ -7513,11 +8339,12 @@ ${grouped}` : grouped;
|
|
|
7513
8339
|
);
|
|
7514
8340
|
}
|
|
7515
8341
|
const shouldObserve = pendingTokens >= threshold;
|
|
7516
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
8342
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
7517
8343
|
const shouldReflect = currentObservationTokens >= reflectThreshold;
|
|
7518
8344
|
const canActivate = bufferedChunkCount > 0;
|
|
7519
|
-
const
|
|
7520
|
-
const
|
|
8345
|
+
const effectiveMessageTokens = this.getEffectiveMessageTokens(record);
|
|
8346
|
+
const isSharedBudget = typeof effectiveMessageTokens !== "number";
|
|
8347
|
+
const totalBudget = isSharedBudget ? effectiveMessageTokens.max : 0;
|
|
7521
8348
|
const effectiveObservationTokensThreshold = isSharedBudget ? Math.max(totalBudget - threshold, 1e3) : reflectThreshold;
|
|
7522
8349
|
const unbufferedPendingTokens = Math.max(0, pendingTokens - bufferedChunkTokens);
|
|
7523
8350
|
return {
|
|
@@ -7561,7 +8388,7 @@ ${grouped}` : grouped;
|
|
|
7561
8388
|
await BufferingCoordinator.awaitBuffering(threadId, resourceId ?? null, this.scope);
|
|
7562
8389
|
const preStatus = await this.getStatus({ threadId, resourceId, messages });
|
|
7563
8390
|
if (preStatus.canActivate) {
|
|
7564
|
-
const actResult = await this.activate({ threadId, resourceId });
|
|
8391
|
+
const actResult = await this.activate({ threadId, resourceId, messages });
|
|
7565
8392
|
activated = actResult.activated;
|
|
7566
8393
|
}
|
|
7567
8394
|
const postStatus = await this.getStatus({ threadId, resourceId, messages });
|
|
@@ -7712,18 +8539,19 @@ ${grouped}` : grouped;
|
|
|
7712
8539
|
}
|
|
7713
8540
|
const cycleId = `buffer-obs-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
7714
8541
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8542
|
+
const startMarker = createBufferingStartMarker({
|
|
8543
|
+
cycleId,
|
|
8544
|
+
operationType: "observation",
|
|
8545
|
+
tokensToBuffer: newTokens,
|
|
8546
|
+
recordId: record.id,
|
|
8547
|
+
threadId,
|
|
8548
|
+
threadIds: [threadId],
|
|
8549
|
+
config: this.getObservationMarkerConfig()
|
|
8550
|
+
});
|
|
8551
|
+
await this.persistMarkerToStorage(startMarker, threadId, record.resourceId ?? void 0);
|
|
7715
8552
|
const writer = opts.writer;
|
|
7716
8553
|
if (writer) {
|
|
7717
|
-
|
|
7718
|
-
cycleId,
|
|
7719
|
-
operationType: "observation",
|
|
7720
|
-
tokensToBuffer: newTokens,
|
|
7721
|
-
recordId: record.id,
|
|
7722
|
-
threadId,
|
|
7723
|
-
threadIds: [threadId],
|
|
7724
|
-
config: this.getObservationMarkerConfig()
|
|
7725
|
-
});
|
|
7726
|
-
void writer.custom(startMarker).catch(() => {
|
|
8554
|
+
void writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
7727
8555
|
});
|
|
7728
8556
|
}
|
|
7729
8557
|
await ObservationStrategy.create(this, {
|
|
@@ -7737,6 +8565,23 @@ ${grouped}` : grouped;
|
|
|
7737
8565
|
requestContext,
|
|
7738
8566
|
observabilityContext
|
|
7739
8567
|
}).run();
|
|
8568
|
+
if (isOmReproCaptureEnabled()) {
|
|
8569
|
+
writeObserverExchangeReproCapture({
|
|
8570
|
+
threadId,
|
|
8571
|
+
resourceId: record.resourceId ?? void 0,
|
|
8572
|
+
label: `buffer-${cycleId}`,
|
|
8573
|
+
observerExchange: this.observer.lastExchange,
|
|
8574
|
+
details: {
|
|
8575
|
+
cycleId,
|
|
8576
|
+
startedAt,
|
|
8577
|
+
buffered: true,
|
|
8578
|
+
candidateMessageIds: candidateMessages.map((message) => message.id),
|
|
8579
|
+
candidateMessageCount: candidateMessages.length,
|
|
8580
|
+
pendingTokens: currentTokens,
|
|
8581
|
+
newTokens
|
|
8582
|
+
}
|
|
8583
|
+
});
|
|
8584
|
+
}
|
|
7740
8585
|
await this.storage.setBufferingObservationFlag(record.id, false, newTokens).catch(() => {
|
|
7741
8586
|
});
|
|
7742
8587
|
flagCleared = true;
|
|
@@ -7804,10 +8649,37 @@ ${grouped}` : grouped;
|
|
|
7804
8649
|
if (!chunks.length) {
|
|
7805
8650
|
return { activated: false, record };
|
|
7806
8651
|
}
|
|
8652
|
+
let activationTriggeredBy = "threshold";
|
|
8653
|
+
let activationLastActivityAt;
|
|
8654
|
+
let activateAfterIdleExpiredMs;
|
|
8655
|
+
let previousModel;
|
|
8656
|
+
let currentModel;
|
|
7807
8657
|
if (opts.checkThreshold) {
|
|
7808
|
-
const
|
|
7809
|
-
|
|
7810
|
-
|
|
8658
|
+
const thresholdMessages = opts.messages ?? await this.loadMessagesFromStorage(
|
|
8659
|
+
threadId,
|
|
8660
|
+
resourceId,
|
|
8661
|
+
record.lastObservedAt ? new Date(record.lastObservedAt) : void 0
|
|
8662
|
+
);
|
|
8663
|
+
const activateAfterIdle = this.observationConfig.activateAfterIdle;
|
|
8664
|
+
const lastActivityAt = getLastActivityFromMessages(thresholdMessages);
|
|
8665
|
+
const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
|
|
8666
|
+
const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
|
|
8667
|
+
const actorModel = getCurrentModel2(opts.currentModel);
|
|
8668
|
+
const lastModel = getLastModelFromMessages(thresholdMessages);
|
|
8669
|
+
const providerChanged = this.observationConfig.activateOnProviderChange === true && didProviderChange(actorModel, lastModel);
|
|
8670
|
+
if (providerChanged) {
|
|
8671
|
+
activationTriggeredBy = "provider_change";
|
|
8672
|
+
previousModel = lastModel;
|
|
8673
|
+
currentModel = actorModel;
|
|
8674
|
+
} else if (ttlExpired) {
|
|
8675
|
+
activationTriggeredBy = "ttl";
|
|
8676
|
+
activationLastActivityAt = lastActivityAt;
|
|
8677
|
+
activateAfterIdleExpiredMs = ttlExpiredMs;
|
|
8678
|
+
} else {
|
|
8679
|
+
const status = await this.getStatus({ threadId, resourceId, messages: thresholdMessages });
|
|
8680
|
+
if (status.pendingTokens < status.threshold) {
|
|
8681
|
+
return { activated: false, record };
|
|
8682
|
+
}
|
|
7811
8683
|
}
|
|
7812
8684
|
}
|
|
7813
8685
|
if (record.isBufferingObservation) {
|
|
@@ -7832,7 +8704,7 @@ ${grouped}` : grouped;
|
|
|
7832
8704
|
if (!freshChunks.length) {
|
|
7833
8705
|
return { activated: false, record };
|
|
7834
8706
|
}
|
|
7835
|
-
const messageTokensThreshold = getMaxThreshold(this.
|
|
8707
|
+
const messageTokensThreshold = getMaxThreshold(this.getEffectiveMessageTokens(freshRecord));
|
|
7836
8708
|
const bufferActivation = this.observationConfig.bufferActivation ?? 0.7;
|
|
7837
8709
|
const activationRatio = resolveActivationRatio(bufferActivation, messageTokensThreshold);
|
|
7838
8710
|
const totalChunkMessageTokens = freshChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
|
|
@@ -7865,9 +8737,14 @@ ${grouped}` : grouped;
|
|
|
7865
8737
|
threadId: postSwapRecord.threadId ?? record.threadId ?? "",
|
|
7866
8738
|
generationCount: postSwapRecord.generationCount ?? 0,
|
|
7867
8739
|
observations: chunkData?.observations ?? activationResult.observations,
|
|
8740
|
+
triggeredBy: activationTriggeredBy,
|
|
8741
|
+
lastActivityAt: activationLastActivityAt,
|
|
8742
|
+
ttlExpiredMs: activateAfterIdleExpiredMs,
|
|
8743
|
+
previousModel,
|
|
8744
|
+
currentModel,
|
|
7868
8745
|
config: this.getObservationMarkerConfig()
|
|
7869
8746
|
});
|
|
7870
|
-
void opts.writer.custom(activationMarker).catch(() => {
|
|
8747
|
+
void opts.writer.custom({ ...activationMarker, transient: true }).catch(() => {
|
|
7871
8748
|
});
|
|
7872
8749
|
await this.persistMarkerToMessage(
|
|
7873
8750
|
activationMarker,
|
|
@@ -7920,6 +8797,7 @@ ${grouped}` : grouped;
|
|
|
7920
8797
|
const lockKey = this.buffering.getLockKey(threadId, resourceId);
|
|
7921
8798
|
const reflectionHooks = hooks ? { onReflectionStart: hooks.onReflectionStart, onReflectionEnd: hooks.onReflectionEnd } : void 0;
|
|
7922
8799
|
let observed = false;
|
|
8800
|
+
let observationUsage;
|
|
7923
8801
|
let generationBefore = -1;
|
|
7924
8802
|
await this.withLock(lockKey, async () => {
|
|
7925
8803
|
const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
|
|
@@ -7936,8 +8814,9 @@ ${grouped}` : grouped;
|
|
|
7936
8814
|
return;
|
|
7937
8815
|
}
|
|
7938
8816
|
hooks?.onObservationStart?.();
|
|
8817
|
+
let observationError;
|
|
7939
8818
|
try {
|
|
7940
|
-
|
|
8819
|
+
const result = await ObservationStrategy.create(this, {
|
|
7941
8820
|
record: freshRecord,
|
|
7942
8821
|
threadId,
|
|
7943
8822
|
resourceId,
|
|
@@ -7947,8 +8826,13 @@ ${grouped}` : grouped;
|
|
|
7947
8826
|
writer: opts.writer,
|
|
7948
8827
|
observabilityContext: opts.observabilityContext
|
|
7949
8828
|
}).run();
|
|
8829
|
+
observed = result.observed;
|
|
8830
|
+
observationUsage = result.usage;
|
|
8831
|
+
} catch (error) {
|
|
8832
|
+
observationError = error instanceof Error ? error : new Error(String(error));
|
|
8833
|
+
throw error;
|
|
7950
8834
|
} finally {
|
|
7951
|
-
hooks?.onObservationEnd?.();
|
|
8835
|
+
hooks?.onObservationEnd?.({ usage: observationUsage, error: observationError });
|
|
7952
8836
|
}
|
|
7953
8837
|
});
|
|
7954
8838
|
const record = await this.getOrCreateRecord(threadId, resourceId);
|
|
@@ -7969,12 +8853,12 @@ ${grouped}` : grouped;
|
|
|
7969
8853
|
async reflect(threadId, resourceId, prompt, requestContext, observabilityContext) {
|
|
7970
8854
|
const record = await this.getOrCreateRecord(threadId, resourceId);
|
|
7971
8855
|
if (!record.activeObservations) {
|
|
7972
|
-
return { reflected: false, record };
|
|
8856
|
+
return { reflected: false, record, usage: void 0 };
|
|
7973
8857
|
}
|
|
7974
8858
|
await this.storage.setReflectingFlag(record.id, true);
|
|
7975
8859
|
registerOp(record.id, "reflecting");
|
|
7976
8860
|
try {
|
|
7977
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
8861
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
7978
8862
|
const reflectResult = await this.reflector.call(
|
|
7979
8863
|
record.activeObservations,
|
|
7980
8864
|
prompt,
|
|
@@ -7994,11 +8878,11 @@ ${grouped}` : grouped;
|
|
|
7994
8878
|
tokenCount: reflectionTokenCount
|
|
7995
8879
|
});
|
|
7996
8880
|
const updatedRecord = await this.getOrCreateRecord(threadId, resourceId);
|
|
7997
|
-
return { reflected: true, record: updatedRecord };
|
|
8881
|
+
return { reflected: true, record: updatedRecord, usage: reflectResult.usage };
|
|
7998
8882
|
} catch (error) {
|
|
7999
8883
|
omError("[OM] reflect() failed", error);
|
|
8000
8884
|
const latestRecord = await this.getOrCreateRecord(threadId, resourceId);
|
|
8001
|
-
return { reflected: false, record: latestRecord };
|
|
8885
|
+
return { reflected: false, record: latestRecord, usage: void 0 };
|
|
8002
8886
|
} finally {
|
|
8003
8887
|
await this.storage.setReflectingFlag(record.id, false);
|
|
8004
8888
|
unregisterOp(record.id, "reflecting");
|
|
@@ -8019,12 +8903,40 @@ ${grouped}` : grouped;
|
|
|
8019
8903
|
const ids = this.getStorageIds(threadId, resourceId);
|
|
8020
8904
|
return this.storage.getObservationalMemory(ids.threadId, ids.resourceId);
|
|
8021
8905
|
}
|
|
8906
|
+
/**
|
|
8907
|
+
* Update per-record config overrides for observation and/or reflection thresholds.
|
|
8908
|
+
* The provided config is deep-merged into the record's `_overrides` key,
|
|
8909
|
+
* so you only need to specify the fields you want to change.
|
|
8910
|
+
*
|
|
8911
|
+
* Overrides that violate buffering invariants (e.g. messageTokens below
|
|
8912
|
+
* bufferTokens) are silently ignored at read time — the helpers fall back
|
|
8913
|
+
* to the instance-level config.
|
|
8914
|
+
*
|
|
8915
|
+
* @example
|
|
8916
|
+
* ```ts
|
|
8917
|
+
* await om.updateRecordConfig('thread-1', undefined, {
|
|
8918
|
+
* observation: { messageTokens: 2000 },
|
|
8919
|
+
* reflection: { observationTokens: 8000 },
|
|
8920
|
+
* });
|
|
8921
|
+
* ```
|
|
8922
|
+
*/
|
|
8923
|
+
async updateRecordConfig(threadId, resourceId, config) {
|
|
8924
|
+
const ids = this.getStorageIds(threadId, resourceId);
|
|
8925
|
+
const record = await this.storage.getObservationalMemory(ids.threadId, ids.resourceId);
|
|
8926
|
+
if (!record) {
|
|
8927
|
+
throw new Error(`No observational memory record found for thread ${ids.threadId}`);
|
|
8928
|
+
}
|
|
8929
|
+
await this.storage.updateObservationalMemoryConfig({
|
|
8930
|
+
id: record.id,
|
|
8931
|
+
config: { _overrides: config }
|
|
8932
|
+
});
|
|
8933
|
+
}
|
|
8022
8934
|
/**
|
|
8023
8935
|
* Get observation history (previous generations)
|
|
8024
8936
|
*/
|
|
8025
|
-
async getHistory(threadId, resourceId, limit) {
|
|
8937
|
+
async getHistory(threadId, resourceId, limit, options) {
|
|
8026
8938
|
const ids = this.getStorageIds(threadId, resourceId);
|
|
8027
|
-
return this.storage.getObservationalMemoryHistory(ids.threadId, ids.resourceId, limit);
|
|
8939
|
+
return this.storage.getObservationalMemoryHistory(ids.threadId, ids.resourceId, limit, options);
|
|
8028
8940
|
}
|
|
8029
8941
|
/**
|
|
8030
8942
|
* Clear all memory for a specific thread/resource
|
|
@@ -8067,293 +8979,137 @@ ${grouped}` : grouped;
|
|
|
8067
8979
|
/**
|
|
8068
8980
|
* Get whether thread IDs should be obscured in observations.
|
|
8069
8981
|
*/
|
|
8070
|
-
getObscureThreadIds() {
|
|
8071
|
-
return this.shouldObscureThreadIds;
|
|
8072
|
-
}
|
|
8073
|
-
/**
|
|
8074
|
-
* Begin a new observation turn — the high-level API for managing the
|
|
8075
|
-
* observe/buffer/activate/reflect lifecycle across agentic loop steps.
|
|
8076
|
-
*
|
|
8077
|
-
* @example
|
|
8078
|
-
* ```ts
|
|
8079
|
-
* const turn = om.beginTurn({ threadId, resourceId, messageList });
|
|
8080
|
-
* await turn.start(memory);
|
|
8081
|
-
*
|
|
8082
|
-
* const step0 = turn.step(0);
|
|
8083
|
-
* const ctx = await step0.prepare();
|
|
8084
|
-
* // ... agent generates ...
|
|
8085
|
-
*
|
|
8086
|
-
* await turn.end();
|
|
8087
|
-
* ```
|
|
8088
|
-
*/
|
|
8089
|
-
beginTurn(opts) {
|
|
8090
|
-
return new ObservationTurn({
|
|
8091
|
-
om: this,
|
|
8092
|
-
threadId: opts.threadId,
|
|
8093
|
-
resourceId: opts.resourceId,
|
|
8094
|
-
messageList: opts.messageList,
|
|
8095
|
-
observabilityContext: opts.observabilityContext,
|
|
8096
|
-
hooks: opts.hooks
|
|
8097
|
-
});
|
|
8098
|
-
}
|
|
8099
|
-
};
|
|
8100
|
-
var OM_REPRO_CAPTURE_DIR = process.env.OM_REPRO_CAPTURE_DIR ?? ".mastra-om-repro";
|
|
8101
|
-
function sanitizeCapturePathSegment(value) {
|
|
8102
|
-
const sanitized = value.replace(/[\\/]+/g, "_").replace(/\.{2,}/g, "_").trim();
|
|
8103
|
-
return sanitized.length > 0 ? sanitized : "unknown-thread";
|
|
8104
|
-
}
|
|
8105
|
-
function isOmReproCaptureEnabled() {
|
|
8106
|
-
return process.env.OM_REPRO_CAPTURE === "1";
|
|
8107
|
-
}
|
|
8108
|
-
function safeCaptureJson(value) {
|
|
8109
|
-
return JSON.parse(
|
|
8110
|
-
JSON.stringify(value, (_key, current) => {
|
|
8111
|
-
if (typeof current === "bigint") return current.toString();
|
|
8112
|
-
if (typeof current === "function") return "[function]";
|
|
8113
|
-
if (typeof current === "symbol") return current.toString();
|
|
8114
|
-
if (current instanceof Error) return { name: current.name, message: current.message, stack: current.stack };
|
|
8115
|
-
if (current instanceof Set) return { __type: "Set", values: Array.from(current.values()) };
|
|
8116
|
-
if (current instanceof Map) return { __type: "Map", entries: Array.from(current.entries()) };
|
|
8117
|
-
return current;
|
|
8118
|
-
})
|
|
8119
|
-
);
|
|
8120
|
-
}
|
|
8121
|
-
function safeCaptureJsonOrError(value) {
|
|
8122
|
-
try {
|
|
8123
|
-
return { ok: true, value: safeCaptureJson(value) };
|
|
8124
|
-
} catch (error) {
|
|
8125
|
-
return {
|
|
8126
|
-
ok: false,
|
|
8127
|
-
error: safeCaptureJson({
|
|
8128
|
-
message: error instanceof Error ? error.message : String(error),
|
|
8129
|
-
stack: error instanceof Error ? error.stack : void 0,
|
|
8130
|
-
inspected: util.inspect(value, { depth: 3, maxArrayLength: 20, breakLength: 120 })
|
|
8131
|
-
})
|
|
8132
|
-
};
|
|
8133
|
-
}
|
|
8134
|
-
}
|
|
8135
|
-
function formatCaptureDate(value) {
|
|
8136
|
-
if (!value) return void 0;
|
|
8137
|
-
if (value instanceof Date) return value.toISOString();
|
|
8138
|
-
try {
|
|
8139
|
-
return new Date(value).toISOString();
|
|
8140
|
-
} catch {
|
|
8141
|
-
return void 0;
|
|
8982
|
+
getObscureThreadIds() {
|
|
8983
|
+
return this.shouldObscureThreadIds;
|
|
8142
8984
|
}
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8985
|
+
/**
|
|
8986
|
+
* Begin a new observation turn — the high-level API for managing the
|
|
8987
|
+
* observe/buffer/activate/reflect lifecycle across agentic loop steps.
|
|
8988
|
+
*
|
|
8989
|
+
* @example
|
|
8990
|
+
* ```ts
|
|
8991
|
+
* const turn = om.beginTurn({ threadId, resourceId, messageList });
|
|
8992
|
+
* await turn.start(memory);
|
|
8993
|
+
*
|
|
8994
|
+
* const step0 = turn.step(0);
|
|
8995
|
+
* const ctx = await step0.prepare();
|
|
8996
|
+
* // ... agent generates ...
|
|
8997
|
+
*
|
|
8998
|
+
* await turn.end();
|
|
8999
|
+
* ```
|
|
9000
|
+
*/
|
|
9001
|
+
beginTurn(opts) {
|
|
9002
|
+
return new ObservationTurn({
|
|
9003
|
+
om: this,
|
|
9004
|
+
threadId: opts.threadId,
|
|
9005
|
+
resourceId: opts.resourceId,
|
|
9006
|
+
messageList: opts.messageList,
|
|
9007
|
+
observabilityContext: opts.observabilityContext,
|
|
9008
|
+
hooks: opts.hooks
|
|
9009
|
+
});
|
|
8147
9010
|
}
|
|
8148
|
-
|
|
9011
|
+
};
|
|
9012
|
+
|
|
9013
|
+
// src/processors/observational-memory/temporal-markers.ts
|
|
9014
|
+
var TEMPORAL_GAP_REMINDER_TYPE = "temporal-gap";
|
|
9015
|
+
function getTemporalGapReminderText(gapText, timestamp) {
|
|
9016
|
+
return `${gapText} \u2014 ${formatTemporalTimestamp(new Date(timestamp))}`;
|
|
9017
|
+
}
|
|
9018
|
+
function getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp) {
|
|
9019
|
+
const formattedTimestamp = formatTemporalTimestamp(new Date(timestamp));
|
|
8149
9020
|
return {
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
observationTokenCount: turn._record.observationTokenCount,
|
|
8166
|
-
pendingMessageTokens: turn._record.pendingMessageTokens,
|
|
8167
|
-
isBufferingObservation: turn._record.isBufferingObservation,
|
|
8168
|
-
isBufferingReflection: turn._record.isBufferingReflection
|
|
8169
|
-
} : void 0,
|
|
8170
|
-
context: turn._context ? {
|
|
8171
|
-
messageCount: Array.isArray(turn._context.messages) ? turn._context.messages.length : void 0,
|
|
8172
|
-
hasSystemMessage: Array.isArray(turn._context.systemMessage) ? turn._context.systemMessage.length > 0 : Boolean(turn._context.systemMessage),
|
|
8173
|
-
continuationId: turn._context.continuation?.id,
|
|
8174
|
-
hasOtherThreadsContext: Boolean(turn._context.otherThreadsContext),
|
|
8175
|
-
recordId: turn._context.record?.id
|
|
8176
|
-
} : void 0,
|
|
8177
|
-
currentStep: turn._currentStep ? {
|
|
8178
|
-
stepNumber: turn._currentStep.stepNumber,
|
|
8179
|
-
prepared: turn._currentStep._prepared,
|
|
8180
|
-
context: turn._currentStep._context ? {
|
|
8181
|
-
activated: turn._currentStep._context.activated,
|
|
8182
|
-
observed: turn._currentStep._context.observed,
|
|
8183
|
-
buffered: turn._currentStep._context.buffered,
|
|
8184
|
-
reflected: turn._currentStep._context.reflected,
|
|
8185
|
-
didThresholdCleanup: turn._currentStep._context.didThresholdCleanup,
|
|
8186
|
-
messageCount: Array.isArray(turn._currentStep._context.messages) ? turn._currentStep._context.messages.length : void 0,
|
|
8187
|
-
systemMessageCount: Array.isArray(turn._currentStep._context.systemMessage) ? turn._currentStep._context.systemMessage.length : void 0
|
|
8188
|
-
} : void 0
|
|
8189
|
-
} : void 0
|
|
9021
|
+
reminderType: TEMPORAL_GAP_REMINDER_TYPE,
|
|
9022
|
+
gapText,
|
|
9023
|
+
gapMs,
|
|
9024
|
+
timestamp: formattedTimestamp,
|
|
9025
|
+
timestampMs: timestamp,
|
|
9026
|
+
precedesMessageId: message.id,
|
|
9027
|
+
systemReminder: {
|
|
9028
|
+
type: TEMPORAL_GAP_REMINDER_TYPE,
|
|
9029
|
+
message: getTemporalGapReminderText(gapText, timestamp),
|
|
9030
|
+
gapText,
|
|
9031
|
+
gapMs,
|
|
9032
|
+
timestamp: formattedTimestamp,
|
|
9033
|
+
timestampMs: timestamp,
|
|
9034
|
+
precedesMessageId: message.id
|
|
9035
|
+
}
|
|
8190
9036
|
};
|
|
8191
9037
|
}
|
|
8192
|
-
function
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
9038
|
+
function isTemporalGapMarkerForMessage(message, targetMessageId) {
|
|
9039
|
+
if (!isTemporalGapMarker(message)) {
|
|
9040
|
+
return false;
|
|
9041
|
+
}
|
|
9042
|
+
const metadata = message.content.metadata;
|
|
9043
|
+
if (metadata?.precedesMessageId === targetMessageId) {
|
|
9044
|
+
return true;
|
|
9045
|
+
}
|
|
9046
|
+
return metadata?.systemReminder?.type === TEMPORAL_GAP_REMINDER_TYPE && metadata.systemReminder.precedesMessageId === targetMessageId;
|
|
8201
9047
|
}
|
|
8202
|
-
function
|
|
8203
|
-
const
|
|
8204
|
-
return
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
9048
|
+
function createTemporalGapMarker(message, gapText, gapMs, timestamp) {
|
|
9049
|
+
const metadata = getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp);
|
|
9050
|
+
return {
|
|
9051
|
+
id: `__temporal_gap_${crypto.randomUUID()}`,
|
|
9052
|
+
role: "user",
|
|
9053
|
+
createdAt: new Date(timestamp - 1),
|
|
9054
|
+
threadId: message.threadId,
|
|
9055
|
+
resourceId: message.resourceId,
|
|
9056
|
+
content: {
|
|
9057
|
+
format: 2,
|
|
9058
|
+
parts: [
|
|
9059
|
+
{
|
|
9060
|
+
type: "text",
|
|
9061
|
+
text: `<system-reminder type="${TEMPORAL_GAP_REMINDER_TYPE}" precedesMessageId="${message.id}">${getTemporalGapReminderText(gapText, timestamp)}</system-reminder>`
|
|
9062
|
+
}
|
|
9063
|
+
],
|
|
9064
|
+
metadata
|
|
9065
|
+
}
|
|
9066
|
+
};
|
|
8209
9067
|
}
|
|
8210
|
-
function
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
preByFingerprint.set(fingerprint, list);
|
|
9068
|
+
async function insertTemporalGapMarkers({
|
|
9069
|
+
messageList,
|
|
9070
|
+
writer
|
|
9071
|
+
}) {
|
|
9072
|
+
const inputMessages = messageList.get.input.db().filter((message) => Boolean(message));
|
|
9073
|
+
const latestInputMessage = inputMessages.at(-1);
|
|
9074
|
+
if (!latestInputMessage || isTemporalGapMarker(latestInputMessage)) {
|
|
9075
|
+
return;
|
|
8219
9076
|
}
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
list.push(message.id);
|
|
8225
|
-
postByFingerprint.set(fingerprint, list);
|
|
9077
|
+
const allMessages = messageList.get.all.db().filter((message) => Boolean(message));
|
|
9078
|
+
const latestInputIndex = allMessages.findIndex((message) => message.id === latestInputMessage.id);
|
|
9079
|
+
if (latestInputIndex <= 0) {
|
|
9080
|
+
return;
|
|
8226
9081
|
}
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
const
|
|
8233
|
-
if (
|
|
8234
|
-
|
|
9082
|
+
if (allMessages.some((message) => isTemporalGapMarkerForMessage(message, latestInputMessage.id))) {
|
|
9083
|
+
return;
|
|
9084
|
+
}
|
|
9085
|
+
let previousNonMarker;
|
|
9086
|
+
for (let index = latestInputIndex - 1; index >= 0; index--) {
|
|
9087
|
+
const candidate = allMessages[index];
|
|
9088
|
+
if (candidate && !isTemporalGapMarker(candidate)) {
|
|
9089
|
+
previousNonMarker = candidate;
|
|
9090
|
+
break;
|
|
8235
9091
|
}
|
|
8236
|
-
remap.push({ fromId, toId, fingerprint });
|
|
8237
9092
|
}
|
|
8238
|
-
|
|
8239
|
-
}
|
|
8240
|
-
function writeProcessInputStepReproCapture(params) {
|
|
8241
|
-
if (!isOmReproCaptureEnabled()) {
|
|
9093
|
+
if (!previousNonMarker) {
|
|
8242
9094
|
return;
|
|
8243
9095
|
}
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
const contextMessages = params.messageList.get.all.db();
|
|
8250
|
-
const memoryContext = memory.parseMemoryRequestContext(params.args.requestContext);
|
|
8251
|
-
const preMessageIds = new Set(params.preMessages.map((message) => message.id));
|
|
8252
|
-
const postMessageIds = new Set(contextMessages.map((message) => message.id));
|
|
8253
|
-
const removedMessageIds = params.preMessages.map((message) => message.id).filter((id) => Boolean(id) && !postMessageIds.has(id));
|
|
8254
|
-
const addedMessageIds = contextMessages.map((message) => message.id).filter((id) => Boolean(id) && !preMessageIds.has(id));
|
|
8255
|
-
const idRemap = inferReproIdRemap(params.preMessages, contextMessages);
|
|
8256
|
-
const rawState = params.args.state ?? {};
|
|
8257
|
-
const sanitizedState = sanitizeCaptureState(rawState);
|
|
8258
|
-
const payloads = [
|
|
8259
|
-
{
|
|
8260
|
-
fileName: "input.json",
|
|
8261
|
-
data: {
|
|
8262
|
-
stepNumber: params.stepNumber,
|
|
8263
|
-
threadId: params.threadId,
|
|
8264
|
-
resourceId: params.resourceId,
|
|
8265
|
-
readOnly: memoryContext?.memoryConfig?.readOnly,
|
|
8266
|
-
messageCount: contextMessages.length,
|
|
8267
|
-
messageIds: contextMessages.map((message) => message.id),
|
|
8268
|
-
stateKeys: Object.keys(rawState),
|
|
8269
|
-
state: sanitizedState,
|
|
8270
|
-
args: {
|
|
8271
|
-
messages: params.args.messages,
|
|
8272
|
-
steps: params.args.steps,
|
|
8273
|
-
systemMessages: params.args.systemMessages,
|
|
8274
|
-
retryCount: params.args.retryCount,
|
|
8275
|
-
toolChoice: params.args.toolChoice,
|
|
8276
|
-
activeTools: params.args.activeTools,
|
|
8277
|
-
modelSettings: params.args.modelSettings,
|
|
8278
|
-
structuredOutput: params.args.structuredOutput
|
|
8279
|
-
}
|
|
8280
|
-
}
|
|
8281
|
-
},
|
|
8282
|
-
{
|
|
8283
|
-
fileName: "pre-state.json",
|
|
8284
|
-
data: {
|
|
8285
|
-
record: params.preRecord,
|
|
8286
|
-
bufferedChunks: params.preBufferedChunks,
|
|
8287
|
-
contextTokenCount: params.preContextTokenCount,
|
|
8288
|
-
messages: params.preMessages,
|
|
8289
|
-
messageList: params.preSerializedMessageList
|
|
8290
|
-
}
|
|
8291
|
-
},
|
|
8292
|
-
{
|
|
8293
|
-
fileName: "output.json",
|
|
8294
|
-
data: {
|
|
8295
|
-
details: params.details,
|
|
8296
|
-
messageDiff: {
|
|
8297
|
-
removedMessageIds,
|
|
8298
|
-
addedMessageIds,
|
|
8299
|
-
idRemap
|
|
8300
|
-
}
|
|
8301
|
-
}
|
|
8302
|
-
},
|
|
8303
|
-
{
|
|
8304
|
-
fileName: "post-state.json",
|
|
8305
|
-
data: {
|
|
8306
|
-
record: params.postRecord,
|
|
8307
|
-
bufferedChunks: params.postBufferedChunks,
|
|
8308
|
-
contextTokenCount: params.postContextTokenCount,
|
|
8309
|
-
messageCount: contextMessages.length,
|
|
8310
|
-
messageIds: contextMessages.map((message) => message.id),
|
|
8311
|
-
messages: contextMessages,
|
|
8312
|
-
messageList: params.messageList.serialize()
|
|
8313
|
-
}
|
|
8314
|
-
}
|
|
8315
|
-
];
|
|
8316
|
-
const captureErrors = [];
|
|
8317
|
-
for (const payload of payloads) {
|
|
8318
|
-
const serialized = safeCaptureJsonOrError(payload.data);
|
|
8319
|
-
if (serialized.ok) {
|
|
8320
|
-
fs.writeFileSync(path.join(captureDir, payload.fileName), `${JSON.stringify(serialized.value, null, 2)}
|
|
8321
|
-
`);
|
|
8322
|
-
continue;
|
|
8323
|
-
}
|
|
8324
|
-
captureErrors.push({ fileName: payload.fileName, error: serialized.error });
|
|
8325
|
-
fs.writeFileSync(
|
|
8326
|
-
path.join(captureDir, payload.fileName),
|
|
8327
|
-
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
8328
|
-
`
|
|
8329
|
-
);
|
|
8330
|
-
}
|
|
8331
|
-
if (params.observerExchange) {
|
|
8332
|
-
const serialized = safeCaptureJsonOrError(params.observerExchange);
|
|
8333
|
-
if (serialized.ok) {
|
|
8334
|
-
fs.writeFileSync(path.join(captureDir, "observer-exchange.json"), `${JSON.stringify(serialized.value, null, 2)}
|
|
8335
|
-
`);
|
|
8336
|
-
} else {
|
|
8337
|
-
captureErrors.push({ fileName: "observer-exchange.json", error: serialized.error });
|
|
8338
|
-
fs.writeFileSync(
|
|
8339
|
-
path.join(captureDir, "observer-exchange.json"),
|
|
8340
|
-
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
8341
|
-
`
|
|
8342
|
-
);
|
|
8343
|
-
}
|
|
8344
|
-
}
|
|
8345
|
-
if (captureErrors.length > 0) {
|
|
8346
|
-
fs.writeFileSync(path.join(captureDir, "capture-error.json"), `${JSON.stringify(captureErrors, null, 2)}
|
|
8347
|
-
`);
|
|
8348
|
-
params.debug?.(
|
|
8349
|
-
`[OM:repro-capture] wrote processInputStep capture with ${captureErrors.length} serialization error(s) to ${captureDir}`
|
|
8350
|
-
);
|
|
8351
|
-
return;
|
|
8352
|
-
}
|
|
8353
|
-
params.debug?.(`[OM:repro-capture] wrote processInputStep capture to ${captureDir}`);
|
|
8354
|
-
} catch (error) {
|
|
8355
|
-
params.debug?.(`[OM:repro-capture] failed to write processInputStep capture: ${String(error)}`);
|
|
9096
|
+
const timestamp = getMessagePartTimestamp(latestInputMessage, "first");
|
|
9097
|
+
const gapMs = timestamp - getMessagePartTimestamp(previousNonMarker, "last");
|
|
9098
|
+
const gapText = formatTemporalGap(gapMs);
|
|
9099
|
+
if (!gapText) {
|
|
9100
|
+
return;
|
|
8356
9101
|
}
|
|
9102
|
+
const reminderMetadata = getTemporalGapReminderMetadata(latestInputMessage, gapText, gapMs, timestamp);
|
|
9103
|
+
await writer?.custom({
|
|
9104
|
+
type: "data-system-reminder",
|
|
9105
|
+
data: {
|
|
9106
|
+
message: getTemporalGapReminderText(gapText, timestamp),
|
|
9107
|
+
...reminderMetadata
|
|
9108
|
+
},
|
|
9109
|
+
transient: true
|
|
9110
|
+
});
|
|
9111
|
+
const marker = createTemporalGapMarker(latestInputMessage, gapText, gapMs, timestamp);
|
|
9112
|
+
messageList.add(marker, "input");
|
|
8357
9113
|
}
|
|
8358
9114
|
|
|
8359
9115
|
// src/processors/observational-memory/processor.ts
|
|
@@ -8368,6 +9124,10 @@ function getOmObservabilityContext(args) {
|
|
|
8368
9124
|
metrics: args.metrics
|
|
8369
9125
|
};
|
|
8370
9126
|
}
|
|
9127
|
+
var GATEWAY_STATE_KEY = "__isGatewayModel";
|
|
9128
|
+
function isMastraGatewayModel(model) {
|
|
9129
|
+
return typeof model === "object" && model !== null && "gatewayId" in model && model.gatewayId === "mastra";
|
|
9130
|
+
}
|
|
8371
9131
|
var ObservationalMemoryProcessor = class {
|
|
8372
9132
|
id = "observational-memory";
|
|
8373
9133
|
name = "Observational Memory";
|
|
@@ -8375,11 +9135,14 @@ var ObservationalMemoryProcessor = class {
|
|
|
8375
9135
|
engine;
|
|
8376
9136
|
/** Memory instance for loading context. */
|
|
8377
9137
|
memory;
|
|
9138
|
+
/** Whether temporal-gap reminder markers should be inserted. */
|
|
9139
|
+
temporalMarkers;
|
|
8378
9140
|
/** Active turn — created on first processInputStep, ended on processOutputResult. */
|
|
8379
9141
|
turn;
|
|
8380
|
-
constructor(engine, memory) {
|
|
9142
|
+
constructor(engine, memory, options) {
|
|
8381
9143
|
this.engine = engine;
|
|
8382
9144
|
this.memory = memory;
|
|
9145
|
+
this.temporalMarkers = options?.temporalMarkers ?? false;
|
|
8383
9146
|
}
|
|
8384
9147
|
// ─── Processor lifecycle hooks ──────────────────────────────────────────
|
|
8385
9148
|
async processInputStep(args) {
|
|
@@ -8403,6 +9166,11 @@ var ObservationalMemoryProcessor = class {
|
|
|
8403
9166
|
omDebug(`[OM:processInputStep:NO-CONTEXT] getThreadContext returned null \u2014 returning early`);
|
|
8404
9167
|
return messageList;
|
|
8405
9168
|
}
|
|
9169
|
+
if (isMastraGatewayModel(model)) {
|
|
9170
|
+
state[GATEWAY_STATE_KEY] = true;
|
|
9171
|
+
omDebug(`[OM:processInputStep:GATEWAY] gateway handles OM \u2014 skipping local processing`);
|
|
9172
|
+
return messageList;
|
|
9173
|
+
}
|
|
8406
9174
|
const { threadId, resourceId } = context;
|
|
8407
9175
|
const memoryContext = memory.parseMemoryRequestContext(requestContext);
|
|
8408
9176
|
const readOnly = memoryContext?.memoryConfig?.readOnly;
|
|
@@ -8416,6 +9184,15 @@ var ObservationalMemoryProcessor = class {
|
|
|
8416
9184
|
if (readOnly) {
|
|
8417
9185
|
return messageList;
|
|
8418
9186
|
}
|
|
9187
|
+
const activeTurn = state.__omTurn ?? this.turn;
|
|
9188
|
+
if (activeTurn && activeTurn.messageList !== messageList) {
|
|
9189
|
+
await activeTurn.end().catch(() => {
|
|
9190
|
+
});
|
|
9191
|
+
if (this.turn === activeTurn) {
|
|
9192
|
+
this.turn = void 0;
|
|
9193
|
+
}
|
|
9194
|
+
state.__omTurn = void 0;
|
|
9195
|
+
}
|
|
8419
9196
|
if (!this.turn || !state.__omTurn) {
|
|
8420
9197
|
if (this.turn && !state.__omTurn) {
|
|
8421
9198
|
await this.turn.end().catch(() => {
|
|
@@ -8427,17 +9204,26 @@ var ObservationalMemoryProcessor = class {
|
|
|
8427
9204
|
messageList,
|
|
8428
9205
|
observabilityContext: getOmObservabilityContext(args),
|
|
8429
9206
|
hooks: {
|
|
8430
|
-
onBufferChunkSealed: rotateResponseMessageId
|
|
9207
|
+
onBufferChunkSealed: rotateResponseMessageId,
|
|
9208
|
+
onSyncObservationComplete: rotateResponseMessageId
|
|
8431
9209
|
}
|
|
8432
9210
|
});
|
|
8433
9211
|
this.turn.writer = writer;
|
|
8434
9212
|
this.turn.requestContext = requestContext;
|
|
8435
9213
|
await this.turn.start(this.memory);
|
|
9214
|
+
if (stepNumber === 0 && this.temporalMarkers) {
|
|
9215
|
+
await insertTemporalGapMarkers({ messageList, writer });
|
|
9216
|
+
}
|
|
8436
9217
|
state.__omTurn = this.turn;
|
|
8437
9218
|
}
|
|
9219
|
+
this.turn.addHooks({
|
|
9220
|
+
onBufferChunkSealed: rotateResponseMessageId,
|
|
9221
|
+
onSyncObservationComplete: rotateResponseMessageId
|
|
9222
|
+
});
|
|
8438
9223
|
const observabilityContext = getOmObservabilityContext(args);
|
|
8439
9224
|
state.__omObservabilityContext = observabilityContext;
|
|
8440
9225
|
this.turn.observabilityContext = observabilityContext;
|
|
9226
|
+
this.turn.actorModelContext = actorModelContext;
|
|
8441
9227
|
{
|
|
8442
9228
|
const step = this.turn.step(stepNumber);
|
|
8443
9229
|
let ctx;
|
|
@@ -8519,6 +9305,7 @@ var ObservationalMemoryProcessor = class {
|
|
|
8519
9305
|
const state = _state ?? {};
|
|
8520
9306
|
const context = this.engine.getThreadContext(requestContext, messageList);
|
|
8521
9307
|
if (!context) return messageList;
|
|
9308
|
+
if (state[GATEWAY_STATE_KEY]) return messageList;
|
|
8522
9309
|
const observabilityContext = getOmObservabilityContext(args);
|
|
8523
9310
|
state.__omObservabilityContext = observabilityContext;
|
|
8524
9311
|
return this.engine.getTokenCounter().runWithModelContext(state.__omActorModelContext, async () => {
|
|
@@ -8595,5 +9382,5 @@ exports.stripEphemeralAnchorIds = stripEphemeralAnchorIds;
|
|
|
8595
9382
|
exports.stripObservationGroups = stripObservationGroups;
|
|
8596
9383
|
exports.truncateStringByTokens = truncateStringByTokens;
|
|
8597
9384
|
exports.wrapInObservationGroup = wrapInObservationGroup;
|
|
8598
|
-
//# sourceMappingURL=chunk-
|
|
8599
|
-
//# sourceMappingURL=chunk-
|
|
9385
|
+
//# sourceMappingURL=chunk-6ODYOMHN.cjs.map
|
|
9386
|
+
//# sourceMappingURL=chunk-6ODYOMHN.cjs.map
|