@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.
- package/CHANGELOG.md +54 -0
- package/dist/{chunk-5UYAHJVJ.cjs → chunk-D6II7EP4.cjs} +660 -531
- package/dist/chunk-D6II7EP4.cjs.map +1 -0
- package/dist/{chunk-A62BQK35.js → chunk-GBBQIJQF.js} +660 -531
- package/dist/chunk-GBBQIJQF.js.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +25 -25
- package/dist/docs/references/docs-agents-agent-approval.md +61 -31
- package/dist/docs/references/docs-agents-supervisor-agents.md +1 -1
- package/dist/docs/references/docs-memory-observational-memory.md +9 -0
- package/dist/docs/references/docs-memory-semantic-recall.md +17 -1
- package/dist/docs/references/reference-core-getMemory.md +2 -2
- package/dist/docs/references/reference-core-listMemory.md +1 -1
- package/dist/docs/references/reference-memory-clone-utilities.md +5 -5
- package/dist/docs/references/reference-memory-cloneThread.md +17 -21
- package/dist/docs/references/reference-memory-createThread.md +10 -10
- package/dist/docs/references/reference-memory-getThreadById.md +2 -2
- package/dist/docs/references/reference-memory-listThreads.md +5 -5
- package/dist/docs/references/reference-memory-memory-class.md +12 -14
- package/dist/docs/references/reference-memory-observational-memory.md +102 -94
- package/dist/docs/references/reference-processors-token-limiter-processor.md +11 -13
- package/dist/docs/references/reference-storage-dynamodb.md +9 -9
- package/dist/docs/references/reference-storage-libsql.md +2 -2
- package/dist/docs/references/reference-storage-mongodb.md +5 -5
- package/dist/docs/references/reference-storage-postgresql.md +25 -25
- package/dist/docs/references/reference-storage-upstash.md +3 -3
- package/dist/docs/references/reference-vectors-libsql.md +31 -31
- package/dist/docs/references/reference-vectors-mongodb.md +32 -32
- package/dist/docs/references/reference-vectors-pg.md +60 -44
- package/dist/docs/references/reference-vectors-upstash.md +25 -25
- package/dist/index.cjs +246 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +246 -57
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-MXI54VC7.cjs → observational-memory-AHVELJX4.cjs} +17 -17
- package/dist/{observational-memory-MXI54VC7.cjs.map → observational-memory-AHVELJX4.cjs.map} +1 -1
- package/dist/{observational-memory-SR6G4HN5.js → observational-memory-QFQUF5EY.js} +3 -3
- package/dist/{observational-memory-SR6G4HN5.js.map → observational-memory-QFQUF5EY.js.map} +1 -1
- package/dist/processors/index.cjs +15 -15
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/date-utils.d.ts +35 -0
- package/dist/processors/observational-memory/date-utils.d.ts.map +1 -0
- package/dist/processors/observational-memory/markers.d.ts +94 -0
- package/dist/processors/observational-memory/markers.d.ts.map +1 -0
- package/dist/processors/observational-memory/observational-memory.d.ts +0 -76
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/operation-registry.d.ts +14 -0
- package/dist/processors/observational-memory/operation-registry.d.ts.map +1 -0
- package/dist/processors/observational-memory/thresholds.d.ts +52 -0
- package/dist/processors/observational-memory/thresholds.d.ts.map +1 -0
- package/dist/processors/observational-memory/token-counter.d.ts +4 -0
- package/dist/processors/observational-memory/token-counter.d.ts.map +1 -1
- package/dist/tools/working-memory.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/chunk-5UYAHJVJ.cjs.map +0 -1
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1492
|
+
payloadTokens += this.readOrPersistPartEstimate(
|
|
1493
|
+
part,
|
|
1494
|
+
`tool-${invocation.state}-args`,
|
|
1495
|
+
invocation.args
|
|
1496
|
+
);
|
|
962
1497
|
} else {
|
|
963
|
-
|
|
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
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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 :
|
|
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 :
|
|
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 :
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
2001
|
-
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 ??
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2756
|
-
const baseReflectionThreshold =
|
|
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 =
|
|
2909
|
+
const projectedMessageRemoval = calculateProjectedMessageRemoval(
|
|
2791
2910
|
bufferedChunks,
|
|
2792
2911
|
this.observationConfig.bufferActivation ?? 1,
|
|
2793
|
-
|
|
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 (
|
|
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" ?
|
|
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 =
|
|
3442
|
-
const baseReflectionThreshold =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4179
|
-
const projectedMessageRemoval =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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-
|
|
5116
|
-
//# sourceMappingURL=chunk-
|
|
5244
|
+
//# sourceMappingURL=chunk-GBBQIJQF.js.map
|
|
5245
|
+
//# sourceMappingURL=chunk-GBBQIJQF.js.map
|