@mastra/memory 1.6.0 → 1.6.1

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 +54 -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
@@ -5,11 +5,300 @@ import { resolveModelConfig } from '@mastra/core/llm';
5
5
  import { setThreadOMMetadata, getThreadOMMetadata, parseMemoryRequestContext } from '@mastra/core/memory';
6
6
  import { MessageHistory } from '@mastra/core/processors';
7
7
  import xxhash from 'xxhash-wasm';
8
+ import { createHash } from 'crypto';
8
9
  import { Tiktoken } from 'js-tiktoken/lite';
9
10
  import o200k_base from 'js-tiktoken/ranks/o200k_base';
10
11
 
11
12
  // src/processors/observational-memory/observational-memory.ts
12
13
 
14
+ // src/processors/observational-memory/date-utils.ts
15
+ function formatRelativeTime(date, currentDate) {
16
+ const diffMs = currentDate.getTime() - date.getTime();
17
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
18
+ if (diffDays < 0) {
19
+ const futureDays = Math.abs(diffDays);
20
+ if (futureDays === 1) return "tomorrow";
21
+ if (futureDays < 7) return `in ${futureDays} days`;
22
+ if (futureDays < 14) return "in 1 week";
23
+ if (futureDays < 30) return `in ${Math.floor(futureDays / 7)} weeks`;
24
+ if (futureDays < 60) return "in 1 month";
25
+ if (futureDays < 365) return `in ${Math.floor(futureDays / 30)} months`;
26
+ const years = Math.floor(futureDays / 365);
27
+ return `in ${years} year${years > 1 ? "s" : ""}`;
28
+ }
29
+ if (diffDays === 0) return "today";
30
+ if (diffDays === 1) return "yesterday";
31
+ if (diffDays < 7) return `${diffDays} days ago`;
32
+ if (diffDays < 14) return "1 week ago";
33
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
34
+ if (diffDays < 60) return "1 month ago";
35
+ if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
36
+ return `${Math.floor(diffDays / 365)} year${Math.floor(diffDays / 365) > 1 ? "s" : ""} ago`;
37
+ }
38
+ function formatGapBetweenDates(prevDate, currDate) {
39
+ const diffMs = currDate.getTime() - prevDate.getTime();
40
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
41
+ if (diffDays <= 1) {
42
+ return null;
43
+ } else if (diffDays < 7) {
44
+ return `[${diffDays} days later]`;
45
+ } else if (diffDays < 14) {
46
+ return `[1 week later]`;
47
+ } else if (diffDays < 30) {
48
+ const weeks = Math.floor(diffDays / 7);
49
+ return `[${weeks} weeks later]`;
50
+ } else if (diffDays < 60) {
51
+ return `[1 month later]`;
52
+ } else {
53
+ const months = Math.floor(diffDays / 30);
54
+ return `[${months} months later]`;
55
+ }
56
+ }
57
+ function parseDateFromContent(dateContent) {
58
+ let targetDate = null;
59
+ const simpleDateMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2}),?\s+(\d{4})/);
60
+ if (simpleDateMatch) {
61
+ const parsed = /* @__PURE__ */ new Date(`${simpleDateMatch[1]} ${simpleDateMatch[2]}, ${simpleDateMatch[3]}`);
62
+ if (!isNaN(parsed.getTime())) {
63
+ targetDate = parsed;
64
+ }
65
+ }
66
+ if (!targetDate) {
67
+ const rangeMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2})-\d{1,2},?\s+(\d{4})/);
68
+ if (rangeMatch) {
69
+ const parsed = /* @__PURE__ */ new Date(`${rangeMatch[1]} ${rangeMatch[2]}, ${rangeMatch[3]}`);
70
+ if (!isNaN(parsed.getTime())) {
71
+ targetDate = parsed;
72
+ }
73
+ }
74
+ }
75
+ if (!targetDate) {
76
+ const vagueMatch = dateContent.match(
77
+ /(late|early|mid)[- ]?(?:to[- ]?(?:late|early|mid)[- ]?)?([A-Z][a-z]+)\s+(\d{4})/i
78
+ );
79
+ if (vagueMatch) {
80
+ const month = vagueMatch[2];
81
+ const year = vagueMatch[3];
82
+ const modifier = vagueMatch[1].toLowerCase();
83
+ let day = 15;
84
+ if (modifier === "early") day = 7;
85
+ if (modifier === "late") day = 23;
86
+ const parsed = /* @__PURE__ */ new Date(`${month} ${day}, ${year}`);
87
+ if (!isNaN(parsed.getTime())) {
88
+ targetDate = parsed;
89
+ }
90
+ }
91
+ }
92
+ if (!targetDate) {
93
+ const crossMonthMatch = dateContent.match(/([A-Z][a-z]+)\s+to\s+(?:early\s+)?([A-Z][a-z]+)\s+(\d{4})/i);
94
+ if (crossMonthMatch) {
95
+ const parsed = /* @__PURE__ */ new Date(`${crossMonthMatch[2]} 1, ${crossMonthMatch[3]}`);
96
+ if (!isNaN(parsed.getTime())) {
97
+ targetDate = parsed;
98
+ }
99
+ }
100
+ }
101
+ return targetDate;
102
+ }
103
+ function isFutureIntentObservation(line) {
104
+ const futureIntentPatterns = [
105
+ /\bwill\s+(?:be\s+)?(?:\w+ing|\w+)\b/i,
106
+ /\bplans?\s+to\b/i,
107
+ /\bplanning\s+to\b/i,
108
+ /\blooking\s+forward\s+to\b/i,
109
+ /\bgoing\s+to\b/i,
110
+ /\bintends?\s+to\b/i,
111
+ /\bwants?\s+to\b/i,
112
+ /\bneeds?\s+to\b/i,
113
+ /\babout\s+to\b/i
114
+ ];
115
+ return futureIntentPatterns.some((pattern) => pattern.test(line));
116
+ }
117
+ function expandInlineEstimatedDates(observations, currentDate) {
118
+ const inlineDateRegex = /\((estimated|meaning)\s+([^)]+\d{4})\)/gi;
119
+ return observations.replace(inlineDateRegex, (match, prefix, dateContent, offset) => {
120
+ const targetDate = parseDateFromContent(dateContent);
121
+ if (targetDate) {
122
+ const relative = formatRelativeTime(targetDate, currentDate);
123
+ const lineStart = observations.lastIndexOf("\n", offset) + 1;
124
+ const lineBeforeDate = observations.slice(lineStart, offset);
125
+ const isPastDate = targetDate < currentDate;
126
+ const isFutureIntent = isFutureIntentObservation(lineBeforeDate);
127
+ if (isPastDate && isFutureIntent) {
128
+ return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
129
+ }
130
+ return `(${prefix} ${dateContent} - ${relative})`;
131
+ }
132
+ return match;
133
+ });
134
+ }
135
+ function addRelativeTimeToObservations(observations, currentDate) {
136
+ const withInlineDates = expandInlineEstimatedDates(observations, currentDate);
137
+ const dateHeaderRegex = /^(Date:\s*)([A-Z][a-z]+ \d{1,2}, \d{4})$/gm;
138
+ const dates = [];
139
+ let regexMatch;
140
+ while ((regexMatch = dateHeaderRegex.exec(withInlineDates)) !== null) {
141
+ const dateStr = regexMatch[2];
142
+ const parsed = new Date(dateStr);
143
+ if (!isNaN(parsed.getTime())) {
144
+ dates.push({
145
+ index: regexMatch.index,
146
+ date: parsed,
147
+ match: regexMatch[0],
148
+ prefix: regexMatch[1],
149
+ dateStr
150
+ });
151
+ }
152
+ }
153
+ if (dates.length === 0) {
154
+ return withInlineDates;
155
+ }
156
+ let result = "";
157
+ let lastIndex = 0;
158
+ for (let i = 0; i < dates.length; i++) {
159
+ const curr = dates[i];
160
+ const prev = i > 0 ? dates[i - 1] : null;
161
+ result += withInlineDates.slice(lastIndex, curr.index);
162
+ if (prev) {
163
+ const gap = formatGapBetweenDates(prev.date, curr.date);
164
+ if (gap) {
165
+ result += `
166
+ ${gap}
167
+
168
+ `;
169
+ }
170
+ }
171
+ const relative = formatRelativeTime(curr.date, currentDate);
172
+ result += `${curr.prefix}${curr.dateStr} (${relative})`;
173
+ lastIndex = curr.index + curr.match.length;
174
+ }
175
+ result += withInlineDates.slice(lastIndex);
176
+ return result;
177
+ }
178
+
179
+ // src/processors/observational-memory/markers.ts
180
+ function createObservationStartMarker(params) {
181
+ return {
182
+ type: "data-om-observation-start",
183
+ data: {
184
+ cycleId: params.cycleId,
185
+ operationType: params.operationType,
186
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
187
+ tokensToObserve: params.tokensToObserve,
188
+ recordId: params.recordId,
189
+ threadId: params.threadId,
190
+ threadIds: params.threadIds,
191
+ config: params.config
192
+ }
193
+ };
194
+ }
195
+ function createObservationEndMarker(params) {
196
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
197
+ const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
198
+ return {
199
+ type: "data-om-observation-end",
200
+ data: {
201
+ cycleId: params.cycleId,
202
+ operationType: params.operationType,
203
+ completedAt,
204
+ durationMs,
205
+ tokensObserved: params.tokensObserved,
206
+ observationTokens: params.observationTokens,
207
+ observations: params.observations,
208
+ currentTask: params.currentTask,
209
+ suggestedResponse: params.suggestedResponse,
210
+ recordId: params.recordId,
211
+ threadId: params.threadId
212
+ }
213
+ };
214
+ }
215
+ function createObservationFailedMarker(params) {
216
+ const failedAt = (/* @__PURE__ */ new Date()).toISOString();
217
+ const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
218
+ return {
219
+ type: "data-om-observation-failed",
220
+ data: {
221
+ cycleId: params.cycleId,
222
+ operationType: params.operationType,
223
+ failedAt,
224
+ durationMs,
225
+ tokensAttempted: params.tokensAttempted,
226
+ error: params.error,
227
+ recordId: params.recordId,
228
+ threadId: params.threadId
229
+ }
230
+ };
231
+ }
232
+ function createBufferingStartMarker(params) {
233
+ return {
234
+ type: "data-om-buffering-start",
235
+ data: {
236
+ cycleId: params.cycleId,
237
+ operationType: params.operationType,
238
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
239
+ tokensToBuffer: params.tokensToBuffer,
240
+ recordId: params.recordId,
241
+ threadId: params.threadId,
242
+ threadIds: params.threadIds,
243
+ config: params.config
244
+ }
245
+ };
246
+ }
247
+ function createBufferingEndMarker(params) {
248
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
249
+ const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
250
+ return {
251
+ type: "data-om-buffering-end",
252
+ data: {
253
+ cycleId: params.cycleId,
254
+ operationType: params.operationType,
255
+ completedAt,
256
+ durationMs,
257
+ tokensBuffered: params.tokensBuffered,
258
+ bufferedTokens: params.bufferedTokens,
259
+ recordId: params.recordId,
260
+ threadId: params.threadId,
261
+ observations: params.observations
262
+ }
263
+ };
264
+ }
265
+ function createBufferingFailedMarker(params) {
266
+ const failedAt = (/* @__PURE__ */ new Date()).toISOString();
267
+ const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
268
+ return {
269
+ type: "data-om-buffering-failed",
270
+ data: {
271
+ cycleId: params.cycleId,
272
+ operationType: params.operationType,
273
+ failedAt,
274
+ durationMs,
275
+ tokensAttempted: params.tokensAttempted,
276
+ error: params.error,
277
+ recordId: params.recordId,
278
+ threadId: params.threadId
279
+ }
280
+ };
281
+ }
282
+ function createActivationMarker(params) {
283
+ return {
284
+ type: "data-om-activation",
285
+ data: {
286
+ cycleId: params.cycleId,
287
+ operationType: params.operationType,
288
+ activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
289
+ chunksActivated: params.chunksActivated,
290
+ tokensActivated: params.tokensActivated,
291
+ observationTokens: params.observationTokens,
292
+ messagesActivated: params.messagesActivated,
293
+ recordId: params.recordId,
294
+ threadId: params.threadId,
295
+ generationCount: params.generationCount,
296
+ config: params.config,
297
+ observations: params.observations
298
+ }
299
+ };
300
+ }
301
+
13
302
  // src/processors/observational-memory/observer-agent.ts
14
303
  var OBSERVER_EXTRACTION_INSTRUCTIONS = `CRITICAL: DISTINGUISH USER ASSERTIONS FROM QUESTIONS
15
304
 
@@ -681,6 +970,29 @@ function optimizeObservationsForContext(observations) {
681
970
  return optimized.trim();
682
971
  }
683
972
 
973
+ // src/processors/observational-memory/operation-registry.ts
974
+ var activeOps = /* @__PURE__ */ new Map();
975
+ function opKey(recordId, op) {
976
+ return `${recordId}:${op}`;
977
+ }
978
+ function registerOp(recordId, op) {
979
+ const key = opKey(recordId, op);
980
+ activeOps.set(key, (activeOps.get(key) ?? 0) + 1);
981
+ }
982
+ function unregisterOp(recordId, op) {
983
+ const key = opKey(recordId, op);
984
+ const count = activeOps.get(key);
985
+ if (!count) return;
986
+ if (count <= 1) {
987
+ activeOps.delete(key);
988
+ } else {
989
+ activeOps.set(key, count - 1);
990
+ }
991
+ }
992
+ function isOpActiveInProcess(recordId, op) {
993
+ return (activeOps.get(opKey(recordId, op)) ?? 0) > 0;
994
+ }
995
+
684
996
  // src/processors/observational-memory/reflector-agent.ts
685
997
  function buildReflectorSystemPrompt(instruction) {
686
998
  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.
@@ -909,6 +1221,91 @@ function extractReflectorListItems(content) {
909
1221
  function validateCompression(reflectedTokens, targetThreshold) {
910
1222
  return reflectedTokens < targetThreshold;
911
1223
  }
1224
+
1225
+ // src/processors/observational-memory/thresholds.ts
1226
+ function getMaxThreshold(threshold) {
1227
+ if (typeof threshold === "number") {
1228
+ return threshold;
1229
+ }
1230
+ return threshold.max;
1231
+ }
1232
+ function calculateDynamicThreshold(threshold, currentObservationTokens) {
1233
+ if (typeof threshold === "number") {
1234
+ return threshold;
1235
+ }
1236
+ const totalBudget = threshold.max;
1237
+ const baseThreshold = threshold.min;
1238
+ const effectiveThreshold = Math.max(totalBudget - currentObservationTokens, baseThreshold);
1239
+ return Math.round(effectiveThreshold);
1240
+ }
1241
+ function resolveBufferTokens(bufferTokens, messageTokens) {
1242
+ if (bufferTokens === false) return void 0;
1243
+ if (bufferTokens === void 0) return void 0;
1244
+ if (bufferTokens > 0 && bufferTokens < 1) {
1245
+ return Math.round(getMaxThreshold(messageTokens) * bufferTokens);
1246
+ }
1247
+ return bufferTokens;
1248
+ }
1249
+ function resolveBlockAfter(blockAfter, messageTokens) {
1250
+ if (blockAfter === void 0) return void 0;
1251
+ if (blockAfter >= 1 && blockAfter < 100) {
1252
+ return Math.round(getMaxThreshold(messageTokens) * blockAfter);
1253
+ }
1254
+ return blockAfter;
1255
+ }
1256
+ function resolveRetentionFloor(bufferActivation, messageTokensThreshold) {
1257
+ if (bufferActivation >= 1e3) return bufferActivation;
1258
+ const ratio = Math.max(0, Math.min(1, bufferActivation));
1259
+ return messageTokensThreshold * (1 - ratio);
1260
+ }
1261
+ function resolveActivationRatio(bufferActivation, messageTokensThreshold) {
1262
+ if (bufferActivation >= 1e3) {
1263
+ return Math.max(0, Math.min(1, 1 - bufferActivation / messageTokensThreshold));
1264
+ }
1265
+ return Math.max(0, Math.min(1, bufferActivation));
1266
+ }
1267
+ function calculateProjectedMessageRemoval(chunks, bufferActivation, messageTokensThreshold, currentPendingTokens) {
1268
+ if (chunks.length === 0) return 0;
1269
+ const retentionFloor = resolveRetentionFloor(bufferActivation, messageTokensThreshold);
1270
+ const targetMessageTokens = Math.max(0, currentPendingTokens - retentionFloor);
1271
+ if (targetMessageTokens === 0) return 0;
1272
+ let cumulativeMessageTokens = 0;
1273
+ let bestOverBoundary = 0;
1274
+ let bestOverTokens = 0;
1275
+ let bestUnderBoundary = 0;
1276
+ let bestUnderTokens = 0;
1277
+ for (let i = 0; i < chunks.length; i++) {
1278
+ cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
1279
+ const boundary = i + 1;
1280
+ if (cumulativeMessageTokens >= targetMessageTokens) {
1281
+ if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
1282
+ bestOverBoundary = boundary;
1283
+ bestOverTokens = cumulativeMessageTokens;
1284
+ }
1285
+ } else {
1286
+ if (cumulativeMessageTokens > bestUnderTokens) {
1287
+ bestUnderBoundary = boundary;
1288
+ bestUnderTokens = cumulativeMessageTokens;
1289
+ }
1290
+ }
1291
+ }
1292
+ const maxOvershoot = retentionFloor * 0.95;
1293
+ const overshoot = bestOverTokens - targetMessageTokens;
1294
+ const remainingAfterOver = currentPendingTokens - bestOverTokens;
1295
+ const remainingAfterUnder = currentPendingTokens - bestUnderTokens;
1296
+ const minRemaining = Math.min(1e3, retentionFloor);
1297
+ let bestBoundaryMessageTokens;
1298
+ if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
1299
+ bestBoundaryMessageTokens = bestOverTokens;
1300
+ } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
1301
+ bestBoundaryMessageTokens = bestUnderTokens;
1302
+ } else if (bestOverBoundary > 0) {
1303
+ bestBoundaryMessageTokens = bestOverTokens;
1304
+ } else {
1305
+ return chunks[0]?.messageTokens ?? 0;
1306
+ }
1307
+ return bestBoundaryMessageTokens;
1308
+ }
912
1309
  var sharedDefaultEncoder;
913
1310
  function getDefaultEncoder() {
914
1311
  if (!sharedDefaultEncoder) {
@@ -916,8 +1313,94 @@ function getDefaultEncoder() {
916
1313
  }
917
1314
  return sharedDefaultEncoder;
918
1315
  }
1316
+ var TOKEN_ESTIMATE_CACHE_VERSION = 1;
1317
+ function buildEstimateKey(kind, text) {
1318
+ const payloadHash = createHash("sha1").update(text).digest("hex");
1319
+ return `${kind}:${payloadHash}`;
1320
+ }
1321
+ function resolveEncodingId(encoding) {
1322
+ if (!encoding) return "o200k_base";
1323
+ try {
1324
+ return `custom:${createHash("sha1").update(JSON.stringify(encoding)).digest("hex")}`;
1325
+ } catch {
1326
+ return "custom:unknown";
1327
+ }
1328
+ }
1329
+ function isTokenEstimateEntry(value) {
1330
+ if (!value || typeof value !== "object") return false;
1331
+ const entry = value;
1332
+ return typeof entry.v === "number" && typeof entry.source === "string" && typeof entry.key === "string" && typeof entry.tokens === "number";
1333
+ }
1334
+ function getCacheEntry(cache, key) {
1335
+ if (!cache || typeof cache !== "object") return void 0;
1336
+ if (isTokenEstimateEntry(cache)) {
1337
+ return cache.key === key ? cache : void 0;
1338
+ }
1339
+ return void 0;
1340
+ }
1341
+ function getPartCacheEntry(part, key) {
1342
+ const cache = part?.providerMetadata?.mastra?.tokenEstimate;
1343
+ return getCacheEntry(cache, key);
1344
+ }
1345
+ function setPartCacheEntry(part, _key, entry) {
1346
+ const mutablePart = part;
1347
+ mutablePart.providerMetadata ??= {};
1348
+ mutablePart.providerMetadata.mastra ??= {};
1349
+ mutablePart.providerMetadata.mastra.tokenEstimate = entry;
1350
+ }
1351
+ function getMessageCacheEntry(message, key) {
1352
+ const content = message.content;
1353
+ if (content && typeof content === "object") {
1354
+ const contentLevelCache = content.metadata?.mastra?.tokenEstimate;
1355
+ const contentLevelEntry = getCacheEntry(contentLevelCache, key);
1356
+ if (contentLevelEntry) return contentLevelEntry;
1357
+ }
1358
+ const messageLevelCache = message?.metadata?.mastra?.tokenEstimate;
1359
+ return getCacheEntry(messageLevelCache, key);
1360
+ }
1361
+ function setMessageCacheEntry(message, _key, entry) {
1362
+ const content = message.content;
1363
+ if (content && typeof content === "object") {
1364
+ content.metadata ??= {};
1365
+ content.metadata.mastra ??= {};
1366
+ content.metadata.mastra.tokenEstimate = entry;
1367
+ return;
1368
+ }
1369
+ message.metadata ??= {};
1370
+ message.metadata.mastra ??= {};
1371
+ message.metadata.mastra.tokenEstimate = entry;
1372
+ }
1373
+ function serializePartForTokenCounting(part) {
1374
+ const hasTokenEstimate = Boolean(part?.providerMetadata?.mastra?.tokenEstimate);
1375
+ if (!hasTokenEstimate) {
1376
+ return JSON.stringify(part);
1377
+ }
1378
+ const clonedPart = {
1379
+ ...part,
1380
+ providerMetadata: {
1381
+ ...part.providerMetadata ?? {},
1382
+ mastra: {
1383
+ ...part.providerMetadata?.mastra ?? {}
1384
+ }
1385
+ }
1386
+ };
1387
+ delete clonedPart.providerMetadata.mastra.tokenEstimate;
1388
+ if (Object.keys(clonedPart.providerMetadata.mastra).length === 0) {
1389
+ delete clonedPart.providerMetadata.mastra;
1390
+ }
1391
+ if (Object.keys(clonedPart.providerMetadata).length === 0) {
1392
+ delete clonedPart.providerMetadata;
1393
+ }
1394
+ return JSON.stringify(clonedPart);
1395
+ }
1396
+ function isValidCacheEntry(entry, expectedKey, expectedSource) {
1397
+ return Boolean(
1398
+ entry && entry.v === TOKEN_ESTIMATE_CACHE_VERSION && entry.source === expectedSource && entry.key === expectedKey && Number.isFinite(entry.tokens)
1399
+ );
1400
+ }
919
1401
  var TokenCounter = class _TokenCounter {
920
1402
  encoder;
1403
+ cacheSource;
921
1404
  // Per-message overhead: accounts for role tokens, message framing, and separators.
922
1405
  // Empirically derived from OpenAI's token counting guide (3 tokens per message base +
923
1406
  // fractional overhead from name/role encoding). 3.8 is a practical average across models.
@@ -926,6 +1409,7 @@ var TokenCounter = class _TokenCounter {
926
1409
  static TOKENS_PER_CONVERSATION = 24;
927
1410
  constructor(encoding) {
928
1411
  this.encoder = encoding ? new Tiktoken(encoding) : getDefaultEncoder();
1412
+ this.cacheSource = `v${TOKEN_ESTIMATE_CACHE_VERSION}:${resolveEncodingId(encoding)}`;
929
1413
  }
930
1414
  /**
931
1415
  * Count tokens in a plain string
@@ -934,43 +1418,108 @@ var TokenCounter = class _TokenCounter {
934
1418
  if (!text) return 0;
935
1419
  return this.encoder.encode(text, "all").length;
936
1420
  }
1421
+ readOrPersistPartEstimate(part, kind, payload) {
1422
+ const key = buildEstimateKey(kind, payload);
1423
+ const cached = getPartCacheEntry(part, key);
1424
+ if (isValidCacheEntry(cached, key, this.cacheSource)) {
1425
+ return cached.tokens;
1426
+ }
1427
+ const tokens = this.countString(payload);
1428
+ setPartCacheEntry(part, key, {
1429
+ v: TOKEN_ESTIMATE_CACHE_VERSION,
1430
+ source: this.cacheSource,
1431
+ key,
1432
+ tokens
1433
+ });
1434
+ return tokens;
1435
+ }
1436
+ readOrPersistMessageEstimate(message, kind, payload) {
1437
+ const key = buildEstimateKey(kind, payload);
1438
+ const cached = getMessageCacheEntry(message, key);
1439
+ if (isValidCacheEntry(cached, key, this.cacheSource)) {
1440
+ return cached.tokens;
1441
+ }
1442
+ const tokens = this.countString(payload);
1443
+ setMessageCacheEntry(message, key, {
1444
+ v: TOKEN_ESTIMATE_CACHE_VERSION,
1445
+ source: this.cacheSource,
1446
+ key,
1447
+ tokens
1448
+ });
1449
+ return tokens;
1450
+ }
1451
+ resolveToolResultForTokenCounting(part, invocationResult) {
1452
+ const mastraMetadata = part?.providerMetadata?.mastra;
1453
+ if (mastraMetadata && typeof mastraMetadata === "object" && "modelOutput" in mastraMetadata) {
1454
+ return {
1455
+ value: mastraMetadata.modelOutput,
1456
+ usingStoredModelOutput: true
1457
+ };
1458
+ }
1459
+ return {
1460
+ value: invocationResult,
1461
+ usingStoredModelOutput: false
1462
+ };
1463
+ }
937
1464
  /**
938
1465
  * Count tokens in a single message
939
1466
  */
940
1467
  countMessage(message) {
941
- let tokenString = message.role;
1468
+ let payloadTokens = this.countString(message.role);
942
1469
  let overhead = _TokenCounter.TOKENS_PER_MESSAGE;
943
1470
  let toolResultCount = 0;
944
1471
  if (typeof message.content === "string") {
945
- tokenString += message.content;
1472
+ payloadTokens += this.readOrPersistMessageEstimate(message, "message-content", message.content);
946
1473
  } else if (message.content && typeof message.content === "object") {
947
1474
  if (message.content.content && !Array.isArray(message.content.parts)) {
948
- tokenString += message.content.content;
1475
+ payloadTokens += this.readOrPersistMessageEstimate(message, "content-content", message.content.content);
949
1476
  } else if (Array.isArray(message.content.parts)) {
950
1477
  for (const part of message.content.parts) {
951
1478
  if (part.type === "text") {
952
- tokenString += part.text;
1479
+ payloadTokens += this.readOrPersistPartEstimate(part, "text", part.text);
953
1480
  } else if (part.type === "tool-invocation") {
954
1481
  const invocation = part.toolInvocation;
955
1482
  if (invocation.state === "call" || invocation.state === "partial-call") {
956
1483
  if (invocation.toolName) {
957
- tokenString += invocation.toolName;
1484
+ payloadTokens += this.readOrPersistPartEstimate(
1485
+ part,
1486
+ `tool-${invocation.state}-name`,
1487
+ invocation.toolName
1488
+ );
958
1489
  }
959
1490
  if (invocation.args) {
960
1491
  if (typeof invocation.args === "string") {
961
- tokenString += invocation.args;
1492
+ payloadTokens += this.readOrPersistPartEstimate(
1493
+ part,
1494
+ `tool-${invocation.state}-args`,
1495
+ invocation.args
1496
+ );
962
1497
  } else {
963
- tokenString += JSON.stringify(invocation.args);
1498
+ const argsJson = JSON.stringify(invocation.args);
1499
+ payloadTokens += this.readOrPersistPartEstimate(part, `tool-${invocation.state}-args-json`, argsJson);
964
1500
  overhead -= 12;
965
1501
  }
966
1502
  }
967
1503
  } else if (invocation.state === "result") {
968
1504
  toolResultCount++;
969
- if (invocation.result !== void 0) {
970
- if (typeof invocation.result === "string") {
971
- tokenString += invocation.result;
1505
+ const { value: resultForCounting, usingStoredModelOutput } = this.resolveToolResultForTokenCounting(
1506
+ part,
1507
+ invocation.result
1508
+ );
1509
+ if (resultForCounting !== void 0) {
1510
+ if (typeof resultForCounting === "string") {
1511
+ payloadTokens += this.readOrPersistPartEstimate(
1512
+ part,
1513
+ usingStoredModelOutput ? "tool-result-model-output" : "tool-result",
1514
+ resultForCounting
1515
+ );
972
1516
  } else {
973
- tokenString += JSON.stringify(invocation.result);
1517
+ const resultJson = JSON.stringify(resultForCounting);
1518
+ payloadTokens += this.readOrPersistPartEstimate(
1519
+ part,
1520
+ usingStoredModelOutput ? "tool-result-model-output-json" : "tool-result-json",
1521
+ resultJson
1522
+ );
974
1523
  overhead -= 12;
975
1524
  }
976
1525
  }
@@ -980,7 +1529,8 @@ var TokenCounter = class _TokenCounter {
980
1529
  );
981
1530
  }
982
1531
  } else if (typeof part.type === "string" && part.type.startsWith("data-")) ; else if (part.type === "reasoning") ; else {
983
- tokenString += JSON.stringify(part);
1532
+ const serialized = serializePartForTokenCounting(part);
1533
+ payloadTokens += this.readOrPersistPartEstimate(part, `part-${part.type}`, serialized);
984
1534
  }
985
1535
  }
986
1536
  }
@@ -988,7 +1538,7 @@ var TokenCounter = class _TokenCounter {
988
1538
  if (toolResultCount > 0) {
989
1539
  overhead += toolResultCount * _TokenCounter.TOKENS_PER_MESSAGE;
990
1540
  }
991
- return Math.round(this.encoder.encode(tokenString, "all").length + overhead);
1541
+ return Math.round(payloadTokens + overhead);
992
1542
  }
993
1543
  /**
994
1544
  * Count tokens in an array of messages
@@ -1025,19 +1575,6 @@ function omError(msg, err) {
1025
1575
  omDebug(`[OM:ERROR] ${full}`);
1026
1576
  }
1027
1577
  omDebug(`[OM:process-start] OM module loaded, pid=${process.pid}`);
1028
- var activeOps = /* @__PURE__ */ new Set();
1029
- function opKey(recordId, op) {
1030
- return `${recordId}:${op}`;
1031
- }
1032
- function registerOp(recordId, op) {
1033
- activeOps.add(opKey(recordId, op));
1034
- }
1035
- function unregisterOp(recordId, op) {
1036
- activeOps.delete(opKey(recordId, op));
1037
- }
1038
- function isOpActiveInProcess(recordId, op) {
1039
- return activeOps.has(opKey(recordId, op));
1040
- }
1041
1578
  if (OM_DEBUG_LOG) {
1042
1579
  const _origConsoleError = console.error;
1043
1580
  console.error = (...args) => {
@@ -1047,159 +1584,6 @@ if (OM_DEBUG_LOG) {
1047
1584
  _origConsoleError.apply(console, args);
1048
1585
  };
1049
1586
  }
1050
- function formatRelativeTime(date, currentDate) {
1051
- const diffMs = currentDate.getTime() - date.getTime();
1052
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
1053
- if (diffDays === 0) return "today";
1054
- if (diffDays === 1) return "yesterday";
1055
- if (diffDays < 7) return `${diffDays} days ago`;
1056
- if (diffDays < 14) return "1 week ago";
1057
- if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
1058
- if (diffDays < 60) return "1 month ago";
1059
- if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
1060
- return `${Math.floor(diffDays / 365)} year${Math.floor(diffDays / 365) > 1 ? "s" : ""} ago`;
1061
- }
1062
- function formatGapBetweenDates(prevDate, currDate) {
1063
- const diffMs = currDate.getTime() - prevDate.getTime();
1064
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
1065
- if (diffDays <= 1) {
1066
- return null;
1067
- } else if (diffDays < 7) {
1068
- return `[${diffDays} days later]`;
1069
- } else if (diffDays < 14) {
1070
- return `[1 week later]`;
1071
- } else if (diffDays < 30) {
1072
- const weeks = Math.floor(diffDays / 7);
1073
- return `[${weeks} weeks later]`;
1074
- } else if (diffDays < 60) {
1075
- return `[1 month later]`;
1076
- } else {
1077
- const months = Math.floor(diffDays / 30);
1078
- return `[${months} months later]`;
1079
- }
1080
- }
1081
- function parseDateFromContent(dateContent) {
1082
- let targetDate = null;
1083
- const simpleDateMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2}),?\s+(\d{4})/);
1084
- if (simpleDateMatch) {
1085
- const parsed = /* @__PURE__ */ new Date(`${simpleDateMatch[1]} ${simpleDateMatch[2]}, ${simpleDateMatch[3]}`);
1086
- if (!isNaN(parsed.getTime())) {
1087
- targetDate = parsed;
1088
- }
1089
- }
1090
- if (!targetDate) {
1091
- const rangeMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2})-\d{1,2},?\s+(\d{4})/);
1092
- if (rangeMatch) {
1093
- const parsed = /* @__PURE__ */ new Date(`${rangeMatch[1]} ${rangeMatch[2]}, ${rangeMatch[3]}`);
1094
- if (!isNaN(parsed.getTime())) {
1095
- targetDate = parsed;
1096
- }
1097
- }
1098
- }
1099
- if (!targetDate) {
1100
- const vagueMatch = dateContent.match(
1101
- /(late|early|mid)[- ]?(?:to[- ]?(?:late|early|mid)[- ]?)?([A-Z][a-z]+)\s+(\d{4})/i
1102
- );
1103
- if (vagueMatch) {
1104
- const month = vagueMatch[2];
1105
- const year = vagueMatch[3];
1106
- const modifier = vagueMatch[1].toLowerCase();
1107
- let day = 15;
1108
- if (modifier === "early") day = 7;
1109
- if (modifier === "late") day = 23;
1110
- const parsed = /* @__PURE__ */ new Date(`${month} ${day}, ${year}`);
1111
- if (!isNaN(parsed.getTime())) {
1112
- targetDate = parsed;
1113
- }
1114
- }
1115
- }
1116
- if (!targetDate) {
1117
- const crossMonthMatch = dateContent.match(/([A-Z][a-z]+)\s+to\s+(?:early\s+)?([A-Z][a-z]+)\s+(\d{4})/i);
1118
- if (crossMonthMatch) {
1119
- const parsed = /* @__PURE__ */ new Date(`${crossMonthMatch[2]} 1, ${crossMonthMatch[3]}`);
1120
- if (!isNaN(parsed.getTime())) {
1121
- targetDate = parsed;
1122
- }
1123
- }
1124
- }
1125
- return targetDate;
1126
- }
1127
- function isFutureIntentObservation(line) {
1128
- const futureIntentPatterns = [
1129
- /\bwill\s+(?:be\s+)?(?:\w+ing|\w+)\b/i,
1130
- /\bplans?\s+to\b/i,
1131
- /\bplanning\s+to\b/i,
1132
- /\blooking\s+forward\s+to\b/i,
1133
- /\bgoing\s+to\b/i,
1134
- /\bintends?\s+to\b/i,
1135
- /\bwants?\s+to\b/i,
1136
- /\bneeds?\s+to\b/i,
1137
- /\babout\s+to\b/i
1138
- ];
1139
- return futureIntentPatterns.some((pattern) => pattern.test(line));
1140
- }
1141
- function expandInlineEstimatedDates(observations, currentDate) {
1142
- const inlineDateRegex = /\((estimated|meaning)\s+([^)]+\d{4})\)/gi;
1143
- return observations.replace(inlineDateRegex, (match, prefix, dateContent) => {
1144
- const targetDate = parseDateFromContent(dateContent);
1145
- if (targetDate) {
1146
- const relative = formatRelativeTime(targetDate, currentDate);
1147
- const matchIndex = observations.indexOf(match);
1148
- const lineStart = observations.lastIndexOf("\n", matchIndex) + 1;
1149
- const lineBeforeDate = observations.substring(lineStart, matchIndex);
1150
- const isPastDate = targetDate < currentDate;
1151
- const isFutureIntent = isFutureIntentObservation(lineBeforeDate);
1152
- if (isPastDate && isFutureIntent) {
1153
- return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
1154
- }
1155
- return `(${prefix} ${dateContent} - ${relative})`;
1156
- }
1157
- return match;
1158
- });
1159
- }
1160
- function addRelativeTimeToObservations(observations, currentDate) {
1161
- const withInlineDates = expandInlineEstimatedDates(observations, currentDate);
1162
- const dateHeaderRegex = /^(Date:\s*)([A-Z][a-z]+ \d{1,2}, \d{4})$/gm;
1163
- const dates = [];
1164
- let regexMatch;
1165
- while ((regexMatch = dateHeaderRegex.exec(withInlineDates)) !== null) {
1166
- const dateStr = regexMatch[2];
1167
- const parsed = new Date(dateStr);
1168
- if (!isNaN(parsed.getTime())) {
1169
- dates.push({
1170
- index: regexMatch.index,
1171
- date: parsed,
1172
- match: regexMatch[0],
1173
- prefix: regexMatch[1],
1174
- dateStr
1175
- });
1176
- }
1177
- }
1178
- if (dates.length === 0) {
1179
- return withInlineDates;
1180
- }
1181
- let result = "";
1182
- let lastIndex = 0;
1183
- for (let i = 0; i < dates.length; i++) {
1184
- const curr = dates[i];
1185
- const prev = i > 0 ? dates[i - 1] : null;
1186
- result += withInlineDates.slice(lastIndex, curr.index);
1187
- if (prev) {
1188
- const gap = formatGapBetweenDates(prev.date, curr.date);
1189
- if (gap) {
1190
- result += `
1191
- ${gap}
1192
-
1193
- `;
1194
- }
1195
- }
1196
- const relative = formatRelativeTime(curr.date, currentDate);
1197
- result += `${curr.prefix}${curr.dateStr} (${relative})`;
1198
- lastIndex = curr.index + curr.match.length;
1199
- }
1200
- result += withInlineDates.slice(lastIndex);
1201
- return result;
1202
- }
1203
1587
  var OBSERVATIONAL_MEMORY_DEFAULTS = {
1204
1588
  observation: {
1205
1589
  model: "google/gemini-2.5-flash",
@@ -1424,71 +1808,6 @@ var ObservationalMemory = class _ObservationalMemory {
1424
1808
  }
1425
1809
  return [];
1426
1810
  }
1427
- /**
1428
- * Resolve bufferActivation config into an absolute retention floor (tokens to keep).
1429
- * - Value in (0, 1]: ratio → retentionFloor = threshold * (1 - value)
1430
- * - Value >= 1000: absolute token count → retentionFloor = value
1431
- */
1432
- resolveRetentionFloor(bufferActivation, messageTokensThreshold) {
1433
- if (bufferActivation >= 1e3) return bufferActivation;
1434
- return messageTokensThreshold * (1 - bufferActivation);
1435
- }
1436
- /**
1437
- * Convert bufferActivation to the equivalent ratio (0-1) for the storage layer.
1438
- * When bufferActivation >= 1000, it's an absolute retention target, so we compute
1439
- * the equivalent ratio: 1 - (bufferActivation / threshold).
1440
- */
1441
- resolveActivationRatio(bufferActivation, messageTokensThreshold) {
1442
- if (bufferActivation >= 1e3) {
1443
- return Math.max(0, Math.min(1, 1 - bufferActivation / messageTokensThreshold));
1444
- }
1445
- return bufferActivation;
1446
- }
1447
- /**
1448
- * Calculate the projected message tokens that would be removed if activation happened now.
1449
- * This replicates the chunk boundary logic in swapBufferedToActive without actually activating.
1450
- */
1451
- calculateProjectedMessageRemoval(chunks, bufferActivation, messageTokensThreshold, currentPendingTokens) {
1452
- if (chunks.length === 0) return 0;
1453
- const retentionFloor = this.resolveRetentionFloor(bufferActivation, messageTokensThreshold);
1454
- const targetMessageTokens = Math.max(0, currentPendingTokens - retentionFloor);
1455
- let cumulativeMessageTokens = 0;
1456
- let bestOverBoundary = 0;
1457
- let bestOverTokens = 0;
1458
- let bestUnderBoundary = 0;
1459
- let bestUnderTokens = 0;
1460
- for (let i = 0; i < chunks.length; i++) {
1461
- cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
1462
- const boundary = i + 1;
1463
- if (cumulativeMessageTokens >= targetMessageTokens) {
1464
- if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
1465
- bestOverBoundary = boundary;
1466
- bestOverTokens = cumulativeMessageTokens;
1467
- }
1468
- } else {
1469
- if (cumulativeMessageTokens > bestUnderTokens) {
1470
- bestUnderBoundary = boundary;
1471
- bestUnderTokens = cumulativeMessageTokens;
1472
- }
1473
- }
1474
- }
1475
- const maxOvershoot = retentionFloor * 0.95;
1476
- const overshoot = bestOverTokens - targetMessageTokens;
1477
- const remainingAfterOver = currentPendingTokens - bestOverTokens;
1478
- const remainingAfterUnder = currentPendingTokens - bestUnderTokens;
1479
- const minRemaining = Math.min(1e3, retentionFloor);
1480
- let bestBoundaryMessageTokens;
1481
- if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
1482
- bestBoundaryMessageTokens = bestOverTokens;
1483
- } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
1484
- bestBoundaryMessageTokens = bestUnderTokens;
1485
- } else if (bestOverBoundary > 0) {
1486
- bestBoundaryMessageTokens = bestOverTokens;
1487
- } else {
1488
- return chunks[0]?.messageTokens ?? 0;
1489
- }
1490
- return bestBoundaryMessageTokens;
1491
- }
1492
1811
  /**
1493
1812
  * Check if we've crossed a new bufferTokens interval boundary.
1494
1813
  * Returns true if async buffering should be triggered.
@@ -1538,7 +1857,7 @@ var ObservationalMemory = class _ObservationalMemory {
1538
1857
  if (this.isAsyncBufferingInProgress(bufferKey)) return false;
1539
1858
  if (_ObservationalMemory.lastBufferedBoundary.has(bufferKey)) return false;
1540
1859
  if (record.bufferedReflection) return false;
1541
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
1860
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
1542
1861
  const activationPoint = reflectThreshold * this.reflectionConfig.bufferActivation;
1543
1862
  const shouldTrigger = currentObservationTokens >= activationPoint;
1544
1863
  omDebug(
@@ -1655,12 +1974,12 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1655
1974
  },
1656
1975
  providerOptions: config.observation?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.providerOptions,
1657
1976
  maxTokensPerBatch: config.observation?.maxTokensPerBatch ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.maxTokensPerBatch,
1658
- bufferTokens: asyncBufferingDisabled ? void 0 : this.resolveBufferTokens(
1977
+ bufferTokens: asyncBufferingDisabled ? void 0 : resolveBufferTokens(
1659
1978
  config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens,
1660
1979
  config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
1661
1980
  ),
1662
1981
  bufferActivation: asyncBufferingDisabled ? void 0 : config.observation?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferActivation,
1663
- blockAfter: asyncBufferingDisabled ? void 0 : this.resolveBlockAfter(
1982
+ blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
1664
1983
  config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0),
1665
1984
  config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
1666
1985
  ),
@@ -1676,7 +1995,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1676
1995
  },
1677
1996
  providerOptions: config.reflection?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.providerOptions,
1678
1997
  bufferActivation: asyncBufferingDisabled ? void 0 : config?.reflection?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation,
1679
- blockAfter: asyncBufferingDisabled ? void 0 : this.resolveBlockAfter(
1998
+ blockAfter: asyncBufferingDisabled ? void 0 : resolveBlockAfter(
1680
1999
  config.reflection?.blockAfter ?? (config.reflection?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation ? 1.2 : void 0),
1681
2000
  config.reflection?.observationTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens
1682
2001
  ),
@@ -1771,7 +2090,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1771
2090
  `Async buffering is not yet supported with scope: 'resource'. Use scope: 'thread', or set observation: { bufferTokens: false } to disable async buffering.`
1772
2091
  );
1773
2092
  }
1774
- const observationThreshold = this.getMaxThreshold(this.observationConfig.messageTokens);
2093
+ const observationThreshold = getMaxThreshold(this.observationConfig.messageTokens);
1775
2094
  if (this.observationConfig.bufferTokens !== void 0) {
1776
2095
  if (this.observationConfig.bufferTokens <= 0) {
1777
2096
  throw new Error(`observation.bufferTokens must be > 0, got ${this.observationConfig.bufferTokens}`);
@@ -1817,7 +2136,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1817
2136
  }
1818
2137
  }
1819
2138
  if (this.reflectionConfig.blockAfter !== void 0) {
1820
- const reflectionThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
2139
+ const reflectionThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
1821
2140
  if (this.reflectionConfig.blockAfter < reflectionThreshold) {
1822
2141
  throw new Error(
1823
2142
  `reflection.blockAfter (${this.reflectionConfig.blockAfter}) must be >= reflection.observationTokens (${reflectionThreshold})`
@@ -1830,65 +2149,6 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1830
2149
  }
1831
2150
  }
1832
2151
  }
1833
- /**
1834
- * Resolve bufferTokens: if it's a fraction (0 < value < 1), multiply by messageTokens threshold.
1835
- * Otherwise return the absolute token count.
1836
- */
1837
- resolveBufferTokens(bufferTokens, messageTokens) {
1838
- if (bufferTokens === false) return void 0;
1839
- if (bufferTokens === void 0) return void 0;
1840
- if (bufferTokens > 0 && bufferTokens < 1) {
1841
- const threshold = typeof messageTokens === "number" ? messageTokens : messageTokens.max;
1842
- return Math.round(threshold * bufferTokens);
1843
- }
1844
- return bufferTokens;
1845
- }
1846
- /**
1847
- * Resolve blockAfter config value.
1848
- * Values in [1, 100) are treated as multipliers of the threshold.
1849
- * e.g. blockAfter: 1.5 with messageTokens: 20_000 → 30_000
1850
- * Values >= 100 are treated as absolute token counts.
1851
- * Defaults to 1.2 (120% of threshold) when async buffering is enabled but blockAfter is omitted.
1852
- */
1853
- resolveBlockAfter(blockAfter, messageTokens) {
1854
- if (blockAfter === void 0) return void 0;
1855
- if (blockAfter >= 1 && blockAfter < 100) {
1856
- const threshold = typeof messageTokens === "number" ? messageTokens : messageTokens.max;
1857
- return Math.round(threshold * blockAfter);
1858
- }
1859
- return blockAfter;
1860
- }
1861
- /**
1862
- * Get the maximum value from a threshold (simple number or range)
1863
- */
1864
- getMaxThreshold(threshold) {
1865
- if (typeof threshold === "number") {
1866
- return threshold;
1867
- }
1868
- return threshold.max;
1869
- }
1870
- /**
1871
- * Calculate dynamic threshold based on observation space.
1872
- * When shareTokenBudget is enabled, the message threshold can expand
1873
- * into unused observation space, up to the total context budget.
1874
- *
1875
- * Total budget = messageTokens + observationTokens
1876
- * Effective threshold = totalBudget - currentObservationTokens
1877
- *
1878
- * Example with 30k:40k thresholds (70k total):
1879
- * - 0 observations → messages can use ~70k
1880
- * - 10k observations → messages can use ~60k
1881
- * - 40k observations → messages back to ~30k
1882
- */
1883
- calculateDynamicThreshold(threshold, currentObservationTokens) {
1884
- if (typeof threshold === "number") {
1885
- return threshold;
1886
- }
1887
- const totalBudget = threshold.max;
1888
- const baseThreshold = threshold.min;
1889
- const effectiveThreshold = Math.max(totalBudget - currentObservationTokens, baseThreshold);
1890
- return Math.round(effectiveThreshold);
1891
- }
1892
2152
  /**
1893
2153
  * Check whether the unobserved message tokens meet the observation threshold.
1894
2154
  */
@@ -1896,7 +2156,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1896
2156
  const { record, unobservedTokens, extraTokens = 0 } = opts;
1897
2157
  const pendingTokens = (record.pendingMessageTokens ?? 0) + unobservedTokens + extraTokens;
1898
2158
  const currentObservationTokens = record.observationTokenCount ?? 0;
1899
- const threshold = this.calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
2159
+ const threshold = calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
1900
2160
  return pendingTokens >= threshold;
1901
2161
  }
1902
2162
  /**
@@ -1976,7 +2236,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1976
2236
  * Check if we need to trigger reflection.
1977
2237
  */
1978
2238
  shouldReflect(observationTokens) {
1979
- const threshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
2239
+ const threshold = getMaxThreshold(this.reflectionConfig.observationTokens);
1980
2240
  return observationTokens > threshold;
1981
2241
  }
1982
2242
  // ════════════════════════════════════════════════════════════════════════════
@@ -1997,153 +2257,11 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1997
2257
  */
1998
2258
  getObservationMarkerConfig() {
1999
2259
  return {
2000
- messageTokens: this.getMaxThreshold(this.observationConfig.messageTokens),
2001
- observationTokens: this.getMaxThreshold(this.reflectionConfig.observationTokens),
2260
+ messageTokens: getMaxThreshold(this.observationConfig.messageTokens),
2261
+ observationTokens: getMaxThreshold(this.reflectionConfig.observationTokens),
2002
2262
  scope: this.scope
2003
2263
  };
2004
2264
  }
2005
- /**
2006
- * Create a start marker for when observation begins.
2007
- */
2008
- createObservationStartMarker(params) {
2009
- return {
2010
- type: "data-om-observation-start",
2011
- data: {
2012
- cycleId: params.cycleId,
2013
- operationType: params.operationType,
2014
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2015
- tokensToObserve: params.tokensToObserve,
2016
- recordId: params.recordId,
2017
- threadId: params.threadId,
2018
- threadIds: params.threadIds,
2019
- config: this.getObservationMarkerConfig()
2020
- }
2021
- };
2022
- }
2023
- /**
2024
- * Create an end marker for when observation completes successfully.
2025
- */
2026
- createObservationEndMarker(params) {
2027
- const completedAt = (/* @__PURE__ */ new Date()).toISOString();
2028
- const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
2029
- return {
2030
- type: "data-om-observation-end",
2031
- data: {
2032
- cycleId: params.cycleId,
2033
- operationType: params.operationType,
2034
- completedAt,
2035
- durationMs,
2036
- tokensObserved: params.tokensObserved,
2037
- observationTokens: params.observationTokens,
2038
- observations: params.observations,
2039
- currentTask: params.currentTask,
2040
- suggestedResponse: params.suggestedResponse,
2041
- recordId: params.recordId,
2042
- threadId: params.threadId
2043
- }
2044
- };
2045
- }
2046
- /**
2047
- * Create a failed marker for when observation fails.
2048
- */
2049
- createObservationFailedMarker(params) {
2050
- const failedAt = (/* @__PURE__ */ new Date()).toISOString();
2051
- const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
2052
- return {
2053
- type: "data-om-observation-failed",
2054
- data: {
2055
- cycleId: params.cycleId,
2056
- operationType: params.operationType,
2057
- failedAt,
2058
- durationMs,
2059
- tokensAttempted: params.tokensAttempted,
2060
- error: params.error,
2061
- recordId: params.recordId,
2062
- threadId: params.threadId
2063
- }
2064
- };
2065
- }
2066
- /**
2067
- * Create a start marker for when async buffering begins.
2068
- */
2069
- createBufferingStartMarker(params) {
2070
- return {
2071
- type: "data-om-buffering-start",
2072
- data: {
2073
- cycleId: params.cycleId,
2074
- operationType: params.operationType,
2075
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2076
- tokensToBuffer: params.tokensToBuffer,
2077
- recordId: params.recordId,
2078
- threadId: params.threadId,
2079
- threadIds: params.threadIds,
2080
- config: this.getObservationMarkerConfig()
2081
- }
2082
- };
2083
- }
2084
- /**
2085
- * Create an end marker for when async buffering completes successfully.
2086
- */
2087
- createBufferingEndMarker(params) {
2088
- const completedAt = (/* @__PURE__ */ new Date()).toISOString();
2089
- const durationMs = new Date(completedAt).getTime() - new Date(params.startedAt).getTime();
2090
- return {
2091
- type: "data-om-buffering-end",
2092
- data: {
2093
- cycleId: params.cycleId,
2094
- operationType: params.operationType,
2095
- completedAt,
2096
- durationMs,
2097
- tokensBuffered: params.tokensBuffered,
2098
- bufferedTokens: params.bufferedTokens,
2099
- recordId: params.recordId,
2100
- threadId: params.threadId,
2101
- observations: params.observations
2102
- }
2103
- };
2104
- }
2105
- /**
2106
- * Create a failed marker for when async buffering fails.
2107
- */
2108
- createBufferingFailedMarker(params) {
2109
- const failedAt = (/* @__PURE__ */ new Date()).toISOString();
2110
- const durationMs = new Date(failedAt).getTime() - new Date(params.startedAt).getTime();
2111
- return {
2112
- type: "data-om-buffering-failed",
2113
- data: {
2114
- cycleId: params.cycleId,
2115
- operationType: params.operationType,
2116
- failedAt,
2117
- durationMs,
2118
- tokensAttempted: params.tokensAttempted,
2119
- error: params.error,
2120
- recordId: params.recordId,
2121
- threadId: params.threadId
2122
- }
2123
- };
2124
- }
2125
- /**
2126
- * Create an activation marker for when buffered observations are activated.
2127
- */
2128
- createActivationMarker(params) {
2129
- return {
2130
- type: "data-om-activation",
2131
- data: {
2132
- cycleId: params.cycleId,
2133
- operationType: params.operationType,
2134
- activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2135
- chunksActivated: params.chunksActivated,
2136
- tokensActivated: params.tokensActivated,
2137
- observationTokens: params.observationTokens,
2138
- messagesActivated: params.messagesActivated,
2139
- recordId: params.recordId,
2140
- threadId: params.threadId,
2141
- generationCount: params.generationCount,
2142
- config: this.getObservationMarkerConfig(),
2143
- observations: params.observations
2144
- }
2145
- };
2146
- }
2147
2265
  /**
2148
2266
  * Persist a data-om-* marker part on the last assistant message in messageList
2149
2267
  * AND save the updated message to the DB so it survives page reload.
@@ -2524,7 +2642,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2524
2642
  async callReflector(observations, manualPrompt, streamContext, observationTokensThreshold, abortSignal, skipContinuationHints, compressionStartLevel, requestContext) {
2525
2643
  const agent = this.getReflectorAgent();
2526
2644
  const originalTokens = this.tokenCounter.countObservations(observations);
2527
- const targetThreshold = observationTokensThreshold ?? this.getMaxThreshold(this.reflectionConfig.observationTokens);
2645
+ const targetThreshold = observationTokensThreshold ?? getMaxThreshold(this.reflectionConfig.observationTokens);
2528
2646
  let totalUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
2529
2647
  let currentLevel = compressionStartLevel ?? 0;
2530
2648
  const maxLevel = 3;
@@ -2599,7 +2717,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2599
2717
  break;
2600
2718
  }
2601
2719
  if (streamContext?.writer) {
2602
- const failedMarker = this.createObservationFailedMarker({
2720
+ const failedMarker = createObservationFailedMarker({
2603
2721
  cycleId: streamContext.cycleId,
2604
2722
  operationType: "reflection",
2605
2723
  startedAt: streamContext.startedAt,
@@ -2612,13 +2730,14 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2612
2730
  });
2613
2731
  const retryCycleId = crypto.randomUUID();
2614
2732
  streamContext.cycleId = retryCycleId;
2615
- const startMarker = this.createObservationStartMarker({
2733
+ const startMarker = createObservationStartMarker({
2616
2734
  cycleId: retryCycleId,
2617
2735
  operationType: "reflection",
2618
2736
  tokensToObserve: originalTokens,
2619
2737
  recordId: streamContext.recordId,
2620
2738
  threadId: streamContext.threadId,
2621
- threadIds: [streamContext.threadId]
2739
+ threadIds: [streamContext.threadId],
2740
+ config: this.getObservationMarkerConfig()
2622
2741
  });
2623
2742
  streamContext.startedAt = startMarker.data.startedAt;
2624
2743
  await streamContext.writer.custom(startMarker).catch(() => {
@@ -2752,8 +2871,8 @@ ${suggestedResponse}
2752
2871
  calculateObservationThresholds(_allMessages, unobservedMessages, _pendingTokens, otherThreadTokens, currentObservationTokens, _record) {
2753
2872
  const contextWindowTokens = this.tokenCounter.countMessages(unobservedMessages);
2754
2873
  const totalPendingTokens = Math.max(0, contextWindowTokens + otherThreadTokens);
2755
- const threshold = this.calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
2756
- const baseReflectionThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
2874
+ const threshold = calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
2875
+ const baseReflectionThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
2757
2876
  const isSharedBudget = typeof this.observationConfig.messageTokens !== "number";
2758
2877
  const totalBudget = isSharedBudget ? this.observationConfig.messageTokens.max : 0;
2759
2878
  const effectiveObservationTokensThreshold = isSharedBudget ? Math.max(totalBudget - threshold, 1e3) : baseReflectionThreshold;
@@ -2787,10 +2906,10 @@ ${suggestedResponse}
2787
2906
  const bufferedObservationTokens = bufferedChunks.reduce((sum, chunk) => sum + (chunk.tokenCount ?? 0), 0);
2788
2907
  const rawBufferedMessageTokens = bufferedChunks.reduce((sum, chunk) => sum + (chunk.messageTokens ?? 0), 0);
2789
2908
  const bufferedMessageTokens = Math.min(rawBufferedMessageTokens, totalPendingTokens);
2790
- const projectedMessageRemoval = this.calculateProjectedMessageRemoval(
2909
+ const projectedMessageRemoval = calculateProjectedMessageRemoval(
2791
2910
  bufferedChunks,
2792
2911
  this.observationConfig.bufferActivation ?? 1,
2793
- this.getMaxThreshold(this.observationConfig.messageTokens),
2912
+ getMaxThreshold(this.observationConfig.messageTokens),
2794
2913
  totalPendingTokens
2795
2914
  );
2796
2915
  let obsBufferStatus = "idle";
@@ -2999,7 +3118,39 @@ ${suggestedResponse}
2999
3118
  omDebug(
3000
3119
  `[OM:cleanupBranch] allMsgs=${allMsgs.length}, markerFound=${markerIdx !== -1}, markerIdx=${markerIdx}, observedMessageIds=${observedMessageIds?.length ?? "undefined"}, allIds=${allMsgs.map((m) => m.id?.slice(0, 8)).join(",")}`
3001
3120
  );
3002
- if (markerMsg && markerIdx !== -1) {
3121
+ if (observedMessageIds && observedMessageIds.length > 0) {
3122
+ const observedSet = new Set(observedMessageIds);
3123
+ const idsToRemove = /* @__PURE__ */ new Set();
3124
+ let skipped = 0;
3125
+ let backoffTriggered = false;
3126
+ for (const msg of allMsgs) {
3127
+ if (!msg?.id || msg.id === "om-continuation" || !observedSet.has(msg.id)) {
3128
+ continue;
3129
+ }
3130
+ if (typeof minRemaining === "number") {
3131
+ const nextRemainingMessages = allMsgs.filter(
3132
+ (m) => m?.id && m.id !== "om-continuation" && !idsToRemove.has(m.id) && m.id !== msg.id
3133
+ );
3134
+ const remainingIfRemoved = this.tokenCounter.countMessages(nextRemainingMessages);
3135
+ if (remainingIfRemoved < minRemaining) {
3136
+ skipped += 1;
3137
+ backoffTriggered = true;
3138
+ break;
3139
+ }
3140
+ }
3141
+ idsToRemove.add(msg.id);
3142
+ }
3143
+ omDebug(
3144
+ `[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(",")}`
3145
+ );
3146
+ const idsToRemoveList = [...idsToRemove];
3147
+ if (idsToRemoveList.length > 0) {
3148
+ messageList.removeByIds(idsToRemoveList);
3149
+ omDebug(
3150
+ `[OM:cleanupActivation] removed ${idsToRemoveList.length} messages, remaining=${messageList.get.all.db().length}`
3151
+ );
3152
+ }
3153
+ } else if (markerMsg && markerIdx !== -1) {
3003
3154
  const idsToRemove = [];
3004
3155
  const messagesToSave = [];
3005
3156
  for (let i = 0; i < markerIdx; i++) {
@@ -3024,35 +3175,6 @@ ${suggestedResponse}
3024
3175
  if (messagesToSave.length > 0) {
3025
3176
  await this.saveMessagesWithSealedIdTracking(messagesToSave, sealedIds, threadId, resourceId, state);
3026
3177
  }
3027
- } else if (observedMessageIds && observedMessageIds.length > 0) {
3028
- const observedSet = new Set(observedMessageIds);
3029
- const idsToRemove = [];
3030
- const totalTokens = typeof minRemaining === "number" ? this.tokenCounter.countMessages(allMsgs) : void 0;
3031
- let removedTokens = 0;
3032
- let skipped = 0;
3033
- for (const msg of allMsgs) {
3034
- if (msg?.id && msg.id !== "om-continuation" && observedSet.has(msg.id)) {
3035
- if (typeof minRemaining === "number") {
3036
- const msgTokens = this.tokenCounter.countMessage(msg);
3037
- const remainingIfRemoved = (totalTokens ?? 0) - removedTokens - msgTokens;
3038
- if (remainingIfRemoved < minRemaining) {
3039
- skipped += 1;
3040
- continue;
3041
- }
3042
- removedTokens += msgTokens;
3043
- }
3044
- idsToRemove.push(msg.id);
3045
- }
3046
- }
3047
- omDebug(
3048
- `[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(",")}`
3049
- );
3050
- if (idsToRemove.length > 0) {
3051
- messageList.removeByIds(idsToRemove);
3052
- omDebug(
3053
- `[OM:cleanupActivation] removed ${idsToRemove.length} messages, remaining=${messageList.get.all.db().length}`
3054
- );
3055
- }
3056
3178
  } else {
3057
3179
  const newInput = messageList.clear.input.db();
3058
3180
  const newOutput = messageList.clear.response.db();
@@ -3394,7 +3516,7 @@ ${suggestedResponse}
3394
3516
  );
3395
3517
  if (observationSucceeded) {
3396
3518
  const observedIds = activatedMessageIds?.length ? activatedMessageIds : Array.isArray(updatedRecord.observedMessageIds) ? updatedRecord.observedMessageIds : void 0;
3397
- const minRemaining = typeof this.observationConfig.bufferActivation === "number" ? Math.min(1e3, this.resolveRetentionFloor(this.observationConfig.bufferActivation, threshold)) : void 0;
3519
+ const minRemaining = typeof this.observationConfig.bufferActivation === "number" ? resolveRetentionFloor(this.observationConfig.bufferActivation, threshold) : void 0;
3398
3520
  omDebug(
3399
3521
  `[OM:cleanup] observedIds=${observedIds?.length ?? "undefined"}, ids=${observedIds?.join(",") ?? "none"}, updatedRecord.observedMessageIds=${JSON.stringify(updatedRecord.observedMessageIds)}, minRemaining=${minRemaining ?? "n/a"}`
3400
3522
  );
@@ -3438,8 +3560,8 @@ ${suggestedResponse}
3438
3560
  const freshUnobservedTokens = this.tokenCounter.countMessages(contextMessages);
3439
3561
  const otherThreadTokens = unobservedContextBlocks ? this.tokenCounter.countString(unobservedContextBlocks) : 0;
3440
3562
  const currentObservationTokens = freshRecord.observationTokenCount ?? 0;
3441
- const threshold = this.calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
3442
- const baseReflectionThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
3563
+ const threshold = calculateDynamicThreshold(this.observationConfig.messageTokens, currentObservationTokens);
3564
+ const baseReflectionThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
3443
3565
  const isSharedBudget = typeof this.observationConfig.messageTokens !== "number";
3444
3566
  const totalBudget = isSharedBudget ? this.observationConfig.messageTokens.max : 0;
3445
3567
  const effectiveObservationTokensThreshold = isSharedBudget ? Math.max(totalBudget - threshold, 1e3) : baseReflectionThreshold;
@@ -3756,13 +3878,14 @@ ${newThreadSection}`;
3756
3878
  const lastMessage = unobservedMessages[unobservedMessages.length - 1];
3757
3879
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3758
3880
  if (lastMessage?.id) {
3759
- const startMarker = this.createObservationStartMarker({
3881
+ const startMarker = createObservationStartMarker({
3760
3882
  cycleId,
3761
3883
  operationType: "observation",
3762
3884
  tokensToObserve,
3763
3885
  recordId: record.id,
3764
3886
  threadId,
3765
- threadIds: [threadId]
3887
+ threadIds: [threadId],
3888
+ config: this.getObservationMarkerConfig()
3766
3889
  });
3767
3890
  if (writer) {
3768
3891
  await writer.custom(startMarker).catch(() => {
@@ -3830,7 +3953,7 @@ ${result.observations}` : result.observations;
3830
3953
  });
3831
3954
  const actualTokensObserved = this.tokenCounter.countMessages(messagesToObserve);
3832
3955
  if (lastMessage?.id) {
3833
- const endMarker = this.createObservationEndMarker({
3956
+ const endMarker = createObservationEndMarker({
3834
3957
  cycleId,
3835
3958
  operationType: "observation",
3836
3959
  startedAt,
@@ -3872,7 +3995,7 @@ ${result.observations}` : result.observations;
3872
3995
  });
3873
3996
  } catch (error) {
3874
3997
  if (lastMessage?.id) {
3875
- const failedMarker = this.createObservationFailedMarker({
3998
+ const failedMarker = createObservationFailedMarker({
3876
3999
  cycleId,
3877
4000
  operationType: "observation",
3878
4001
  startedAt,
@@ -3994,13 +4117,14 @@ ${result.observations}` : result.observations;
3994
4117
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3995
4118
  const tokensToBuffer = this.tokenCounter.countMessages(messagesToBuffer);
3996
4119
  if (writer) {
3997
- const startMarker = this.createBufferingStartMarker({
4120
+ const startMarker = createBufferingStartMarker({
3998
4121
  cycleId,
3999
4122
  operationType: "observation",
4000
4123
  tokensToBuffer,
4001
4124
  recordId: freshRecord.id,
4002
4125
  threadId,
4003
- threadIds: [threadId]
4126
+ threadIds: [threadId],
4127
+ config: this.getObservationMarkerConfig()
4004
4128
  });
4005
4129
  void writer.custom(startMarker).catch(() => {
4006
4130
  });
@@ -4023,7 +4147,7 @@ ${result.observations}` : result.observations;
4023
4147
  _ObservationalMemory.lastBufferedAtTime.set(bufferKey, cursor);
4024
4148
  } catch (error) {
4025
4149
  if (writer) {
4026
- const failedMarker = this.createBufferingFailedMarker({
4150
+ const failedMarker = createBufferingFailedMarker({
4027
4151
  cycleId,
4028
4152
  operationType: "observation",
4029
4153
  startedAt,
@@ -4091,7 +4215,7 @@ ${result.observations}` : result.observations;
4091
4215
  const updatedRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
4092
4216
  const updatedChunks = this.getBufferedChunks(updatedRecord);
4093
4217
  const totalBufferedTokens = updatedChunks.reduce((sum, c) => sum + (c.tokenCount ?? 0), 0) || newTokenCount;
4094
- const endMarker = this.createBufferingEndMarker({
4218
+ const endMarker = createBufferingEndMarker({
4095
4219
  cycleId,
4096
4220
  operationType: "observation",
4097
4221
  startedAt,
@@ -4160,7 +4284,7 @@ ${bufferedObservations}`;
4160
4284
  if (!freshChunks.length) {
4161
4285
  return { success: false };
4162
4286
  }
4163
- const messageTokensThreshold = this.getMaxThreshold(this.observationConfig.messageTokens);
4287
+ const messageTokensThreshold = getMaxThreshold(this.observationConfig.messageTokens);
4164
4288
  let effectivePendingTokens = currentPendingTokens;
4165
4289
  if (messageList) {
4166
4290
  effectivePendingTokens = this.tokenCounter.countMessages(messageList.get.all.db());
@@ -4172,11 +4296,11 @@ ${bufferedObservations}`;
4172
4296
  }
4173
4297
  }
4174
4298
  const bufferActivation = this.observationConfig.bufferActivation ?? 0.7;
4175
- const activationRatio = this.resolveActivationRatio(bufferActivation, messageTokensThreshold);
4299
+ const activationRatio = resolveActivationRatio(bufferActivation, messageTokensThreshold);
4176
4300
  const forceMaxActivation = !!(this.observationConfig.blockAfter && effectivePendingTokens >= this.observationConfig.blockAfter);
4177
4301
  const bufferTokens = this.observationConfig.bufferTokens ?? 0;
4178
- const retentionFloor = this.resolveRetentionFloor(bufferActivation, messageTokensThreshold);
4179
- const projectedMessageRemoval = this.calculateProjectedMessageRemoval(
4302
+ const retentionFloor = resolveRetentionFloor(bufferActivation, messageTokensThreshold);
4303
+ const projectedMessageRemoval = calculateProjectedMessageRemoval(
4180
4304
  freshChunks,
4181
4305
  bufferActivation,
4182
4306
  messageTokensThreshold,
@@ -4210,7 +4334,7 @@ ${bufferedObservations}`;
4210
4334
  const perChunkMap = new Map(activationResult.perChunk?.map((c) => [c.cycleId, c]));
4211
4335
  for (const cycleId of activationResult.activatedCycleIds) {
4212
4336
  const chunkData = perChunkMap.get(cycleId);
4213
- const activationMarker = this.createActivationMarker({
4337
+ const activationMarker = createActivationMarker({
4214
4338
  cycleId,
4215
4339
  // Use the original buffering cycleId so UI can link them
4216
4340
  operationType: "observation",
@@ -4221,7 +4345,8 @@ ${bufferedObservations}`;
4221
4345
  recordId: updatedRecord.id,
4222
4346
  threadId: updatedRecord.threadId ?? record.threadId ?? "",
4223
4347
  generationCount: updatedRecord.generationCount ?? 0,
4224
- observations: chunkData?.observations ?? activationResult.observations
4348
+ observations: chunkData?.observations ?? activationResult.observations,
4349
+ config: this.getObservationMarkerConfig()
4225
4350
  });
4226
4351
  void writer.custom(activationMarker).catch(() => {
4227
4352
  });
@@ -4263,7 +4388,7 @@ ${bufferedObservations}`;
4263
4388
  });
4264
4389
  const asyncOp = this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext).catch(async (error) => {
4265
4390
  if (writer) {
4266
- const failedMarker = this.createBufferingFailedMarker({
4391
+ const failedMarker = createBufferingFailedMarker({
4267
4392
  cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
4268
4393
  operationType: "reflection",
4269
4394
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -4294,7 +4419,7 @@ ${bufferedObservations}`;
4294
4419
  const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
4295
4420
  const currentRecord = freshRecord ?? record;
4296
4421
  const observationTokens = currentRecord.observationTokenCount ?? 0;
4297
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
4422
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
4298
4423
  const bufferActivation = this.reflectionConfig.bufferActivation ?? 0.5;
4299
4424
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
4300
4425
  const cycleId = `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
@@ -4316,13 +4441,14 @@ ${bufferedObservations}`;
4316
4441
  `[OM:reflect] doAsyncBufferedReflection: starting reflector call, recordId=${currentRecord.id}, observationTokens=${sliceTokenEstimate}, compressionTarget=${compressionTarget} (inputTokens), activeObsLength=${activeObservations.length}, reflectedLineCount=${reflectedObservationLineCount}`
4317
4442
  );
4318
4443
  if (writer) {
4319
- const startMarker = this.createBufferingStartMarker({
4444
+ const startMarker = createBufferingStartMarker({
4320
4445
  cycleId,
4321
4446
  operationType: "reflection",
4322
4447
  tokensToBuffer: sliceTokenEstimate,
4323
4448
  recordId: record.id,
4324
4449
  threadId: record.threadId ?? "",
4325
- threadIds: record.threadId ? [record.threadId] : []
4450
+ threadIds: record.threadId ? [record.threadId] : [],
4451
+ config: this.getObservationMarkerConfig()
4326
4452
  });
4327
4453
  void writer.custom(startMarker).catch(() => {
4328
4454
  });
@@ -4357,7 +4483,7 @@ ${bufferedObservations}`;
4357
4483
  `[OM:reflect] doAsyncBufferedReflection: bufferedReflection saved with lineCount=${reflectedObservationLineCount}`
4358
4484
  );
4359
4485
  if (writer) {
4360
- const endMarker = this.createBufferingEndMarker({
4486
+ const endMarker = createBufferingEndMarker({
4361
4487
  cycleId,
4362
4488
  operationType: "reflection",
4363
4489
  startedAt,
@@ -4428,7 +4554,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4428
4554
  );
4429
4555
  if (writer) {
4430
4556
  const originalCycleId = _ObservationalMemory.reflectionBufferCycleIds.get(bufferKey);
4431
- const activationMarker = this.createActivationMarker({
4557
+ const activationMarker = createActivationMarker({
4432
4558
  cycleId: originalCycleId ?? `reflect-act-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
4433
4559
  operationType: "reflection",
4434
4560
  chunksActivated: 1,
@@ -4438,7 +4564,8 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4438
4564
  recordId: freshRecord.id,
4439
4565
  threadId: freshRecord.threadId ?? "",
4440
4566
  generationCount: afterRecord?.generationCount ?? freshRecord.generationCount ?? 0,
4441
- observations: afterRecord?.activeObservations
4567
+ observations: afterRecord?.activeObservations,
4568
+ config: this.getObservationMarkerConfig()
4442
4569
  });
4443
4570
  void writer.custom(activationMarker).catch(() => {
4444
4571
  });
@@ -4520,7 +4647,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4520
4647
  if (totalMessages === 0) {
4521
4648
  return;
4522
4649
  }
4523
- const threshold = this.getMaxThreshold(this.observationConfig.messageTokens);
4650
+ const threshold = getMaxThreshold(this.observationConfig.messageTokens);
4524
4651
  const threadTokenCounts = /* @__PURE__ */ new Map();
4525
4652
  for (const [threadId, msgs] of messagesByThread) {
4526
4653
  let tokens = 0;
@@ -4586,13 +4713,14 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4586
4713
  const tokensToObserve = this.tokenCounter.countMessages(msgs);
4587
4714
  threadTokensToObserve.set(threadId, tokensToObserve);
4588
4715
  if (lastMessage?.id) {
4589
- const startMarker = this.createObservationStartMarker({
4716
+ const startMarker = createObservationStartMarker({
4590
4717
  cycleId,
4591
4718
  operationType: "observation",
4592
4719
  tokensToObserve,
4593
4720
  recordId: record.id,
4594
4721
  threadId,
4595
- threadIds: allThreadIds
4722
+ threadIds: allThreadIds,
4723
+ config: this.getObservationMarkerConfig()
4596
4724
  });
4597
4725
  if (writer) {
4598
4726
  await writer.custom(startMarker).catch(() => {
@@ -4718,7 +4846,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4718
4846
  const lastMessage = threadMessages[threadMessages.length - 1];
4719
4847
  if (lastMessage?.id) {
4720
4848
  const tokensObserved = threadTokensToObserve.get(threadId) ?? this.tokenCounter.countMessages(threadMessages);
4721
- const endMarker = this.createObservationEndMarker({
4849
+ const endMarker = createObservationEndMarker({
4722
4850
  cycleId,
4723
4851
  operationType: "observation",
4724
4852
  startedAt: observationStartedAt,
@@ -4750,7 +4878,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4750
4878
  const lastMessage = msgs[msgs.length - 1];
4751
4879
  if (lastMessage?.id) {
4752
4880
  const tokensAttempted = threadTokensToObserve.get(threadId) ?? 0;
4753
- const failedMarker = this.createObservationFailedMarker({
4881
+ const failedMarker = createObservationFailedMarker({
4754
4882
  cycleId,
4755
4883
  operationType: "observation",
4756
4884
  startedAt: observationStartedAt,
@@ -4782,7 +4910,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4782
4910
  async maybeAsyncReflect(record, observationTokens, writer, messageList, requestContext) {
4783
4911
  if (!this.isAsyncReflectionEnabled()) return;
4784
4912
  const lockKey = this.getLockKey(record.threadId, record.resourceId);
4785
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
4913
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
4786
4914
  omDebug(
4787
4915
  `[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}`
4788
4916
  );
@@ -4818,7 +4946,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4818
4946
  async maybeReflect(opts) {
4819
4947
  const { record, observationTokens, writer, abortSignal, messageList, reflectionHooks, requestContext } = opts;
4820
4948
  const lockKey = this.getLockKey(record.threadId, record.resourceId);
4821
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
4949
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
4822
4950
  if (this.isAsyncReflectionEnabled() && observationTokens < reflectThreshold) {
4823
4951
  if (this.shouldTriggerAsyncReflection(observationTokens, lockKey, record)) {
4824
4952
  this.startAsyncBufferedReflection(record, observationTokens, lockKey, writer, requestContext);
@@ -4859,13 +4987,14 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4859
4987
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
4860
4988
  const threadId = opts.threadId ?? "unknown";
4861
4989
  if (writer) {
4862
- const startMarker = this.createObservationStartMarker({
4990
+ const startMarker = createObservationStartMarker({
4863
4991
  cycleId,
4864
4992
  operationType: "reflection",
4865
4993
  tokensToObserve: observationTokens,
4866
4994
  recordId: record.id,
4867
4995
  threadId,
4868
- threadIds: [threadId]
4996
+ threadIds: [threadId],
4997
+ config: this.getObservationMarkerConfig()
4869
4998
  });
4870
4999
  await writer.custom(startMarker).catch(() => {
4871
5000
  });
@@ -4903,7 +5032,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4903
5032
  tokenCount: reflectionTokenCount
4904
5033
  });
4905
5034
  if (writer && streamContext) {
4906
- const endMarker = this.createObservationEndMarker({
5035
+ const endMarker = createObservationEndMarker({
4907
5036
  cycleId: streamContext.cycleId,
4908
5037
  operationType: "reflection",
4909
5038
  startedAt: streamContext.startedAt,
@@ -4928,7 +5057,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4928
5057
  });
4929
5058
  } catch (error) {
4930
5059
  if (writer && streamContext) {
4931
- const failedMarker = this.createObservationFailedMarker({
5060
+ const failedMarker = createObservationFailedMarker({
4932
5061
  cycleId: streamContext.cycleId,
4933
5062
  operationType: "reflection",
4934
5063
  startedAt: streamContext.startedAt,
@@ -5033,7 +5162,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5033
5162
  await this.storage.setReflectingFlag(record.id, true);
5034
5163
  registerOp(record.id, "reflecting");
5035
5164
  try {
5036
- const reflectThreshold = this.getMaxThreshold(this.reflectionConfig.observationTokens);
5165
+ const reflectThreshold = getMaxThreshold(this.reflectionConfig.observationTokens);
5037
5166
  const reflectResult = await this.callReflector(
5038
5167
  record.activeObservations,
5039
5168
  prompt,
@@ -5112,5 +5241,5 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5112
5241
  };
5113
5242
 
5114
5243
  export { OBSERVATIONAL_MEMORY_DEFAULTS, OBSERVATION_CONTEXT_INSTRUCTIONS, OBSERVATION_CONTEXT_PROMPT, OBSERVATION_CONTINUATION_HINT, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, extractCurrentTask, formatMessagesForObserver, hasCurrentTaskSection, optimizeObservationsForContext, parseObserverOutput };
5115
- //# sourceMappingURL=chunk-A62BQK35.js.map
5116
- //# sourceMappingURL=chunk-A62BQK35.js.map
5244
+ //# sourceMappingURL=chunk-GBBQIJQF.js.map
5245
+ //# sourceMappingURL=chunk-GBBQIJQF.js.map