@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
|
@@ -4,15 +4,15 @@ import { resolveModelConfig } from '@mastra/core/llm';
|
|
|
4
4
|
import { getThreadOMMetadata, setThreadOMMetadata, parseMemoryRequestContext } from '@mastra/core/memory';
|
|
5
5
|
import { MessageHistory } from '@mastra/core/processors';
|
|
6
6
|
import xxhash from 'xxhash-wasm';
|
|
7
|
-
import { appendFileSync,
|
|
7
|
+
import { appendFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import { randomBytes, createHash, randomUUID } from 'crypto';
|
|
10
10
|
import { estimateTokenCount } from 'tokenx';
|
|
11
11
|
import { Agent } from '@mastra/core/agent';
|
|
12
12
|
import { getOrCreateSpan, EntityType, SpanType, createObservabilityContext } from '@mastra/core/observability';
|
|
13
|
+
import { inspect } from 'util';
|
|
13
14
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
14
15
|
import imageSize from 'image-size';
|
|
15
|
-
import { inspect } from 'util';
|
|
16
16
|
|
|
17
17
|
var OM_DEBUG_LOG = process.env.OM_DEBUG ? join(process.cwd(), "om-debug.log") : null;
|
|
18
18
|
function omDebug(msg) {
|
|
@@ -201,6 +201,19 @@ var BufferingCoordinator = class _BufferingCoordinator {
|
|
|
201
201
|
}
|
|
202
202
|
};
|
|
203
203
|
|
|
204
|
+
// src/processors/observational-memory/model-context.ts
|
|
205
|
+
function didProviderChange(actorModel, lastModel) {
|
|
206
|
+
if (actorModel === void 0 || lastModel === void 0) return false;
|
|
207
|
+
const actorHasSlash = actorModel.includes("/");
|
|
208
|
+
const lastHasSlash = lastModel.includes("/");
|
|
209
|
+
if (actorHasSlash && lastHasSlash) {
|
|
210
|
+
return actorModel !== lastModel;
|
|
211
|
+
}
|
|
212
|
+
const actorModelId = actorHasSlash ? actorModel.slice(actorModel.indexOf("/") + 1) : actorModel;
|
|
213
|
+
const lastModelId = lastHasSlash ? lastModel.slice(lastModel.indexOf("/") + 1) : lastModel;
|
|
214
|
+
return actorModelId !== lastModelId;
|
|
215
|
+
}
|
|
216
|
+
|
|
204
217
|
// src/processors/observational-memory/date-utils.ts
|
|
205
218
|
function formatRelativeTime(date, currentDate) {
|
|
206
219
|
const diffMs = currentDate.getTime() - date.getTime();
|
|
@@ -365,6 +378,67 @@ ${gap}
|
|
|
365
378
|
result += withInlineDates.slice(lastIndex);
|
|
366
379
|
return result;
|
|
367
380
|
}
|
|
381
|
+
var MIN_TEMPORAL_GAP_MS = 10 * 60 * 1e3;
|
|
382
|
+
function formatTemporalGap(diffMs) {
|
|
383
|
+
if (diffMs < MIN_TEMPORAL_GAP_MS) return null;
|
|
384
|
+
const minute = 60 * 1e3;
|
|
385
|
+
const hour = 60 * minute;
|
|
386
|
+
const day = 24 * hour;
|
|
387
|
+
const week = 7 * day;
|
|
388
|
+
const month = 30 * day;
|
|
389
|
+
const year = 365 * day;
|
|
390
|
+
const formatUnit = (value, unit) => `${value} ${unit}${value === 1 ? "" : "s"}`;
|
|
391
|
+
if (diffMs < hour) {
|
|
392
|
+
const minutes = Math.max(1, Math.round(diffMs / minute));
|
|
393
|
+
return `${formatUnit(minutes, "minute")} later`;
|
|
394
|
+
}
|
|
395
|
+
const formatTwoUnits = (primaryMs, primaryUnit, secondaryMs, secondaryUnit) => {
|
|
396
|
+
const primary = Math.floor(diffMs / primaryMs);
|
|
397
|
+
const remainder = diffMs - primary * primaryMs;
|
|
398
|
+
const secondary = Math.floor(remainder / secondaryMs);
|
|
399
|
+
const parts = [formatUnit(primary, primaryUnit)];
|
|
400
|
+
if (secondary > 0) {
|
|
401
|
+
parts.push(formatUnit(secondary, secondaryUnit));
|
|
402
|
+
}
|
|
403
|
+
return `${parts.join(" ")} later`;
|
|
404
|
+
};
|
|
405
|
+
if (diffMs < day) {
|
|
406
|
+
return formatTwoUnits(hour, "hour", minute, "minute");
|
|
407
|
+
}
|
|
408
|
+
if (diffMs < week) {
|
|
409
|
+
return formatTwoUnits(day, "day", hour, "hour");
|
|
410
|
+
}
|
|
411
|
+
if (diffMs < month) {
|
|
412
|
+
return formatTwoUnits(week, "week", day, "day");
|
|
413
|
+
}
|
|
414
|
+
if (diffMs < year) {
|
|
415
|
+
return formatTwoUnits(month, "month", week, "week");
|
|
416
|
+
}
|
|
417
|
+
return formatTwoUnits(year, "year", month, "month");
|
|
418
|
+
}
|
|
419
|
+
function formatTemporalTimestamp(date) {
|
|
420
|
+
return date.toLocaleString("en-US", {
|
|
421
|
+
year: "numeric",
|
|
422
|
+
month: "2-digit",
|
|
423
|
+
day: "2-digit",
|
|
424
|
+
hour: "numeric",
|
|
425
|
+
minute: "2-digit",
|
|
426
|
+
hour12: true,
|
|
427
|
+
timeZoneName: "short"
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function getMessagePartTimestamp(msg, position) {
|
|
431
|
+
const timestamps = msg.content?.parts?.map((part) => "createdAt" in part ? part.createdAt : void 0).filter((timestamp) => typeof timestamp === "number");
|
|
432
|
+
if (timestamps && timestamps.length > 0) {
|
|
433
|
+
const index = position === "first" ? 0 : timestamps.length - 1;
|
|
434
|
+
const timestamp = timestamps[index];
|
|
435
|
+
if (timestamp !== void 0) return timestamp;
|
|
436
|
+
}
|
|
437
|
+
return new Date(msg.createdAt).getTime();
|
|
438
|
+
}
|
|
439
|
+
function isTemporalGapMarker(msg) {
|
|
440
|
+
return msg.id.startsWith("__temporal_");
|
|
441
|
+
}
|
|
368
442
|
|
|
369
443
|
// src/processors/observational-memory/markers.ts
|
|
370
444
|
function createObservationStartMarker(params) {
|
|
@@ -484,7 +558,12 @@ function createActivationMarker(params) {
|
|
|
484
558
|
threadId: params.threadId,
|
|
485
559
|
generationCount: params.generationCount,
|
|
486
560
|
config: params.config,
|
|
487
|
-
observations: params.observations
|
|
561
|
+
observations: params.observations,
|
|
562
|
+
triggeredBy: params.triggeredBy,
|
|
563
|
+
lastActivityAt: params.lastActivityAt,
|
|
564
|
+
ttlExpiredMs: params.ttlExpiredMs,
|
|
565
|
+
previousModel: params.previousModel,
|
|
566
|
+
currentModel: params.currentModel
|
|
488
567
|
}
|
|
489
568
|
};
|
|
490
569
|
}
|
|
@@ -634,7 +713,7 @@ function sortThreadsByOldestMessage(messagesByThread) {
|
|
|
634
713
|
})).sort((a, b) => a.oldestTimestamp - b.oldestTimestamp).map((t) => t.threadId);
|
|
635
714
|
}
|
|
636
715
|
function stripThreadTags(observations) {
|
|
637
|
-
return observations.replace(
|
|
716
|
+
return observations.replace(/<\/?thread\b[^>]{0,1024}>/gi, "").trim();
|
|
638
717
|
}
|
|
639
718
|
|
|
640
719
|
// src/processors/observational-memory/model-by-input-tokens.ts
|
|
@@ -934,6 +1013,8 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
934
1013
|
this.scope = deps.scope;
|
|
935
1014
|
this.retrieval = deps.retrieval;
|
|
936
1015
|
}
|
|
1016
|
+
deps;
|
|
1017
|
+
opts;
|
|
937
1018
|
storage;
|
|
938
1019
|
messageHistory;
|
|
939
1020
|
tokenCounter;
|
|
@@ -945,7 +1026,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
945
1026
|
static create;
|
|
946
1027
|
/**
|
|
947
1028
|
* Run the full observation lifecycle.
|
|
948
|
-
* @returns `
|
|
1029
|
+
* @returns Result with `observed` flag and optional `usage` from the observer LLM call.
|
|
949
1030
|
* @throws On sync/resource-scoped observer failure after failed markers (same as pre–Option-A contract).
|
|
950
1031
|
*/
|
|
951
1032
|
async run() {
|
|
@@ -955,7 +1036,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
955
1036
|
if (this.needsLock) {
|
|
956
1037
|
const fresh = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
957
1038
|
if (fresh?.lastObservedAt && record.lastObservedAt && fresh.lastObservedAt > record.lastObservedAt) {
|
|
958
|
-
return false;
|
|
1039
|
+
return { observed: false };
|
|
959
1040
|
}
|
|
960
1041
|
}
|
|
961
1042
|
const { messages, existingObservations } = await this.prepare();
|
|
@@ -976,7 +1057,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
976
1057
|
observabilityContext: this.opts.observabilityContext
|
|
977
1058
|
});
|
|
978
1059
|
}
|
|
979
|
-
return true;
|
|
1060
|
+
return { observed: true, usage: output.usage };
|
|
980
1061
|
} catch (error) {
|
|
981
1062
|
await this.emitFailedMarkers(cycleId, error);
|
|
982
1063
|
if (!this.rethrowOnFailure) {
|
|
@@ -995,7 +1076,7 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
995
1076
|
});
|
|
996
1077
|
if (abortSignal?.aborted) throw error;
|
|
997
1078
|
omError("[OM] Observation failed", error);
|
|
998
|
-
return false;
|
|
1079
|
+
return { observed: false };
|
|
999
1080
|
}
|
|
1000
1081
|
omError("[OM] Observation failed", error);
|
|
1001
1082
|
throw error;
|
|
@@ -1007,9 +1088,11 @@ var ObservationStrategy = class _ObservationStrategy {
|
|
|
1007
1088
|
}
|
|
1008
1089
|
async streamMarker(marker) {
|
|
1009
1090
|
if (this.opts.writer) {
|
|
1010
|
-
await this.opts.writer.custom(marker).catch(() => {
|
|
1091
|
+
await this.opts.writer.custom({ ...marker, transient: true }).catch(() => {
|
|
1011
1092
|
});
|
|
1012
1093
|
}
|
|
1094
|
+
const markerThreadId = marker.data?.threadId ?? this.opts.threadId;
|
|
1095
|
+
await this.persistMarkerToStorage(marker, markerThreadId, this.opts.resourceId);
|
|
1013
1096
|
}
|
|
1014
1097
|
getObservationMarkerConfig() {
|
|
1015
1098
|
return {
|
|
@@ -1515,7 +1598,7 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
1515
1598
|
}
|
|
1516
1599
|
}
|
|
1517
1600
|
async emitEndMarkers(_cycleId, processed) {
|
|
1518
|
-
if (!processed.observations
|
|
1601
|
+
if (!processed.observations) return;
|
|
1519
1602
|
const { record, threadId, messages } = this.opts;
|
|
1520
1603
|
const tokensBuffered = await this.tokenCounter.countMessagesAsync(messages);
|
|
1521
1604
|
const updatedRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
@@ -1531,12 +1614,13 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
1531
1614
|
threadId,
|
|
1532
1615
|
observations: processed.observations
|
|
1533
1616
|
});
|
|
1534
|
-
|
|
1535
|
-
|
|
1617
|
+
if (this.opts.writer) {
|
|
1618
|
+
void this.opts.writer.custom({ ...endMarker, transient: true }).catch(() => {
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1536
1621
|
await this.persistMarkerToStorage(endMarker, threadId, record.resourceId ?? void 0);
|
|
1537
1622
|
}
|
|
1538
1623
|
async emitFailedMarkers(_cycleId, error) {
|
|
1539
|
-
if (!this.opts.writer) return;
|
|
1540
1624
|
const { record, threadId, messages } = this.opts;
|
|
1541
1625
|
const tokensAttempted = await this.tokenCounter.countMessagesAsync(messages);
|
|
1542
1626
|
const failedMarker = createBufferingFailedMarker({
|
|
@@ -1548,8 +1632,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
1548
1632
|
recordId: record.id,
|
|
1549
1633
|
threadId
|
|
1550
1634
|
});
|
|
1551
|
-
|
|
1552
|
-
|
|
1635
|
+
if (this.opts.writer) {
|
|
1636
|
+
void this.opts.writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1553
1639
|
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
|
|
1554
1640
|
}
|
|
1555
1641
|
};
|
|
@@ -1940,6 +2026,8 @@ var ObservationStep = class {
|
|
|
1940
2026
|
this.turn = turn;
|
|
1941
2027
|
this.stepNumber = stepNumber;
|
|
1942
2028
|
}
|
|
2029
|
+
turn;
|
|
2030
|
+
stepNumber;
|
|
1943
2031
|
_prepared = false;
|
|
1944
2032
|
_context;
|
|
1945
2033
|
/** Whether this step has been prepared. */
|
|
@@ -1975,6 +2063,7 @@ var ObservationStep = class {
|
|
|
1975
2063
|
resourceId,
|
|
1976
2064
|
checkThreshold: true,
|
|
1977
2065
|
messages: step0Messages,
|
|
2066
|
+
currentModel: this.turn.actorModelContext,
|
|
1978
2067
|
writer: this.turn.writer,
|
|
1979
2068
|
messageList
|
|
1980
2069
|
});
|
|
@@ -1998,8 +2087,11 @@ var ObservationStep = class {
|
|
|
1998
2087
|
observationTokens: obsTokens,
|
|
1999
2088
|
threadId,
|
|
2000
2089
|
writer: this.turn.writer,
|
|
2090
|
+
messageList,
|
|
2091
|
+
currentModel: this.turn.actorModelContext,
|
|
2001
2092
|
requestContext: this.turn.requestContext,
|
|
2002
|
-
observabilityContext: this.turn.observabilityContext
|
|
2093
|
+
observabilityContext: this.turn.observabilityContext,
|
|
2094
|
+
lastActivityAt: getLastActivityFromMessages(messageList.get.all.db())
|
|
2003
2095
|
});
|
|
2004
2096
|
await this.turn.refreshRecord();
|
|
2005
2097
|
if (this.turn.record.generationCount > preReflectGeneration) {
|
|
@@ -2008,7 +2100,11 @@ var ObservationStep = class {
|
|
|
2008
2100
|
}
|
|
2009
2101
|
const allMsgsForToolCheck = messageList.get.all.db();
|
|
2010
2102
|
const lastMessage = allMsgsForToolCheck[allMsgsForToolCheck.length - 1];
|
|
2011
|
-
const
|
|
2103
|
+
const pendingStepMessages = [...messageList.get.input.db(), ...messageList.get.response.db()];
|
|
2104
|
+
const latestStepParts = [
|
|
2105
|
+
...getLatestStepParts(lastMessage?.content?.parts ?? []),
|
|
2106
|
+
...pendingStepMessages.flatMap((msg) => getLatestStepParts(msg.content?.parts ?? []))
|
|
2107
|
+
];
|
|
2012
2108
|
const hasIncompleteToolCalls = latestStepParts.some(
|
|
2013
2109
|
(part) => part?.type === "tool-invocation" && part.toolInvocation?.state === "call"
|
|
2014
2110
|
);
|
|
@@ -2163,6 +2259,7 @@ var ObservationStep = class {
|
|
|
2163
2259
|
threadId,
|
|
2164
2260
|
resourceId,
|
|
2165
2261
|
messages: messageList.get.all.db(),
|
|
2262
|
+
currentModel: this.turn.actorModelContext,
|
|
2166
2263
|
writer: this.turn.writer,
|
|
2167
2264
|
messageList
|
|
2168
2265
|
});
|
|
@@ -2174,8 +2271,10 @@ var ObservationStep = class {
|
|
|
2174
2271
|
threadId,
|
|
2175
2272
|
writer: this.turn.writer,
|
|
2176
2273
|
messageList,
|
|
2274
|
+
currentModel: this.turn.actorModelContext,
|
|
2177
2275
|
requestContext: this.turn.requestContext,
|
|
2178
|
-
observabilityContext: this.turn.observabilityContext
|
|
2276
|
+
observabilityContext: this.turn.observabilityContext,
|
|
2277
|
+
lastActivityAt: getLastActivityFromMessages(messageList.get.all.db())
|
|
2179
2278
|
});
|
|
2180
2279
|
return {
|
|
2181
2280
|
succeeded: true,
|
|
@@ -2192,6 +2291,31 @@ var ObservationStep = class {
|
|
|
2192
2291
|
writer: this.turn.writer,
|
|
2193
2292
|
observabilityContext: this.turn.observabilityContext
|
|
2194
2293
|
});
|
|
2294
|
+
if (obsResult.observed) {
|
|
2295
|
+
const observedMessageIds = new Set(obsResult.record.observedMessageIds ?? []);
|
|
2296
|
+
const liveMessages = messageList.get.all.db();
|
|
2297
|
+
let latestObservedIndex = -1;
|
|
2298
|
+
for (let i = liveMessages.length - 1; i >= 0; i--) {
|
|
2299
|
+
const message = liveMessages[i];
|
|
2300
|
+
if (message && observedMessageIds.has(message.id)) {
|
|
2301
|
+
latestObservedIndex = i;
|
|
2302
|
+
break;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
const messageToSeal = latestObservedIndex >= 0 ? liveMessages[latestObservedIndex] : void 0;
|
|
2306
|
+
const messagesToSeal = messageToSeal ? [messageToSeal] : [];
|
|
2307
|
+
om.sealMessagesForBuffering(messagesToSeal);
|
|
2308
|
+
try {
|
|
2309
|
+
await this.turn.hooks?.onSyncObservationComplete?.();
|
|
2310
|
+
} catch (error) {
|
|
2311
|
+
omDebug(
|
|
2312
|
+
`[OM:observe] onSyncObservationComplete hook failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
if (messagesToSeal.length > 0) {
|
|
2316
|
+
await om.persistMessages(messagesToSeal, threadId, resourceId);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2195
2319
|
return {
|
|
2196
2320
|
succeeded: obsResult.observed,
|
|
2197
2321
|
record: obsResult.record,
|
|
@@ -2217,7 +2341,9 @@ var ObservationTurn = class {
|
|
|
2217
2341
|
requestContext;
|
|
2218
2342
|
/** Optional observability context for nested OM spans. */
|
|
2219
2343
|
observabilityContext;
|
|
2220
|
-
/**
|
|
2344
|
+
/** Current actor model for this step. Updated by the processor before prepare(). */
|
|
2345
|
+
actorModelContext;
|
|
2346
|
+
/** Processor-provided hooks for turn/step lifecycle integration. */
|
|
2221
2347
|
hooks;
|
|
2222
2348
|
constructor(opts) {
|
|
2223
2349
|
this.om = opts.om;
|
|
@@ -2225,7 +2351,7 @@ var ObservationTurn = class {
|
|
|
2225
2351
|
this.resourceId = opts.resourceId;
|
|
2226
2352
|
this.messageList = opts.messageList;
|
|
2227
2353
|
this.observabilityContext = opts.observabilityContext;
|
|
2228
|
-
this.hooks = opts.hooks;
|
|
2354
|
+
this.hooks = opts.hooks ?? {};
|
|
2229
2355
|
}
|
|
2230
2356
|
om;
|
|
2231
2357
|
threadId;
|
|
@@ -2245,6 +2371,10 @@ var ObservationTurn = class {
|
|
|
2245
2371
|
get currentStep() {
|
|
2246
2372
|
return this._currentStep;
|
|
2247
2373
|
}
|
|
2374
|
+
addHooks(hooks) {
|
|
2375
|
+
if (!hooks) return;
|
|
2376
|
+
Object.assign(this.hooks, hooks);
|
|
2377
|
+
}
|
|
2248
2378
|
/**
|
|
2249
2379
|
* Load context and cache the record. Call once at the start of the turn.
|
|
2250
2380
|
*
|
|
@@ -2401,6 +2531,15 @@ function stripEphemeralAnchorIds(observations) {
|
|
|
2401
2531
|
}
|
|
2402
2532
|
return observations.replace(/(^|\n)([^\S\n]*)\[(O\d+(?:-N\d+)?)\][^\S\n]*/g, "$1$2");
|
|
2403
2533
|
}
|
|
2534
|
+
|
|
2535
|
+
// src/processors/observational-memory/string-utils.ts
|
|
2536
|
+
function safeSlice(str, end) {
|
|
2537
|
+
if (end <= 0) return "";
|
|
2538
|
+
if (end >= str.length) return str;
|
|
2539
|
+
const code = str.charCodeAt(end - 1);
|
|
2540
|
+
const safeEnd = code >= 55296 && code <= 56319 ? end - 1 : end;
|
|
2541
|
+
return str.slice(0, safeEnd);
|
|
2542
|
+
}
|
|
2404
2543
|
var ENCRYPTED_CONTENT_KEY = "encryptedContent";
|
|
2405
2544
|
var ENCRYPTED_CONTENT_REDACTION_THRESHOLD = 256;
|
|
2406
2545
|
var DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS = 1e4;
|
|
@@ -2466,7 +2605,7 @@ function truncateStringByTokens(text, maxTokens) {
|
|
|
2466
2605
|
return text;
|
|
2467
2606
|
}
|
|
2468
2607
|
const buildCandidate = (sliceEnd) => {
|
|
2469
|
-
const visible = text
|
|
2608
|
+
const visible = safeSlice(text, sliceEnd);
|
|
2470
2609
|
return `${visible}
|
|
2471
2610
|
... [truncated ~${totalTokens - estimateTokenCount(visible)} tokens]`;
|
|
2472
2611
|
};
|
|
@@ -2922,11 +3061,13 @@ var OBSERVER_IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
2922
3061
|
"heif",
|
|
2923
3062
|
"avif"
|
|
2924
3063
|
]);
|
|
2925
|
-
function
|
|
2926
|
-
return createdAt ?
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
3064
|
+
function formatObserverDate(createdAt) {
|
|
3065
|
+
return createdAt ? `${createdAt.toLocaleDateString("en-US", {
|
|
3066
|
+
month: "short"
|
|
3067
|
+
})} ${createdAt.getDate()} ${createdAt.getFullYear()}` : "";
|
|
3068
|
+
}
|
|
3069
|
+
function formatObserverTime(createdAt) {
|
|
3070
|
+
return createdAt ? createdAt.toLocaleTimeString("en-US", {
|
|
2930
3071
|
hour: "numeric",
|
|
2931
3072
|
minute: "2-digit",
|
|
2932
3073
|
hour12: true
|
|
@@ -3017,19 +3158,88 @@ function formatObserverAttachmentPlaceholder(part, counter) {
|
|
|
3017
3158
|
const label = resolveObserverAttachmentLabel(part);
|
|
3018
3159
|
return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
|
|
3019
3160
|
}
|
|
3161
|
+
function formatObserverPartLine(title, body, time, previousTime) {
|
|
3162
|
+
const timeLabel = time && time !== previousTime ? `(${time})` : "";
|
|
3163
|
+
if (!title) {
|
|
3164
|
+
return timeLabel ? `${timeLabel}: ${body}` : body;
|
|
3165
|
+
}
|
|
3166
|
+
return `${title}${timeLabel ? ` ${timeLabel}` : ""}: ${body}`;
|
|
3167
|
+
}
|
|
3168
|
+
function normalizeObserverCreatedAt(createdAt) {
|
|
3169
|
+
if (createdAt instanceof Date) {
|
|
3170
|
+
if (Number.isNaN(createdAt.getTime())) {
|
|
3171
|
+
return void 0;
|
|
3172
|
+
}
|
|
3173
|
+
return createdAt;
|
|
3174
|
+
}
|
|
3175
|
+
if (typeof createdAt === "number" || typeof createdAt === "string") {
|
|
3176
|
+
const date = new Date(createdAt);
|
|
3177
|
+
if (Number.isNaN(date.getTime())) {
|
|
3178
|
+
return void 0;
|
|
3179
|
+
}
|
|
3180
|
+
return date;
|
|
3181
|
+
}
|
|
3182
|
+
return void 0;
|
|
3183
|
+
}
|
|
3184
|
+
function formatObserverLines(lines, context = {}) {
|
|
3185
|
+
const output = [];
|
|
3186
|
+
let previousDate = context.previousDate;
|
|
3187
|
+
let previousTime = context.previousTime;
|
|
3188
|
+
for (const line of lines) {
|
|
3189
|
+
if (line.date && line.date !== previousDate) {
|
|
3190
|
+
output.push(`${line.date}:`);
|
|
3191
|
+
previousDate = line.date;
|
|
3192
|
+
previousTime = void 0;
|
|
3193
|
+
}
|
|
3194
|
+
output.push(formatObserverPartLine(line.title, line.body, line.time, previousTime));
|
|
3195
|
+
previousTime = line.time || previousTime;
|
|
3196
|
+
}
|
|
3197
|
+
return {
|
|
3198
|
+
text: output.join("\n"),
|
|
3199
|
+
context: { previousDate, previousTime }
|
|
3200
|
+
};
|
|
3201
|
+
}
|
|
3202
|
+
function getTemporalGapMarkerText(msg) {
|
|
3203
|
+
const metadata = typeof msg.content === "object" && msg.content && "metadata" in msg.content ? msg.content.metadata : void 0;
|
|
3204
|
+
if (metadata?.reminderType === "temporal-gap" && typeof metadata.gapText === "string") {
|
|
3205
|
+
return metadata.gapText;
|
|
3206
|
+
}
|
|
3207
|
+
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") {
|
|
3208
|
+
return metadata.systemReminder.gapText;
|
|
3209
|
+
}
|
|
3210
|
+
return void 0;
|
|
3211
|
+
}
|
|
3020
3212
|
function formatObserverMessage(msg, counter, options) {
|
|
3021
3213
|
const maxLen = options?.maxPartLength;
|
|
3022
3214
|
const maxToolResultTokens = options?.maxToolResultTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
|
|
3023
|
-
const timestamp = formatObserverTimestamp(msg.createdAt);
|
|
3024
3215
|
const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
|
|
3025
|
-
const timestampStr = timestamp ? ` (${timestamp})` : "";
|
|
3026
3216
|
const attachments = [];
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3217
|
+
const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
|
|
3218
|
+
let lines = [];
|
|
3219
|
+
const temporalGapText = isTemporalGapMarker(msg) ? getTemporalGapMarkerText(msg) : void 0;
|
|
3220
|
+
const pushLine = (title, body, createdAt) => {
|
|
3221
|
+
if (!body) {
|
|
3222
|
+
return;
|
|
3223
|
+
}
|
|
3224
|
+
const normalizedCreatedAt = normalizeObserverCreatedAt(createdAt) ?? messageCreatedAt;
|
|
3225
|
+
lines.push({
|
|
3226
|
+
date: formatObserverDate(normalizedCreatedAt),
|
|
3227
|
+
time: formatObserverTime(normalizedCreatedAt),
|
|
3228
|
+
title,
|
|
3229
|
+
body
|
|
3230
|
+
});
|
|
3231
|
+
};
|
|
3232
|
+
if (temporalGapText) {
|
|
3233
|
+
pushLine("", temporalGapText, messageCreatedAt);
|
|
3234
|
+
} else if (typeof msg.content === "string") {
|
|
3235
|
+
pushLine(role, maybeTruncate(msg.content, maxLen), messageCreatedAt);
|
|
3030
3236
|
} else if (msg.content?.parts && Array.isArray(msg.content.parts) && msg.content.parts.length > 0) {
|
|
3031
|
-
|
|
3032
|
-
|
|
3237
|
+
msg.content.parts.forEach((part) => {
|
|
3238
|
+
const partCreatedAt = normalizeObserverCreatedAt(part.createdAt) ?? messageCreatedAt;
|
|
3239
|
+
if (part.type === "text") {
|
|
3240
|
+
pushLine(role, maybeTruncate(part.text, maxLen), partCreatedAt);
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3033
3243
|
if (part.type === "tool-invocation") {
|
|
3034
3244
|
const inv = part.toolInvocation;
|
|
3035
3245
|
if (inv.state === "result") {
|
|
@@ -3037,19 +3247,24 @@ function formatObserverMessage(msg, counter, options) {
|
|
|
3037
3247
|
part,
|
|
3038
3248
|
inv.result
|
|
3039
3249
|
);
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3250
|
+
pushLine(
|
|
3251
|
+
`Tool Result ${inv.toolName}`,
|
|
3252
|
+
maybeTruncate(formatToolResultForObserver(resultForObserver, { maxTokens: maxToolResultTokens }), maxLen),
|
|
3253
|
+
partCreatedAt
|
|
3254
|
+
);
|
|
3255
|
+
return;
|
|
3043
3256
|
}
|
|
3044
|
-
|
|
3045
|
-
return
|
|
3046
|
-
${maybeTruncate(argsStr, maxLen)}`;
|
|
3257
|
+
pushLine(`Tool Call ${inv.toolName}`, maybeTruncate(JSON.stringify(inv.args, null, 2), maxLen), partCreatedAt);
|
|
3258
|
+
return;
|
|
3047
3259
|
}
|
|
3048
3260
|
const partType = part.type;
|
|
3049
3261
|
if (partType === "reasoning") {
|
|
3050
3262
|
const reasoning = part.reasoning;
|
|
3051
|
-
if (reasoning)
|
|
3052
|
-
|
|
3263
|
+
if (!reasoning) {
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3266
|
+
pushLine("Reasoning", maybeTruncate(reasoning, maxLen), partCreatedAt);
|
|
3267
|
+
return;
|
|
3053
3268
|
}
|
|
3054
3269
|
if (partType === "image" || partType === "file") {
|
|
3055
3270
|
const attachment = part;
|
|
@@ -3057,40 +3272,58 @@ ${maybeTruncate(argsStr, maxLen)}`;
|
|
|
3057
3272
|
if (inputAttachment) {
|
|
3058
3273
|
attachments.push(inputAttachment);
|
|
3059
3274
|
}
|
|
3060
|
-
|
|
3275
|
+
pushLine(
|
|
3276
|
+
partType === "image" ? "Image" : "File",
|
|
3277
|
+
formatObserverAttachmentPlaceholder(attachment, counter),
|
|
3278
|
+
partCreatedAt
|
|
3279
|
+
);
|
|
3061
3280
|
}
|
|
3062
|
-
|
|
3063
|
-
return "";
|
|
3064
|
-
}).filter(Boolean).join("\n");
|
|
3281
|
+
});
|
|
3065
3282
|
} else if (msg.content?.content) {
|
|
3066
|
-
|
|
3283
|
+
pushLine(role, maybeTruncate(msg.content.content, maxLen), messageCreatedAt);
|
|
3067
3284
|
}
|
|
3068
|
-
if (
|
|
3069
|
-
return {
|
|
3285
|
+
if (lines.length === 0 && attachments.length === 0) {
|
|
3286
|
+
return { lines: [], attachments };
|
|
3070
3287
|
}
|
|
3071
3288
|
return {
|
|
3072
|
-
|
|
3073
|
-
${content}`,
|
|
3289
|
+
lines,
|
|
3074
3290
|
attachments
|
|
3075
3291
|
};
|
|
3076
3292
|
}
|
|
3077
3293
|
function formatMessagesForObserver(messages, options) {
|
|
3078
3294
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
3079
|
-
|
|
3295
|
+
const sections = [];
|
|
3296
|
+
let context = {};
|
|
3297
|
+
for (const message of messages) {
|
|
3298
|
+
const formatted = formatObserverMessage(message, counter, options);
|
|
3299
|
+
if (formatted.lines.length === 0) {
|
|
3300
|
+
continue;
|
|
3301
|
+
}
|
|
3302
|
+
const rendered = formatObserverLines(formatted.lines, context);
|
|
3303
|
+
if (!rendered.text) {
|
|
3304
|
+
continue;
|
|
3305
|
+
}
|
|
3306
|
+
sections.push(rendered.text);
|
|
3307
|
+
context = rendered.context;
|
|
3308
|
+
}
|
|
3309
|
+
return sections.join("\n");
|
|
3310
|
+
}
|
|
3311
|
+
function appendFormattedObserverMessage(content, formatted, context) {
|
|
3312
|
+
const rendered = formatObserverLines(formatted.lines, context);
|
|
3313
|
+
if (rendered.text) {
|
|
3314
|
+
content.push({ type: "text", text: rendered.text });
|
|
3315
|
+
}
|
|
3316
|
+
content.push(...formatted.attachments);
|
|
3317
|
+
return rendered.context;
|
|
3080
3318
|
}
|
|
3081
3319
|
function buildObserverHistoryMessage(messages, options) {
|
|
3082
3320
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
3083
3321
|
const content = [{ type: "text", text: "## New Message History to Observe\n\n" }];
|
|
3084
|
-
let
|
|
3322
|
+
let context = {};
|
|
3085
3323
|
messages.forEach((message) => {
|
|
3086
3324
|
const formatted = formatObserverMessage(message, counter, options);
|
|
3087
|
-
if (
|
|
3088
|
-
|
|
3089
|
-
content.push({ type: "text", text: "\n\n---\n\n" });
|
|
3090
|
-
}
|
|
3091
|
-
content.push({ type: "text", text: formatted.text });
|
|
3092
|
-
content.push(...formatted.attachments);
|
|
3093
|
-
visibleCount++;
|
|
3325
|
+
if (formatted.lines.length === 0 && formatted.attachments.length === 0) return;
|
|
3326
|
+
context = appendFormattedObserverMessage(content, formatted, context);
|
|
3094
3327
|
});
|
|
3095
3328
|
return {
|
|
3096
3329
|
role: "user",
|
|
@@ -3099,8 +3332,8 @@ function buildObserverHistoryMessage(messages, options) {
|
|
|
3099
3332
|
}
|
|
3100
3333
|
function maybeTruncate(str, maxLen) {
|
|
3101
3334
|
if (!maxLen || str.length <= maxLen) return str;
|
|
3102
|
-
const truncated = str
|
|
3103
|
-
const remaining = str.length -
|
|
3335
|
+
const truncated = safeSlice(str, maxLen);
|
|
3336
|
+
const remaining = str.length - truncated.length;
|
|
3104
3337
|
return `${truncated}
|
|
3105
3338
|
... [truncated ${remaining} characters]`;
|
|
3106
3339
|
}
|
|
@@ -3120,18 +3353,15 @@ The following messages are from ${threadOrder.length} different conversation thr
|
|
|
3120
3353
|
const messages = messagesByThread.get(threadId);
|
|
3121
3354
|
if (!messages || messages.length === 0) return;
|
|
3122
3355
|
const threadContent = [];
|
|
3123
|
-
let
|
|
3356
|
+
let context = {};
|
|
3357
|
+
let hasVisibleContent = false;
|
|
3124
3358
|
messages.forEach((message) => {
|
|
3125
3359
|
const formatted = formatObserverMessage(message, counter, options);
|
|
3126
|
-
if (
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
}
|
|
3130
|
-
threadContent.push({ type: "text", text: formatted.text });
|
|
3131
|
-
threadContent.push(...formatted.attachments);
|
|
3132
|
-
visibleCount++;
|
|
3360
|
+
if (formatted.lines.length === 0 && formatted.attachments.length === 0) return;
|
|
3361
|
+
context = appendFormattedObserverMessage(threadContent, formatted, context);
|
|
3362
|
+
hasVisibleContent = true;
|
|
3133
3363
|
});
|
|
3134
|
-
if (
|
|
3364
|
+
if (!hasVisibleContent) return;
|
|
3135
3365
|
content.push({ type: "text", text: `<thread id="${threadId}">
|
|
3136
3366
|
` });
|
|
3137
3367
|
content.push(...threadContent);
|
|
@@ -3407,7 +3637,7 @@ function sanitizeObservationLines(observations) {
|
|
|
3407
3637
|
let changed = false;
|
|
3408
3638
|
for (let i = 0; i < lines.length; i++) {
|
|
3409
3639
|
if (lines[i].length > MAX_OBSERVATION_LINE_CHARS) {
|
|
3410
|
-
lines[i] = lines[i]
|
|
3640
|
+
lines[i] = safeSlice(lines[i], MAX_OBSERVATION_LINE_CHARS) + " \u2026 [truncated]";
|
|
3411
3641
|
changed = true;
|
|
3412
3642
|
}
|
|
3413
3643
|
}
|
|
@@ -4010,12 +4240,44 @@ function validateCompression(reflectedTokens, targetThreshold) {
|
|
|
4010
4240
|
}
|
|
4011
4241
|
|
|
4012
4242
|
// src/processors/observational-memory/reflector-runner.ts
|
|
4243
|
+
function formatModelContext(provider, modelId) {
|
|
4244
|
+
if (provider && modelId) {
|
|
4245
|
+
return `${provider}/${modelId}`;
|
|
4246
|
+
}
|
|
4247
|
+
return modelId;
|
|
4248
|
+
}
|
|
4249
|
+
function getCurrentModel(model) {
|
|
4250
|
+
return formatModelContext(model?.provider, model?.modelId);
|
|
4251
|
+
}
|
|
4252
|
+
function getLastModelFromMessageList(messageList) {
|
|
4253
|
+
const messages = messageList?.get.all.db();
|
|
4254
|
+
if (!messages) return void 0;
|
|
4255
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
4256
|
+
const message = messages[i];
|
|
4257
|
+
if (!message || message.role !== "assistant" || !message.content || typeof message.content === "string") {
|
|
4258
|
+
continue;
|
|
4259
|
+
}
|
|
4260
|
+
for (let j = message.content.parts.length - 1; j >= 0; j--) {
|
|
4261
|
+
const part = message.content.parts[j];
|
|
4262
|
+
if (part?.type === "step-start" && typeof part.model === "string" && part.model.length > 0) {
|
|
4263
|
+
return part.model;
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
const metadata = message.content.metadata;
|
|
4267
|
+
const model = formatModelContext(metadata?.provider, metadata?.modelId);
|
|
4268
|
+
if (model) {
|
|
4269
|
+
return model;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
return void 0;
|
|
4273
|
+
}
|
|
4013
4274
|
async function withAbortCheck(fn, abortSignal) {
|
|
4014
4275
|
if (abortSignal?.aborted) throw new Error("The operation was aborted.");
|
|
4015
4276
|
const result = await fn();
|
|
4016
4277
|
if (abortSignal?.aborted) throw new Error("The operation was aborted.");
|
|
4017
4278
|
return result;
|
|
4018
4279
|
}
|
|
4280
|
+
var EARLY_ACTIVATION_SIZE_FLOOR_RATIO = 0.75;
|
|
4019
4281
|
var ReflectorRunner = class {
|
|
4020
4282
|
reflectionConfig;
|
|
4021
4283
|
observationConfig;
|
|
@@ -4049,13 +4311,30 @@ var ReflectorRunner = class {
|
|
|
4049
4311
|
model
|
|
4050
4312
|
});
|
|
4051
4313
|
}
|
|
4052
|
-
getObservationMarkerConfig() {
|
|
4314
|
+
getObservationMarkerConfig(record) {
|
|
4053
4315
|
return {
|
|
4054
4316
|
messageTokens: getMaxThreshold(this.observationConfig.messageTokens),
|
|
4055
|
-
observationTokens: getMaxThreshold(
|
|
4056
|
-
|
|
4317
|
+
observationTokens: getMaxThreshold(
|
|
4318
|
+
record ? this.getEffectiveReflectionTokens(record) : this.reflectionConfig.observationTokens
|
|
4319
|
+
),
|
|
4320
|
+
scope: this.scope,
|
|
4321
|
+
activateAfterIdle: this.reflectionConfig.activateAfterIdle
|
|
4057
4322
|
};
|
|
4058
4323
|
}
|
|
4324
|
+
/**
|
|
4325
|
+
* Resolve the effective reflection observationTokens for a record.
|
|
4326
|
+
* Only explicit per-record overrides (stored under `_overrides`) win;
|
|
4327
|
+
* the initial config snapshot is ignored so instance-level changes
|
|
4328
|
+
* still take effect for existing records.
|
|
4329
|
+
*/
|
|
4330
|
+
getEffectiveReflectionTokens(record) {
|
|
4331
|
+
const overrides = record.config?._overrides;
|
|
4332
|
+
const recordTokens = overrides?.reflection?.observationTokens;
|
|
4333
|
+
if (recordTokens) {
|
|
4334
|
+
return recordTokens;
|
|
4335
|
+
}
|
|
4336
|
+
return this.reflectionConfig.observationTokens;
|
|
4337
|
+
}
|
|
4059
4338
|
/**
|
|
4060
4339
|
* Call the Reflector agent with escalating compression levels.
|
|
4061
4340
|
*/
|
|
@@ -4165,8 +4444,9 @@ var ReflectorRunner = class {
|
|
|
4165
4444
|
recordId: streamContext.recordId,
|
|
4166
4445
|
threadId: streamContext.threadId
|
|
4167
4446
|
});
|
|
4168
|
-
await streamContext.writer.custom(failedMarker).catch(() => {
|
|
4447
|
+
await streamContext.writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
4169
4448
|
});
|
|
4449
|
+
await this.persistMarkerToStorage(failedMarker, streamContext.threadId, streamContext.resourceId);
|
|
4170
4450
|
const retryCycleId = crypto.randomUUID();
|
|
4171
4451
|
streamContext.cycleId = retryCycleId;
|
|
4172
4452
|
const startMarker = createObservationStartMarker({
|
|
@@ -4179,8 +4459,9 @@ var ReflectorRunner = class {
|
|
|
4179
4459
|
config: this.getObservationMarkerConfig()
|
|
4180
4460
|
});
|
|
4181
4461
|
streamContext.startedAt = startMarker.data.startedAt;
|
|
4182
|
-
await streamContext.writer.custom(startMarker).catch(() => {
|
|
4462
|
+
await streamContext.writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
4183
4463
|
});
|
|
4464
|
+
await this.persistMarkerToStorage(startMarker, streamContext.threadId, streamContext.resourceId);
|
|
4184
4465
|
}
|
|
4185
4466
|
currentLevel = Math.min(currentLevel + 1, maxLevel);
|
|
4186
4467
|
}
|
|
@@ -4193,7 +4474,7 @@ var ReflectorRunner = class {
|
|
|
4193
4474
|
/**
|
|
4194
4475
|
* Start an async buffered reflection in the background.
|
|
4195
4476
|
*/
|
|
4196
|
-
startAsyncBufferedReflection(record, observationTokens, lockKey, writer, requestContext, observabilityContext) {
|
|
4477
|
+
startAsyncBufferedReflection(record, observationTokens, lockKey, writer, requestContext, observabilityContext, reflectionHooks) {
|
|
4197
4478
|
const bufferKey = this.buffering.getReflectionBufferKey(lockKey);
|
|
4198
4479
|
if (this.buffering.isAsyncBufferingInProgress(bufferKey)) {
|
|
4199
4480
|
return;
|
|
@@ -4203,7 +4484,10 @@ var ReflectorRunner = class {
|
|
|
4203
4484
|
this.storage.setBufferingReflectionFlag(record.id, true).catch((err) => {
|
|
4204
4485
|
omError("[OM] Failed to set buffering reflection flag", err);
|
|
4205
4486
|
});
|
|
4206
|
-
|
|
4487
|
+
reflectionHooks?.onReflectionStart?.();
|
|
4488
|
+
const asyncOp = this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext, observabilityContext).then((usage) => {
|
|
4489
|
+
reflectionHooks?.onReflectionEnd?.({ usage });
|
|
4490
|
+
}).catch(async (error) => {
|
|
4207
4491
|
if (writer) {
|
|
4208
4492
|
const failedMarker = createBufferingFailedMarker({
|
|
4209
4493
|
cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
|
|
@@ -4214,11 +4498,15 @@ var ReflectorRunner = class {
|
|
|
4214
4498
|
recordId: record.id,
|
|
4215
4499
|
threadId: record.threadId ?? ""
|
|
4216
4500
|
});
|
|
4217
|
-
void writer.custom(failedMarker).catch(() => {
|
|
4501
|
+
void writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
4218
4502
|
});
|
|
4219
4503
|
await this.persistMarkerToStorage(failedMarker, record.threadId ?? "", record.resourceId ?? void 0);
|
|
4220
4504
|
}
|
|
4221
4505
|
omError("[OM] Async buffered reflection failed", error);
|
|
4506
|
+
reflectionHooks?.onReflectionEnd?.({
|
|
4507
|
+
usage: void 0,
|
|
4508
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
4509
|
+
});
|
|
4222
4510
|
BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
|
|
4223
4511
|
}).finally(() => {
|
|
4224
4512
|
BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
|
|
@@ -4237,7 +4525,7 @@ var ReflectorRunner = class {
|
|
|
4237
4525
|
const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
4238
4526
|
const currentRecord = freshRecord ?? record;
|
|
4239
4527
|
const observationTokens = currentRecord.observationTokenCount ?? 0;
|
|
4240
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
4528
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(currentRecord));
|
|
4241
4529
|
const bufferActivation = this.reflectionConfig.bufferActivation ?? 0.5;
|
|
4242
4530
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4243
4531
|
const cycleId = `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
@@ -4266,10 +4554,15 @@ var ReflectorRunner = class {
|
|
|
4266
4554
|
recordId: record.id,
|
|
4267
4555
|
threadId: record.threadId ?? "",
|
|
4268
4556
|
threadIds: record.threadId ? [record.threadId] : [],
|
|
4269
|
-
config: this.getObservationMarkerConfig()
|
|
4557
|
+
config: this.getObservationMarkerConfig(currentRecord)
|
|
4270
4558
|
});
|
|
4271
|
-
void writer.custom(startMarker).catch(() => {
|
|
4559
|
+
void writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
4272
4560
|
});
|
|
4561
|
+
await this.persistMarkerToStorage(
|
|
4562
|
+
startMarker,
|
|
4563
|
+
currentRecord.threadId ?? "",
|
|
4564
|
+
currentRecord.resourceId ?? void 0
|
|
4565
|
+
);
|
|
4273
4566
|
}
|
|
4274
4567
|
const compressionStartLevel = await this.getCompressionStartLevel(requestContext);
|
|
4275
4568
|
const reflectResult = await this.call(
|
|
@@ -4308,26 +4601,35 @@ var ReflectorRunner = class {
|
|
|
4308
4601
|
threadId: currentRecord.threadId ?? "",
|
|
4309
4602
|
observations: reflectResult.observations
|
|
4310
4603
|
});
|
|
4311
|
-
void writer.custom(endMarker).catch(() => {
|
|
4604
|
+
void writer.custom({ ...endMarker, transient: true }).catch(() => {
|
|
4312
4605
|
});
|
|
4313
4606
|
await this.persistMarkerToStorage(endMarker, currentRecord.threadId ?? "", currentRecord.resourceId ?? void 0);
|
|
4314
4607
|
}
|
|
4608
|
+
return reflectResult.usage;
|
|
4315
4609
|
}
|
|
4316
4610
|
/**
|
|
4317
4611
|
* Try to activate buffered reflection when threshold is reached.
|
|
4318
|
-
* Returns
|
|
4612
|
+
* Returns a discriminated result so the caller can distinguish between
|
|
4613
|
+
* "activated", "no buffer present", and "suppressed by overshoot guard"
|
|
4614
|
+
* without re-deriving that state.
|
|
4319
4615
|
*/
|
|
4320
|
-
async tryActivateBufferedReflection(record, lockKey, writer, messageList) {
|
|
4616
|
+
async tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata) {
|
|
4321
4617
|
const bufferKey = this.buffering.getReflectionBufferKey(lockKey);
|
|
4322
4618
|
const asyncOp = BufferingCoordinator.asyncBufferingOps.get(bufferKey);
|
|
4323
4619
|
if (asyncOp) {
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
]);
|
|
4330
|
-
|
|
4620
|
+
if (activationMetadata?.triggeredBy === "ttl" || activationMetadata?.triggeredBy === "provider_change") {
|
|
4621
|
+
omDebug(
|
|
4622
|
+
`[OM:reflect] tryActivateBufferedReflection: async op in progress, not blocking for ${activationMetadata.triggeredBy} trigger`
|
|
4623
|
+
);
|
|
4624
|
+
} else {
|
|
4625
|
+
omDebug(`[OM:reflect] tryActivateBufferedReflection: waiting for in-progress op...`);
|
|
4626
|
+
try {
|
|
4627
|
+
await Promise.race([
|
|
4628
|
+
asyncOp,
|
|
4629
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 5e3))
|
|
4630
|
+
]);
|
|
4631
|
+
} catch {
|
|
4632
|
+
}
|
|
4331
4633
|
}
|
|
4332
4634
|
}
|
|
4333
4635
|
const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
@@ -4338,8 +4640,8 @@ var ReflectorRunner = class {
|
|
|
4338
4640
|
`[OM:reflect] tryActivateBufferedReflection: freshRecord.id=${freshRecord?.id}, freshBufferedReflection=${freshRecord?.bufferedReflection ? "present (" + freshRecord.bufferedReflection.length + " chars)" : "empty"}, freshObsTokens=${freshRecord?.observationTokenCount}`
|
|
4339
4641
|
);
|
|
4340
4642
|
if (!freshRecord?.bufferedReflection) {
|
|
4341
|
-
omDebug(`[OM:reflect] tryActivateBufferedReflection: no buffered reflection after re-fetch
|
|
4342
|
-
return
|
|
4643
|
+
omDebug(`[OM:reflect] tryActivateBufferedReflection: no buffered reflection after re-fetch`);
|
|
4644
|
+
return { status: "no-buffer" };
|
|
4343
4645
|
}
|
|
4344
4646
|
const beforeTokens = freshRecord.observationTokenCount ?? 0;
|
|
4345
4647
|
const reflectedLineCount = freshRecord.reflectedObservationLineCount ?? 0;
|
|
@@ -4351,6 +4653,26 @@ var ReflectorRunner = class {
|
|
|
4351
4653
|
|
|
4352
4654
|
${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
4353
4655
|
const combinedTokenCount = this.tokenCounter.countObservations(combinedObservations);
|
|
4656
|
+
if (activationMetadata?.triggeredBy === "ttl" || activationMetadata?.triggeredBy === "provider_change") {
|
|
4657
|
+
const unreflectedTailTokens = unreflectedContent ? this.tokenCounter.countObservations(unreflectedContent) : 0;
|
|
4658
|
+
const bufferedReflectionTokens = freshRecord.bufferedReflectionTokens ?? 0;
|
|
4659
|
+
if (unreflectedTailTokens < bufferedReflectionTokens) {
|
|
4660
|
+
omDebug(
|
|
4661
|
+
`[OM:reflect] tryActivateBufferedReflection: suppressing early ${activationMetadata.triggeredBy} activation \u2014 unreflectedTailTokens=${unreflectedTailTokens} < bufferedReflectionTokens=${bufferedReflectionTokens}; keeping buffer for threshold activation`
|
|
4662
|
+
);
|
|
4663
|
+
return { status: "suppressed", reason: "composition" };
|
|
4664
|
+
}
|
|
4665
|
+
const bufferActivation = this.reflectionConfig.bufferActivation;
|
|
4666
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(freshRecord));
|
|
4667
|
+
const regularActivationTarget = reflectThreshold * (1 - bufferActivation);
|
|
4668
|
+
const minCombinedTokens = Math.round(regularActivationTarget * EARLY_ACTIVATION_SIZE_FLOOR_RATIO);
|
|
4669
|
+
if (combinedTokenCount < minCombinedTokens) {
|
|
4670
|
+
omDebug(
|
|
4671
|
+
`[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`
|
|
4672
|
+
);
|
|
4673
|
+
return { status: "suppressed", reason: "size" };
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4354
4676
|
omDebug(
|
|
4355
4677
|
`[OM:reflect] tryActivateBufferedReflection: activating, beforeTokens=${beforeTokens}, combinedTokenCount=${combinedTokenCount}, reflectedLineCount=${reflectedLineCount}, unreflectedLines=${unreflectedLines.length}`
|
|
4356
4678
|
);
|
|
@@ -4377,9 +4699,14 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4377
4699
|
threadId: freshRecord.threadId ?? "",
|
|
4378
4700
|
generationCount: afterRecord?.generationCount ?? freshRecord.generationCount ?? 0,
|
|
4379
4701
|
observations: afterRecord?.activeObservations,
|
|
4380
|
-
|
|
4702
|
+
triggeredBy: activationMetadata?.triggeredBy,
|
|
4703
|
+
lastActivityAt: activationMetadata?.lastActivityAt,
|
|
4704
|
+
ttlExpiredMs: activationMetadata?.ttlExpiredMs,
|
|
4705
|
+
previousModel: activationMetadata?.previousModel,
|
|
4706
|
+
currentModel: activationMetadata?.currentModel,
|
|
4707
|
+
config: this.getObservationMarkerConfig(freshRecord)
|
|
4381
4708
|
});
|
|
4382
|
-
void writer.custom(activationMarker).catch(() => {
|
|
4709
|
+
void writer.custom({ ...activationMarker, transient: true }).catch(() => {
|
|
4383
4710
|
});
|
|
4384
4711
|
await this.persistMarkerToMessage(
|
|
4385
4712
|
activationMarker,
|
|
@@ -4389,7 +4716,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4389
4716
|
);
|
|
4390
4717
|
}
|
|
4391
4718
|
BufferingCoordinator.reflectionBufferCycleIds.delete(bufferKey);
|
|
4392
|
-
return
|
|
4719
|
+
return { status: "activated" };
|
|
4393
4720
|
}
|
|
4394
4721
|
/**
|
|
4395
4722
|
* Check if reflection needed and trigger if so.
|
|
@@ -4403,12 +4730,15 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4403
4730
|
writer,
|
|
4404
4731
|
abortSignal,
|
|
4405
4732
|
messageList,
|
|
4733
|
+
currentModel,
|
|
4406
4734
|
reflectionHooks,
|
|
4407
4735
|
requestContext,
|
|
4408
|
-
observabilityContext
|
|
4736
|
+
observabilityContext,
|
|
4737
|
+
lastActivityAt,
|
|
4738
|
+
threadId: requestedThreadId
|
|
4409
4739
|
} = opts;
|
|
4410
4740
|
const lockKey = this.buffering.getLockKey(record.threadId, record.resourceId);
|
|
4411
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
4741
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
4412
4742
|
if (this.buffering.isAsyncReflectionEnabled() && observationTokens < reflectThreshold) {
|
|
4413
4743
|
const shouldTrigger = (() => {
|
|
4414
4744
|
if (!this.buffering.isAsyncReflectionEnabled()) return false;
|
|
@@ -4432,13 +4762,28 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4432
4762
|
lockKey,
|
|
4433
4763
|
writer,
|
|
4434
4764
|
requestContext,
|
|
4435
|
-
observabilityContext
|
|
4765
|
+
observabilityContext,
|
|
4766
|
+
reflectionHooks
|
|
4436
4767
|
);
|
|
4437
4768
|
}
|
|
4438
4769
|
}
|
|
4439
|
-
|
|
4770
|
+
const activateAfterIdle = this.reflectionConfig.activateAfterIdle;
|
|
4771
|
+
const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
|
|
4772
|
+
const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
|
|
4773
|
+
const actorModel = getCurrentModel(currentModel);
|
|
4774
|
+
const lastModel = getLastModelFromMessageList(messageList);
|
|
4775
|
+
const providerChanged = this.reflectionConfig.activateOnProviderChange === true && didProviderChange(actorModel, lastModel);
|
|
4776
|
+
if (observationTokens < reflectThreshold && !ttlExpired && !providerChanged) {
|
|
4440
4777
|
return;
|
|
4441
4778
|
}
|
|
4779
|
+
const activationTriggeredBy = observationTokens >= reflectThreshold ? "threshold" : providerChanged ? "provider_change" : "ttl";
|
|
4780
|
+
const activationMetadata = {
|
|
4781
|
+
triggeredBy: activationTriggeredBy,
|
|
4782
|
+
lastActivityAt: activationTriggeredBy === "ttl" ? lastActivityAt : void 0,
|
|
4783
|
+
ttlExpiredMs: activationTriggeredBy === "ttl" ? ttlExpiredMs : void 0,
|
|
4784
|
+
previousModel: activationTriggeredBy === "provider_change" ? lastModel : void 0,
|
|
4785
|
+
currentModel: activationTriggeredBy === "provider_change" ? actorModel : void 0
|
|
4786
|
+
};
|
|
4442
4787
|
if (record.isReflecting) {
|
|
4443
4788
|
if (isOpActiveInProcess(record.id, "reflecting")) {
|
|
4444
4789
|
omDebug(`[OM:reflect] isReflecting=true and active in this process, skipping`);
|
|
@@ -4448,8 +4793,20 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4448
4793
|
await this.storage.setReflectingFlag(record.id, false);
|
|
4449
4794
|
}
|
|
4450
4795
|
if (this.buffering.isAsyncReflectionEnabled()) {
|
|
4451
|
-
const
|
|
4452
|
-
|
|
4796
|
+
const activationResult = await this.tryActivateBufferedReflection(
|
|
4797
|
+
record,
|
|
4798
|
+
lockKey,
|
|
4799
|
+
writer,
|
|
4800
|
+
messageList,
|
|
4801
|
+
activationMetadata
|
|
4802
|
+
);
|
|
4803
|
+
if (activationResult.status === "activated") {
|
|
4804
|
+
return;
|
|
4805
|
+
}
|
|
4806
|
+
if (activationResult.status === "suppressed") {
|
|
4807
|
+
omDebug(
|
|
4808
|
+
`[OM:reflect] skipping sync fallback / re-buffer after suppressed early ${activationMetadata.triggeredBy} activation (reason=${activationResult.reason})`
|
|
4809
|
+
);
|
|
4453
4810
|
return;
|
|
4454
4811
|
}
|
|
4455
4812
|
if (this.reflectionConfig.blockAfter && observationTokens >= this.reflectionConfig.blockAfter) {
|
|
@@ -4457,6 +4814,13 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4457
4814
|
`[OM:reflect] blockAfter exceeded (${observationTokens} >= ${this.reflectionConfig.blockAfter}), falling through to sync reflection`
|
|
4458
4815
|
);
|
|
4459
4816
|
} else {
|
|
4817
|
+
const activationPoint = reflectThreshold * this.reflectionConfig.bufferActivation;
|
|
4818
|
+
if (observationTokens < activationPoint) {
|
|
4819
|
+
omDebug(
|
|
4820
|
+
`[OM:reflect] skipping async reflection \u2014 observationTokens (${observationTokens}) below activation point (${activationPoint}), triggered by ${activationTriggeredBy}`
|
|
4821
|
+
);
|
|
4822
|
+
return;
|
|
4823
|
+
}
|
|
4460
4824
|
omDebug(
|
|
4461
4825
|
`[OM:reflect] async activation failed, no blockAfter or below it (obsTokens=${observationTokens}, blockAfter=${this.reflectionConfig.blockAfter}) \u2014 starting background reflection`
|
|
4462
4826
|
);
|
|
@@ -4466,7 +4830,8 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4466
4830
|
lockKey,
|
|
4467
4831
|
writer,
|
|
4468
4832
|
requestContext,
|
|
4469
|
-
observabilityContext
|
|
4833
|
+
observabilityContext,
|
|
4834
|
+
reflectionHooks
|
|
4470
4835
|
);
|
|
4471
4836
|
return;
|
|
4472
4837
|
}
|
|
@@ -4476,7 +4841,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4476
4841
|
registerOp(record.id, "reflecting");
|
|
4477
4842
|
const cycleId = crypto.randomUUID();
|
|
4478
4843
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4479
|
-
const threadId =
|
|
4844
|
+
const threadId = requestedThreadId ?? "unknown";
|
|
4480
4845
|
if (writer) {
|
|
4481
4846
|
const startMarker = createObservationStartMarker({
|
|
4482
4847
|
cycleId,
|
|
@@ -4485,10 +4850,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4485
4850
|
recordId: record.id,
|
|
4486
4851
|
threadId,
|
|
4487
4852
|
threadIds: [threadId],
|
|
4488
|
-
config: this.getObservationMarkerConfig()
|
|
4853
|
+
config: this.getObservationMarkerConfig(record)
|
|
4489
4854
|
});
|
|
4490
|
-
await writer.custom(startMarker).catch(() => {
|
|
4855
|
+
await writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
4491
4856
|
});
|
|
4857
|
+
await this.persistMarkerToStorage(startMarker, threadId, record.resourceId ?? void 0);
|
|
4492
4858
|
}
|
|
4493
4859
|
this.emitDebugEvent({
|
|
4494
4860
|
type: "reflection_triggered",
|
|
@@ -4503,8 +4869,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4503
4869
|
cycleId,
|
|
4504
4870
|
startedAt,
|
|
4505
4871
|
recordId: record.id,
|
|
4506
|
-
threadId
|
|
4872
|
+
threadId,
|
|
4873
|
+
resourceId: record.resourceId ?? void 0
|
|
4507
4874
|
} : void 0;
|
|
4875
|
+
let reflectionUsage;
|
|
4876
|
+
let reflectionError;
|
|
4508
4877
|
try {
|
|
4509
4878
|
const compressionStartLevel = await this.getCompressionStartLevel(requestContext);
|
|
4510
4879
|
const reflectResult = await this.call(
|
|
@@ -4518,6 +4887,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4518
4887
|
requestContext,
|
|
4519
4888
|
observabilityContext
|
|
4520
4889
|
);
|
|
4890
|
+
reflectionUsage = reflectResult.usage;
|
|
4521
4891
|
const reflectionTokenCount = this.tokenCounter.countObservations(reflectResult.observations);
|
|
4522
4892
|
await this.storage.createReflectionGeneration({
|
|
4523
4893
|
currentRecord: record,
|
|
@@ -4535,8 +4905,9 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4535
4905
|
recordId: record.id,
|
|
4536
4906
|
threadId
|
|
4537
4907
|
});
|
|
4538
|
-
await writer.custom(endMarker).catch(() => {
|
|
4908
|
+
await writer.custom({ ...endMarker, transient: true }).catch(() => {
|
|
4539
4909
|
});
|
|
4910
|
+
await this.persistMarkerToStorage(endMarker, threadId, record.resourceId ?? void 0);
|
|
4540
4911
|
}
|
|
4541
4912
|
this.emitDebugEvent({
|
|
4542
4913
|
type: "reflection_complete",
|
|
@@ -4559,89 +4930,413 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4559
4930
|
recordId: record.id,
|
|
4560
4931
|
threadId
|
|
4561
4932
|
});
|
|
4562
|
-
await writer.custom(failedMarker).catch(() => {
|
|
4933
|
+
await writer.custom({ ...failedMarker, transient: true }).catch(() => {
|
|
4563
4934
|
});
|
|
4935
|
+
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
|
|
4564
4936
|
}
|
|
4937
|
+
reflectionError = error instanceof Error ? error : new Error(String(error));
|
|
4565
4938
|
if (abortSignal?.aborted) {
|
|
4566
4939
|
throw error;
|
|
4567
4940
|
}
|
|
4568
4941
|
omError("[OM] Reflection failed", error);
|
|
4569
4942
|
} finally {
|
|
4570
4943
|
await this.storage.setReflectingFlag(record.id, false);
|
|
4571
|
-
reflectionHooks?.onReflectionEnd?.();
|
|
4944
|
+
reflectionHooks?.onReflectionEnd?.({ usage: reflectionUsage, error: reflectionError });
|
|
4572
4945
|
unregisterOp(record.id, "reflecting");
|
|
4573
4946
|
}
|
|
4574
4947
|
}
|
|
4575
4948
|
};
|
|
4576
|
-
|
|
4577
|
-
"
|
|
4578
|
-
"jpg",
|
|
4579
|
-
"jpeg",
|
|
4580
|
-
"webp",
|
|
4581
|
-
"gif",
|
|
4582
|
-
"bmp",
|
|
4583
|
-
"tiff",
|
|
4584
|
-
"tif",
|
|
4585
|
-
"heic",
|
|
4586
|
-
"heif",
|
|
4587
|
-
"avif"
|
|
4588
|
-
]);
|
|
4589
|
-
var TOKEN_ESTIMATE_CACHE_VERSION = 6;
|
|
4590
|
-
var DEFAULT_IMAGE_ESTIMATOR = {
|
|
4591
|
-
baseTokens: 85,
|
|
4592
|
-
tileTokens: 170,
|
|
4593
|
-
fallbackTiles: 4
|
|
4594
|
-
};
|
|
4595
|
-
var GOOGLE_LEGACY_IMAGE_TOKENS_PER_TILE = 258;
|
|
4596
|
-
var GOOGLE_GEMINI_3_IMAGE_TOKENS_BY_RESOLUTION = {
|
|
4597
|
-
low: 280,
|
|
4598
|
-
medium: 560,
|
|
4599
|
-
high: 1120,
|
|
4600
|
-
ultra_high: 2240,
|
|
4601
|
-
unspecified: 1120
|
|
4602
|
-
};
|
|
4603
|
-
var ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
4604
|
-
var ANTHROPIC_IMAGE_MAX_LONG_EDGE = 1568;
|
|
4605
|
-
var GOOGLE_MEDIA_RESOLUTION_VALUES = /* @__PURE__ */ new Set([
|
|
4606
|
-
"low",
|
|
4607
|
-
"medium",
|
|
4608
|
-
"high",
|
|
4609
|
-
"ultra_high",
|
|
4610
|
-
"unspecified"
|
|
4611
|
-
]);
|
|
4612
|
-
var ATTACHMENT_COUNT_TIMEOUT_MS = 2e4;
|
|
4613
|
-
var REMOTE_IMAGE_PROBE_TIMEOUT_MS = 2500;
|
|
4614
|
-
var PROVIDER_API_KEY_ENV_VARS = {
|
|
4615
|
-
openai: ["OPENAI_API_KEY"],
|
|
4616
|
-
google: ["GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY"],
|
|
4617
|
-
anthropic: ["ANTHROPIC_API_KEY"]
|
|
4618
|
-
};
|
|
4619
|
-
function getPartMastraMetadata(part) {
|
|
4620
|
-
return part.providerMetadata?.mastra;
|
|
4949
|
+
function getOmReproCaptureDir() {
|
|
4950
|
+
return process.env.OM_REPRO_CAPTURE_DIR ?? ".mastra-om-repro";
|
|
4621
4951
|
}
|
|
4622
|
-
function
|
|
4623
|
-
const
|
|
4624
|
-
|
|
4625
|
-
typedPart.providerMetadata.mastra ??= {};
|
|
4626
|
-
return typedPart.providerMetadata.mastra;
|
|
4952
|
+
function sanitizeCapturePathSegment(value) {
|
|
4953
|
+
const sanitized = value.replace(/[\\/]+/g, "_").replace(/\.{2,}/g, "_").trim();
|
|
4954
|
+
return sanitized.length > 0 ? sanitized : "unknown-thread";
|
|
4627
4955
|
}
|
|
4628
|
-
function
|
|
4629
|
-
|
|
4630
|
-
|
|
4956
|
+
function isOmReproCaptureEnabled() {
|
|
4957
|
+
return process.env.OM_REPRO_CAPTURE === "1";
|
|
4958
|
+
}
|
|
4959
|
+
function safeCaptureJson(value) {
|
|
4960
|
+
return JSON.parse(
|
|
4961
|
+
JSON.stringify(value, (_key, current) => {
|
|
4962
|
+
if (typeof current === "bigint") return current.toString();
|
|
4963
|
+
if (typeof current === "function") return "[function]";
|
|
4964
|
+
if (typeof current === "symbol") return current.toString();
|
|
4965
|
+
if (current instanceof Error) return { name: current.name, message: current.message, stack: current.stack };
|
|
4966
|
+
if (current instanceof Set) return { __type: "Set", values: Array.from(current.values()) };
|
|
4967
|
+
if (current instanceof Map) return { __type: "Map", entries: Array.from(current.entries()) };
|
|
4968
|
+
return current;
|
|
4969
|
+
})
|
|
4970
|
+
);
|
|
4971
|
+
}
|
|
4972
|
+
function safeCaptureJsonOrError(value) {
|
|
4973
|
+
try {
|
|
4974
|
+
return { ok: true, value: safeCaptureJson(value) };
|
|
4975
|
+
} catch (error) {
|
|
4976
|
+
return {
|
|
4977
|
+
ok: false,
|
|
4978
|
+
error: safeCaptureJson({
|
|
4979
|
+
message: error instanceof Error ? error.message : String(error),
|
|
4980
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
4981
|
+
inspected: inspect(value, { depth: 3, maxArrayLength: 20, breakLength: 120 })
|
|
4982
|
+
})
|
|
4983
|
+
};
|
|
4631
4984
|
}
|
|
4632
|
-
return content.metadata?.mastra;
|
|
4633
4985
|
}
|
|
4634
|
-
function
|
|
4635
|
-
if (!
|
|
4986
|
+
function formatCaptureDate(value) {
|
|
4987
|
+
if (!value) return void 0;
|
|
4988
|
+
if (value instanceof Date) return value.toISOString();
|
|
4989
|
+
try {
|
|
4990
|
+
return new Date(value).toISOString();
|
|
4991
|
+
} catch {
|
|
4636
4992
|
return void 0;
|
|
4637
4993
|
}
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
}
|
|
4643
|
-
|
|
4644
|
-
return
|
|
4994
|
+
}
|
|
4995
|
+
function summarizeOmTurn(value) {
|
|
4996
|
+
if (!value || typeof value !== "object") {
|
|
4997
|
+
return value;
|
|
4998
|
+
}
|
|
4999
|
+
const turn = value;
|
|
5000
|
+
return {
|
|
5001
|
+
__type: "ObservationTurn",
|
|
5002
|
+
threadId: turn.threadId,
|
|
5003
|
+
resourceId: turn.resourceId,
|
|
5004
|
+
started: turn._started,
|
|
5005
|
+
ended: turn._ended,
|
|
5006
|
+
generationCountAtStart: turn._generationCountAtStart,
|
|
5007
|
+
record: turn._record ? {
|
|
5008
|
+
id: turn._record.id,
|
|
5009
|
+
scope: turn._record.scope,
|
|
5010
|
+
threadId: turn._record.threadId,
|
|
5011
|
+
resourceId: turn._record.resourceId,
|
|
5012
|
+
createdAt: formatCaptureDate(turn._record.createdAt),
|
|
5013
|
+
updatedAt: formatCaptureDate(turn._record.updatedAt),
|
|
5014
|
+
lastObservedAt: formatCaptureDate(turn._record.lastObservedAt),
|
|
5015
|
+
generationCount: turn._record.generationCount,
|
|
5016
|
+
observationTokenCount: turn._record.observationTokenCount,
|
|
5017
|
+
pendingMessageTokens: turn._record.pendingMessageTokens,
|
|
5018
|
+
isBufferingObservation: turn._record.isBufferingObservation,
|
|
5019
|
+
isBufferingReflection: turn._record.isBufferingReflection
|
|
5020
|
+
} : void 0,
|
|
5021
|
+
context: turn._context ? {
|
|
5022
|
+
messageCount: Array.isArray(turn._context.messages) ? turn._context.messages.length : void 0,
|
|
5023
|
+
hasSystemMessage: Array.isArray(turn._context.systemMessage) ? turn._context.systemMessage.length > 0 : Boolean(turn._context.systemMessage),
|
|
5024
|
+
continuationId: turn._context.continuation?.id,
|
|
5025
|
+
hasOtherThreadsContext: Boolean(turn._context.otherThreadsContext),
|
|
5026
|
+
recordId: turn._context.record?.id
|
|
5027
|
+
} : void 0,
|
|
5028
|
+
currentStep: turn._currentStep ? {
|
|
5029
|
+
stepNumber: turn._currentStep.stepNumber,
|
|
5030
|
+
prepared: turn._currentStep._prepared,
|
|
5031
|
+
context: turn._currentStep._context ? {
|
|
5032
|
+
activated: turn._currentStep._context.activated,
|
|
5033
|
+
observed: turn._currentStep._context.observed,
|
|
5034
|
+
buffered: turn._currentStep._context.buffered,
|
|
5035
|
+
reflected: turn._currentStep._context.reflected,
|
|
5036
|
+
didThresholdCleanup: turn._currentStep._context.didThresholdCleanup,
|
|
5037
|
+
messageCount: Array.isArray(turn._currentStep._context.messages) ? turn._currentStep._context.messages.length : void 0,
|
|
5038
|
+
systemMessageCount: Array.isArray(turn._currentStep._context.systemMessage) ? turn._currentStep._context.systemMessage.length : void 0
|
|
5039
|
+
} : void 0
|
|
5040
|
+
} : void 0
|
|
5041
|
+
};
|
|
5042
|
+
}
|
|
5043
|
+
function sanitizeCaptureState(rawState) {
|
|
5044
|
+
return Object.fromEntries(
|
|
5045
|
+
Object.entries(rawState).map(([key, value]) => {
|
|
5046
|
+
if (key === "__omTurn") {
|
|
5047
|
+
return [key, summarizeOmTurn(value)];
|
|
5048
|
+
}
|
|
5049
|
+
return [key, value];
|
|
5050
|
+
})
|
|
5051
|
+
);
|
|
5052
|
+
}
|
|
5053
|
+
function buildReproMessageFingerprint(message) {
|
|
5054
|
+
const createdAt = message.createdAt instanceof Date ? message.createdAt.toISOString() : message.createdAt ? new Date(message.createdAt).toISOString() : "";
|
|
5055
|
+
return JSON.stringify({
|
|
5056
|
+
role: message.role,
|
|
5057
|
+
createdAt,
|
|
5058
|
+
content: message.content
|
|
5059
|
+
});
|
|
5060
|
+
}
|
|
5061
|
+
function inferReproIdRemap(preMessages, postMessages) {
|
|
5062
|
+
const preByFingerprint = /* @__PURE__ */ new Map();
|
|
5063
|
+
const postByFingerprint = /* @__PURE__ */ new Map();
|
|
5064
|
+
for (const message of preMessages) {
|
|
5065
|
+
if (!message.id) continue;
|
|
5066
|
+
const fingerprint = buildReproMessageFingerprint(message);
|
|
5067
|
+
const list = preByFingerprint.get(fingerprint) ?? [];
|
|
5068
|
+
list.push(message.id);
|
|
5069
|
+
preByFingerprint.set(fingerprint, list);
|
|
5070
|
+
}
|
|
5071
|
+
for (const message of postMessages) {
|
|
5072
|
+
if (!message.id) continue;
|
|
5073
|
+
const fingerprint = buildReproMessageFingerprint(message);
|
|
5074
|
+
const list = postByFingerprint.get(fingerprint) ?? [];
|
|
5075
|
+
list.push(message.id);
|
|
5076
|
+
postByFingerprint.set(fingerprint, list);
|
|
5077
|
+
}
|
|
5078
|
+
const remap = [];
|
|
5079
|
+
for (const [fingerprint, preIds] of preByFingerprint.entries()) {
|
|
5080
|
+
const postIds = postByFingerprint.get(fingerprint);
|
|
5081
|
+
if (!postIds || preIds.length !== 1 || postIds.length !== 1) continue;
|
|
5082
|
+
const fromId = preIds[0];
|
|
5083
|
+
const toId = postIds[0];
|
|
5084
|
+
if (!fromId || !toId || fromId === toId) {
|
|
5085
|
+
continue;
|
|
5086
|
+
}
|
|
5087
|
+
remap.push({ fromId, toId, fingerprint });
|
|
5088
|
+
}
|
|
5089
|
+
return remap;
|
|
5090
|
+
}
|
|
5091
|
+
function createOmReproCaptureDir(threadId, label) {
|
|
5092
|
+
const sanitizedThreadId = sanitizeCapturePathSegment(threadId);
|
|
5093
|
+
const captureDir = join(
|
|
5094
|
+
process.cwd(),
|
|
5095
|
+
getOmReproCaptureDir(),
|
|
5096
|
+
sanitizedThreadId,
|
|
5097
|
+
`${Date.now()}-${label}-${randomUUID()}`
|
|
5098
|
+
);
|
|
5099
|
+
mkdirSync(captureDir, { recursive: true });
|
|
5100
|
+
return captureDir;
|
|
5101
|
+
}
|
|
5102
|
+
function writeObserverExchangeReproCapture(params) {
|
|
5103
|
+
if (!isOmReproCaptureEnabled() || !params.observerExchange) {
|
|
5104
|
+
return;
|
|
5105
|
+
}
|
|
5106
|
+
try {
|
|
5107
|
+
const captureDir = createOmReproCaptureDir(params.threadId, params.label);
|
|
5108
|
+
const payloads = [
|
|
5109
|
+
{
|
|
5110
|
+
fileName: "input.json",
|
|
5111
|
+
data: {
|
|
5112
|
+
threadId: params.threadId,
|
|
5113
|
+
resourceId: params.resourceId,
|
|
5114
|
+
label: params.label
|
|
5115
|
+
}
|
|
5116
|
+
},
|
|
5117
|
+
{
|
|
5118
|
+
fileName: "output.json",
|
|
5119
|
+
data: {
|
|
5120
|
+
details: params.details ?? {}
|
|
5121
|
+
}
|
|
5122
|
+
},
|
|
5123
|
+
{
|
|
5124
|
+
fileName: "observer-exchange.json",
|
|
5125
|
+
data: params.observerExchange
|
|
5126
|
+
}
|
|
5127
|
+
];
|
|
5128
|
+
const captureErrors = [];
|
|
5129
|
+
for (const payload of payloads) {
|
|
5130
|
+
const serialized = safeCaptureJsonOrError(payload.data);
|
|
5131
|
+
if (serialized.ok) {
|
|
5132
|
+
writeFileSync(join(captureDir, payload.fileName), `${JSON.stringify(serialized.value, null, 2)}
|
|
5133
|
+
`);
|
|
5134
|
+
continue;
|
|
5135
|
+
}
|
|
5136
|
+
captureErrors.push({ fileName: payload.fileName, error: serialized.error });
|
|
5137
|
+
writeFileSync(
|
|
5138
|
+
join(captureDir, payload.fileName),
|
|
5139
|
+
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
5140
|
+
`
|
|
5141
|
+
);
|
|
5142
|
+
}
|
|
5143
|
+
if (captureErrors.length > 0) {
|
|
5144
|
+
writeFileSync(join(captureDir, "capture-error.json"), `${JSON.stringify(captureErrors, null, 2)}
|
|
5145
|
+
`);
|
|
5146
|
+
params.debug?.(
|
|
5147
|
+
`[OM:repro-capture] wrote ${params.label} capture with ${captureErrors.length} serialization error(s) to ${captureDir}`
|
|
5148
|
+
);
|
|
5149
|
+
return;
|
|
5150
|
+
}
|
|
5151
|
+
params.debug?.(`[OM:repro-capture] wrote ${params.label} capture to ${captureDir}`);
|
|
5152
|
+
} catch (error) {
|
|
5153
|
+
params.debug?.(`[OM:repro-capture] failed to write ${params.label} capture: ${String(error)}`);
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
function writeProcessInputStepReproCapture(params) {
|
|
5157
|
+
if (!isOmReproCaptureEnabled()) {
|
|
5158
|
+
return;
|
|
5159
|
+
}
|
|
5160
|
+
try {
|
|
5161
|
+
const captureDir = createOmReproCaptureDir(params.threadId, `step-${params.stepNumber}`);
|
|
5162
|
+
const contextMessages = params.messageList.get.all.db();
|
|
5163
|
+
const memoryContext = parseMemoryRequestContext(params.args.requestContext);
|
|
5164
|
+
const preMessageIds = new Set(params.preMessages.map((message) => message.id));
|
|
5165
|
+
const postMessageIds = new Set(contextMessages.map((message) => message.id));
|
|
5166
|
+
const removedMessageIds = params.preMessages.map((message) => message.id).filter((id) => Boolean(id) && !postMessageIds.has(id));
|
|
5167
|
+
const addedMessageIds = contextMessages.map((message) => message.id).filter((id) => Boolean(id) && !preMessageIds.has(id));
|
|
5168
|
+
const idRemap = inferReproIdRemap(params.preMessages, contextMessages);
|
|
5169
|
+
const rawState = params.args.state ?? {};
|
|
5170
|
+
const sanitizedState = sanitizeCaptureState(rawState);
|
|
5171
|
+
const payloads = [
|
|
5172
|
+
{
|
|
5173
|
+
fileName: "input.json",
|
|
5174
|
+
data: {
|
|
5175
|
+
stepNumber: params.stepNumber,
|
|
5176
|
+
threadId: params.threadId,
|
|
5177
|
+
resourceId: params.resourceId,
|
|
5178
|
+
readOnly: memoryContext?.memoryConfig?.readOnly,
|
|
5179
|
+
messageCount: contextMessages.length,
|
|
5180
|
+
messageIds: contextMessages.map((message) => message.id),
|
|
5181
|
+
stateKeys: Object.keys(rawState),
|
|
5182
|
+
state: sanitizedState,
|
|
5183
|
+
args: {
|
|
5184
|
+
messages: params.args.messages,
|
|
5185
|
+
steps: params.args.steps,
|
|
5186
|
+
systemMessages: params.args.systemMessages,
|
|
5187
|
+
retryCount: params.args.retryCount,
|
|
5188
|
+
toolChoice: params.args.toolChoice,
|
|
5189
|
+
activeTools: params.args.activeTools,
|
|
5190
|
+
modelSettings: params.args.modelSettings,
|
|
5191
|
+
structuredOutput: params.args.structuredOutput
|
|
5192
|
+
}
|
|
5193
|
+
}
|
|
5194
|
+
},
|
|
5195
|
+
{
|
|
5196
|
+
fileName: "pre-state.json",
|
|
5197
|
+
data: {
|
|
5198
|
+
record: params.preRecord,
|
|
5199
|
+
bufferedChunks: params.preBufferedChunks,
|
|
5200
|
+
contextTokenCount: params.preContextTokenCount,
|
|
5201
|
+
messages: params.preMessages,
|
|
5202
|
+
messageList: params.preSerializedMessageList
|
|
5203
|
+
}
|
|
5204
|
+
},
|
|
5205
|
+
{
|
|
5206
|
+
fileName: "output.json",
|
|
5207
|
+
data: {
|
|
5208
|
+
details: params.details,
|
|
5209
|
+
messageDiff: {
|
|
5210
|
+
removedMessageIds,
|
|
5211
|
+
addedMessageIds,
|
|
5212
|
+
idRemap
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
},
|
|
5216
|
+
{
|
|
5217
|
+
fileName: "post-state.json",
|
|
5218
|
+
data: {
|
|
5219
|
+
record: params.postRecord,
|
|
5220
|
+
bufferedChunks: params.postBufferedChunks,
|
|
5221
|
+
contextTokenCount: params.postContextTokenCount,
|
|
5222
|
+
messageCount: contextMessages.length,
|
|
5223
|
+
messageIds: contextMessages.map((message) => message.id),
|
|
5224
|
+
messages: contextMessages,
|
|
5225
|
+
messageList: params.messageList.serialize()
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
];
|
|
5229
|
+
const captureErrors = [];
|
|
5230
|
+
for (const payload of payloads) {
|
|
5231
|
+
const serialized = safeCaptureJsonOrError(payload.data);
|
|
5232
|
+
if (serialized.ok) {
|
|
5233
|
+
writeFileSync(join(captureDir, payload.fileName), `${JSON.stringify(serialized.value, null, 2)}
|
|
5234
|
+
`);
|
|
5235
|
+
continue;
|
|
5236
|
+
}
|
|
5237
|
+
captureErrors.push({ fileName: payload.fileName, error: serialized.error });
|
|
5238
|
+
writeFileSync(
|
|
5239
|
+
join(captureDir, payload.fileName),
|
|
5240
|
+
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
5241
|
+
`
|
|
5242
|
+
);
|
|
5243
|
+
}
|
|
5244
|
+
if (params.observerExchange) {
|
|
5245
|
+
const serialized = safeCaptureJsonOrError(params.observerExchange);
|
|
5246
|
+
if (serialized.ok) {
|
|
5247
|
+
writeFileSync(join(captureDir, "observer-exchange.json"), `${JSON.stringify(serialized.value, null, 2)}
|
|
5248
|
+
`);
|
|
5249
|
+
} else {
|
|
5250
|
+
captureErrors.push({ fileName: "observer-exchange.json", error: serialized.error });
|
|
5251
|
+
writeFileSync(
|
|
5252
|
+
join(captureDir, "observer-exchange.json"),
|
|
5253
|
+
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
5254
|
+
`
|
|
5255
|
+
);
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5258
|
+
if (captureErrors.length > 0) {
|
|
5259
|
+
writeFileSync(join(captureDir, "capture-error.json"), `${JSON.stringify(captureErrors, null, 2)}
|
|
5260
|
+
`);
|
|
5261
|
+
params.debug?.(
|
|
5262
|
+
`[OM:repro-capture] wrote processInputStep capture with ${captureErrors.length} serialization error(s) to ${captureDir}`
|
|
5263
|
+
);
|
|
5264
|
+
return;
|
|
5265
|
+
}
|
|
5266
|
+
params.debug?.(`[OM:repro-capture] wrote processInputStep capture to ${captureDir}`);
|
|
5267
|
+
} catch (error) {
|
|
5268
|
+
params.debug?.(`[OM:repro-capture] failed to write processInputStep capture: ${String(error)}`);
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
var IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5272
|
+
"png",
|
|
5273
|
+
"jpg",
|
|
5274
|
+
"jpeg",
|
|
5275
|
+
"webp",
|
|
5276
|
+
"gif",
|
|
5277
|
+
"bmp",
|
|
5278
|
+
"tiff",
|
|
5279
|
+
"tif",
|
|
5280
|
+
"heic",
|
|
5281
|
+
"heif",
|
|
5282
|
+
"avif"
|
|
5283
|
+
]);
|
|
5284
|
+
var TOKEN_ESTIMATE_CACHE_VERSION = 6;
|
|
5285
|
+
var DEFAULT_IMAGE_ESTIMATOR = {
|
|
5286
|
+
baseTokens: 85,
|
|
5287
|
+
tileTokens: 170,
|
|
5288
|
+
fallbackTiles: 4
|
|
5289
|
+
};
|
|
5290
|
+
var GOOGLE_LEGACY_IMAGE_TOKENS_PER_TILE = 258;
|
|
5291
|
+
var GOOGLE_GEMINI_3_IMAGE_TOKENS_BY_RESOLUTION = {
|
|
5292
|
+
low: 280,
|
|
5293
|
+
medium: 560,
|
|
5294
|
+
high: 1120,
|
|
5295
|
+
ultra_high: 2240,
|
|
5296
|
+
unspecified: 1120
|
|
5297
|
+
};
|
|
5298
|
+
var ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
5299
|
+
var ANTHROPIC_IMAGE_MAX_LONG_EDGE = 1568;
|
|
5300
|
+
var GOOGLE_MEDIA_RESOLUTION_VALUES = /* @__PURE__ */ new Set([
|
|
5301
|
+
"low",
|
|
5302
|
+
"medium",
|
|
5303
|
+
"high",
|
|
5304
|
+
"ultra_high",
|
|
5305
|
+
"unspecified"
|
|
5306
|
+
]);
|
|
5307
|
+
var ATTACHMENT_COUNT_TIMEOUT_MS = 2e4;
|
|
5308
|
+
var REMOTE_IMAGE_PROBE_TIMEOUT_MS = 2500;
|
|
5309
|
+
var PROVIDER_API_KEY_ENV_VARS = {
|
|
5310
|
+
openai: ["OPENAI_API_KEY"],
|
|
5311
|
+
google: ["GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY"],
|
|
5312
|
+
anthropic: ["ANTHROPIC_API_KEY"]
|
|
5313
|
+
};
|
|
5314
|
+
function getPartMastraMetadata(part) {
|
|
5315
|
+
return part.providerMetadata?.mastra;
|
|
5316
|
+
}
|
|
5317
|
+
function ensurePartMastraMetadata(part) {
|
|
5318
|
+
const typedPart = part;
|
|
5319
|
+
typedPart.providerMetadata ??= {};
|
|
5320
|
+
typedPart.providerMetadata.mastra ??= {};
|
|
5321
|
+
return typedPart.providerMetadata.mastra;
|
|
5322
|
+
}
|
|
5323
|
+
function getContentMastraMetadata(content) {
|
|
5324
|
+
if (!content || typeof content !== "object") {
|
|
5325
|
+
return void 0;
|
|
5326
|
+
}
|
|
5327
|
+
return content.metadata?.mastra;
|
|
5328
|
+
}
|
|
5329
|
+
function ensureContentMastraMetadata(content) {
|
|
5330
|
+
if (!content || typeof content !== "object") {
|
|
5331
|
+
return void 0;
|
|
5332
|
+
}
|
|
5333
|
+
const typedContent = content;
|
|
5334
|
+
typedContent.metadata ??= {};
|
|
5335
|
+
typedContent.metadata.mastra ??= {};
|
|
5336
|
+
return typedContent.metadata.mastra;
|
|
5337
|
+
}
|
|
5338
|
+
function getMessageMastraMetadata(message) {
|
|
5339
|
+
return message.metadata?.mastra;
|
|
4645
5340
|
}
|
|
4646
5341
|
function ensureMessageMastraMetadata(message) {
|
|
4647
5342
|
const typedMessage = message;
|
|
@@ -5800,6 +6495,88 @@ function buildMessageRange(messages) {
|
|
|
5800
6495
|
const last = [...messages].reverse().find(messageHasVisibleContent) ?? messages[messages.length - 1];
|
|
5801
6496
|
return `${first.id}:${last.id}`;
|
|
5802
6497
|
}
|
|
6498
|
+
function getLastActivityFromMessages(messages) {
|
|
6499
|
+
if (!messages) return void 0;
|
|
6500
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6501
|
+
const message = messages[i];
|
|
6502
|
+
if (!message || message.role !== "assistant") {
|
|
6503
|
+
continue;
|
|
6504
|
+
}
|
|
6505
|
+
if (!message.content || typeof message.content === "string") {
|
|
6506
|
+
return message.createdAt ? new Date(message.createdAt).getTime() : void 0;
|
|
6507
|
+
}
|
|
6508
|
+
for (let j = message.content.parts.length - 1; j >= 0; j--) {
|
|
6509
|
+
const part = message.content.parts[j];
|
|
6510
|
+
if (!part || part.type?.startsWith("data-")) {
|
|
6511
|
+
continue;
|
|
6512
|
+
}
|
|
6513
|
+
if (part.createdAt !== void 0) {
|
|
6514
|
+
return part.createdAt;
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
return message.createdAt ? new Date(message.createdAt).getTime() : void 0;
|
|
6518
|
+
}
|
|
6519
|
+
return void 0;
|
|
6520
|
+
}
|
|
6521
|
+
function formatModelContext2(provider, modelId) {
|
|
6522
|
+
if (provider && modelId) {
|
|
6523
|
+
return `${provider}/${modelId}`;
|
|
6524
|
+
}
|
|
6525
|
+
return modelId;
|
|
6526
|
+
}
|
|
6527
|
+
function getLastModelFromMessages(messages) {
|
|
6528
|
+
if (!messages) return void 0;
|
|
6529
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6530
|
+
const message = messages[i];
|
|
6531
|
+
if (!message || message.role !== "assistant" || !message.content || typeof message.content === "string") {
|
|
6532
|
+
continue;
|
|
6533
|
+
}
|
|
6534
|
+
for (let j = message.content.parts.length - 1; j >= 0; j--) {
|
|
6535
|
+
const part = message.content.parts[j];
|
|
6536
|
+
if (part?.type === "step-start" && typeof part.model === "string" && part.model.length > 0) {
|
|
6537
|
+
return part.model;
|
|
6538
|
+
}
|
|
6539
|
+
}
|
|
6540
|
+
const metadata = message.content.metadata;
|
|
6541
|
+
const model = formatModelContext2(metadata?.provider, metadata?.modelId);
|
|
6542
|
+
if (model) {
|
|
6543
|
+
return model;
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
return void 0;
|
|
6547
|
+
}
|
|
6548
|
+
function getCurrentModel2(model) {
|
|
6549
|
+
return formatModelContext2(model?.provider, model?.modelId);
|
|
6550
|
+
}
|
|
6551
|
+
function parseActivationTTL(value, fieldPath) {
|
|
6552
|
+
if (value === void 0) {
|
|
6553
|
+
return void 0;
|
|
6554
|
+
}
|
|
6555
|
+
if (typeof value === "number") {
|
|
6556
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
6557
|
+
throw new Error(`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".`);
|
|
6558
|
+
}
|
|
6559
|
+
return value;
|
|
6560
|
+
}
|
|
6561
|
+
const trimmed = value.trim();
|
|
6562
|
+
const match = trimmed.match(
|
|
6563
|
+
/^(\d+(?:\.\d+)?)\s*(ms|msec|msecs|millisecond|milliseconds|s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours)$/i
|
|
6564
|
+
);
|
|
6565
|
+
if (!match) {
|
|
6566
|
+
throw new Error(
|
|
6567
|
+
`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m" or "1hr".`
|
|
6568
|
+
);
|
|
6569
|
+
}
|
|
6570
|
+
const rawAmount = match[1];
|
|
6571
|
+
const rawUnit = match[2];
|
|
6572
|
+
const amount = Number(rawAmount);
|
|
6573
|
+
const unit = rawUnit.toLowerCase();
|
|
6574
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
6575
|
+
throw new Error(`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".`);
|
|
6576
|
+
}
|
|
6577
|
+
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;
|
|
6578
|
+
return amount * multiplier;
|
|
6579
|
+
}
|
|
5803
6580
|
var ObservationalMemory = class _ObservationalMemory {
|
|
5804
6581
|
storage;
|
|
5805
6582
|
tokenCounter;
|
|
@@ -5946,6 +6723,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
5946
6723
|
config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
|
|
5947
6724
|
),
|
|
5948
6725
|
bufferActivation: asyncBufferingDisabled ? void 0 : config.observation?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferActivation,
|
|
6726
|
+
activateAfterIdle: parseActivationTTL(config.activateAfterIdle, "activateAfterIdle"),
|
|
6727
|
+
activateOnProviderChange: config.activateOnProviderChange ?? false,
|
|
5949
6728
|
blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
|
|
5950
6729
|
config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0),
|
|
5951
6730
|
config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
|
|
@@ -5964,6 +6743,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
5964
6743
|
},
|
|
5965
6744
|
providerOptions: config.reflection?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.providerOptions,
|
|
5966
6745
|
bufferActivation: asyncBufferingDisabled ? void 0 : config?.reflection?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation,
|
|
6746
|
+
activateAfterIdle: parseActivationTTL(config.activateAfterIdle, "activateAfterIdle"),
|
|
6747
|
+
activateOnProviderChange: config.activateOnProviderChange ?? false,
|
|
5967
6748
|
blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
|
|
5968
6749
|
config.reflection?.blockAfter ?? (config.reflection?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation ? 1.2 : void 0),
|
|
5969
6750
|
config.reflection?.observationTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens
|
|
@@ -6244,6 +7025,47 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
6244
7025
|
}
|
|
6245
7026
|
}
|
|
6246
7027
|
}
|
|
7028
|
+
/**
|
|
7029
|
+
* Resolve the effective messageTokens for a record.
|
|
7030
|
+
* Only explicit per-record overrides (stored under `_overrides`) win;
|
|
7031
|
+
* the initial config snapshot written by getOrCreateRecord() is ignored
|
|
7032
|
+
* so that later instance-level changes still take effect.
|
|
7033
|
+
*
|
|
7034
|
+
* Overrides that fall below the instance-level buffering floor
|
|
7035
|
+
* (bufferTokens / absolute bufferActivation) are clamped to the
|
|
7036
|
+
* instance threshold to preserve buffering invariants.
|
|
7037
|
+
*/
|
|
7038
|
+
getEffectiveMessageTokens(record) {
|
|
7039
|
+
const overrides = record.config?._overrides;
|
|
7040
|
+
const recordTokens = overrides?.observation?.messageTokens;
|
|
7041
|
+
if (recordTokens) {
|
|
7042
|
+
const maxOverride = getMaxThreshold(recordTokens);
|
|
7043
|
+
const bufferTokens = this.observationConfig.bufferTokens;
|
|
7044
|
+
if (bufferTokens && maxOverride <= bufferTokens) {
|
|
7045
|
+
return this.observationConfig.messageTokens;
|
|
7046
|
+
}
|
|
7047
|
+
const bufferActivation = this.observationConfig.bufferActivation;
|
|
7048
|
+
if (bufferActivation && bufferActivation >= 1e3 && maxOverride <= bufferActivation) {
|
|
7049
|
+
return this.observationConfig.messageTokens;
|
|
7050
|
+
}
|
|
7051
|
+
return recordTokens;
|
|
7052
|
+
}
|
|
7053
|
+
return this.observationConfig.messageTokens;
|
|
7054
|
+
}
|
|
7055
|
+
/**
|
|
7056
|
+
* Resolve the effective reflection observationTokens for a record.
|
|
7057
|
+
* Only explicit per-record overrides (stored under `_overrides`) win;
|
|
7058
|
+
* the initial config snapshot is ignored so instance-level changes
|
|
7059
|
+
* still take effect for existing records.
|
|
7060
|
+
*/
|
|
7061
|
+
getEffectiveReflectionTokens(record) {
|
|
7062
|
+
const overrides = record.config?._overrides;
|
|
7063
|
+
const recordTokens = overrides?.reflection?.observationTokens;
|
|
7064
|
+
if (recordTokens) {
|
|
7065
|
+
return recordTokens;
|
|
7066
|
+
}
|
|
7067
|
+
return this.reflectionConfig.observationTokens;
|
|
7068
|
+
}
|
|
6247
7069
|
/**
|
|
6248
7070
|
* Check whether the unobserved message tokens meet the observation threshold.
|
|
6249
7071
|
*/
|
|
@@ -6251,7 +7073,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
6251
7073
|
const { record, unobservedTokens, extraTokens = 0 } = opts;
|
|
6252
7074
|
const pendingTokens = (record.pendingMessageTokens ?? 0) + unobservedTokens + extraTokens;
|
|
6253
7075
|
const currentObservationTokens = record.observationTokenCount ?? 0;
|
|
6254
|
-
const threshold = calculateDynamicThreshold(this.
|
|
7076
|
+
const threshold = calculateDynamicThreshold(this.getEffectiveMessageTokens(record), currentObservationTokens);
|
|
6255
7077
|
return pendingTokens >= threshold;
|
|
6256
7078
|
}
|
|
6257
7079
|
/**
|
|
@@ -6317,7 +7139,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
6317
7139
|
return {
|
|
6318
7140
|
messageTokens: getMaxThreshold(this.observationConfig.messageTokens),
|
|
6319
7141
|
observationTokens: getMaxThreshold(this.reflectionConfig.observationTokens),
|
|
6320
|
-
scope: this.scope
|
|
7142
|
+
scope: this.scope,
|
|
7143
|
+
activateAfterIdle: this.observationConfig.activateAfterIdle
|
|
6321
7144
|
};
|
|
6322
7145
|
}
|
|
6323
7146
|
/**
|
|
@@ -7038,17 +7861,18 @@ ${grouped}` : grouped;
|
|
|
7038
7861
|
const cycleId = `buffer-obs-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
7039
7862
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7040
7863
|
const tokensToBuffer = await this.tokenCounter.countMessagesAsync(messagesToBuffer);
|
|
7864
|
+
const startMarker = createBufferingStartMarker({
|
|
7865
|
+
cycleId,
|
|
7866
|
+
operationType: "observation",
|
|
7867
|
+
tokensToBuffer,
|
|
7868
|
+
recordId: freshRecord.id,
|
|
7869
|
+
threadId,
|
|
7870
|
+
threadIds: [threadId],
|
|
7871
|
+
config: this.getObservationMarkerConfig()
|
|
7872
|
+
});
|
|
7873
|
+
await this.persistMarkerToStorage(startMarker, threadId, freshRecord.resourceId ?? void 0);
|
|
7041
7874
|
if (writer) {
|
|
7042
|
-
|
|
7043
|
-
cycleId,
|
|
7044
|
-
operationType: "observation",
|
|
7045
|
-
tokensToBuffer,
|
|
7046
|
-
recordId: freshRecord.id,
|
|
7047
|
-
threadId,
|
|
7048
|
-
threadIds: [threadId],
|
|
7049
|
-
config: this.getObservationMarkerConfig()
|
|
7050
|
-
});
|
|
7051
|
-
void writer.custom(startMarker).catch(() => {
|
|
7875
|
+
void writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
7052
7876
|
});
|
|
7053
7877
|
}
|
|
7054
7878
|
omDebug(
|
|
@@ -7344,10 +8168,12 @@ ${grouped}` : grouped;
|
|
|
7344
8168
|
async getOtherThreadsContext(resourceId, currentThreadId) {
|
|
7345
8169
|
const { threads: allThreads } = await this.storage.listThreads({ filter: { resourceId } });
|
|
7346
8170
|
const messagesByThread = /* @__PURE__ */ new Map();
|
|
8171
|
+
const record = await this.getRecord(currentThreadId, resourceId);
|
|
8172
|
+
const recordLastObservedAt = record?.lastObservedAt;
|
|
7347
8173
|
for (const thread of allThreads) {
|
|
7348
8174
|
if (thread.id === currentThreadId) continue;
|
|
7349
8175
|
const omMetadata = getThreadOMMetadata(thread.metadata);
|
|
7350
|
-
const threadLastObservedAt = omMetadata?.lastObservedAt;
|
|
8176
|
+
const threadLastObservedAt = omMetadata?.lastObservedAt ?? recordLastObservedAt;
|
|
7351
8177
|
const startDate = threadLastObservedAt ? new Date(new Date(threadLastObservedAt).getTime() + 1) : void 0;
|
|
7352
8178
|
const result = await this.storage.listMessages({
|
|
7353
8179
|
threadId: thread.id,
|
|
@@ -7400,7 +8226,7 @@ ${grouped}` : grouped;
|
|
|
7400
8226
|
const projectedMessageRemoval = calculateProjectedMessageRemoval(
|
|
7401
8227
|
bufferedChunks,
|
|
7402
8228
|
this.observationConfig.bufferActivation ?? 1,
|
|
7403
|
-
getMaxThreshold(this.
|
|
8229
|
+
getMaxThreshold(this.getEffectiveMessageTokens(record)),
|
|
7404
8230
|
pendingTokens
|
|
7405
8231
|
);
|
|
7406
8232
|
let obsBufferStatus = "idle";
|
|
@@ -7489,7 +8315,7 @@ ${grouped}` : grouped;
|
|
|
7489
8315
|
otherThreadTokens = otherContext ? this.tokenCounter.countString(otherContext) : 0;
|
|
7490
8316
|
}
|
|
7491
8317
|
const pendingTokens = Math.max(0, contextWindowTokens + otherThreadTokens);
|
|
7492
|
-
const threshold = calculateDynamicThreshold(this.
|
|
8318
|
+
const threshold = calculateDynamicThreshold(this.getEffectiveMessageTokens(record), currentObservationTokens);
|
|
7493
8319
|
const bufferedChunks = getBufferedChunks(record);
|
|
7494
8320
|
const bufferedChunkCount = bufferedChunks.length;
|
|
7495
8321
|
const bufferedChunkTokens = bufferedChunks.reduce((sum, chunk) => sum + (chunk.messageTokens ?? 0), 0);
|
|
@@ -7506,11 +8332,12 @@ ${grouped}` : grouped;
|
|
|
7506
8332
|
);
|
|
7507
8333
|
}
|
|
7508
8334
|
const shouldObserve = pendingTokens >= threshold;
|
|
7509
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
8335
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
7510
8336
|
const shouldReflect = currentObservationTokens >= reflectThreshold;
|
|
7511
8337
|
const canActivate = bufferedChunkCount > 0;
|
|
7512
|
-
const
|
|
7513
|
-
const
|
|
8338
|
+
const effectiveMessageTokens = this.getEffectiveMessageTokens(record);
|
|
8339
|
+
const isSharedBudget = typeof effectiveMessageTokens !== "number";
|
|
8340
|
+
const totalBudget = isSharedBudget ? effectiveMessageTokens.max : 0;
|
|
7514
8341
|
const effectiveObservationTokensThreshold = isSharedBudget ? Math.max(totalBudget - threshold, 1e3) : reflectThreshold;
|
|
7515
8342
|
const unbufferedPendingTokens = Math.max(0, pendingTokens - bufferedChunkTokens);
|
|
7516
8343
|
return {
|
|
@@ -7554,7 +8381,7 @@ ${grouped}` : grouped;
|
|
|
7554
8381
|
await BufferingCoordinator.awaitBuffering(threadId, resourceId ?? null, this.scope);
|
|
7555
8382
|
const preStatus = await this.getStatus({ threadId, resourceId, messages });
|
|
7556
8383
|
if (preStatus.canActivate) {
|
|
7557
|
-
const actResult = await this.activate({ threadId, resourceId });
|
|
8384
|
+
const actResult = await this.activate({ threadId, resourceId, messages });
|
|
7558
8385
|
activated = actResult.activated;
|
|
7559
8386
|
}
|
|
7560
8387
|
const postStatus = await this.getStatus({ threadId, resourceId, messages });
|
|
@@ -7705,18 +8532,19 @@ ${grouped}` : grouped;
|
|
|
7705
8532
|
}
|
|
7706
8533
|
const cycleId = `buffer-obs-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
7707
8534
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8535
|
+
const startMarker = createBufferingStartMarker({
|
|
8536
|
+
cycleId,
|
|
8537
|
+
operationType: "observation",
|
|
8538
|
+
tokensToBuffer: newTokens,
|
|
8539
|
+
recordId: record.id,
|
|
8540
|
+
threadId,
|
|
8541
|
+
threadIds: [threadId],
|
|
8542
|
+
config: this.getObservationMarkerConfig()
|
|
8543
|
+
});
|
|
8544
|
+
await this.persistMarkerToStorage(startMarker, threadId, record.resourceId ?? void 0);
|
|
7708
8545
|
const writer = opts.writer;
|
|
7709
8546
|
if (writer) {
|
|
7710
|
-
|
|
7711
|
-
cycleId,
|
|
7712
|
-
operationType: "observation",
|
|
7713
|
-
tokensToBuffer: newTokens,
|
|
7714
|
-
recordId: record.id,
|
|
7715
|
-
threadId,
|
|
7716
|
-
threadIds: [threadId],
|
|
7717
|
-
config: this.getObservationMarkerConfig()
|
|
7718
|
-
});
|
|
7719
|
-
void writer.custom(startMarker).catch(() => {
|
|
8547
|
+
void writer.custom({ ...startMarker, transient: true }).catch(() => {
|
|
7720
8548
|
});
|
|
7721
8549
|
}
|
|
7722
8550
|
await ObservationStrategy.create(this, {
|
|
@@ -7730,6 +8558,23 @@ ${grouped}` : grouped;
|
|
|
7730
8558
|
requestContext,
|
|
7731
8559
|
observabilityContext
|
|
7732
8560
|
}).run();
|
|
8561
|
+
if (isOmReproCaptureEnabled()) {
|
|
8562
|
+
writeObserverExchangeReproCapture({
|
|
8563
|
+
threadId,
|
|
8564
|
+
resourceId: record.resourceId ?? void 0,
|
|
8565
|
+
label: `buffer-${cycleId}`,
|
|
8566
|
+
observerExchange: this.observer.lastExchange,
|
|
8567
|
+
details: {
|
|
8568
|
+
cycleId,
|
|
8569
|
+
startedAt,
|
|
8570
|
+
buffered: true,
|
|
8571
|
+
candidateMessageIds: candidateMessages.map((message) => message.id),
|
|
8572
|
+
candidateMessageCount: candidateMessages.length,
|
|
8573
|
+
pendingTokens: currentTokens,
|
|
8574
|
+
newTokens
|
|
8575
|
+
}
|
|
8576
|
+
});
|
|
8577
|
+
}
|
|
7733
8578
|
await this.storage.setBufferingObservationFlag(record.id, false, newTokens).catch(() => {
|
|
7734
8579
|
});
|
|
7735
8580
|
flagCleared = true;
|
|
@@ -7797,10 +8642,37 @@ ${grouped}` : grouped;
|
|
|
7797
8642
|
if (!chunks.length) {
|
|
7798
8643
|
return { activated: false, record };
|
|
7799
8644
|
}
|
|
8645
|
+
let activationTriggeredBy = "threshold";
|
|
8646
|
+
let activationLastActivityAt;
|
|
8647
|
+
let activateAfterIdleExpiredMs;
|
|
8648
|
+
let previousModel;
|
|
8649
|
+
let currentModel;
|
|
7800
8650
|
if (opts.checkThreshold) {
|
|
7801
|
-
const
|
|
7802
|
-
|
|
7803
|
-
|
|
8651
|
+
const thresholdMessages = opts.messages ?? await this.loadMessagesFromStorage(
|
|
8652
|
+
threadId,
|
|
8653
|
+
resourceId,
|
|
8654
|
+
record.lastObservedAt ? new Date(record.lastObservedAt) : void 0
|
|
8655
|
+
);
|
|
8656
|
+
const activateAfterIdle = this.observationConfig.activateAfterIdle;
|
|
8657
|
+
const lastActivityAt = getLastActivityFromMessages(thresholdMessages);
|
|
8658
|
+
const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
|
|
8659
|
+
const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
|
|
8660
|
+
const actorModel = getCurrentModel2(opts.currentModel);
|
|
8661
|
+
const lastModel = getLastModelFromMessages(thresholdMessages);
|
|
8662
|
+
const providerChanged = this.observationConfig.activateOnProviderChange === true && didProviderChange(actorModel, lastModel);
|
|
8663
|
+
if (providerChanged) {
|
|
8664
|
+
activationTriggeredBy = "provider_change";
|
|
8665
|
+
previousModel = lastModel;
|
|
8666
|
+
currentModel = actorModel;
|
|
8667
|
+
} else if (ttlExpired) {
|
|
8668
|
+
activationTriggeredBy = "ttl";
|
|
8669
|
+
activationLastActivityAt = lastActivityAt;
|
|
8670
|
+
activateAfterIdleExpiredMs = ttlExpiredMs;
|
|
8671
|
+
} else {
|
|
8672
|
+
const status = await this.getStatus({ threadId, resourceId, messages: thresholdMessages });
|
|
8673
|
+
if (status.pendingTokens < status.threshold) {
|
|
8674
|
+
return { activated: false, record };
|
|
8675
|
+
}
|
|
7804
8676
|
}
|
|
7805
8677
|
}
|
|
7806
8678
|
if (record.isBufferingObservation) {
|
|
@@ -7825,7 +8697,7 @@ ${grouped}` : grouped;
|
|
|
7825
8697
|
if (!freshChunks.length) {
|
|
7826
8698
|
return { activated: false, record };
|
|
7827
8699
|
}
|
|
7828
|
-
const messageTokensThreshold = getMaxThreshold(this.
|
|
8700
|
+
const messageTokensThreshold = getMaxThreshold(this.getEffectiveMessageTokens(freshRecord));
|
|
7829
8701
|
const bufferActivation = this.observationConfig.bufferActivation ?? 0.7;
|
|
7830
8702
|
const activationRatio = resolveActivationRatio(bufferActivation, messageTokensThreshold);
|
|
7831
8703
|
const totalChunkMessageTokens = freshChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
|
|
@@ -7858,9 +8730,14 @@ ${grouped}` : grouped;
|
|
|
7858
8730
|
threadId: postSwapRecord.threadId ?? record.threadId ?? "",
|
|
7859
8731
|
generationCount: postSwapRecord.generationCount ?? 0,
|
|
7860
8732
|
observations: chunkData?.observations ?? activationResult.observations,
|
|
8733
|
+
triggeredBy: activationTriggeredBy,
|
|
8734
|
+
lastActivityAt: activationLastActivityAt,
|
|
8735
|
+
ttlExpiredMs: activateAfterIdleExpiredMs,
|
|
8736
|
+
previousModel,
|
|
8737
|
+
currentModel,
|
|
7861
8738
|
config: this.getObservationMarkerConfig()
|
|
7862
8739
|
});
|
|
7863
|
-
void opts.writer.custom(activationMarker).catch(() => {
|
|
8740
|
+
void opts.writer.custom({ ...activationMarker, transient: true }).catch(() => {
|
|
7864
8741
|
});
|
|
7865
8742
|
await this.persistMarkerToMessage(
|
|
7866
8743
|
activationMarker,
|
|
@@ -7913,6 +8790,7 @@ ${grouped}` : grouped;
|
|
|
7913
8790
|
const lockKey = this.buffering.getLockKey(threadId, resourceId);
|
|
7914
8791
|
const reflectionHooks = hooks ? { onReflectionStart: hooks.onReflectionStart, onReflectionEnd: hooks.onReflectionEnd } : void 0;
|
|
7915
8792
|
let observed = false;
|
|
8793
|
+
let observationUsage;
|
|
7916
8794
|
let generationBefore = -1;
|
|
7917
8795
|
await this.withLock(lockKey, async () => {
|
|
7918
8796
|
const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
|
|
@@ -7929,8 +8807,9 @@ ${grouped}` : grouped;
|
|
|
7929
8807
|
return;
|
|
7930
8808
|
}
|
|
7931
8809
|
hooks?.onObservationStart?.();
|
|
8810
|
+
let observationError;
|
|
7932
8811
|
try {
|
|
7933
|
-
|
|
8812
|
+
const result = await ObservationStrategy.create(this, {
|
|
7934
8813
|
record: freshRecord,
|
|
7935
8814
|
threadId,
|
|
7936
8815
|
resourceId,
|
|
@@ -7940,8 +8819,13 @@ ${grouped}` : grouped;
|
|
|
7940
8819
|
writer: opts.writer,
|
|
7941
8820
|
observabilityContext: opts.observabilityContext
|
|
7942
8821
|
}).run();
|
|
8822
|
+
observed = result.observed;
|
|
8823
|
+
observationUsage = result.usage;
|
|
8824
|
+
} catch (error) {
|
|
8825
|
+
observationError = error instanceof Error ? error : new Error(String(error));
|
|
8826
|
+
throw error;
|
|
7943
8827
|
} finally {
|
|
7944
|
-
hooks?.onObservationEnd?.();
|
|
8828
|
+
hooks?.onObservationEnd?.({ usage: observationUsage, error: observationError });
|
|
7945
8829
|
}
|
|
7946
8830
|
});
|
|
7947
8831
|
const record = await this.getOrCreateRecord(threadId, resourceId);
|
|
@@ -7962,12 +8846,12 @@ ${grouped}` : grouped;
|
|
|
7962
8846
|
async reflect(threadId, resourceId, prompt, requestContext, observabilityContext) {
|
|
7963
8847
|
const record = await this.getOrCreateRecord(threadId, resourceId);
|
|
7964
8848
|
if (!record.activeObservations) {
|
|
7965
|
-
return { reflected: false, record };
|
|
8849
|
+
return { reflected: false, record, usage: void 0 };
|
|
7966
8850
|
}
|
|
7967
8851
|
await this.storage.setReflectingFlag(record.id, true);
|
|
7968
8852
|
registerOp(record.id, "reflecting");
|
|
7969
8853
|
try {
|
|
7970
|
-
const reflectThreshold = getMaxThreshold(this.
|
|
8854
|
+
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
7971
8855
|
const reflectResult = await this.reflector.call(
|
|
7972
8856
|
record.activeObservations,
|
|
7973
8857
|
prompt,
|
|
@@ -7987,11 +8871,11 @@ ${grouped}` : grouped;
|
|
|
7987
8871
|
tokenCount: reflectionTokenCount
|
|
7988
8872
|
});
|
|
7989
8873
|
const updatedRecord = await this.getOrCreateRecord(threadId, resourceId);
|
|
7990
|
-
return { reflected: true, record: updatedRecord };
|
|
8874
|
+
return { reflected: true, record: updatedRecord, usage: reflectResult.usage };
|
|
7991
8875
|
} catch (error) {
|
|
7992
8876
|
omError("[OM] reflect() failed", error);
|
|
7993
8877
|
const latestRecord = await this.getOrCreateRecord(threadId, resourceId);
|
|
7994
|
-
return { reflected: false, record: latestRecord };
|
|
8878
|
+
return { reflected: false, record: latestRecord, usage: void 0 };
|
|
7995
8879
|
} finally {
|
|
7996
8880
|
await this.storage.setReflectingFlag(record.id, false);
|
|
7997
8881
|
unregisterOp(record.id, "reflecting");
|
|
@@ -8012,12 +8896,40 @@ ${grouped}` : grouped;
|
|
|
8012
8896
|
const ids = this.getStorageIds(threadId, resourceId);
|
|
8013
8897
|
return this.storage.getObservationalMemory(ids.threadId, ids.resourceId);
|
|
8014
8898
|
}
|
|
8899
|
+
/**
|
|
8900
|
+
* Update per-record config overrides for observation and/or reflection thresholds.
|
|
8901
|
+
* The provided config is deep-merged into the record's `_overrides` key,
|
|
8902
|
+
* so you only need to specify the fields you want to change.
|
|
8903
|
+
*
|
|
8904
|
+
* Overrides that violate buffering invariants (e.g. messageTokens below
|
|
8905
|
+
* bufferTokens) are silently ignored at read time — the helpers fall back
|
|
8906
|
+
* to the instance-level config.
|
|
8907
|
+
*
|
|
8908
|
+
* @example
|
|
8909
|
+
* ```ts
|
|
8910
|
+
* await om.updateRecordConfig('thread-1', undefined, {
|
|
8911
|
+
* observation: { messageTokens: 2000 },
|
|
8912
|
+
* reflection: { observationTokens: 8000 },
|
|
8913
|
+
* });
|
|
8914
|
+
* ```
|
|
8915
|
+
*/
|
|
8916
|
+
async updateRecordConfig(threadId, resourceId, config) {
|
|
8917
|
+
const ids = this.getStorageIds(threadId, resourceId);
|
|
8918
|
+
const record = await this.storage.getObservationalMemory(ids.threadId, ids.resourceId);
|
|
8919
|
+
if (!record) {
|
|
8920
|
+
throw new Error(`No observational memory record found for thread ${ids.threadId}`);
|
|
8921
|
+
}
|
|
8922
|
+
await this.storage.updateObservationalMemoryConfig({
|
|
8923
|
+
id: record.id,
|
|
8924
|
+
config: { _overrides: config }
|
|
8925
|
+
});
|
|
8926
|
+
}
|
|
8015
8927
|
/**
|
|
8016
8928
|
* Get observation history (previous generations)
|
|
8017
8929
|
*/
|
|
8018
|
-
async getHistory(threadId, resourceId, limit) {
|
|
8930
|
+
async getHistory(threadId, resourceId, limit, options) {
|
|
8019
8931
|
const ids = this.getStorageIds(threadId, resourceId);
|
|
8020
|
-
return this.storage.getObservationalMemoryHistory(ids.threadId, ids.resourceId, limit);
|
|
8932
|
+
return this.storage.getObservationalMemoryHistory(ids.threadId, ids.resourceId, limit, options);
|
|
8021
8933
|
}
|
|
8022
8934
|
/**
|
|
8023
8935
|
* Clear all memory for a specific thread/resource
|
|
@@ -8060,293 +8972,137 @@ ${grouped}` : grouped;
|
|
|
8060
8972
|
/**
|
|
8061
8973
|
* Get whether thread IDs should be obscured in observations.
|
|
8062
8974
|
*/
|
|
8063
|
-
getObscureThreadIds() {
|
|
8064
|
-
return this.shouldObscureThreadIds;
|
|
8065
|
-
}
|
|
8066
|
-
/**
|
|
8067
|
-
* Begin a new observation turn — the high-level API for managing the
|
|
8068
|
-
* observe/buffer/activate/reflect lifecycle across agentic loop steps.
|
|
8069
|
-
*
|
|
8070
|
-
* @example
|
|
8071
|
-
* ```ts
|
|
8072
|
-
* const turn = om.beginTurn({ threadId, resourceId, messageList });
|
|
8073
|
-
* await turn.start(memory);
|
|
8074
|
-
*
|
|
8075
|
-
* const step0 = turn.step(0);
|
|
8076
|
-
* const ctx = await step0.prepare();
|
|
8077
|
-
* // ... agent generates ...
|
|
8078
|
-
*
|
|
8079
|
-
* await turn.end();
|
|
8080
|
-
* ```
|
|
8081
|
-
*/
|
|
8082
|
-
beginTurn(opts) {
|
|
8083
|
-
return new ObservationTurn({
|
|
8084
|
-
om: this,
|
|
8085
|
-
threadId: opts.threadId,
|
|
8086
|
-
resourceId: opts.resourceId,
|
|
8087
|
-
messageList: opts.messageList,
|
|
8088
|
-
observabilityContext: opts.observabilityContext,
|
|
8089
|
-
hooks: opts.hooks
|
|
8090
|
-
});
|
|
8091
|
-
}
|
|
8092
|
-
};
|
|
8093
|
-
var OM_REPRO_CAPTURE_DIR = process.env.OM_REPRO_CAPTURE_DIR ?? ".mastra-om-repro";
|
|
8094
|
-
function sanitizeCapturePathSegment(value) {
|
|
8095
|
-
const sanitized = value.replace(/[\\/]+/g, "_").replace(/\.{2,}/g, "_").trim();
|
|
8096
|
-
return sanitized.length > 0 ? sanitized : "unknown-thread";
|
|
8097
|
-
}
|
|
8098
|
-
function isOmReproCaptureEnabled() {
|
|
8099
|
-
return process.env.OM_REPRO_CAPTURE === "1";
|
|
8100
|
-
}
|
|
8101
|
-
function safeCaptureJson(value) {
|
|
8102
|
-
return JSON.parse(
|
|
8103
|
-
JSON.stringify(value, (_key, current) => {
|
|
8104
|
-
if (typeof current === "bigint") return current.toString();
|
|
8105
|
-
if (typeof current === "function") return "[function]";
|
|
8106
|
-
if (typeof current === "symbol") return current.toString();
|
|
8107
|
-
if (current instanceof Error) return { name: current.name, message: current.message, stack: current.stack };
|
|
8108
|
-
if (current instanceof Set) return { __type: "Set", values: Array.from(current.values()) };
|
|
8109
|
-
if (current instanceof Map) return { __type: "Map", entries: Array.from(current.entries()) };
|
|
8110
|
-
return current;
|
|
8111
|
-
})
|
|
8112
|
-
);
|
|
8113
|
-
}
|
|
8114
|
-
function safeCaptureJsonOrError(value) {
|
|
8115
|
-
try {
|
|
8116
|
-
return { ok: true, value: safeCaptureJson(value) };
|
|
8117
|
-
} catch (error) {
|
|
8118
|
-
return {
|
|
8119
|
-
ok: false,
|
|
8120
|
-
error: safeCaptureJson({
|
|
8121
|
-
message: error instanceof Error ? error.message : String(error),
|
|
8122
|
-
stack: error instanceof Error ? error.stack : void 0,
|
|
8123
|
-
inspected: inspect(value, { depth: 3, maxArrayLength: 20, breakLength: 120 })
|
|
8124
|
-
})
|
|
8125
|
-
};
|
|
8126
|
-
}
|
|
8127
|
-
}
|
|
8128
|
-
function formatCaptureDate(value) {
|
|
8129
|
-
if (!value) return void 0;
|
|
8130
|
-
if (value instanceof Date) return value.toISOString();
|
|
8131
|
-
try {
|
|
8132
|
-
return new Date(value).toISOString();
|
|
8133
|
-
} catch {
|
|
8134
|
-
return void 0;
|
|
8975
|
+
getObscureThreadIds() {
|
|
8976
|
+
return this.shouldObscureThreadIds;
|
|
8135
8977
|
}
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8978
|
+
/**
|
|
8979
|
+
* Begin a new observation turn — the high-level API for managing the
|
|
8980
|
+
* observe/buffer/activate/reflect lifecycle across agentic loop steps.
|
|
8981
|
+
*
|
|
8982
|
+
* @example
|
|
8983
|
+
* ```ts
|
|
8984
|
+
* const turn = om.beginTurn({ threadId, resourceId, messageList });
|
|
8985
|
+
* await turn.start(memory);
|
|
8986
|
+
*
|
|
8987
|
+
* const step0 = turn.step(0);
|
|
8988
|
+
* const ctx = await step0.prepare();
|
|
8989
|
+
* // ... agent generates ...
|
|
8990
|
+
*
|
|
8991
|
+
* await turn.end();
|
|
8992
|
+
* ```
|
|
8993
|
+
*/
|
|
8994
|
+
beginTurn(opts) {
|
|
8995
|
+
return new ObservationTurn({
|
|
8996
|
+
om: this,
|
|
8997
|
+
threadId: opts.threadId,
|
|
8998
|
+
resourceId: opts.resourceId,
|
|
8999
|
+
messageList: opts.messageList,
|
|
9000
|
+
observabilityContext: opts.observabilityContext,
|
|
9001
|
+
hooks: opts.hooks
|
|
9002
|
+
});
|
|
8140
9003
|
}
|
|
8141
|
-
|
|
9004
|
+
};
|
|
9005
|
+
|
|
9006
|
+
// src/processors/observational-memory/temporal-markers.ts
|
|
9007
|
+
var TEMPORAL_GAP_REMINDER_TYPE = "temporal-gap";
|
|
9008
|
+
function getTemporalGapReminderText(gapText, timestamp) {
|
|
9009
|
+
return `${gapText} \u2014 ${formatTemporalTimestamp(new Date(timestamp))}`;
|
|
9010
|
+
}
|
|
9011
|
+
function getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp) {
|
|
9012
|
+
const formattedTimestamp = formatTemporalTimestamp(new Date(timestamp));
|
|
8142
9013
|
return {
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
observationTokenCount: turn._record.observationTokenCount,
|
|
8159
|
-
pendingMessageTokens: turn._record.pendingMessageTokens,
|
|
8160
|
-
isBufferingObservation: turn._record.isBufferingObservation,
|
|
8161
|
-
isBufferingReflection: turn._record.isBufferingReflection
|
|
8162
|
-
} : void 0,
|
|
8163
|
-
context: turn._context ? {
|
|
8164
|
-
messageCount: Array.isArray(turn._context.messages) ? turn._context.messages.length : void 0,
|
|
8165
|
-
hasSystemMessage: Array.isArray(turn._context.systemMessage) ? turn._context.systemMessage.length > 0 : Boolean(turn._context.systemMessage),
|
|
8166
|
-
continuationId: turn._context.continuation?.id,
|
|
8167
|
-
hasOtherThreadsContext: Boolean(turn._context.otherThreadsContext),
|
|
8168
|
-
recordId: turn._context.record?.id
|
|
8169
|
-
} : void 0,
|
|
8170
|
-
currentStep: turn._currentStep ? {
|
|
8171
|
-
stepNumber: turn._currentStep.stepNumber,
|
|
8172
|
-
prepared: turn._currentStep._prepared,
|
|
8173
|
-
context: turn._currentStep._context ? {
|
|
8174
|
-
activated: turn._currentStep._context.activated,
|
|
8175
|
-
observed: turn._currentStep._context.observed,
|
|
8176
|
-
buffered: turn._currentStep._context.buffered,
|
|
8177
|
-
reflected: turn._currentStep._context.reflected,
|
|
8178
|
-
didThresholdCleanup: turn._currentStep._context.didThresholdCleanup,
|
|
8179
|
-
messageCount: Array.isArray(turn._currentStep._context.messages) ? turn._currentStep._context.messages.length : void 0,
|
|
8180
|
-
systemMessageCount: Array.isArray(turn._currentStep._context.systemMessage) ? turn._currentStep._context.systemMessage.length : void 0
|
|
8181
|
-
} : void 0
|
|
8182
|
-
} : void 0
|
|
9014
|
+
reminderType: TEMPORAL_GAP_REMINDER_TYPE,
|
|
9015
|
+
gapText,
|
|
9016
|
+
gapMs,
|
|
9017
|
+
timestamp: formattedTimestamp,
|
|
9018
|
+
timestampMs: timestamp,
|
|
9019
|
+
precedesMessageId: message.id,
|
|
9020
|
+
systemReminder: {
|
|
9021
|
+
type: TEMPORAL_GAP_REMINDER_TYPE,
|
|
9022
|
+
message: getTemporalGapReminderText(gapText, timestamp),
|
|
9023
|
+
gapText,
|
|
9024
|
+
gapMs,
|
|
9025
|
+
timestamp: formattedTimestamp,
|
|
9026
|
+
timestampMs: timestamp,
|
|
9027
|
+
precedesMessageId: message.id
|
|
9028
|
+
}
|
|
8183
9029
|
};
|
|
8184
9030
|
}
|
|
8185
|
-
function
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
9031
|
+
function isTemporalGapMarkerForMessage(message, targetMessageId) {
|
|
9032
|
+
if (!isTemporalGapMarker(message)) {
|
|
9033
|
+
return false;
|
|
9034
|
+
}
|
|
9035
|
+
const metadata = message.content.metadata;
|
|
9036
|
+
if (metadata?.precedesMessageId === targetMessageId) {
|
|
9037
|
+
return true;
|
|
9038
|
+
}
|
|
9039
|
+
return metadata?.systemReminder?.type === TEMPORAL_GAP_REMINDER_TYPE && metadata.systemReminder.precedesMessageId === targetMessageId;
|
|
8194
9040
|
}
|
|
8195
|
-
function
|
|
8196
|
-
const
|
|
8197
|
-
return
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
9041
|
+
function createTemporalGapMarker(message, gapText, gapMs, timestamp) {
|
|
9042
|
+
const metadata = getTemporalGapReminderMetadata(message, gapText, gapMs, timestamp);
|
|
9043
|
+
return {
|
|
9044
|
+
id: `__temporal_gap_${crypto.randomUUID()}`,
|
|
9045
|
+
role: "user",
|
|
9046
|
+
createdAt: new Date(timestamp - 1),
|
|
9047
|
+
threadId: message.threadId,
|
|
9048
|
+
resourceId: message.resourceId,
|
|
9049
|
+
content: {
|
|
9050
|
+
format: 2,
|
|
9051
|
+
parts: [
|
|
9052
|
+
{
|
|
9053
|
+
type: "text",
|
|
9054
|
+
text: `<system-reminder type="${TEMPORAL_GAP_REMINDER_TYPE}" precedesMessageId="${message.id}">${getTemporalGapReminderText(gapText, timestamp)}</system-reminder>`
|
|
9055
|
+
}
|
|
9056
|
+
],
|
|
9057
|
+
metadata
|
|
9058
|
+
}
|
|
9059
|
+
};
|
|
8202
9060
|
}
|
|
8203
|
-
function
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
preByFingerprint.set(fingerprint, list);
|
|
9061
|
+
async function insertTemporalGapMarkers({
|
|
9062
|
+
messageList,
|
|
9063
|
+
writer
|
|
9064
|
+
}) {
|
|
9065
|
+
const inputMessages = messageList.get.input.db().filter((message) => Boolean(message));
|
|
9066
|
+
const latestInputMessage = inputMessages.at(-1);
|
|
9067
|
+
if (!latestInputMessage || isTemporalGapMarker(latestInputMessage)) {
|
|
9068
|
+
return;
|
|
8212
9069
|
}
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
list.push(message.id);
|
|
8218
|
-
postByFingerprint.set(fingerprint, list);
|
|
9070
|
+
const allMessages = messageList.get.all.db().filter((message) => Boolean(message));
|
|
9071
|
+
const latestInputIndex = allMessages.findIndex((message) => message.id === latestInputMessage.id);
|
|
9072
|
+
if (latestInputIndex <= 0) {
|
|
9073
|
+
return;
|
|
8219
9074
|
}
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
const
|
|
8226
|
-
if (
|
|
8227
|
-
|
|
9075
|
+
if (allMessages.some((message) => isTemporalGapMarkerForMessage(message, latestInputMessage.id))) {
|
|
9076
|
+
return;
|
|
9077
|
+
}
|
|
9078
|
+
let previousNonMarker;
|
|
9079
|
+
for (let index = latestInputIndex - 1; index >= 0; index--) {
|
|
9080
|
+
const candidate = allMessages[index];
|
|
9081
|
+
if (candidate && !isTemporalGapMarker(candidate)) {
|
|
9082
|
+
previousNonMarker = candidate;
|
|
9083
|
+
break;
|
|
8228
9084
|
}
|
|
8229
|
-
remap.push({ fromId, toId, fingerprint });
|
|
8230
9085
|
}
|
|
8231
|
-
|
|
8232
|
-
}
|
|
8233
|
-
function writeProcessInputStepReproCapture(params) {
|
|
8234
|
-
if (!isOmReproCaptureEnabled()) {
|
|
9086
|
+
if (!previousNonMarker) {
|
|
8235
9087
|
return;
|
|
8236
9088
|
}
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
const contextMessages = params.messageList.get.all.db();
|
|
8243
|
-
const memoryContext = parseMemoryRequestContext(params.args.requestContext);
|
|
8244
|
-
const preMessageIds = new Set(params.preMessages.map((message) => message.id));
|
|
8245
|
-
const postMessageIds = new Set(contextMessages.map((message) => message.id));
|
|
8246
|
-
const removedMessageIds = params.preMessages.map((message) => message.id).filter((id) => Boolean(id) && !postMessageIds.has(id));
|
|
8247
|
-
const addedMessageIds = contextMessages.map((message) => message.id).filter((id) => Boolean(id) && !preMessageIds.has(id));
|
|
8248
|
-
const idRemap = inferReproIdRemap(params.preMessages, contextMessages);
|
|
8249
|
-
const rawState = params.args.state ?? {};
|
|
8250
|
-
const sanitizedState = sanitizeCaptureState(rawState);
|
|
8251
|
-
const payloads = [
|
|
8252
|
-
{
|
|
8253
|
-
fileName: "input.json",
|
|
8254
|
-
data: {
|
|
8255
|
-
stepNumber: params.stepNumber,
|
|
8256
|
-
threadId: params.threadId,
|
|
8257
|
-
resourceId: params.resourceId,
|
|
8258
|
-
readOnly: memoryContext?.memoryConfig?.readOnly,
|
|
8259
|
-
messageCount: contextMessages.length,
|
|
8260
|
-
messageIds: contextMessages.map((message) => message.id),
|
|
8261
|
-
stateKeys: Object.keys(rawState),
|
|
8262
|
-
state: sanitizedState,
|
|
8263
|
-
args: {
|
|
8264
|
-
messages: params.args.messages,
|
|
8265
|
-
steps: params.args.steps,
|
|
8266
|
-
systemMessages: params.args.systemMessages,
|
|
8267
|
-
retryCount: params.args.retryCount,
|
|
8268
|
-
toolChoice: params.args.toolChoice,
|
|
8269
|
-
activeTools: params.args.activeTools,
|
|
8270
|
-
modelSettings: params.args.modelSettings,
|
|
8271
|
-
structuredOutput: params.args.structuredOutput
|
|
8272
|
-
}
|
|
8273
|
-
}
|
|
8274
|
-
},
|
|
8275
|
-
{
|
|
8276
|
-
fileName: "pre-state.json",
|
|
8277
|
-
data: {
|
|
8278
|
-
record: params.preRecord,
|
|
8279
|
-
bufferedChunks: params.preBufferedChunks,
|
|
8280
|
-
contextTokenCount: params.preContextTokenCount,
|
|
8281
|
-
messages: params.preMessages,
|
|
8282
|
-
messageList: params.preSerializedMessageList
|
|
8283
|
-
}
|
|
8284
|
-
},
|
|
8285
|
-
{
|
|
8286
|
-
fileName: "output.json",
|
|
8287
|
-
data: {
|
|
8288
|
-
details: params.details,
|
|
8289
|
-
messageDiff: {
|
|
8290
|
-
removedMessageIds,
|
|
8291
|
-
addedMessageIds,
|
|
8292
|
-
idRemap
|
|
8293
|
-
}
|
|
8294
|
-
}
|
|
8295
|
-
},
|
|
8296
|
-
{
|
|
8297
|
-
fileName: "post-state.json",
|
|
8298
|
-
data: {
|
|
8299
|
-
record: params.postRecord,
|
|
8300
|
-
bufferedChunks: params.postBufferedChunks,
|
|
8301
|
-
contextTokenCount: params.postContextTokenCount,
|
|
8302
|
-
messageCount: contextMessages.length,
|
|
8303
|
-
messageIds: contextMessages.map((message) => message.id),
|
|
8304
|
-
messages: contextMessages,
|
|
8305
|
-
messageList: params.messageList.serialize()
|
|
8306
|
-
}
|
|
8307
|
-
}
|
|
8308
|
-
];
|
|
8309
|
-
const captureErrors = [];
|
|
8310
|
-
for (const payload of payloads) {
|
|
8311
|
-
const serialized = safeCaptureJsonOrError(payload.data);
|
|
8312
|
-
if (serialized.ok) {
|
|
8313
|
-
writeFileSync(join(captureDir, payload.fileName), `${JSON.stringify(serialized.value, null, 2)}
|
|
8314
|
-
`);
|
|
8315
|
-
continue;
|
|
8316
|
-
}
|
|
8317
|
-
captureErrors.push({ fileName: payload.fileName, error: serialized.error });
|
|
8318
|
-
writeFileSync(
|
|
8319
|
-
join(captureDir, payload.fileName),
|
|
8320
|
-
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
8321
|
-
`
|
|
8322
|
-
);
|
|
8323
|
-
}
|
|
8324
|
-
if (params.observerExchange) {
|
|
8325
|
-
const serialized = safeCaptureJsonOrError(params.observerExchange);
|
|
8326
|
-
if (serialized.ok) {
|
|
8327
|
-
writeFileSync(join(captureDir, "observer-exchange.json"), `${JSON.stringify(serialized.value, null, 2)}
|
|
8328
|
-
`);
|
|
8329
|
-
} else {
|
|
8330
|
-
captureErrors.push({ fileName: "observer-exchange.json", error: serialized.error });
|
|
8331
|
-
writeFileSync(
|
|
8332
|
-
join(captureDir, "observer-exchange.json"),
|
|
8333
|
-
`${JSON.stringify({ __captureError: serialized.error }, null, 2)}
|
|
8334
|
-
`
|
|
8335
|
-
);
|
|
8336
|
-
}
|
|
8337
|
-
}
|
|
8338
|
-
if (captureErrors.length > 0) {
|
|
8339
|
-
writeFileSync(join(captureDir, "capture-error.json"), `${JSON.stringify(captureErrors, null, 2)}
|
|
8340
|
-
`);
|
|
8341
|
-
params.debug?.(
|
|
8342
|
-
`[OM:repro-capture] wrote processInputStep capture with ${captureErrors.length} serialization error(s) to ${captureDir}`
|
|
8343
|
-
);
|
|
8344
|
-
return;
|
|
8345
|
-
}
|
|
8346
|
-
params.debug?.(`[OM:repro-capture] wrote processInputStep capture to ${captureDir}`);
|
|
8347
|
-
} catch (error) {
|
|
8348
|
-
params.debug?.(`[OM:repro-capture] failed to write processInputStep capture: ${String(error)}`);
|
|
9089
|
+
const timestamp = getMessagePartTimestamp(latestInputMessage, "first");
|
|
9090
|
+
const gapMs = timestamp - getMessagePartTimestamp(previousNonMarker, "last");
|
|
9091
|
+
const gapText = formatTemporalGap(gapMs);
|
|
9092
|
+
if (!gapText) {
|
|
9093
|
+
return;
|
|
8349
9094
|
}
|
|
9095
|
+
const reminderMetadata = getTemporalGapReminderMetadata(latestInputMessage, gapText, gapMs, timestamp);
|
|
9096
|
+
await writer?.custom({
|
|
9097
|
+
type: "data-system-reminder",
|
|
9098
|
+
data: {
|
|
9099
|
+
message: getTemporalGapReminderText(gapText, timestamp),
|
|
9100
|
+
...reminderMetadata
|
|
9101
|
+
},
|
|
9102
|
+
transient: true
|
|
9103
|
+
});
|
|
9104
|
+
const marker = createTemporalGapMarker(latestInputMessage, gapText, gapMs, timestamp);
|
|
9105
|
+
messageList.add(marker, "input");
|
|
8350
9106
|
}
|
|
8351
9107
|
|
|
8352
9108
|
// src/processors/observational-memory/processor.ts
|
|
@@ -8361,6 +9117,10 @@ function getOmObservabilityContext(args) {
|
|
|
8361
9117
|
metrics: args.metrics
|
|
8362
9118
|
};
|
|
8363
9119
|
}
|
|
9120
|
+
var GATEWAY_STATE_KEY = "__isGatewayModel";
|
|
9121
|
+
function isMastraGatewayModel(model) {
|
|
9122
|
+
return typeof model === "object" && model !== null && "gatewayId" in model && model.gatewayId === "mastra";
|
|
9123
|
+
}
|
|
8364
9124
|
var ObservationalMemoryProcessor = class {
|
|
8365
9125
|
id = "observational-memory";
|
|
8366
9126
|
name = "Observational Memory";
|
|
@@ -8368,11 +9128,14 @@ var ObservationalMemoryProcessor = class {
|
|
|
8368
9128
|
engine;
|
|
8369
9129
|
/** Memory instance for loading context. */
|
|
8370
9130
|
memory;
|
|
9131
|
+
/** Whether temporal-gap reminder markers should be inserted. */
|
|
9132
|
+
temporalMarkers;
|
|
8371
9133
|
/** Active turn — created on first processInputStep, ended on processOutputResult. */
|
|
8372
9134
|
turn;
|
|
8373
|
-
constructor(engine, memory) {
|
|
9135
|
+
constructor(engine, memory, options) {
|
|
8374
9136
|
this.engine = engine;
|
|
8375
9137
|
this.memory = memory;
|
|
9138
|
+
this.temporalMarkers = options?.temporalMarkers ?? false;
|
|
8376
9139
|
}
|
|
8377
9140
|
// ─── Processor lifecycle hooks ──────────────────────────────────────────
|
|
8378
9141
|
async processInputStep(args) {
|
|
@@ -8396,6 +9159,11 @@ var ObservationalMemoryProcessor = class {
|
|
|
8396
9159
|
omDebug(`[OM:processInputStep:NO-CONTEXT] getThreadContext returned null \u2014 returning early`);
|
|
8397
9160
|
return messageList;
|
|
8398
9161
|
}
|
|
9162
|
+
if (isMastraGatewayModel(model)) {
|
|
9163
|
+
state[GATEWAY_STATE_KEY] = true;
|
|
9164
|
+
omDebug(`[OM:processInputStep:GATEWAY] gateway handles OM \u2014 skipping local processing`);
|
|
9165
|
+
return messageList;
|
|
9166
|
+
}
|
|
8399
9167
|
const { threadId, resourceId } = context;
|
|
8400
9168
|
const memoryContext = parseMemoryRequestContext(requestContext);
|
|
8401
9169
|
const readOnly = memoryContext?.memoryConfig?.readOnly;
|
|
@@ -8409,6 +9177,15 @@ var ObservationalMemoryProcessor = class {
|
|
|
8409
9177
|
if (readOnly) {
|
|
8410
9178
|
return messageList;
|
|
8411
9179
|
}
|
|
9180
|
+
const activeTurn = state.__omTurn ?? this.turn;
|
|
9181
|
+
if (activeTurn && activeTurn.messageList !== messageList) {
|
|
9182
|
+
await activeTurn.end().catch(() => {
|
|
9183
|
+
});
|
|
9184
|
+
if (this.turn === activeTurn) {
|
|
9185
|
+
this.turn = void 0;
|
|
9186
|
+
}
|
|
9187
|
+
state.__omTurn = void 0;
|
|
9188
|
+
}
|
|
8412
9189
|
if (!this.turn || !state.__omTurn) {
|
|
8413
9190
|
if (this.turn && !state.__omTurn) {
|
|
8414
9191
|
await this.turn.end().catch(() => {
|
|
@@ -8420,17 +9197,26 @@ var ObservationalMemoryProcessor = class {
|
|
|
8420
9197
|
messageList,
|
|
8421
9198
|
observabilityContext: getOmObservabilityContext(args),
|
|
8422
9199
|
hooks: {
|
|
8423
|
-
onBufferChunkSealed: rotateResponseMessageId
|
|
9200
|
+
onBufferChunkSealed: rotateResponseMessageId,
|
|
9201
|
+
onSyncObservationComplete: rotateResponseMessageId
|
|
8424
9202
|
}
|
|
8425
9203
|
});
|
|
8426
9204
|
this.turn.writer = writer;
|
|
8427
9205
|
this.turn.requestContext = requestContext;
|
|
8428
9206
|
await this.turn.start(this.memory);
|
|
9207
|
+
if (stepNumber === 0 && this.temporalMarkers) {
|
|
9208
|
+
await insertTemporalGapMarkers({ messageList, writer });
|
|
9209
|
+
}
|
|
8429
9210
|
state.__omTurn = this.turn;
|
|
8430
9211
|
}
|
|
9212
|
+
this.turn.addHooks({
|
|
9213
|
+
onBufferChunkSealed: rotateResponseMessageId,
|
|
9214
|
+
onSyncObservationComplete: rotateResponseMessageId
|
|
9215
|
+
});
|
|
8431
9216
|
const observabilityContext = getOmObservabilityContext(args);
|
|
8432
9217
|
state.__omObservabilityContext = observabilityContext;
|
|
8433
9218
|
this.turn.observabilityContext = observabilityContext;
|
|
9219
|
+
this.turn.actorModelContext = actorModelContext;
|
|
8434
9220
|
{
|
|
8435
9221
|
const step = this.turn.step(stepNumber);
|
|
8436
9222
|
let ctx;
|
|
@@ -8512,6 +9298,7 @@ var ObservationalMemoryProcessor = class {
|
|
|
8512
9298
|
const state = _state ?? {};
|
|
8513
9299
|
const context = this.engine.getThreadContext(requestContext, messageList);
|
|
8514
9300
|
if (!context) return messageList;
|
|
9301
|
+
if (state[GATEWAY_STATE_KEY]) return messageList;
|
|
8515
9302
|
const observabilityContext = getOmObservabilityContext(args);
|
|
8516
9303
|
state.__omObservabilityContext = observabilityContext;
|
|
8517
9304
|
return this.engine.getTokenCounter().runWithModelContext(state.__omActorModelContext, async () => {
|
|
@@ -8563,5 +9350,5 @@ function getObservationsAsOf(activeObservations, asOf) {
|
|
|
8563
9350
|
}
|
|
8564
9351
|
|
|
8565
9352
|
export { ModelByInputTokens, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, ObservationalMemoryProcessor, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, combineObservationGroupRanges, deriveObservationGroupProvenance, extractCurrentTask, formatMessagesForObserver, formatToolResultForObserver, getObservationsAsOf, hasCurrentTaskSection, injectAnchorIds, optimizeObservationsForContext, parseAnchorId, parseObservationGroups, parseObserverOutput, reconcileObservationGroupsFromReflection, renderObservationGroupsForReflection, resolveToolResultValue, stripEphemeralAnchorIds, stripObservationGroups, truncateStringByTokens, wrapInObservationGroup };
|
|
8566
|
-
//# sourceMappingURL=chunk-
|
|
8567
|
-
//# sourceMappingURL=chunk-
|
|
9353
|
+
//# sourceMappingURL=chunk-Z5GWA7LB.js.map
|
|
9354
|
+
//# sourceMappingURL=chunk-Z5GWA7LB.js.map
|