@mastra/memory 1.6.0 → 1.6.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/{chunk-5UYAHJVJ.cjs → chunk-D6II7EP4.cjs} +660 -531
  3. package/dist/chunk-D6II7EP4.cjs.map +1 -0
  4. package/dist/{chunk-A62BQK35.js → chunk-GBBQIJQF.js} +660 -531
  5. package/dist/chunk-GBBQIJQF.js.map +1 -0
  6. package/dist/docs/SKILL.md +1 -1
  7. package/dist/docs/assets/SOURCE_MAP.json +25 -25
  8. package/dist/docs/references/docs-agents-agent-approval.md +61 -31
  9. package/dist/docs/references/docs-agents-supervisor-agents.md +1 -1
  10. package/dist/docs/references/docs-memory-observational-memory.md +9 -0
  11. package/dist/docs/references/docs-memory-semantic-recall.md +17 -1
  12. package/dist/docs/references/reference-core-getMemory.md +2 -2
  13. package/dist/docs/references/reference-core-listMemory.md +1 -1
  14. package/dist/docs/references/reference-memory-clone-utilities.md +5 -5
  15. package/dist/docs/references/reference-memory-cloneThread.md +17 -21
  16. package/dist/docs/references/reference-memory-createThread.md +10 -10
  17. package/dist/docs/references/reference-memory-getThreadById.md +2 -2
  18. package/dist/docs/references/reference-memory-listThreads.md +5 -5
  19. package/dist/docs/references/reference-memory-memory-class.md +12 -14
  20. package/dist/docs/references/reference-memory-observational-memory.md +102 -94
  21. package/dist/docs/references/reference-processors-token-limiter-processor.md +11 -13
  22. package/dist/docs/references/reference-storage-dynamodb.md +9 -9
  23. package/dist/docs/references/reference-storage-libsql.md +2 -2
  24. package/dist/docs/references/reference-storage-mongodb.md +5 -5
  25. package/dist/docs/references/reference-storage-postgresql.md +25 -25
  26. package/dist/docs/references/reference-storage-upstash.md +3 -3
  27. package/dist/docs/references/reference-vectors-libsql.md +31 -31
  28. package/dist/docs/references/reference-vectors-mongodb.md +32 -32
  29. package/dist/docs/references/reference-vectors-pg.md +60 -44
  30. package/dist/docs/references/reference-vectors-upstash.md +25 -25
  31. package/dist/index.cjs +246 -57
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.ts +19 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +246 -57
  36. package/dist/index.js.map +1 -1
  37. package/dist/{observational-memory-MXI54VC7.cjs → observational-memory-AHVELJX4.cjs} +17 -17
  38. package/dist/{observational-memory-MXI54VC7.cjs.map → observational-memory-AHVELJX4.cjs.map} +1 -1
  39. package/dist/{observational-memory-SR6G4HN5.js → observational-memory-QFQUF5EY.js} +3 -3
  40. package/dist/{observational-memory-SR6G4HN5.js.map → observational-memory-QFQUF5EY.js.map} +1 -1
  41. package/dist/processors/index.cjs +15 -15
  42. package/dist/processors/index.js +1 -1
  43. package/dist/processors/observational-memory/date-utils.d.ts +35 -0
  44. package/dist/processors/observational-memory/date-utils.d.ts.map +1 -0
  45. package/dist/processors/observational-memory/markers.d.ts +94 -0
  46. package/dist/processors/observational-memory/markers.d.ts.map +1 -0
  47. package/dist/processors/observational-memory/observational-memory.d.ts +0 -76
  48. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  49. package/dist/processors/observational-memory/operation-registry.d.ts +14 -0
  50. package/dist/processors/observational-memory/operation-registry.d.ts.map +1 -0
  51. package/dist/processors/observational-memory/thresholds.d.ts +52 -0
  52. package/dist/processors/observational-memory/thresholds.d.ts.map +1 -0
  53. package/dist/processors/observational-memory/token-counter.d.ts +4 -0
  54. package/dist/processors/observational-memory/token-counter.d.ts.map +1 -1
  55. package/dist/tools/working-memory.d.ts.map +1 -1
  56. package/package.json +7 -7
  57. package/dist/chunk-5UYAHJVJ.cjs.map +0 -1
  58. package/dist/chunk-A62BQK35.js.map +0 -1
@@ -7,6 +7,7 @@ var llm = require('@mastra/core/llm');
7
7
  var memory = require('@mastra/core/memory');
8
8
  var processors = require('@mastra/core/processors');
9
9
  var xxhash = require('xxhash-wasm');
10
+ var crypto$1 = require('crypto');
10
11
  var lite = require('js-tiktoken/lite');
11
12
  var o200k_base = require('js-tiktoken/ranks/o200k_base');
12
13
 
@@ -17,6 +18,294 @@ var o200k_base__default = /*#__PURE__*/_interopDefault(o200k_base);
17
18
 
18
19
  // src/processors/observational-memory/observational-memory.ts
19
20
 
21
+ // src/processors/observational-memory/date-utils.ts
22
+ function formatRelativeTime(date, currentDate) {
23
+ const diffMs = currentDate.getTime() - date.getTime();
24
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
25
+ if (diffDays < 0) {
26
+ const futureDays = Math.abs(diffDays);
27
+ if (futureDays === 1) return "tomorrow";
28
+ if (futureDays < 7) return `in ${futureDays} days`;
29
+ if (futureDays < 14) return "in 1 week";
30
+ if (futureDays < 30) return `in ${Math.floor(futureDays / 7)} weeks`;
31
+ if (futureDays < 60) return "in 1 month";
32
+ if (futureDays < 365) return `in ${Math.floor(futureDays / 30)} months`;
33
+ const years = Math.floor(futureDays / 365);
34
+ return `in ${years} year${years > 1 ? "s" : ""}`;
35
+ }
36
+ if (diffDays === 0) return "today";
37
+ if (diffDays === 1) return "yesterday";
38
+ if (diffDays < 7) return `${diffDays} days ago`;
39
+ if (diffDays < 14) return "1 week ago";
40
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
41
+ if (diffDays < 60) return "1 month ago";
42
+ if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
43
+ return `${Math.floor(diffDays / 365)} year${Math.floor(diffDays / 365) > 1 ? "s" : ""} ago`;
44
+ }
45
+ function formatGapBetweenDates(prevDate, currDate) {
46
+ const diffMs = currDate.getTime() - prevDate.getTime();
47
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
48
+ if (diffDays <= 1) {
49
+ return null;
50
+ } else if (diffDays < 7) {
51
+ return `[${diffDays} days later]`;
52
+ } else if (diffDays < 14) {
53
+ return `[1 week later]`;
54
+ } else if (diffDays < 30) {
55
+ const weeks = Math.floor(diffDays / 7);
56
+ return `[${weeks} weeks later]`;
57
+ } else if (diffDays < 60) {
58
+ return `[1 month later]`;
59
+ } else {
60
+ const months = Math.floor(diffDays / 30);
61
+ return `[${months} months later]`;
62
+ }
63
+ }
64
+ function parseDateFromContent(dateContent) {
65
+ let targetDate = null;
66
+ const simpleDateMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2}),?\s+(\d{4})/);
67
+ if (simpleDateMatch) {
68
+ const parsed = /* @__PURE__ */ new Date(`${simpleDateMatch[1]} ${simpleDateMatch[2]}, ${simpleDateMatch[3]}`);
69
+ if (!isNaN(parsed.getTime())) {
70
+ targetDate = parsed;
71
+ }
72
+ }
73
+ if (!targetDate) {
74
+ const rangeMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2})-\d{1,2},?\s+(\d{4})/);
75
+ if (rangeMatch) {
76
+ const parsed = /* @__PURE__ */ new Date(`${rangeMatch[1]} ${rangeMatch[2]}, ${rangeMatch[3]}`);
77
+ if (!isNaN(parsed.getTime())) {
78
+ targetDate = parsed;
79
+ }
80
+ }
81
+ }
82
+ if (!targetDate) {
83
+ const vagueMatch = dateContent.match(
84
+ /(late|early|mid)[- ]?(?:to[- ]?(?:late|early|mid)[- ]?)?([A-Z][a-z]+)\s+(\d{4})/i
85
+ );
86
+ if (vagueMatch) {
87
+ const month = vagueMatch[2];
88
+ const year = vagueMatch[3];
89
+ const modifier = vagueMatch[1].toLowerCase();
90
+ let day = 15;
91
+ if (modifier === "early") day = 7;
92
+ if (modifier === "late") day = 23;
93
+ const parsed = /* @__PURE__ */ new Date(`${month} ${day}, ${year}`);
94
+ if (!isNaN(parsed.getTime())) {
95
+ targetDate = parsed;
96
+ }
97
+ }
98
+ }
99
+ if (!targetDate) {
100
+ const crossMonthMatch = dateContent.match(/([A-Z][a-z]+)\s+to\s+(?:early\s+)?([A-Z][a-z]+)\s+(\d{4})/i);
101
+ if (crossMonthMatch) {
102
+ const parsed = /* @__PURE__ */ new Date(`${crossMonthMatch[2]} 1, ${crossMonthMatch[3]}`);
103
+ if (!isNaN(parsed.getTime())) {
104
+ targetDate = parsed;
105
+ }
106
+ }
107
+ }
108
+ return targetDate;
109
+ }
110
+ function isFutureIntentObservation(line) {
111
+ const futureIntentPatterns = [
112
+ /\bwill\s+(?:be\s+)?(?:\w+ing|\w+)\b/i,
113
+ /\bplans?\s+to\b/i,
114
+ /\bplanning\s+to\b/i,
115
+ /\blooking\s+forward\s+to\b/i,
116
+ /\bgoing\s+to\b/i,
117
+ /\bintends?\s+to\b/i,
118
+ /\bwants?\s+to\b/i,
119
+ /\bneeds?\s+to\b/i,
120
+ /\babout\s+to\b/i
121
+ ];
122
+ return futureIntentPatterns.some((pattern) => pattern.test(line));
123
+ }
124
+ function expandInlineEstimatedDates(observations, currentDate) {
125
+ const inlineDateRegex = /\((estimated|meaning)\s+([^)]+\d{4})\)/gi;
126
+ return observations.replace(inlineDateRegex, (match, prefix, dateContent, offset) => {
127
+ const targetDate = parseDateFromContent(dateContent);
128
+ if (targetDate) {
129
+ const relative = formatRelativeTime(targetDate, currentDate);
130
+ const lineStart = observations.lastIndexOf("\n", offset) + 1;
131
+ const lineBeforeDate = observations.slice(lineStart, offset);
132
+ const isPastDate = targetDate < currentDate;
133
+ const isFutureIntent = isFutureIntentObservation(lineBeforeDate);
134
+ if (isPastDate && isFutureIntent) {
135
+ return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
136
+ }
137
+ return `(${prefix} ${dateContent} - ${relative})`;
138
+ }
139
+ return match;
140
+ });
141
+ }
142
+ function addRelativeTimeToObservations(observations, currentDate) {
143
+ const withInlineDates = expandInlineEstimatedDates(observations, currentDate);
144
+ const dateHeaderRegex = /^(Date:\s*)([A-Z][a-z]+ \d{1,2}, \d{4})$/gm;
145
+ const dates = [];
146
+ let regexMatch;
147
+ while ((regexMatch = dateHeaderRegex.exec(withInlineDates)) !== null) {
148
+ const dateStr = regexMatch[2];
149
+ const parsed = new Date(dateStr);
150
+ if (!isNaN(parsed.getTime())) {
151
+ dates.push({
152
+ index: regexMatch.index,
153
+ date: parsed,
154
+ match: regexMatch[0],
155
+ prefix: regexMatch[1],
156
+ dateStr
157
+ });
158
+ }
159
+ }
160
+ if (dates.length === 0) {
161
+ return withInlineDates;
162
+ }
163
+ let result = "";
164
+ let lastIndex = 0;
165
+ for (let i = 0; i < dates.length; i++) {
166
+ const curr = dates[i];
167
+ const prev = i > 0 ? dates[i - 1] : null;
168
+ result += withInlineDates.slice(lastIndex, curr.index);
169
+ if (prev) {
170
+ const gap = formatGapBetweenDates(prev.date, curr.date);
171
+ if (gap) {
172
+ result += `
173
+ ${gap}
174
+
175
+ `;
176
+ }
177
+ }
178
+ const relative = formatRelativeTime(curr.date, currentDate);
179
+ result += `${curr.prefix}${curr.dateStr} (${relative})`;
180
+ lastIndex = curr.index + curr.match.length;
181
+ }
182
+ result += withInlineDates.slice(lastIndex);
183
+ return result;
184
+ }
185
+
186
+ // src/processors/observational-memory/markers.ts
187
+ function createObservationStartMarker(params) {
188
+ return {
189
+ type: "data-om-observation-start",
190
+ data: {
191
+ cycleId: params.cycleId,
192
+ operationType: params.operationType,
193
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
194
+ tokensToObserve: params.tokensToObserve,
195
+ recordId: params.recordId,
196
+ threadId: params.threadId,
197
+ threadIds: params.threadIds,
198
+ config: params.config
199
+ }
200
+ };
201
+ }
202
+ function createObservationEndMarker(params) {
203
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
204
+ const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
205
+ return {
206
+ type: "data-om-observation-end",
207
+ data: {
208
+ cycleId: params.cycleId,
209
+ operationType: params.operationType,
210
+ completedAt,
211
+ durationMs,
212
+ tokensObserved: params.tokensObserved,
213
+ observationTokens: params.observationTokens,
214
+ observations: params.observations,
215
+ currentTask: params.currentTask,
216
+ suggestedResponse: params.suggestedResponse,
217
+ recordId: params.recordId,
218
+ threadId: params.threadId
219
+ }
220
+ };
221
+ }
222
+ function createObservationFailedMarker(params) {
223
+ const failedAt = (/* @__PURE__ */ new Date()).toISOString();
224
+ const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
225
+ return {
226
+ type: "data-om-observation-failed",
227
+ data: {
228
+ cycleId: params.cycleId,
229
+ operationType: params.operationType,
230
+ failedAt,
231
+ durationMs,
232
+ tokensAttempted: params.tokensAttempted,
233
+ error: params.error,
234
+ recordId: params.recordId,
235
+ threadId: params.threadId
236
+ }
237
+ };
238
+ }
239
+ function createBufferingStartMarker(params) {
240
+ return {
241
+ type: "data-om-buffering-start",
242
+ data: {
243
+ cycleId: params.cycleId,
244
+ operationType: params.operationType,
245
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
246
+ tokensToBuffer: params.tokensToBuffer,
247
+ recordId: params.recordId,
248
+ threadId: params.threadId,
249
+ threadIds: params.threadIds,
250
+ config: params.config
251
+ }
252
+ };
253
+ }
254
+ function createBufferingEndMarker(params) {
255
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
256
+ const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
257
+ return {
258
+ type: "data-om-buffering-end",
259
+ data: {
260
+ cycleId: params.cycleId,
261
+ operationType: params.operationType,
262
+ completedAt,
263
+ durationMs,
264
+ tokensBuffered: params.tokensBuffered,
265
+ bufferedTokens: params.bufferedTokens,
266
+ recordId: params.recordId,
267
+ threadId: params.threadId,
268
+ observations: params.observations
269
+ }
270
+ };
271
+ }
272
+ function createBufferingFailedMarker(params) {
273
+ const failedAt = (/* @__PURE__ */ new Date()).toISOString();
274
+ const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
275
+ return {
276
+ type: "data-om-buffering-failed",
277
+ data: {
278
+ cycleId: params.cycleId,
279
+ operationType: params.operationType,
280
+ failedAt,
281
+ durationMs,
282
+ tokensAttempted: params.tokensAttempted,
283
+ error: params.error,
284
+ recordId: params.recordId,
285
+ threadId: params.threadId
286
+ }
287
+ };
288
+ }
289
+ function createActivationMarker(params) {
290
+ return {
291
+ type: "data-om-activation",
292
+ data: {
293
+ cycleId: params.cycleId,
294
+ operationType: params.operationType,
295
+ activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
296
+ chunksActivated: params.chunksActivated,
297
+ tokensActivated: params.tokensActivated,
298
+ observationTokens: params.observationTokens,
299
+ messagesActivated: params.messagesActivated,
300
+ recordId: params.recordId,
301
+ threadId: params.threadId,
302
+ generationCount: params.generationCount,
303
+ config: params.config,
304
+ observations: params.observations
305
+ }
306
+ };
307
+ }
308
+
20
309
  // src/processors/observational-memory/observer-agent.ts
21
310
  var OBSERVER_EXTRACTION_INSTRUCTIONS = `CRITICAL: DISTINGUISH USER ASSERTIONS FROM QUESTIONS
22
311
 
@@ -688,6 +977,29 @@ function optimizeObservationsForContext(observations) {
688
977
  return optimized.trim();
689
978
  }
690
979
 
980
+ // src/processors/observational-memory/operation-registry.ts
981
+ var activeOps = /* @__PURE__ */ new Map();
982
+ function opKey(recordId, op) {
983
+ return `${recordId}:${op}`;
984
+ }
985
+ function registerOp(recordId, op) {
986
+ const key = opKey(recordId, op);
987
+ activeOps.set(key, (activeOps.get(key) ?? 0) + 1);
988
+ }
989
+ function unregisterOp(recordId, op) {
990
+ const key = opKey(recordId, op);
991
+ const count = activeOps.get(key);
992
+ if (!count) return;
993
+ if (count <= 1) {
994
+ activeOps.delete(key);
995
+ } else {
996
+ activeOps.set(key, count - 1);
997
+ }
998
+ }
999
+ function isOpActiveInProcess(recordId, op) {
1000
+ return (activeOps.get(opKey(recordId, op)) ?? 0) > 0;
1001
+ }
1002
+
691
1003
  // src/processors/observational-memory/reflector-agent.ts
692
1004
  function buildReflectorSystemPrompt(instruction) {
693
1005
  return `You are the memory consciousness of an AI assistant. Your memory observation reflections will be the ONLY information the assistant has about past interactions with this user.
@@ -916,6 +1228,91 @@ function extractReflectorListItems(content) {
916
1228
  function validateCompression(reflectedTokens, targetThreshold) {
917
1229
  return reflectedTokens < targetThreshold;
918
1230
  }
1231
+
1232
+ // src/processors/observational-memory/thresholds.ts
1233
+ function getMaxThreshold(threshold) {
1234
+ if (typeof threshold === "number") {
1235
+ return threshold;
1236
+ }
1237
+ return threshold.max;
1238
+ }
1239
+ function calculateDynamicThreshold(threshold, currentObservationTokens) {
1240
+ if (typeof threshold === "number") {
1241
+ return threshold;
1242
+ }
1243
+ const totalBudget = threshold.max;
1244
+ const baseThreshold = threshold.min;
1245
+ const effectiveThreshold = Math.max(totalBudget - currentObservationTokens, baseThreshold);
1246
+ return Math.round(effectiveThreshold);
1247
+ }
1248
+ function resolveBufferTokens(bufferTokens, messageTokens) {
1249
+ if (bufferTokens === false) return void 0;
1250
+ if (bufferTokens === void 0) return void 0;
1251
+ if (bufferTokens > 0 && bufferTokens < 1) {
1252
+ return Math.round(getMaxThreshold(messageTokens) * bufferTokens);
1253
+ }
1254
+ return bufferTokens;
1255
+ }
1256
+ function resolveBlockAfter(blockAfter, messageTokens) {
1257
+ if (blockAfter === void 0) return void 0;
1258
+ if (blockAfter >= 1 && blockAfter < 100) {
1259
+ return Math.round(getMaxThreshold(messageTokens) * blockAfter);
1260
+ }
1261
+ return blockAfter;
1262
+ }
1263
+ function resolveRetentionFloor(bufferActivation, messageTokensThreshold) {
1264
+ if (bufferActivation >= 1e3) return bufferActivation;
1265
+ const ratio = Math.max(0, Math.min(1, bufferActivation));
1266
+ return messageTokensThreshold * (1 - ratio);
1267
+ }
1268
+ function resolveActivationRatio(bufferActivation, messageTokensThreshold) {
1269
+ if (bufferActivation >= 1e3) {
1270
+ return Math.max(0, Math.min(1, 1 - bufferActivation / messageTokensThreshold));
1271
+ }
1272
+ return Math.max(0, Math.min(1, bufferActivation));
1273
+ }
1274
+ function calculateProjectedMessageRemoval(chunks, bufferActivation, messageTokensThreshold, currentPendingTokens) {
1275
+ if (chunks.length === 0) return 0;
1276
+ const retentionFloor = resolveRetentionFloor(bufferActivation, messageTokensThreshold);
1277
+ const targetMessageTokens = Math.max(0, currentPendingTokens - retentionFloor);
1278
+ if (targetMessageTokens === 0) return 0;
1279
+ let cumulativeMessageTokens = 0;
1280
+ let bestOverBoundary = 0;
1281
+ let bestOverTokens = 0;
1282
+ let bestUnderBoundary = 0;
1283
+ let bestUnderTokens = 0;
1284
+ for (let i = 0; i < chunks.length; i++) {
1285
+ cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
1286
+ const boundary = i + 1;
1287
+ if (cumulativeMessageTokens >= targetMessageTokens) {
1288
+ if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
1289
+ bestOverBoundary = boundary;
1290
+ bestOverTokens = cumulativeMessageTokens;
1291
+ }
1292
+ } else {
1293
+ if (cumulativeMessageTokens > bestUnderTokens) {
1294
+ bestUnderBoundary = boundary;
1295
+ bestUnderTokens = cumulativeMessageTokens;
1296
+ }
1297
+ }
1298
+ }
1299
+ const maxOvershoot = retentionFloor * 0.95;
1300
+ const overshoot = bestOverTokens - targetMessageTokens;
1301
+ const remainingAfterOver = currentPendingTokens - bestOverTokens;
1302
+ const remainingAfterUnder = currentPendingTokens - bestUnderTokens;
1303
+ const minRemaining = Math.min(1e3, retentionFloor);
1304
+ let bestBoundaryMessageTokens;
1305
+ if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
1306
+ bestBoundaryMessageTokens = bestOverTokens;
1307
+ } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
1308
+ bestBoundaryMessageTokens = bestUnderTokens;
1309
+ } else if (bestOverBoundary > 0) {
1310
+ bestBoundaryMessageTokens = bestOverTokens;
1311
+ } else {
1312
+ return chunks[0]?.messageTokens ?? 0;
1313
+ }
1314
+ return bestBoundaryMessageTokens;
1315
+ }
919
1316
  var sharedDefaultEncoder;
920
1317
  function getDefaultEncoder() {
921
1318
  if (!sharedDefaultEncoder) {
@@ -923,8 +1320,94 @@ function getDefaultEncoder() {
923
1320
  }
924
1321
  return sharedDefaultEncoder;
925
1322
  }
1323
+ var TOKEN_ESTIMATE_CACHE_VERSION = 1;
1324
+ function buildEstimateKey(kind, text) {
1325
+ const payloadHash = crypto$1.createHash("sha1").update(text).digest("hex");
1326
+ return `${kind}:${payloadHash}`;
1327
+ }
1328
+ function resolveEncodingId(encoding) {
1329
+ if (!encoding) return "o200k_base";
1330
+ try {
1331
+ return `custom:${crypto$1.createHash("sha1").update(JSON.stringify(encoding)).digest("hex")}`;
1332
+ } catch {
1333
+ return "custom:unknown";
1334
+ }
1335
+ }
1336
+ function isTokenEstimateEntry(value) {
1337
+ if (!value || typeof value !== "object") return false;
1338
+ const entry = value;
1339
+ return typeof entry.v === "number" && typeof entry.source === "string" && typeof entry.key === "string" && typeof entry.tokens === "number";
1340
+ }
1341
+ function getCacheEntry(cache, key) {
1342
+ if (!cache || typeof cache !== "object") return void 0;
1343
+ if (isTokenEstimateEntry(cache)) {
1344
+ return cache.key === key ? cache : void 0;
1345
+ }
1346
+ return void 0;
1347
+ }
1348
+ function getPartCacheEntry(part, key) {
1349
+ const cache = part?.providerMetadata?.mastra?.tokenEstimate;
1350
+ return getCacheEntry(cache, key);
1351
+ }
1352
+ function setPartCacheEntry(part, _key, entry) {
1353
+ const mutablePart = part;
1354
+ mutablePart.providerMetadata ??= {};
1355
+ mutablePart.providerMetadata.mastra ??= {};
1356
+ mutablePart.providerMetadata.mastra.tokenEstimate = entry;
1357
+ }
1358
+ function getMessageCacheEntry(message, key) {
1359
+ const content = message.content;
1360
+ if (content && typeof content === "object") {
1361
+ const contentLevelCache = content.metadata?.mastra?.tokenEstimate;
1362
+ const contentLevelEntry = getCacheEntry(contentLevelCache, key);
1363
+ if (contentLevelEntry) return contentLevelEntry;
1364
+ }
1365
+ const messageLevelCache = message?.metadata?.mastra?.tokenEstimate;
1366
+ return getCacheEntry(messageLevelCache, key);
1367
+ }
1368
+ function setMessageCacheEntry(message, _key, entry) {
1369
+ const content = message.content;
1370
+ if (content && typeof content === "object") {
1371
+ content.metadata ??= {};
1372
+ content.metadata.mastra ??= {};
1373
+ content.metadata.mastra.tokenEstimate = entry;
1374
+ return;
1375
+ }
1376
+ message.metadata ??= {};
1377
+ message.metadata.mastra ??= {};
1378
+ message.metadata.mastra.tokenEstimate = entry;
1379
+ }
1380
+ function serializePartForTokenCounting(part) {
1381
+ const hasTokenEstimate = Boolean(part?.providerMetadata?.mastra?.tokenEstimate);
1382
+ if (!hasTokenEstimate) {
1383
+ return JSON.stringify(part);
1384
+ }
1385
+ const clonedPart = {
1386
+ ...part,
1387
+ providerMetadata: {
1388
+ ...part.providerMetadata ?? {},
1389
+ mastra: {
1390
+ ...part.providerMetadata?.mastra ?? {}
1391
+ }
1392
+ }
1393
+ };
1394
+ delete clonedPart.providerMetadata.mastra.tokenEstimate;
1395
+ if (Object.keys(clonedPart.providerMetadata.mastra).length === 0) {
1396
+ delete clonedPart.providerMetadata.mastra;
1397
+ }
1398
+ if (Object.keys(clonedPart.providerMetadata).length === 0) {
1399
+ delete clonedPart.providerMetadata;
1400
+ }
1401
+ return JSON.stringify(clonedPart);
1402
+ }
1403
+ function isValidCacheEntry(entry, expectedKey, expectedSource) {
1404
+ return Boolean(
1405
+ entry && entry.v === TOKEN_ESTIMATE_CACHE_VERSION && entry.source === expectedSource && entry.key === expectedKey && Number.isFinite(entry.tokens)
1406
+ );
1407
+ }
926
1408
  var TokenCounter = class _TokenCounter {
927
1409
  encoder;
1410
+ cacheSource;
928
1411
  // Per-message overhead: accounts for role tokens, message framing, and separators.
929
1412
  // Empirically derived from OpenAI's token counting guide (3 tokens per message base +
930
1413
  // fractional overhead from name/role encoding). 3.8 is a practical average across models.
@@ -933,6 +1416,7 @@ var TokenCounter = class _TokenCounter {
933
1416
  static TOKENS_PER_CONVERSATION = 24;
934
1417
  constructor(encoding) {
935
1418
  this.encoder = encoding ? new lite.Tiktoken(encoding) : getDefaultEncoder();
1419
+ this.cacheSource = `v${TOKEN_ESTIMATE_CACHE_VERSION}:${resolveEncodingId(encoding)}`;
936
1420
  }
937
1421
  /**
938
1422
  * Count tokens in a plain string
@@ -941,43 +1425,108 @@ var TokenCounter = class _TokenCounter {
941
1425
  if (!text) return 0;
942
1426
  return this.encoder.encode(text, "all").length;
943
1427
  }
1428
+ readOrPersistPartEstimate(part, kind, payload) {
1429
+ const key = buildEstimateKey(kind, payload);
1430
+ const cached = getPartCacheEntry(part, key);
1431
+ if (isValidCacheEntry(cached, key, this.cacheSource)) {
1432
+ return cached.tokens;
1433
+ }
1434
+ const tokens = this.countString(payload);
1435
+ setPartCacheEntry(part, key, {
1436
+ v: TOKEN_ESTIMATE_CACHE_VERSION,
1437
+ source: this.cacheSource,
1438
+ key,
1439
+ tokens
1440
+ });
1441
+ return tokens;
1442
+ }
1443
+ readOrPersistMessageEstimate(message, kind, payload) {
1444
+ const key = buildEstimateKey(kind, payload);
1445
+ const cached = getMessageCacheEntry(message, key);
1446
+ if (isValidCacheEntry(cached, key, this.cacheSource)) {
1447
+ return cached.tokens;
1448
+ }
1449
+ const tokens = this.countString(payload);
1450
+ setMessageCacheEntry(message, key, {
1451
+ v: TOKEN_ESTIMATE_CACHE_VERSION,
1452
+ source: this.cacheSource,
1453
+ key,
1454
+ tokens
1455
+ });
1456
+ return tokens;
1457
+ }
1458
+ resolveToolResultForTokenCounting(part, invocationResult) {
1459
+ const mastraMetadata = part?.providerMetadata?.mastra;
1460
+ if (mastraMetadata && typeof mastraMetadata === "object" && "modelOutput" in mastraMetadata) {
1461
+ return {
1462
+ value: mastraMetadata.modelOutput,
1463
+ usingStoredModelOutput: true
1464
+ };
1465
+ }
1466
+ return {
1467
+ value: invocationResult,
1468
+ usingStoredModelOutput: false
1469
+ };
1470
+ }
944
1471
  /**
945
1472
  * Count tokens in a single message
946
1473
  */
947
1474
  countMessage(message) {
948
- let tokenString = message.role;
1475
+ let payloadTokens = this.countString(message.role);
949
1476
  let overhead = _TokenCounter.TOKENS_PER_MESSAGE;
950
1477
  let toolResultCount = 0;
951
1478
  if (typeof message.content === "string") {
952
- tokenString += message.content;
1479
+ payloadTokens += this.readOrPersistMessageEstimate(message, "message-content", message.content);
953
1480
  } else if (message.content && typeof message.content === "object") {
954
1481
  if (message.content.content && !Array.isArray(message.content.parts)) {
955
- tokenString += message.content.content;
1482
+ payloadTokens += this.readOrPersistMessageEstimate(message, "content-content", message.content.content);
956
1483
  } else if (Array.isArray(message.content.parts)) {
957
1484
  for (const part of message.content.parts) {
958
1485
  if (part.type === "text") {
959
- tokenString += part.text;
1486
+ payloadTokens += this.readOrPersistPartEstimate(part, "text", part.text);
960
1487
  } else if (part.type === "tool-invocation") {
961
1488
  const invocation = part.toolInvocation;
962
1489
  if (invocation.state === "call" || invocation.state === "partial-call") {
963
1490
  if (invocation.toolName) {
964
- tokenString += invocation.toolName;
1491
+ payloadTokens += this.readOrPersistPartEstimate(
1492
+ part,
1493
+ `tool-${invocation.state}-name`,
1494
+ invocation.toolName
1495
+ );
965
1496
  }
966
1497
  if (invocation.args) {
967
1498
  if (typeof invocation.args === "string") {
968
- tokenString += invocation.args;
1499
+ payloadTokens += this.readOrPersistPartEstimate(
1500
+ part,
1501
+ `tool-${invocation.state}-args`,
1502
+ invocation.args
1503
+ );
969
1504
  } else {
970
- tokenString += JSON.stringify(invocation.args);
1505
+ const argsJson = JSON.stringify(invocation.args);
1506
+ payloadTokens += this.readOrPersistPartEstimate(part, `tool-${invocation.state}-args-json`, argsJson);
971
1507
  overhead -= 12;
972
1508
  }
973
1509
  }
974
1510
  } else if (invocation.state === "result") {
975
1511
  toolResultCount++;
976
- if (invocation.result !== void 0) {
977
- if (typeof invocation.result === "string") {
978
- tokenString += invocation.result;
1512
+ const { value: resultForCounting, usingStoredModelOutput } = this.resolveToolResultForTokenCounting(
1513
+ part,
1514
+ invocation.result
1515
+ );
1516
+ if (resultForCounting !== void 0) {
1517
+ if (typeof resultForCounting === "string") {
1518
+ payloadTokens += this.readOrPersistPartEstimate(
1519
+ part,
1520
+ usingStoredModelOutput ? "tool-result-model-output" : "tool-result",
1521
+ resultForCounting
1522
+ );
979
1523
  } else {
980
- tokenString += JSON.stringify(invocation.result);
1524
+ const resultJson = JSON.stringify(resultForCounting);
1525
+ payloadTokens += this.readOrPersistPartEstimate(
1526
+ part,
1527
+ usingStoredModelOutput ? "tool-result-model-output-json" : "tool-result-json",
1528
+ resultJson
1529
+ );
981
1530
  overhead -= 12;
982
1531
  }
983
1532
  }
@@ -987,7 +1536,8 @@ var TokenCounter = class _TokenCounter {
987
1536
  );
988
1537
  }
989
1538
  } else if (typeof part.type === "string" && part.type.startsWith("data-")) ; else if (part.type === "reasoning") ; else {
990
- tokenString += JSON.stringify(part);
1539
+ const serialized = serializePartForTokenCounting(part);
1540
+ payloadTokens += this.readOrPersistPartEstimate(part, `part-${part.type}`, serialized);
991
1541
  }
992
1542
  }
993
1543
  }
@@ -995,7 +1545,7 @@ var TokenCounter = class _TokenCounter {
995
1545
  if (toolResultCount > 0) {
996
1546
  overhead += toolResultCount * _TokenCounter.TOKENS_PER_MESSAGE;
997
1547
  }
998
- return Math.round(this.encoder.encode(tokenString, "all").length + overhead);
1548
+ return Math.round(payloadTokens + overhead);
999
1549
  }
1000
1550
  /**
1001
1551
  * Count tokens in an array of messages
@@ -1032,19 +1582,6 @@ function omError(msg, err) {
1032
1582
  omDebug(`[OM:ERROR] ${full}`);
1033
1583
  }
1034
1584
  omDebug(`[OM:process-start] OM module loaded, pid=${process.pid}`);
1035
- var activeOps = /* @__PURE__ */ new Set();
1036
- function opKey(recordId, op) {
1037
- return `${recordId}:${op}`;
1038
- }
1039
- function registerOp(recordId, op) {
1040
- activeOps.add(opKey(recordId, op));
1041
- }
1042
- function unregisterOp(recordId, op) {
1043
- activeOps.delete(opKey(recordId, op));
1044
- }
1045
- function isOpActiveInProcess(recordId, op) {
1046
- return activeOps.has(opKey(recordId, op));
1047
- }
1048
1585
  if (OM_DEBUG_LOG) {
1049
1586
  const _origConsoleError = console.error;
1050
1587
  console.error = (...args) => {
@@ -1054,159 +1591,6 @@ if (OM_DEBUG_LOG) {
1054
1591
  _origConsoleError.apply(console, args);
1055
1592
  };
1056
1593
  }
1057
- function formatRelativeTime(date, currentDate) {
1058
- const diffMs = currentDate.getTime() - date.getTime();
1059
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
1060
- if (diffDays === 0) return "today";
1061
- if (diffDays === 1) return "yesterday";
1062
- if (diffDays < 7) return `${diffDays} days ago`;
1063
- if (diffDays < 14) return "1 week ago";
1064
- if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
1065
- if (diffDays < 60) return "1 month ago";
1066
- if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
1067
- return `${Math.floor(diffDays / 365)} year${Math.floor(diffDays / 365) > 1 ? "s" : ""} ago`;
1068
- }
1069
- function formatGapBetweenDates(prevDate, currDate) {
1070
- const diffMs = currDate.getTime() - prevDate.getTime();
1071
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
1072
- if (diffDays <= 1) {
1073
- return null;
1074
- } else if (diffDays < 7) {
1075
- return `[${diffDays} days later]`;
1076
- } else if (diffDays < 14) {
1077
- return `[1 week later]`;
1078
- } else if (diffDays < 30) {
1079
- const weeks = Math.floor(diffDays / 7);
1080
- return `[${weeks} weeks later]`;
1081
- } else if (diffDays < 60) {
1082
- return `[1 month later]`;
1083
- } else {
1084
- const months = Math.floor(diffDays / 30);
1085
- return `[${months} months later]`;
1086
- }
1087
- }
1088
- function parseDateFromContent(dateContent) {
1089
- let targetDate = null;
1090
- const simpleDateMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2}),?\s+(\d{4})/);
1091
- if (simpleDateMatch) {
1092
- const parsed = /* @__PURE__ */ new Date(`${simpleDateMatch[1]} ${simpleDateMatch[2]}, ${simpleDateMatch[3]}`);
1093
- if (!isNaN(parsed.getTime())) {
1094
- targetDate = parsed;
1095
- }
1096
- }
1097
- if (!targetDate) {
1098
- const rangeMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2})-\d{1,2},?\s+(\d{4})/);
1099
- if (rangeMatch) {
1100
- const parsed = /* @__PURE__ */ new Date(`${rangeMatch[1]} ${rangeMatch[2]}, ${rangeMatch[3]}`);
1101
- if (!isNaN(parsed.getTime())) {
1102
- targetDate = parsed;
1103
- }
1104
- }
1105
- }
1106
- if (!targetDate) {
1107
- const vagueMatch = dateContent.match(
1108
- /(late|early|mid)[- ]?(?:to[- ]?(?:late|early|mid)[- ]?)?([A-Z][a-z]+)\s+(\d{4})/i
1109
- );
1110
- if (vagueMatch) {
1111
- const month = vagueMatch[2];
1112
- const year = vagueMatch[3];
1113
- const modifier = vagueMatch[1].toLowerCase();
1114
- let day = 15;
1115
- if (modifier === "early") day = 7;
1116
- if (modifier === "late") day = 23;
1117
- const parsed = /* @__PURE__ */ new Date(`${month} ${day}, ${year}`);
1118
- if (!isNaN(parsed.getTime())) {
1119
- targetDate = parsed;
1120
- }
1121
- }
1122
- }
1123
- if (!targetDate) {
1124
- const crossMonthMatch = dateContent.match(/([A-Z][a-z]+)\s+to\s+(?:early\s+)?([A-Z][a-z]+)\s+(\d{4})/i);
1125
- if (crossMonthMatch) {
1126
- const parsed = /* @__PURE__ */ new Date(`${crossMonthMatch[2]} 1, ${crossMonthMatch[3]}`);
1127
- if (!isNaN(parsed.getTime())) {
1128
- targetDate = parsed;
1129
- }
1130
- }
1131
- }
1132
- return targetDate;
1133
- }
1134
- function isFutureIntentObservation(line) {
1135
- const futureIntentPatterns = [
1136
- /\bwill\s+(?:be\s+)?(?:\w+ing|\w+)\b/i,
1137
- /\bplans?\s+to\b/i,
1138
- /\bplanning\s+to\b/i,
1139
- /\blooking\s+forward\s+to\b/i,
1140
- /\bgoing\s+to\b/i,
1141
- /\bintends?\s+to\b/i,
1142
- /\bwants?\s+to\b/i,
1143
- /\bneeds?\s+to\b/i,
1144
- /\babout\s+to\b/i
1145
- ];
1146
- return futureIntentPatterns.some((pattern) => pattern.test(line));
1147
- }
1148
- function expandInlineEstimatedDates(observations, currentDate) {
1149
- const inlineDateRegex = /\((estimated|meaning)\s+([^)]+\d{4})\)/gi;
1150
- return observations.replace(inlineDateRegex, (match, prefix, dateContent) => {
1151
- const targetDate = parseDateFromContent(dateContent);
1152
- if (targetDate) {
1153
- const relative = formatRelativeTime(targetDate, currentDate);
1154
- const matchIndex = observations.indexOf(match);
1155
- const lineStart = observations.lastIndexOf("\n", matchIndex) + 1;
1156
- const lineBeforeDate = observations.substring(lineStart, matchIndex);
1157
- const isPastDate = targetDate < currentDate;
1158
- const isFutureIntent = isFutureIntentObservation(lineBeforeDate);
1159
- if (isPastDate && isFutureIntent) {
1160
- return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
1161
- }
1162
- return `(${prefix} ${dateContent} - ${relative})`;
1163
- }
1164
- return match;
1165
- });
1166
- }
1167
- function addRelativeTimeToObservations(observations, currentDate) {
1168
- const withInlineDates = expandInlineEstimatedDates(observations, currentDate);
1169
- const dateHeaderRegex = /^(Date:\s*)([A-Z][a-z]+ \d{1,2}, \d{4})$/gm;
1170
- const dates = [];
1171
- let regexMatch;
1172
- while ((regexMatch = dateHeaderRegex.exec(withInlineDates)) !== null) {
1173
- const dateStr = regexMatch[2];
1174
- const parsed = new Date(dateStr);
1175
- if (!isNaN(parsed.getTime())) {
1176
- dates.push({
1177
- index: regexMatch.index,
1178
- date: parsed,
1179
- match: regexMatch[0],
1180
- prefix: regexMatch[1],
1181
- dateStr
1182
- });
1183
- }
1184
- }
1185
- if (dates.length === 0) {
1186
- return withInlineDates;
1187
- }
1188
- let result = "";
1189
- let lastIndex = 0;
1190
- for (let i = 0; i < dates.length; i++) {
1191
- const curr = dates[i];
1192
- const prev = i > 0 ? dates[i - 1] : null;
1193
- result += withInlineDates.slice(lastIndex, curr.index);
1194
- if (prev) {
1195
- const gap = formatGapBetweenDates(prev.date, curr.date);
1196
- if (gap) {
1197
- result += `
1198
- ${gap}
1199
-
1200
- `;
1201
- }
1202
- }
1203
- const relative = formatRelativeTime(curr.date, currentDate);
1204
- result += `${curr.prefix}${curr.dateStr} (${relative})`;
1205
- lastIndex = curr.index + curr.match.length;
1206
- }
1207
- result += withInlineDates.slice(lastIndex);
1208
- return result;
1209
- }
1210
1594
  var OBSERVATIONAL_MEMORY_DEFAULTS = {
1211
1595
  observation: {
1212
1596
  model: "google/gemini-2.5-flash",
@@ -1431,71 +1815,6 @@ var ObservationalMemory = class _ObservationalMemory {
1431
1815
  }
1432
1816
  return [];
1433
1817
  }
1434
- /**
1435
- * Resolve bufferActivation config into an absolute retention floor (tokens to keep).
1436
- * - Value in (0, 1]: ratio → retentionFloor = threshold * (1 - value)
1437
- * - Value >= 1000: absolute token count → retentionFloor = value
1438
- */
1439
- resolveRetentionFloor(bufferActivation, messageTokensThreshold) {
1440
- if (bufferActivation >= 1e3) return bufferActivation;
1441
- return messageTokensThreshold * (1 - bufferActivation);
1442
- }
1443
- /**
1444
- * Convert bufferActivation to the equivalent ratio (0-1) for the storage layer.
1445
- * When bufferActivation >= 1000, it's an absolute retention target, so we compute
1446
- * the equivalent ratio: 1 - (bufferActivation / threshold).
1447
- */
1448
- resolveActivationRatio(bufferActivation, messageTokensThreshold) {
1449
- if (bufferActivation >= 1e3) {
1450
- return Math.max(0, Math.min(1, 1 - bufferActivation / messageTokensThreshold));
1451
- }
1452
- return bufferActivation;
1453
- }
1454
- /**
1455
- * Calculate the projected message tokens that would be removed if activation happened now.
1456
- * This replicates the chunk boundary logic in swapBufferedToActive without actually activating.
1457
- */
1458
- calculateProjectedMessageRemoval(chunks, bufferActivation, messageTokensThreshold, currentPendingTokens) {
1459
- if (chunks.length === 0) return 0;
1460
- const retentionFloor = this.resolveRetentionFloor(bufferActivation, messageTokensThreshold);
1461
- const targetMessageTokens = Math.max(0, currentPendingTokens - retentionFloor);
1462
- let cumulativeMessageTokens = 0;
1463
- let bestOverBoundary = 0;
1464
- let bestOverTokens = 0;
1465
- let bestUnderBoundary = 0;
1466
- let bestUnderTokens = 0;
1467
- for (let i = 0; i < chunks.length; i++) {
1468
- cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
1469
- const boundary = i + 1;
1470
- if (cumulativeMessageTokens >= targetMessageTokens) {
1471
- if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
1472
- bestOverBoundary = boundary;
1473
- bestOverTokens = cumulativeMessageTokens;
1474
- }
1475
- } else {
1476
- if (cumulativeMessageTokens > bestUnderTokens) {
1477
- bestUnderBoundary = boundary;
1478
- bestUnderTokens = cumulativeMessageTokens;
1479
- }
1480
- }
1481
- }
1482
- const maxOvershoot = retentionFloor * 0.95;
1483
- const overshoot = bestOverTokens - targetMessageTokens;
1484
- const remainingAfterOver = currentPendingTokens - bestOverTokens;
1485
- const remainingAfterUnder = currentPendingTokens - bestUnderTokens;
1486
- const minRemaining = Math.min(1e3, retentionFloor);
1487
- let bestBoundaryMessageTokens;
1488
- if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
1489
- bestBoundaryMessageTokens = bestOverTokens;
1490
- } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
1491
- bestBoundaryMessageTokens = bestUnderTokens;
1492
- } else if (bestOverBoundary > 0) {
1493
- bestBoundaryMessageTokens = bestOverTokens;
1494
- } else {
1495
- return chunks[0]?.messageTokens ?? 0;
1496
- }
1497
- return bestBoundaryMessageTokens;
1498
- }
1499
1818
  /**
1500
1819
  * Check if we've crossed a new bufferTokens interval boundary.
1501
1820
  * Returns true if async buffering should be triggered.
@@ -1545,7 +1864,7 @@ var ObservationalMemory = class _ObservationalMemory {
1545
1864
  if (this.isAsyncBufferingInProgress(bufferKey)) return false;
1546
1865
  if (_ObservationalMemory.lastBufferedBoundary.has(bufferKey)) return false;
1547
1866
  if (record.bufferedReflection) return false;
1548
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
1867
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
1549
1868
  const activationPoint = reflectThreshold * this.reflectionConfig.bufferActivation;
1550
1869
  const shouldTrigger = currentObservationTokens >= activationPoint;
1551
1870
  omDebug(
@@ -1662,12 +1981,12 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1662
1981
  },
1663
1982
  providerOptions: config.observation?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.providerOptions,
1664
1983
  maxTokensPerBatch: config.observation?.maxTokensPerBatch ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.maxTokensPerBatch,
1665
- bufferTokens: asyncBufferingDisabled ? void 0 : this.resolveBufferTokens(
1984
+ bufferTokens: asyncBufferingDisabled ? void 0 : resolveBufferTokens(
1666
1985
  config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens,
1667
1986
  config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
1668
1987
  ),
1669
1988
  bufferActivation: asyncBufferingDisabled ? void 0 : config.observation?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferActivation,
1670
- blockAfter: asyncBufferingDisabled ? void 0 : this.resolveBlockAfter(
1989
+ blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
1671
1990
  config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0),
1672
1991
  config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
1673
1992
  ),
@@ -1683,7 +2002,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1683
2002
  },
1684
2003
  providerOptions: config.reflection?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.providerOptions,
1685
2004
  bufferActivation: asyncBufferingDisabled ? void 0 : config?.reflection?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation,
1686
- blockAfter: asyncBufferingDisabled ? void 0 : this.resolveBlockAfter(
2005
+ blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
1687
2006
  config.reflection?.blockAfter ?? (config.reflection?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation ? 1.2 : void 0),
1688
2007
  config.reflection?.observationTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens
1689
2008
  ),
@@ -1778,7 +2097,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1778
2097
  `Async buffering is not yet supported with scope: 'resource'. Use scope: 'thread', or set observation: { bufferTokens: false } to disable async buffering.`
1779
2098
  );
1780
2099
  }
1781
- const observationThreshold = this.getMaxThreshold(this.observationConfig.messageTokens);
2100
+ const observationThreshold = getMaxThreshold(this.observationConfig.messageTokens);
1782
2101
  if (this.observationConfig.bufferTokens !== void 0) {
1783
2102
  if (this.observationConfig.bufferTokens <= 0) {
1784
2103
  throw new Error(`observation.bufferTokens must be > 0, got ${this.observationConfig.bufferTokens}`);
@@ -1824,7 +2143,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1824
2143
  }
1825
2144
  }
1826
2145
  if (this.reflectionConfig.blockAfter !== void 0) {
1827
- const reflectionThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
2146
+ const reflectionThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
1828
2147
  if (this.reflectionConfig.blockAfter < reflectionThreshold) {
1829
2148
  throw new Error(
1830
2149
  `reflection.blockAfter (${this.reflectionConfig.blockAfter}) must be >= reflection.observationTokens (${reflectionThreshold})`
@@ -1837,65 +2156,6 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1837
2156
  }
1838
2157
  }
1839
2158
  }
1840
- /**
1841
- * Resolve bufferTokens: if it's a fraction (0 < value < 1), multiply by messageTokens threshold.
1842
- * Otherwise return the absolute token count.
1843
- */
1844
- resolveBufferTokens(bufferTokens, messageTokens) {
1845
- if (bufferTokens === false) return void 0;
1846
- if (bufferTokens === void 0) return void 0;
1847
- if (bufferTokens > 0 && bufferTokens < 1) {
1848
- const threshold = typeof messageTokens === "number" ? messageTokens : messageTokens.max;
1849
- return Math.round(threshold * bufferTokens);
1850
- }
1851
- return bufferTokens;
1852
- }
1853
- /**
1854
- * Resolve blockAfter config value.
1855
- * Values in [1, 100) are treated as multipliers of the threshold.
1856
- * e.g. blockAfter: 1.5 with messageTokens: 20_000 → 30_000
1857
- * Values >= 100 are treated as absolute token counts.
1858
- * Defaults to 1.2 (120% of threshold) when async buffering is enabled but blockAfter is omitted.
1859
- */
1860
- resolveBlockAfter(blockAfter, messageTokens) {
1861
- if (blockAfter === void 0) return void 0;
1862
- if (blockAfter >= 1 && blockAfter < 100) {
1863
- const threshold = typeof messageTokens === "number" ? messageTokens : messageTokens.max;
1864
- return Math.round(threshold * blockAfter);
1865
- }
1866
- return blockAfter;
1867
- }
1868
- /**
1869
- * Get the maximum value from a threshold (simple number or range)
1870
- */
1871
- getMaxThreshold(threshold) {
1872
- if (typeof threshold === "number") {
1873
- return threshold;
1874
- }
1875
- return threshold.max;
1876
- }
1877
- /**
1878
- * Calculate dynamic threshold based on observation space.
1879
- * When shareTokenBudget is enabled, the message threshold can expand
1880
- * into unused observation space, up to the total context budget.
1881
- *
1882
- * Total budget = messageTokens + observationTokens
1883
- * Effective threshold = totalBudget - currentObservationTokens
1884
- *
1885
- * Example with 30k:40k thresholds (70k total):
1886
- * - 0 observations → messages can use ~70k
1887
- * - 10k observations → messages can use ~60k
1888
- * - 40k observations → messages back to ~30k
1889
- */
1890
- calculateDynamicThreshold(threshold, currentObservationTokens) {
1891
- if (typeof threshold === "number") {
1892
- return threshold;
1893
- }
1894
- const totalBudget = threshold.max;
1895
- const baseThreshold = threshold.min;
1896
- const effectiveThreshold = Math.max(totalBudget - currentObservationTokens, baseThreshold);
1897
- return Math.round(effectiveThreshold);
1898
- }
1899
2159
  /**
1900
2160
  * Check whether the unobserved message tokens meet the observation threshold.
1901
2161
  */
@@ -1903,7 +2163,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1903
2163
  const { record, unobservedTokens, extraTokens = 0 } = opts;
1904
2164
  const pendingTokens = (record.pendingMessageTokens ?? 0) + unobservedTokens + extraTokens;
1905
2165
  const currentObservationTokens = record.observationTokenCount ?? 0;
1906
- const threshold = this.calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
2166
+ const threshold = calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
1907
2167
  return pendingTokens >= threshold;
1908
2168
  }
1909
2169
  /**
@@ -1983,7 +2243,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1983
2243
  * Check if we need to trigger reflection.
1984
2244
  */
1985
2245
  shouldReflect(observationTokens) {
1986
- const threshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
2246
+ const threshold = getMaxThreshold(this.reflectionConfig.observationTokens);
1987
2247
  return observationTokens > threshold;
1988
2248
  }
1989
2249
  // ════════════════════════════════════════════════════════════════════════════
@@ -2004,153 +2264,11 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2004
2264
  */
2005
2265
  getObservationMarkerConfig() {
2006
2266
  return {
2007
- messageTokens: this.getMaxThreshold(this.observationConfig.messageTokens),
2008
- observationTokens: this.getMaxThreshold(this.reflectionConfig.observationTokens),
2267
+ messageTokens: getMaxThreshold(this.observationConfig.messageTokens),
2268
+ observationTokens: getMaxThreshold(this.reflectionConfig.observationTokens),
2009
2269
  scope: this.scope
2010
2270
  };
2011
2271
  }
2012
- /**
2013
- * Create a start marker for when observation begins.
2014
- */
2015
- createObservationStartMarker(params) {
2016
- return {
2017
- type: "data-om-observation-start",
2018
- data: {
2019
- cycleId: params.cycleId,
2020
- operationType: params.operationType,
2021
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2022
- tokensToObserve: params.tokensToObserve,
2023
- recordId: params.recordId,
2024
- threadId: params.threadId,
2025
- threadIds: params.threadIds,
2026
- config: this.getObservationMarkerConfig()
2027
- }
2028
- };
2029
- }
2030
- /**
2031
- * Create an end marker for when observation completes successfully.
2032
- */
2033
- createObservationEndMarker(params) {
2034
- const completedAt = (/* @__PURE__ */ new Date()).toISOString();
2035
- const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
2036
- return {
2037
- type: "data-om-observation-end",
2038
- data: {
2039
- cycleId: params.cycleId,
2040
- operationType: params.operationType,
2041
- completedAt,
2042
- durationMs,
2043
- tokensObserved: params.tokensObserved,
2044
- observationTokens: params.observationTokens,
2045
- observations: params.observations,
2046
- currentTask: params.currentTask,
2047
- suggestedResponse: params.suggestedResponse,
2048
- recordId: params.recordId,
2049
- threadId: params.threadId
2050
- }
2051
- };
2052
- }
2053
- /**
2054
- * Create a failed marker for when observation fails.
2055
- */
2056
- createObservationFailedMarker(params) {
2057
- const failedAt = (/* @__PURE__ */ new Date()).toISOString();
2058
- const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
2059
- return {
2060
- type: "data-om-observation-failed",
2061
- data: {
2062
- cycleId: params.cycleId,
2063
- operationType: params.operationType,
2064
- failedAt,
2065
- durationMs,
2066
- tokensAttempted: params.tokensAttempted,
2067
- error: params.error,
2068
- recordId: params.recordId,
2069
- threadId: params.threadId
2070
- }
2071
- };
2072
- }
2073
- /**
2074
- * Create a start marker for when async buffering begins.
2075
- */
2076
- createBufferingStartMarker(params) {
2077
- return {
2078
- type: "data-om-buffering-start",
2079
- data: {
2080
- cycleId: params.cycleId,
2081
- operationType: params.operationType,
2082
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2083
- tokensToBuffer: params.tokensToBuffer,
2084
- recordId: params.recordId,
2085
- threadId: params.threadId,
2086
- threadIds: params.threadIds,
2087
- config: this.getObservationMarkerConfig()
2088
- }
2089
- };
2090
- }
2091
- /**
2092
- * Create an end marker for when async buffering completes successfully.
2093
- */
2094
- createBufferingEndMarker(params) {
2095
- const completedAt = (/* @__PURE__ */ new Date()).toISOString();
2096
- const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
2097
- return {
2098
- type: "data-om-buffering-end",
2099
- data: {
2100
- cycleId: params.cycleId,
2101
- operationType: params.operationType,
2102
- completedAt,
2103
- durationMs,
2104
- tokensBuffered: params.tokensBuffered,
2105
- bufferedTokens: params.bufferedTokens,
2106
- recordId: params.recordId,
2107
- threadId: params.threadId,
2108
- observations: params.observations
2109
- }
2110
- };
2111
- }
2112
- /**
2113
- * Create a failed marker for when async buffering fails.
2114
- */
2115
- createBufferingFailedMarker(params) {
2116
- const failedAt = (/* @__PURE__ */ new Date()).toISOString();
2117
- const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
2118
- return {
2119
- type: "data-om-buffering-failed",
2120
- data: {
2121
- cycleId: params.cycleId,
2122
- operationType: params.operationType,
2123
- failedAt,
2124
- durationMs,
2125
- tokensAttempted: params.tokensAttempted,
2126
- error: params.error,
2127
- recordId: params.recordId,
2128
- threadId: params.threadId
2129
- }
2130
- };
2131
- }
2132
- /**
2133
- * Create an activation marker for when buffered observations are activated.
2134
- */
2135
- createActivationMarker(params) {
2136
- return {
2137
- type: "data-om-activation",
2138
- data: {
2139
- cycleId: params.cycleId,
2140
- operationType: params.operationType,
2141
- activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2142
- chunksActivated: params.chunksActivated,
2143
- tokensActivated: params.tokensActivated,
2144
- observationTokens: params.observationTokens,
2145
- messagesActivated: params.messagesActivated,
2146
- recordId: params.recordId,
2147
- threadId: params.threadId,
2148
- generationCount: params.generationCount,
2149
- config: this.getObservationMarkerConfig(),
2150
- observations: params.observations
2151
- }
2152
- };
2153
- }
2154
2272
  /**
2155
2273
  * Persist a data-om-* marker part on the last assistant message in messageList
2156
2274
  * AND save the updated message to the DB so it survives page reload.
@@ -2531,7 +2649,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2531
2649
  async callReflector(observations, manualPrompt, streamContext, observationTokensThreshold, abortSignal, skipContinuationHints, compressionStartLevel, requestContext) {
2532
2650
  const agent = this.getReflectorAgent();
2533
2651
  const originalTokens = this.tokenCounter.countObservations(observations);
2534
- const targetThreshold = observationTokensThreshold ?? this.getMaxThreshold(this.reflectionConfig.observationTokens);
2652
+ const targetThreshold = observationTokensThreshold ?? getMaxThreshold(this.reflectionConfig.observationTokens);
2535
2653
  let totalUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
2536
2654
  let currentLevel = compressionStartLevel ?? 0;
2537
2655
  const maxLevel = 3;
@@ -2606,7 +2724,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2606
2724
  break;
2607
2725
  }
2608
2726
  if (streamContext?.writer) {
2609
- const failedMarker = this.createObservationFailedMarker({
2727
+ const failedMarker = createObservationFailedMarker({
2610
2728
  cycleId: streamContext.cycleId,
2611
2729
  operationType: "reflection",
2612
2730
  startedAt: streamContext.startedAt,
@@ -2619,13 +2737,14 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2619
2737
  });
2620
2738
  const retryCycleId = crypto.randomUUID();
2621
2739
  streamContext.cycleId = retryCycleId;
2622
- const startMarker = this.createObservationStartMarker({
2740
+ const startMarker = createObservationStartMarker({
2623
2741
  cycleId: retryCycleId,
2624
2742
  operationType: "reflection",
2625
2743
  tokensToObserve: originalTokens,
2626
2744
  recordId: streamContext.recordId,
2627
2745
  threadId: streamContext.threadId,
2628
- threadIds: [streamContext.threadId]
2746
+ threadIds: [streamContext.threadId],
2747
+ config: this.getObservationMarkerConfig()
2629
2748
  });
2630
2749
  streamContext.startedAt = startMarker.data.startedAt;
2631
2750
  await streamContext.writer.custom(startMarker).catch(() => {
@@ -2759,8 +2878,8 @@ ${suggestedResponse}
2759
2878
  calculateObservationThresholds(_allMessages, unobservedMessages, _pendingTokens, otherThreadTokens, currentObservationTokens, _record) {
2760
2879
  const contextWindowTokens = this.tokenCounter.countMessages(unobservedMessages);
2761
2880
  const totalPendingTokens = Math.max(0, contextWindowTokens + otherThreadTokens);
2762
- const threshold = this.calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
2763
- const baseReflectionThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
2881
+ const threshold = calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
2882
+ const baseReflectionThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
2764
2883
  const isSharedBudget = typeof this.observationConfig.messageTokens !== "number";
2765
2884
  const totalBudget = isSharedBudget ? this.observationConfig.messageTokens.max : 0;
2766
2885
  const effectiveObservationTokensThreshold = isSharedBudget ? Math.max(totalBudget - threshold, 1e3) : baseReflectionThreshold;
@@ -2794,10 +2913,10 @@ ${suggestedResponse}
2794
2913
  const bufferedObservationTokens = bufferedChunks.reduce((sum, chunk) => sum + (chunk.tokenCount ?? 0), 0);
2795
2914
  const rawBufferedMessageTokens = bufferedChunks.reduce((sum, chunk) => sum + (chunk.messageTokens ?? 0), 0);
2796
2915
  const bufferedMessageTokens = Math.min(rawBufferedMessageTokens, totalPendingTokens);
2797
- const projectedMessageRemoval = this.calculateProjectedMessageRemoval(
2916
+ const projectedMessageRemoval = calculateProjectedMessageRemoval(
2798
2917
  bufferedChunks,
2799
2918
  this.observationConfig.bufferActivation ?? 1,
2800
- this.getMaxThreshold(this.observationConfig.messageTokens),
2919
+ getMaxThreshold(this.observationConfig.messageTokens),
2801
2920
  totalPendingTokens
2802
2921
  );
2803
2922
  let obsBufferStatus = "idle";
@@ -3006,7 +3125,39 @@ ${suggestedResponse}
3006
3125
  omDebug(
3007
3126
  `[OM:cleanupBranch] allMsgs=${allMsgs.length}, markerFound=${markerIdx !== -1}, markerIdx=${markerIdx}, observedMessageIds=${observedMessageIds?.length ?? "undefined"}, allIds=${allMsgs.map((m) => m.id?.slice(0, 8)).join(",")}`
3008
3127
  );
3009
- if (markerMsg && markerIdx !== -1) {
3128
+ if (observedMessageIds && observedMessageIds.length > 0) {
3129
+ const observedSet = new Set(observedMessageIds);
3130
+ const idsToRemove = /* @__PURE__ */ new Set();
3131
+ let skipped = 0;
3132
+ let backoffTriggered = false;
3133
+ for (const msg of allMsgs) {
3134
+ if (!msg?.id || msg.id === "om-continuation" || !observedSet.has(msg.id)) {
3135
+ continue;
3136
+ }
3137
+ if (typeof minRemaining === "number") {
3138
+ const nextRemainingMessages = allMsgs.filter(
3139
+ (m) => m?.id && m.id !== "om-continuation" && !idsToRemove.has(m.id) && m.id !== msg.id
3140
+ );
3141
+ const remainingIfRemoved = this.tokenCounter.countMessages(nextRemainingMessages);
3142
+ if (remainingIfRemoved < minRemaining) {
3143
+ skipped += 1;
3144
+ backoffTriggered = true;
3145
+ break;
3146
+ }
3147
+ }
3148
+ idsToRemove.add(msg.id);
3149
+ }
3150
+ omDebug(
3151
+ `[OM:cleanupActivation] observedSet=${[...observedSet].map((id) => id.slice(0, 8)).join(",")}, matched=${idsToRemove.size}, skipped=${skipped}, backoffTriggered=${backoffTriggered}, idsToRemove=${[...idsToRemove].map((id) => id.slice(0, 8)).join(",")}`
3152
+ );
3153
+ const idsToRemoveList = [...idsToRemove];
3154
+ if (idsToRemoveList.length > 0) {
3155
+ messageList.removeByIds(idsToRemoveList);
3156
+ omDebug(
3157
+ `[OM:cleanupActivation] removed ${idsToRemoveList.length} messages, remaining=${messageList.get.all.db().length}`
3158
+ );
3159
+ }
3160
+ } else if (markerMsg && markerIdx !== -1) {
3010
3161
  const idsToRemove = [];
3011
3162
  const messagesToSave = [];
3012
3163
  for (let i = 0; i < markerIdx; i++) {
@@ -3031,35 +3182,6 @@ ${suggestedResponse}
3031
3182
  if (messagesToSave.length > 0) {
3032
3183
  await this.saveMessagesWithSealedIdTracking(messagesToSave, sealedIds, threadId, resourceId, state);
3033
3184
  }
3034
- } else if (observedMessageIds && observedMessageIds.length > 0) {
3035
- const observedSet = new Set(observedMessageIds);
3036
- const idsToRemove = [];
3037
- const totalTokens = typeof minRemaining === "number" ? this.tokenCounter.countMessages(allMsgs) : void 0;
3038
- let removedTokens = 0;
3039
- let skipped = 0;
3040
- for (const msg of allMsgs) {
3041
- if (msg?.id && msg.id !== "om-continuation" && observedSet.has(msg.id)) {
3042
- if (typeof minRemaining === "number") {
3043
- const msgTokens = this.tokenCounter.countMessage(msg);
3044
- const remainingIfRemoved = (totalTokens ?? 0) - removedTokens - msgTokens;
3045
- if (remainingIfRemoved < minRemaining) {
3046
- skipped += 1;
3047
- continue;
3048
- }
3049
- removedTokens += msgTokens;
3050
- }
3051
- idsToRemove.push(msg.id);
3052
- }
3053
- }
3054
- omDebug(
3055
- `[OM:cleanupActivation] observedSet=${[...observedSet].map((id) => id.slice(0, 8)).join(",")}, matched=${idsToRemove.length}, skipped=${skipped}, idsToRemove=${idsToRemove.map((id) => id.slice(0, 8)).join(",")}`
3056
- );
3057
- if (idsToRemove.length > 0) {
3058
- messageList.removeByIds(idsToRemove);
3059
- omDebug(
3060
- `[OM:cleanupActivation] removed ${idsToRemove.length} messages, remaining=${messageList.get.all.db().length}`
3061
- );
3062
- }
3063
3185
  } else {
3064
3186
  const newInput = messageList.clear.input.db();
3065
3187
  const newOutput = messageList.clear.response.db();
@@ -3401,7 +3523,7 @@ ${suggestedResponse}
3401
3523
  );
3402
3524
  if (observationSucceeded) {
3403
3525
  const observedIds = activatedMessageIds?.length ? activatedMessageIds : Array.isArray(updatedRecord.observedMessageIds) ? updatedRecord.observedMessageIds : void 0;
3404
- const minRemaining = typeof this.observationConfig.bufferActivation === "number" ? Math.min(1e3, this.resolveRetentionFloor(this.observationConfig.bufferActivation, threshold)) : void 0;
3526
+ const minRemaining = typeof this.observationConfig.bufferActivation === "number" ? resolveRetentionFloor(this.observationConfig.bufferActivation, threshold) : void 0;
3405
3527
  omDebug(
3406
3528
  `[OM:cleanup] observedIds=${observedIds?.length ?? "undefined"}, ids=${observedIds?.join(",") ?? "none"}, updatedRecord.observedMessageIds=${JSON.stringify(updatedRecord.observedMessageIds)}, minRemaining=${minRemaining ?? "n/a"}`
3407
3529
  );
@@ -3445,8 +3567,8 @@ ${suggestedResponse}
3445
3567
  const freshUnobservedTokens = this.tokenCounter.countMessages(contextMessages);
3446
3568
  const otherThreadTokens = unobservedContextBlocks ? this.tokenCounter.countString(unobservedContextBlocks) : 0;
3447
3569
  const currentObservationTokens = freshRecord.observationTokenCount ?? 0;
3448
- const threshold = this.calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
3449
- const baseReflectionThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
3570
+ const threshold = calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
3571
+ const baseReflectionThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
3450
3572
  const isSharedBudget = typeof this.observationConfig.messageTokens !== "number";
3451
3573
  const totalBudget = isSharedBudget ? this.observationConfig.messageTokens.max : 0;
3452
3574
  const effectiveObservationTokensThreshold = isSharedBudget ? Math.max(totalBudget - threshold, 1e3) : baseReflectionThreshold;
@@ -3763,13 +3885,14 @@ ${newThreadSection}`;
3763
3885
  const lastMessage = unobservedMessages[unobservedMessages.length - 1];
3764
3886
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3765
3887
  if (lastMessage?.id) {
3766
- const startMarker = this.createObservationStartMarker({
3888
+ const startMarker = createObservationStartMarker({
3767
3889
  cycleId,
3768
3890
  operationType: "observation",
3769
3891
  tokensToObserve,
3770
3892
  recordId: record.id,
3771
3893
  threadId,
3772
- threadIds: [threadId]
3894
+ threadIds: [threadId],
3895
+ config: this.getObservationMarkerConfig()
3773
3896
  });
3774
3897
  if (writer) {
3775
3898
  await writer.custom(startMarker).catch(() => {
@@ -3837,7 +3960,7 @@ ${result.observations}` : result.observations;
3837
3960
  });
3838
3961
  const actualTokensObserved = this.tokenCounter.countMessages(messagesToObserve);
3839
3962
  if (lastMessage?.id) {
3840
- const endMarker = this.createObservationEndMarker({
3963
+ const endMarker = createObservationEndMarker({
3841
3964
  cycleId,
3842
3965
  operationType: "observation",
3843
3966
  startedAt,
@@ -3879,7 +4002,7 @@ ${result.observations}` : result.observations;
3879
4002
  });
3880
4003
  } catch (error) {
3881
4004
  if (lastMessage?.id) {
3882
- const failedMarker = this.createObservationFailedMarker({
4005
+ const failedMarker = createObservationFailedMarker({
3883
4006
  cycleId,
3884
4007
  operationType: "observation",
3885
4008
  startedAt,
@@ -4001,13 +4124,14 @@ ${result.observations}` : result.observations;
4001
4124
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
4002
4125
  const tokensToBuffer = this.tokenCounter.countMessages(messagesToBuffer);
4003
4126
  if (writer) {
4004
- const startMarker = this.createBufferingStartMarker({
4127
+ const startMarker = createBufferingStartMarker({
4005
4128
  cycleId,
4006
4129
  operationType: "observation",
4007
4130
  tokensToBuffer,
4008
4131
  recordId: freshRecord.id,
4009
4132
  threadId,
4010
- threadIds: [threadId]
4133
+ threadIds: [threadId],
4134
+ config: this.getObservationMarkerConfig()
4011
4135
  });
4012
4136
  void writer.custom(startMarker).catch(() => {
4013
4137
  });
@@ -4030,7 +4154,7 @@ ${result.observations}` : result.observations;
4030
4154
  _ObservationalMemory.lastBufferedAtTime.set(bufferKey, cursor);
4031
4155
  } catch (error) {
4032
4156
  if (writer) {
4033
- const failedMarker = this.createBufferingFailedMarker({
4157
+ const failedMarker = createBufferingFailedMarker({
4034
4158
  cycleId,
4035
4159
  operationType: "observation",
4036
4160
  startedAt,
@@ -4098,7 +4222,7 @@ ${result.observations}` : result.observations;
4098
4222
  const updatedRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
4099
4223
  const updatedChunks = this.getBufferedChunks(updatedRecord);
4100
4224
  const totalBufferedTokens = updatedChunks.reduce((sum, c) => sum + (c.tokenCount ?? 0), 0) || newTokenCount;
4101
- const endMarker = this.createBufferingEndMarker({
4225
+ const endMarker = createBufferingEndMarker({
4102
4226
  cycleId,
4103
4227
  operationType: "observation",
4104
4228
  startedAt,
@@ -4167,7 +4291,7 @@ ${bufferedObservations}`;
4167
4291
  if (!freshChunks.length) {
4168
4292
  return { success: false };
4169
4293
  }
4170
- const messageTokensThreshold = this.getMaxThreshold(this.observationConfig.messageTokens);
4294
+ const messageTokensThreshold = getMaxThreshold(this.observationConfig.messageTokens);
4171
4295
  let effectivePendingTokens = currentPendingTokens;
4172
4296
  if (messageList) {
4173
4297
  effectivePendingTokens = this.tokenCounter.countMessages(messageList.get.all.db());
@@ -4179,11 +4303,11 @@ ${bufferedObservations}`;
4179
4303
  }
4180
4304
  }
4181
4305
  const bufferActivation = this.observationConfig.bufferActivation ?? 0.7;
4182
- const activationRatio = this.resolveActivationRatio(bufferActivation, messageTokensThreshold);
4306
+ const activationRatio = resolveActivationRatio(bufferActivation, messageTokensThreshold);
4183
4307
  const forceMaxActivation = !!(this.observationConfig.blockAfter && effectivePendingTokens >= this.observationConfig.blockAfter);
4184
4308
  const bufferTokens = this.observationConfig.bufferTokens ?? 0;
4185
- const retentionFloor = this.resolveRetentionFloor(bufferActivation, messageTokensThreshold);
4186
- const projectedMessageRemoval = this.calculateProjectedMessageRemoval(
4309
+ const retentionFloor = resolveRetentionFloor(bufferActivation, messageTokensThreshold);
4310
+ const projectedMessageRemoval = calculateProjectedMessageRemoval(
4187
4311
  freshChunks,
4188
4312
  bufferActivation,
4189
4313
  messageTokensThreshold,
@@ -4217,7 +4341,7 @@ ${bufferedObservations}`;
4217
4341
  const perChunkMap = new Map(activationResult.perChunk?.map((c) => [c.cycleId, c]));
4218
4342
  for (const cycleId of activationResult.activatedCycleIds) {
4219
4343
  const chunkData = perChunkMap.get(cycleId);
4220
- const activationMarker = this.createActivationMarker({
4344
+ const activationMarker = createActivationMarker({
4221
4345
  cycleId,
4222
4346
  // Use the original buffering cycleId so UI can link them
4223
4347
  operationType: "observation",
@@ -4228,7 +4352,8 @@ ${bufferedObservations}`;
4228
4352
  recordId: updatedRecord.id,
4229
4353
  threadId: updatedRecord.threadId ?? record.threadId ?? "",
4230
4354
  generationCount: updatedRecord.generationCount ?? 0,
4231
- observations: chunkData?.observations ?? activationResult.observations
4355
+ observations: chunkData?.observations ?? activationResult.observations,
4356
+ config: this.getObservationMarkerConfig()
4232
4357
  });
4233
4358
  void writer.custom(activationMarker).catch(() => {
4234
4359
  });
@@ -4270,7 +4395,7 @@ ${bufferedObservations}`;
4270
4395
  });
4271
4396
  const asyncOp = this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext).catch(async (error) => {
4272
4397
  if (writer) {
4273
- const failedMarker = this.createBufferingFailedMarker({
4398
+ const failedMarker = createBufferingFailedMarker({
4274
4399
  cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
4275
4400
  operationType: "reflection",
4276
4401
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -4301,7 +4426,7 @@ ${bufferedObservations}`;
4301
4426
  const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
4302
4427
  const currentRecord = freshRecord ?? record;
4303
4428
  const observationTokens = currentRecord.observationTokenCount ?? 0;
4304
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
4429
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
4305
4430
  const bufferActivation = this.reflectionConfig.bufferActivation ?? 0.5;
4306
4431
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
4307
4432
  const cycleId = `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
@@ -4323,13 +4448,14 @@ ${bufferedObservations}`;
4323
4448
  `[OM:reflect] doAsyncBufferedReflection: starting reflector call, recordId=${currentRecord.id}, observationTokens=${sliceTokenEstimate}, compressionTarget=${compressionTarget} (inputTokens), activeObsLength=${activeObservations.length}, reflectedLineCount=${reflectedObservationLineCount}`
4324
4449
  );
4325
4450
  if (writer) {
4326
- const startMarker = this.createBufferingStartMarker({
4451
+ const startMarker = createBufferingStartMarker({
4327
4452
  cycleId,
4328
4453
  operationType: "reflection",
4329
4454
  tokensToBuffer: sliceTokenEstimate,
4330
4455
  recordId: record.id,
4331
4456
  threadId: record.threadId ?? "",
4332
- threadIds: record.threadId ? [record.threadId] : []
4457
+ threadIds: record.threadId ? [record.threadId] : [],
4458
+ config: this.getObservationMarkerConfig()
4333
4459
  });
4334
4460
  void writer.custom(startMarker).catch(() => {
4335
4461
  });
@@ -4364,7 +4490,7 @@ ${bufferedObservations}`;
4364
4490
  `[OM:reflect] doAsyncBufferedReflection: bufferedReflection saved with lineCount=${reflectedObservationLineCount}`
4365
4491
  );
4366
4492
  if (writer) {
4367
- const endMarker = this.createBufferingEndMarker({
4493
+ const endMarker = createBufferingEndMarker({
4368
4494
  cycleId,
4369
4495
  operationType: "reflection",
4370
4496
  startedAt,
@@ -4435,7 +4561,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4435
4561
  );
4436
4562
  if (writer) {
4437
4563
  const originalCycleId = _ObservationalMemory.reflectionBufferCycleIds.get(bufferKey);
4438
- const activationMarker = this.createActivationMarker({
4564
+ const activationMarker = createActivationMarker({
4439
4565
  cycleId: originalCycleId ?? `reflect-act-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
4440
4566
  operationType: "reflection",
4441
4567
  chunksActivated: 1,
@@ -4445,7 +4571,8 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4445
4571
  recordId: freshRecord.id,
4446
4572
  threadId: freshRecord.threadId ?? "",
4447
4573
  generationCount: afterRecord?.generationCount ?? freshRecord.generationCount ?? 0,
4448
- observations: afterRecord?.activeObservations
4574
+ observations: afterRecord?.activeObservations,
4575
+ config: this.getObservationMarkerConfig()
4449
4576
  });
4450
4577
  void writer.custom(activationMarker).catch(() => {
4451
4578
  });
@@ -4527,7 +4654,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4527
4654
  if (totalMessages === 0) {
4528
4655
  return;
4529
4656
  }
4530
- const threshold = this.getMaxThreshold(this.observationConfig.messageTokens);
4657
+ const threshold = getMaxThreshold(this.observationConfig.messageTokens);
4531
4658
  const threadTokenCounts = /* @__PURE__ */ new Map();
4532
4659
  for (const [threadId, msgs] of messagesByThread) {
4533
4660
  let tokens = 0;
@@ -4593,13 +4720,14 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4593
4720
  const tokensToObserve = this.tokenCounter.countMessages(msgs);
4594
4721
  threadTokensToObserve.set(threadId, tokensToObserve);
4595
4722
  if (lastMessage?.id) {
4596
- const startMarker = this.createObservationStartMarker({
4723
+ const startMarker = createObservationStartMarker({
4597
4724
  cycleId,
4598
4725
  operationType: "observation",
4599
4726
  tokensToObserve,
4600
4727
  recordId: record.id,
4601
4728
  threadId,
4602
- threadIds: allThreadIds
4729
+ threadIds: allThreadIds,
4730
+ config: this.getObservationMarkerConfig()
4603
4731
  });
4604
4732
  if (writer) {
4605
4733
  await writer.custom(startMarker).catch(() => {
@@ -4725,7 +4853,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4725
4853
  const lastMessage = threadMessages[threadMessages.length - 1];
4726
4854
  if (lastMessage?.id) {
4727
4855
  const tokensObserved = threadTokensToObserve.get(threadId) ?? this.tokenCounter.countMessages(threadMessages);
4728
- const endMarker = this.createObservationEndMarker({
4856
+ const endMarker = createObservationEndMarker({
4729
4857
  cycleId,
4730
4858
  operationType: "observation",
4731
4859
  startedAt: observationStartedAt,
@@ -4757,7 +4885,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4757
4885
  const lastMessage = msgs[msgs.length - 1];
4758
4886
  if (lastMessage?.id) {
4759
4887
  const tokensAttempted = threadTokensToObserve.get(threadId) ?? 0;
4760
- const failedMarker = this.createObservationFailedMarker({
4888
+ const failedMarker = createObservationFailedMarker({
4761
4889
  cycleId,
4762
4890
  operationType: "observation",
4763
4891
  startedAt: observationStartedAt,
@@ -4789,7 +4917,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4789
4917
  async maybeAsyncReflect(record, observationTokens, writer, messageList, requestContext) {
4790
4918
  if (!this.isAsyncReflectionEnabled()) return;
4791
4919
  const lockKey = this.getLockKey(record.threadId, record.resourceId);
4792
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
4920
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
4793
4921
  omDebug(
4794
4922
  `[OM:reflect] maybeAsyncReflect: observationTokens=${observationTokens}, reflectThreshold=${reflectThreshold}, isReflecting=${record.isReflecting}, bufferedReflection=${record.bufferedReflection ? "present (" + record.bufferedReflection.length + " chars)" : "empty"}, recordId=${record.id}, genCount=${record.generationCount}`
4795
4923
  );
@@ -4825,7 +4953,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4825
4953
  async maybeReflect(opts) {
4826
4954
  const { record, observationTokens, writer, abortSignal, messageList, reflectionHooks, requestContext } = opts;
4827
4955
  const lockKey = this.getLockKey(record.threadId, record.resourceId);
4828
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
4956
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
4829
4957
  if (this.isAsyncReflectionEnabled() && observationTokens < reflectThreshold) {
4830
4958
  if (this.shouldTriggerAsyncReflection(observationTokens, lockKey, record)) {
4831
4959
  this.startAsyncBufferedReflection(record, observationTokens, lockKey, writer, requestContext);
@@ -4866,13 +4994,14 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4866
4994
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
4867
4995
  const threadId = opts.threadId ?? "unknown";
4868
4996
  if (writer) {
4869
- const startMarker = this.createObservationStartMarker({
4997
+ const startMarker = createObservationStartMarker({
4870
4998
  cycleId,
4871
4999
  operationType: "reflection",
4872
5000
  tokensToObserve: observationTokens,
4873
5001
  recordId: record.id,
4874
5002
  threadId,
4875
- threadIds: [threadId]
5003
+ threadIds: [threadId],
5004
+ config: this.getObservationMarkerConfig()
4876
5005
  });
4877
5006
  await writer.custom(startMarker).catch(() => {
4878
5007
  });
@@ -4910,7 +5039,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4910
5039
  tokenCount: reflectionTokenCount
4911
5040
  });
4912
5041
  if (writer && streamContext) {
4913
- const endMarker = this.createObservationEndMarker({
5042
+ const endMarker = createObservationEndMarker({
4914
5043
  cycleId: streamContext.cycleId,
4915
5044
  operationType: "reflection",
4916
5045
  startedAt: streamContext.startedAt,
@@ -4935,7 +5064,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4935
5064
  });
4936
5065
  } catch (error) {
4937
5066
  if (writer && streamContext) {
4938
- const failedMarker = this.createObservationFailedMarker({
5067
+ const failedMarker = createObservationFailedMarker({
4939
5068
  cycleId: streamContext.cycleId,
4940
5069
  operationType: "reflection",
4941
5070
  startedAt: streamContext.startedAt,
@@ -5040,7 +5169,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5040
5169
  await this.storage.setReflectingFlag(record.id, true);
5041
5170
  registerOp(record.id, "reflecting");
5042
5171
  try {
5043
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
5172
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
5044
5173
  const reflectResult = await this.callReflector(
5045
5174
  record.activeObservations,
5046
5175
  prompt,
@@ -5132,5 +5261,5 @@ exports.formatMessagesForObserver = formatMessagesForObserver;
5132
5261
  exports.hasCurrentTaskSection = hasCurrentTaskSection;
5133
5262
  exports.optimizeObservationsForContext = optimizeObservationsForContext;
5134
5263
  exports.parseObserverOutput = parseObserverOutput;
5135
- //# sourceMappingURL=chunk-5UYAHJVJ.cjs.map
5136
- //# sourceMappingURL=chunk-5UYAHJVJ.cjs.map
5264
+ //# sourceMappingURL=chunk-D6II7EP4.cjs.map
5265
+ //# sourceMappingURL=chunk-D6II7EP4.cjs.map