@mastra/memory 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/dist/{chunk-D4AWAGLM.js → chunk-DF7NDDSM.js} +282 -190
- package/dist/chunk-DF7NDDSM.js.map +1 -0
- package/dist/{chunk-QRKB5I2S.cjs → chunk-LLTHE64H.cjs} +281 -189
- package/dist/chunk-LLTHE64H.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +25 -25
- package/dist/docs/references/docs-memory-observational-memory.md +2 -0
- package/dist/docs/references/reference-memory-memory-class.md +1 -1
- package/dist/docs/references/reference-memory-observational-memory.md +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-UCOMAMSF.cjs → observational-memory-4PCXEZIS.cjs} +17 -17
- package/dist/{observational-memory-UCOMAMSF.cjs.map → observational-memory-4PCXEZIS.cjs.map} +1 -1
- package/dist/{observational-memory-53AFLLSH.js → observational-memory-ZNTAIUGT.js} +3 -3
- package/dist/{observational-memory-53AFLLSH.js.map → observational-memory-ZNTAIUGT.js.map} +1 -1
- package/dist/processors/index.cjs +15 -15
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +17 -1
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts +26 -5
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/reflector-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +14 -3
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/package.json +11 -11
- package/dist/chunk-D4AWAGLM.js.map +0 -1
- package/dist/chunk-QRKB5I2S.cjs.map +0 -1
|
@@ -2,7 +2,7 @@ import { appendFileSync } from 'fs';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { Agent } from '@mastra/core/agent';
|
|
4
4
|
import { resolveModelConfig } from '@mastra/core/llm';
|
|
5
|
-
import { getThreadOMMetadata, parseMemoryRequestContext
|
|
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
8
|
import { Tiktoken } from 'js-tiktoken/lite';
|
|
@@ -11,54 +11,7 @@ import o200k_base from 'js-tiktoken/ranks/o200k_base';
|
|
|
11
11
|
// src/processors/observational-memory/observational-memory.ts
|
|
12
12
|
|
|
13
13
|
// src/processors/observational-memory/observer-agent.ts
|
|
14
|
-
var
|
|
15
|
-
var CONDENSED_OBSERVER_EXTRACTION_INSTRUCTIONS = `You are the memory consciousness of an AI assistant. Your observations will be the ONLY information the assistant has about past interactions with this user.
|
|
16
|
-
|
|
17
|
-
CORE PRINCIPLES:
|
|
18
|
-
|
|
19
|
-
1. BE SPECIFIC - Vague observations are useless. Capture details that distinguish and identify.
|
|
20
|
-
2. ANCHOR IN TIME - Note when things happened and when they were said.
|
|
21
|
-
3. TRACK STATE CHANGES - When information updates or supersedes previous info, make it explicit.
|
|
22
|
-
4. USE COMMON SENSE - If it would help the assistant remember later, observe it.
|
|
23
|
-
|
|
24
|
-
ASSERTIONS VS QUESTIONS:
|
|
25
|
-
- User TELLS you something \u2192 \u{1F534} "User stated [fact]"
|
|
26
|
-
- User ASKS something \u2192 \u{1F7E1} "User asked [question]"
|
|
27
|
-
- User assertions are authoritative. They are the source of truth about their own life.
|
|
28
|
-
|
|
29
|
-
TEMPORAL ANCHORING:
|
|
30
|
-
- Always include message time at the start: (14:30) User stated...
|
|
31
|
-
- Add estimated date at the END only for relative time references:
|
|
32
|
-
"User will visit parents this weekend. (meaning Jan 18-19)"
|
|
33
|
-
- Don't add end dates for present-moment statements or vague terms like "recently"
|
|
34
|
-
- Split multi-event statements into separate observations, each with its own date
|
|
35
|
-
|
|
36
|
-
DETAILS TO ALWAYS PRESERVE:
|
|
37
|
-
- Names, handles, usernames, titles (@username, "Dr. Smith")
|
|
38
|
-
- Numbers, counts, quantities (4 items, 3 sessions, 27th in list)
|
|
39
|
-
- Measurements, percentages, statistics (5kg, 20% improvement, 85% accuracy)
|
|
40
|
-
- Sequences and orderings (steps 1-5, chord progression, lucky numbers)
|
|
41
|
-
- Prices, dates, times, durations ($50, March 15, 2 hours)
|
|
42
|
-
- Locations and distinguishing attributes (near X, based in Y, specializes in Z)
|
|
43
|
-
- User's specific role (presenter, volunteer, organizer - not just "attended")
|
|
44
|
-
- Exact phrasing when unusual ("movement session" for exercise)
|
|
45
|
-
- Verbatim text being collaborated on (code, formatted text, ASCII art)
|
|
46
|
-
|
|
47
|
-
WHEN ASSISTANT PROVIDES LISTS/RECOMMENDATIONS:
|
|
48
|
-
Don't just say "Assistant recommended 5 hotels." Capture what distinguishes each:
|
|
49
|
-
"Assistant recommended: Hotel A (near station), Hotel B (pet-friendly), Hotel C (has pool)..."
|
|
50
|
-
|
|
51
|
-
STATE CHANGES:
|
|
52
|
-
When user updates information, note what changed:
|
|
53
|
-
"User will use the new method (replacing the old approach)"
|
|
54
|
-
|
|
55
|
-
WHO/WHAT/WHERE/WHEN:
|
|
56
|
-
Capture all dimensions. Not just "User went on a trip" but who with, where, when, and what happened.
|
|
57
|
-
|
|
58
|
-
Don't repeat observations that have already been captured in previous sessions.
|
|
59
|
-
|
|
60
|
-
REMEMBER: These observations are your ENTIRE memory. Any detail you fail to observe is permanently forgotten. Use common sense - if something seems like it might be important to remember, it probably is. When in doubt, observe it.`;
|
|
61
|
-
var CURRENT_OBSERVER_EXTRACTION_INSTRUCTIONS = `CRITICAL: DISTINGUISH USER ASSERTIONS FROM QUESTIONS
|
|
14
|
+
var OBSERVER_EXTRACTION_INSTRUCTIONS = `CRITICAL: DISTINGUISH USER ASSERTIONS FROM QUESTIONS
|
|
62
15
|
|
|
63
16
|
When the user TELLS you something about themselves, mark it as an assertion:
|
|
64
17
|
- "I have two kids" \u2192 \u{1F534} (14:30) User stated has two kids
|
|
@@ -66,8 +19,8 @@ When the user TELLS you something about themselves, mark it as an assertion:
|
|
|
66
19
|
- "I graduated in 2019" \u2192 \u{1F534} (14:32) User stated graduated in 2019
|
|
67
20
|
|
|
68
21
|
When the user ASKS about something, mark it as a question/request:
|
|
69
|
-
- "Can you help me with X?" \u2192 \u{
|
|
70
|
-
- "What's the best way to do Y?" \u2192 \u{
|
|
22
|
+
- "Can you help me with X?" \u2192 \u{1F534} (15:00) User asked help with X
|
|
23
|
+
- "What's the best way to do Y?" \u2192 \u{1F534} (15:01) User asked best way to do Y
|
|
71
24
|
|
|
72
25
|
Distinguish between QUESTIONS and STATEMENTS OF INTENT:
|
|
73
26
|
- "Can you recommend..." \u2192 Question (extract as "User asked...")
|
|
@@ -249,60 +202,39 @@ CONVERSATION CONTEXT:
|
|
|
249
202
|
- When who/what/where/when is mentioned, note that in the observation. Example: if the user received went on a trip with someone, observe who that someone was, where the trip was, when it happened, and what happened, not just that the user went on the trip.
|
|
250
203
|
- For any described entity (like a person, place, thing, etc), preserve the attributes that would help identify or describe the specific entity later: location ("near X"), specialty ("focuses on Y"), unique feature ("has Z"), relationship ("owned by W"), or other details. The entity's name is important, but so are any additional details that distinguish it. If there are a list of entities, preserve these details for each of them.
|
|
251
204
|
|
|
252
|
-
|
|
253
|
-
-
|
|
254
|
-
-
|
|
255
|
-
-
|
|
256
|
-
var OBSERVER_EXTRACTION_INSTRUCTIONS = USE_CONDENSED_PROMPT ? CONDENSED_OBSERVER_EXTRACTION_INSTRUCTIONS : CURRENT_OBSERVER_EXTRACTION_INSTRUCTIONS;
|
|
257
|
-
var CONDENSED_OBSERVER_OUTPUT_FORMAT = `Use priority levels:
|
|
258
|
-
- \u{1F534} High: explicit user facts, preferences, goals achieved, critical context
|
|
259
|
-
- \u{1F7E1} Medium: project details, learned information, tool results
|
|
260
|
-
- \u{1F7E2} Low: minor details, uncertain observations
|
|
205
|
+
USER MESSAGE CAPTURE:
|
|
206
|
+
- Short and medium-length user messages should be captured nearly verbatim in your own words.
|
|
207
|
+
- For very long user messages, summarize but quote key phrases that carry specific intent or meaning.
|
|
208
|
+
- This is critical for continuity: when the conversation window shrinks, the observations are the only record of what the user said.
|
|
261
209
|
|
|
262
|
-
|
|
263
|
-
|
|
210
|
+
AVOIDING REPETITIVE OBSERVATIONS:
|
|
211
|
+
- Do NOT repeat the same observation across multiple turns if there is no new information.
|
|
212
|
+
- When the agent performs repeated similar actions (e.g., browsing files, running the same tool type multiple times), group them into a single parent observation with sub-bullets for each new result.
|
|
264
213
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
* \u{
|
|
268
|
-
* \u{
|
|
269
|
-
* \u{1F7E1} (09:20) User asked how to optimize database queries
|
|
270
|
-
* \u{1F7E1} (10:30) User working on auth refactor - targeting 50% latency reduction
|
|
271
|
-
* \u{1F7E1} (10:45) Assistant recommended hotels: Grand Plaza (downtown, $180/night), Seaside Inn (near beach, pet-friendly), Mountain Lodge (has pool, free breakfast)
|
|
272
|
-
* \u{1F534} (11:00) User's friend @maria_dev recommended using Redis for caching
|
|
273
|
-
* \u{1F7E1} (11:15) User attended the tech conference as a speaker (presented on microservices)
|
|
274
|
-
* \u{1F534} (11:30) User will visit parents this weekend (meaning Dec 7-8, 2025)
|
|
275
|
-
* \u{1F7E1} (14:00) Agent debugging auth issue
|
|
276
|
-
* -> ran git status, found 3 modified files
|
|
277
|
-
* -> viewed auth.ts:45-60, found missing null check
|
|
278
|
-
* -> applied fix, tests now pass
|
|
279
|
-
* \u{1F7E1} (14:30) Assistant provided dataset stats: 7,342 samples, 89.6% accuracy, 23ms inference time
|
|
280
|
-
* \u{1F534} (15:00) User's lucky numbers from fortune cookie: 7, 14, 23, 38, 42, 49
|
|
214
|
+
Example \u2014 BAD (repetitive):
|
|
215
|
+
* \u{1F7E1} (14:30) Agent used view tool on src/auth.ts
|
|
216
|
+
* \u{1F7E1} (14:31) Agent used view tool on src/users.ts
|
|
217
|
+
* \u{1F7E1} (14:32) Agent used view tool on src/routes.ts
|
|
281
218
|
|
|
282
|
-
|
|
283
|
-
* \u{
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* \u{1F534} (10:45) User's dentist appointment is next Tuesday (meaning Dec 10, 2025)
|
|
288
|
-
* \u{1F7E2} (11:00) User mentioned they might try the new coffee shop
|
|
289
|
-
</observations>
|
|
219
|
+
Example \u2014 GOOD (grouped):
|
|
220
|
+
* \u{1F7E1} (14:30) Agent browsed source files for auth flow
|
|
221
|
+
* -> viewed src/auth.ts \u2014 found token validation logic
|
|
222
|
+
* -> viewed src/users.ts \u2014 found user lookup by email
|
|
223
|
+
* -> viewed src/routes.ts \u2014 found middleware chain
|
|
290
224
|
|
|
291
|
-
|
|
292
|
-
Primary: Implementing OAuth2 flow for the auth refactor
|
|
293
|
-
Secondary: Waiting for user to confirm database schema changes
|
|
294
|
-
</current-task>
|
|
225
|
+
Only add a new observation for a repeated action if the NEW result changes the picture.
|
|
295
226
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
227
|
+
ACTIONABLE INSIGHTS:
|
|
228
|
+
- What worked well in explanations
|
|
229
|
+
- What needs follow-up or clarification
|
|
230
|
+
- User's stated goals or next steps (note if the user tells you not to do a next step, or asks for something specific, other next steps besides the users request should be marked as "waiting for user", unless the user explicitly says to continue all next steps)`;
|
|
299
231
|
var OBSERVER_OUTPUT_FORMAT_BASE = `Use priority levels:
|
|
300
232
|
- \u{1F534} High: explicit user facts, preferences, goals achieved, critical context
|
|
301
233
|
- \u{1F7E1} Medium: project details, learned information, tool results
|
|
302
234
|
- \u{1F7E2} Low: minor details, uncertain observations
|
|
303
235
|
|
|
304
236
|
Group related observations (like tool sequences) by indenting:
|
|
305
|
-
* \u{
|
|
237
|
+
* \u{1F534} (14:33) Agent debugging auth issue
|
|
306
238
|
* -> ran git status, found 3 modified files
|
|
307
239
|
* -> viewed auth.ts:45-60, found missing null check
|
|
308
240
|
* -> applied fix, tests now pass
|
|
@@ -312,11 +244,11 @@ Group observations by date, then list each with 24-hour time.
|
|
|
312
244
|
<observations>
|
|
313
245
|
Date: Dec 4, 2025
|
|
314
246
|
* \u{1F534} (14:30) User prefers direct answers
|
|
315
|
-
* \u{
|
|
316
|
-
* \u{
|
|
247
|
+
* \u{1F534} (14:31) Working on feature X
|
|
248
|
+
* \u{1F7E1} (14:32) User might prefer dark mode
|
|
317
249
|
|
|
318
250
|
Date: Dec 5, 2025
|
|
319
|
-
* \u{
|
|
251
|
+
* \u{1F534} (09:15) Continued work on feature X
|
|
320
252
|
</observations>
|
|
321
253
|
|
|
322
254
|
<current-task>
|
|
@@ -333,29 +265,21 @@ Hint for the agent's immediate next message. Examples:
|
|
|
333
265
|
- "The assistant should wait for the user to respond before continuing."
|
|
334
266
|
- Call the view tool on src/example.ts to continue debugging.
|
|
335
267
|
</suggested-response>`;
|
|
336
|
-
var
|
|
337
|
-
- Use terse language - dense sentences without unnecessary words
|
|
338
|
-
- Don't repeat observations that have already been captured
|
|
339
|
-
- When the agent calls tools, observe what was called, why, and what was learned
|
|
340
|
-
- Include line numbers when observing code files
|
|
341
|
-
- If the agent provides a detailed response, observe the key points so it could be repeated
|
|
342
|
-
- Start each observation with a priority emoji (\u{1F534}, \u{1F7E1}, \u{1F7E2})
|
|
343
|
-
- Observe WHAT happened and WHAT it means, not HOW well it was done
|
|
344
|
-
- If the user provides detailed messages or code snippets, observe all important details`;
|
|
345
|
-
var OBSERVER_GUIDELINES = USE_CONDENSED_PROMPT ? CONDENSED_OBSERVER_GUIDELINES : `- Be specific enough for the assistant to act on
|
|
268
|
+
var OBSERVER_GUIDELINES = `- Be specific enough for the assistant to act on
|
|
346
269
|
- Good: "User prefers short, direct answers without lengthy explanations"
|
|
347
270
|
- Bad: "User stated a preference" (too vague)
|
|
348
271
|
- Add 1 to 5 observations per exchange
|
|
349
|
-
- Use terse language to save tokens. Sentences should be dense without unnecessary words
|
|
350
|
-
- Do not add repetitive observations that have already been observed.
|
|
351
|
-
- If the agent calls tools, observe what was called, why, and what was learned
|
|
352
|
-
- When observing files with line numbers, include the line number if useful
|
|
353
|
-
- If the agent provides a detailed response, observe the contents so it could be repeated
|
|
272
|
+
- Use terse language to save tokens. Sentences should be dense without unnecessary words
|
|
273
|
+
- Do not add repetitive observations that have already been observed. Group repeated similar actions (tool calls, file browsing) under a single parent with sub-bullets for new results
|
|
274
|
+
- If the agent calls tools, observe what was called, why, and what was learned
|
|
275
|
+
- When observing files with line numbers, include the line number if useful
|
|
276
|
+
- If the agent provides a detailed response, observe the contents so it could be repeated
|
|
354
277
|
- Make sure you start each observation with a priority emoji (\u{1F534}, \u{1F7E1}, \u{1F7E2})
|
|
355
|
-
-
|
|
356
|
-
-
|
|
278
|
+
- User messages are always \u{1F534} priority, so are the completions of tasks. Capture the user's words closely \u2014 short/medium messages near-verbatim, long messages summarized with key quotes
|
|
279
|
+
- Observe WHAT the agent did and WHAT it means
|
|
280
|
+
- If the user provides detailed messages or code snippets, observe all important details`;
|
|
357
281
|
function buildObserverSystemPrompt(multiThread = false, instruction) {
|
|
358
|
-
const outputFormat =
|
|
282
|
+
const outputFormat = OBSERVER_OUTPUT_FORMAT_BASE;
|
|
359
283
|
if (multiThread) {
|
|
360
284
|
return `You are the memory consciousness of an AI assistant. Your observations will be the ONLY information the assistant has about past interactions with this user.
|
|
361
285
|
|
|
@@ -376,7 +300,7 @@ Your output MUST use XML tags to structure the response. Each thread's observati
|
|
|
376
300
|
<thread id="thread_id_1">
|
|
377
301
|
Date: Dec 4, 2025
|
|
378
302
|
* \u{1F534} (14:30) User prefers direct answers
|
|
379
|
-
* \u{
|
|
303
|
+
* \u{1F534} (14:31) Working on feature X
|
|
380
304
|
|
|
381
305
|
<current-task>
|
|
382
306
|
What the agent is currently working on in this thread
|
|
@@ -389,7 +313,7 @@ Hint for the agent's next message in this thread
|
|
|
389
313
|
|
|
390
314
|
<thread id="thread_id_2">
|
|
391
315
|
Date: Dec 5, 2025
|
|
392
|
-
* \u{
|
|
316
|
+
* \u{1F534} (09:15) User asked about deployment
|
|
393
317
|
|
|
394
318
|
<current-task>
|
|
395
319
|
Current task for this thread
|
|
@@ -402,7 +326,7 @@ Suggested response for this thread
|
|
|
402
326
|
</observations>
|
|
403
327
|
|
|
404
328
|
Use priority levels:
|
|
405
|
-
- \u{1F534} High: explicit user facts, preferences, goals achieved, critical context
|
|
329
|
+
- \u{1F534} High: explicit user facts, preferences, goals achieved, critical context, user messages
|
|
406
330
|
- \u{1F7E1} Medium: project details, learned information, tool results
|
|
407
331
|
- \u{1F7E2} Low: minor details, uncertain observations
|
|
408
332
|
|
|
@@ -556,7 +480,7 @@ ${formattedMessages}
|
|
|
556
480
|
`;
|
|
557
481
|
prompt += `Date: Dec 5, 2025
|
|
558
482
|
`;
|
|
559
|
-
prompt += `* \u{
|
|
483
|
+
prompt += `* \u{1F534} (09:15) User asked about deployment
|
|
560
484
|
`;
|
|
561
485
|
prompt += `<current-task>Discussing deployment options</current-task>
|
|
562
486
|
`;
|
|
@@ -569,6 +493,9 @@ ${formattedMessages}
|
|
|
569
493
|
}
|
|
570
494
|
function parseMultiThreadObserverOutput(output) {
|
|
571
495
|
const threads = /* @__PURE__ */ new Map();
|
|
496
|
+
if (detectDegenerateRepetition(output)) {
|
|
497
|
+
return { threads, rawOutput: output, degenerate: true };
|
|
498
|
+
}
|
|
572
499
|
const observationsMatch = output.match(/^[ \t]*<observations>([\s\S]*?)^[ \t]*<\/observations>/im);
|
|
573
500
|
const observationsContent = observationsMatch?.[1] ?? output;
|
|
574
501
|
const threadRegex = /<thread\s+id="([^"]+)">([\s\S]*?)<\/thread>/gi;
|
|
@@ -590,7 +517,7 @@ function parseMultiThreadObserverOutput(output) {
|
|
|
590
517
|
suggestedContinuation = suggestedMatch[1].trim();
|
|
591
518
|
observations = observations.replace(/<suggested-response>[\s\S]*?<\/suggested-response>/i, "");
|
|
592
519
|
}
|
|
593
|
-
observations = observations.trim();
|
|
520
|
+
observations = sanitizeObservationLines(observations.trim());
|
|
594
521
|
threads.set(threadId, {
|
|
595
522
|
observations,
|
|
596
523
|
currentTask,
|
|
@@ -635,8 +562,15 @@ IMPORTANT: Do NOT include <current-task> or <suggested-response> sections in you
|
|
|
635
562
|
return prompt;
|
|
636
563
|
}
|
|
637
564
|
function parseObserverOutput(output) {
|
|
565
|
+
if (detectDegenerateRepetition(output)) {
|
|
566
|
+
return {
|
|
567
|
+
observations: "",
|
|
568
|
+
rawOutput: output,
|
|
569
|
+
degenerate: true
|
|
570
|
+
};
|
|
571
|
+
}
|
|
638
572
|
const parsed = parseMemorySectionXml(output);
|
|
639
|
-
const observations = parsed.observations || "";
|
|
573
|
+
const observations = sanitizeObservationLines(parsed.observations || "");
|
|
640
574
|
return {
|
|
641
575
|
observations,
|
|
642
576
|
currentTask: parsed.currentTask || void 0,
|
|
@@ -677,6 +611,42 @@ function extractListItemsOnly(content) {
|
|
|
677
611
|
}
|
|
678
612
|
return listLines.join("\n").trim();
|
|
679
613
|
}
|
|
614
|
+
var MAX_OBSERVATION_LINE_CHARS = 1e4;
|
|
615
|
+
function sanitizeObservationLines(observations) {
|
|
616
|
+
if (!observations) return observations;
|
|
617
|
+
const lines = observations.split("\n");
|
|
618
|
+
let changed = false;
|
|
619
|
+
for (let i = 0; i < lines.length; i++) {
|
|
620
|
+
if (lines[i].length > MAX_OBSERVATION_LINE_CHARS) {
|
|
621
|
+
lines[i] = lines[i].slice(0, MAX_OBSERVATION_LINE_CHARS) + " \u2026 [truncated]";
|
|
622
|
+
changed = true;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return changed ? lines.join("\n") : observations;
|
|
626
|
+
}
|
|
627
|
+
function detectDegenerateRepetition(text) {
|
|
628
|
+
if (!text || text.length < 2e3) return false;
|
|
629
|
+
const windowSize = 200;
|
|
630
|
+
const step = Math.max(1, Math.floor(text.length / 50));
|
|
631
|
+
const seen = /* @__PURE__ */ new Map();
|
|
632
|
+
let duplicateWindows = 0;
|
|
633
|
+
let totalWindows = 0;
|
|
634
|
+
for (let i = 0; i + windowSize <= text.length; i += step) {
|
|
635
|
+
const window = text.slice(i, i + windowSize);
|
|
636
|
+
totalWindows++;
|
|
637
|
+
const count = (seen.get(window) ?? 0) + 1;
|
|
638
|
+
seen.set(window, count);
|
|
639
|
+
if (count > 1) duplicateWindows++;
|
|
640
|
+
}
|
|
641
|
+
if (totalWindows > 5 && duplicateWindows / totalWindows > 0.4) {
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
const lines = text.split("\n");
|
|
645
|
+
for (const line of lines) {
|
|
646
|
+
if (line.length > 5e4) return true;
|
|
647
|
+
}
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
680
650
|
function hasCurrentTaskSection(observations) {
|
|
681
651
|
if (/<current-task>/i.test(observations)) {
|
|
682
652
|
return true;
|
|
@@ -888,8 +858,14 @@ IMPORTANT: Do NOT include <current-task> or <suggested-response> sections in you
|
|
|
888
858
|
return prompt;
|
|
889
859
|
}
|
|
890
860
|
function parseReflectorOutput(output) {
|
|
861
|
+
if (detectDegenerateRepetition(output)) {
|
|
862
|
+
return {
|
|
863
|
+
observations: "",
|
|
864
|
+
degenerate: true
|
|
865
|
+
};
|
|
866
|
+
}
|
|
891
867
|
const parsed = parseReflectorSectionXml(output);
|
|
892
|
-
const observations = parsed.observations || "";
|
|
868
|
+
const observations = sanitizeObservationLines(parsed.observations || "");
|
|
893
869
|
return {
|
|
894
870
|
observations,
|
|
895
871
|
suggestedContinuation: parsed.suggestedResponse || void 0
|
|
@@ -1269,7 +1245,9 @@ var OBSERVATION_CONTEXT_INSTRUCTIONS = `IMPORTANT: When responding, reference sp
|
|
|
1269
1245
|
|
|
1270
1246
|
KNOWLEDGE UPDATES: When asked about current state (e.g., "where do I currently...", "what is my current..."), always prefer the MOST RECENT information. Observations include dates - if you see conflicting information, the newer observation supersedes the older one. Look for phrases like "will start", "is switching", "changed to", "moved to" as indicators that previous information has been updated.
|
|
1271
1247
|
|
|
1272
|
-
PLANNED ACTIONS: If the user stated they planned to do something (e.g., "I'm going to...", "I'm looking forward to...", "I will...") and the date they planned to do it is now in the past (check the relative time like "3 weeks ago"), assume they completed the action unless there's evidence they didn't. For example, if someone said "I'll start my new diet on Monday" and that was 2 weeks ago, assume they started the diet
|
|
1248
|
+
PLANNED ACTIONS: If the user stated they planned to do something (e.g., "I'm going to...", "I'm looking forward to...", "I will...") and the date they planned to do it is now in the past (check the relative time like "3 weeks ago"), assume they completed the action unless there's evidence they didn't. For example, if someone said "I'll start my new diet on Monday" and that was 2 weeks ago, assume they started the diet.
|
|
1249
|
+
|
|
1250
|
+
MOST RECENT USER INPUT: Treat the most recent user message as the highest-priority signal for what to do next. Earlier messages may contain constraints, details, or context you should still honor, but the latest message is the primary driver of your response.`;
|
|
1273
1251
|
var ObservationalMemory = class _ObservationalMemory {
|
|
1274
1252
|
id = "observational-memory";
|
|
1275
1253
|
name = "Observational Memory";
|
|
@@ -1439,41 +1417,65 @@ var ObservationalMemory = class _ObservationalMemory {
|
|
|
1439
1417
|
}
|
|
1440
1418
|
return [];
|
|
1441
1419
|
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Resolve bufferActivation config into an absolute retention floor (tokens to keep).
|
|
1422
|
+
* - Value in (0, 1]: ratio → retentionFloor = threshold * (1 - value)
|
|
1423
|
+
* - Value >= 1000: absolute token count → retentionFloor = value
|
|
1424
|
+
*/
|
|
1425
|
+
resolveRetentionFloor(bufferActivation, messageTokensThreshold) {
|
|
1426
|
+
if (bufferActivation >= 1e3) return bufferActivation;
|
|
1427
|
+
return messageTokensThreshold * (1 - bufferActivation);
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Convert bufferActivation to the equivalent ratio (0-1) for the storage layer.
|
|
1431
|
+
* When bufferActivation >= 1000, it's an absolute retention target, so we compute
|
|
1432
|
+
* the equivalent ratio: 1 - (bufferActivation / threshold).
|
|
1433
|
+
*/
|
|
1434
|
+
resolveActivationRatio(bufferActivation, messageTokensThreshold) {
|
|
1435
|
+
if (bufferActivation >= 1e3) {
|
|
1436
|
+
return Math.max(0, Math.min(1, 1 - bufferActivation / messageTokensThreshold));
|
|
1437
|
+
}
|
|
1438
|
+
return bufferActivation;
|
|
1439
|
+
}
|
|
1442
1440
|
/**
|
|
1443
1441
|
* Calculate the projected message tokens that would be removed if activation happened now.
|
|
1444
1442
|
* This replicates the chunk boundary logic in swapBufferedToActive without actually activating.
|
|
1445
1443
|
*/
|
|
1446
|
-
calculateProjectedMessageRemoval(chunks,
|
|
1444
|
+
calculateProjectedMessageRemoval(chunks, bufferActivation, messageTokensThreshold, currentPendingTokens) {
|
|
1447
1445
|
if (chunks.length === 0) return 0;
|
|
1448
|
-
const retentionFloor =
|
|
1446
|
+
const retentionFloor = this.resolveRetentionFloor(bufferActivation, messageTokensThreshold);
|
|
1449
1447
|
const targetMessageTokens = Math.max(0, currentPendingTokens - retentionFloor);
|
|
1450
1448
|
let cumulativeMessageTokens = 0;
|
|
1451
|
-
let
|
|
1452
|
-
let
|
|
1449
|
+
let bestOverBoundary = 0;
|
|
1450
|
+
let bestOverTokens = 0;
|
|
1451
|
+
let bestUnderBoundary = 0;
|
|
1452
|
+
let bestUnderTokens = 0;
|
|
1453
1453
|
for (let i = 0; i < chunks.length; i++) {
|
|
1454
1454
|
cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
|
|
1455
1455
|
const boundary = i + 1;
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
bestBoundaryMessageTokens = cumulativeMessageTokens;
|
|
1461
|
-
} else if (isUnder && !bestIsUnder) {
|
|
1462
|
-
bestBoundary = boundary;
|
|
1463
|
-
bestBoundaryMessageTokens = cumulativeMessageTokens;
|
|
1464
|
-
} else if (isUnder && bestIsUnder) {
|
|
1465
|
-
if (cumulativeMessageTokens > bestBoundaryMessageTokens) {
|
|
1466
|
-
bestBoundary = boundary;
|
|
1467
|
-
bestBoundaryMessageTokens = cumulativeMessageTokens;
|
|
1456
|
+
if (cumulativeMessageTokens >= targetMessageTokens) {
|
|
1457
|
+
if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
|
|
1458
|
+
bestOverBoundary = boundary;
|
|
1459
|
+
bestOverTokens = cumulativeMessageTokens;
|
|
1468
1460
|
}
|
|
1469
|
-
} else
|
|
1470
|
-
if (cumulativeMessageTokens
|
|
1471
|
-
|
|
1472
|
-
|
|
1461
|
+
} else {
|
|
1462
|
+
if (cumulativeMessageTokens > bestUnderTokens) {
|
|
1463
|
+
bestUnderBoundary = boundary;
|
|
1464
|
+
bestUnderTokens = cumulativeMessageTokens;
|
|
1473
1465
|
}
|
|
1474
1466
|
}
|
|
1475
1467
|
}
|
|
1476
|
-
|
|
1468
|
+
const maxOvershoot = retentionFloor * 0.95;
|
|
1469
|
+
const overshoot = bestOverTokens - targetMessageTokens;
|
|
1470
|
+
const remainingAfterOver = currentPendingTokens - bestOverTokens;
|
|
1471
|
+
let bestBoundaryMessageTokens;
|
|
1472
|
+
if (bestOverBoundary > 0 && overshoot <= maxOvershoot && (remainingAfterOver >= 1e3 || retentionFloor === 0)) {
|
|
1473
|
+
bestBoundaryMessageTokens = bestOverTokens;
|
|
1474
|
+
} else if (bestUnderBoundary > 0) {
|
|
1475
|
+
bestBoundaryMessageTokens = bestUnderTokens;
|
|
1476
|
+
} else if (bestOverBoundary > 0) {
|
|
1477
|
+
bestBoundaryMessageTokens = bestOverTokens;
|
|
1478
|
+
} else {
|
|
1477
1479
|
return chunks[0]?.messageTokens ?? 0;
|
|
1478
1480
|
}
|
|
1479
1481
|
return bestBoundaryMessageTokens;
|
|
@@ -1481,8 +1483,12 @@ var ObservationalMemory = class _ObservationalMemory {
|
|
|
1481
1483
|
/**
|
|
1482
1484
|
* Check if we've crossed a new bufferTokens interval boundary.
|
|
1483
1485
|
* Returns true if async buffering should be triggered.
|
|
1486
|
+
*
|
|
1487
|
+
* When pending tokens are within ~1 bufferTokens of the observation threshold,
|
|
1488
|
+
* the buffer interval is halved to produce finer-grained chunks right before
|
|
1489
|
+
* activation. This improves chunk boundary selection, reducing overshoot.
|
|
1484
1490
|
*/
|
|
1485
|
-
shouldTriggerAsyncObservation(currentTokens, lockKey, record) {
|
|
1491
|
+
shouldTriggerAsyncObservation(currentTokens, lockKey, record, messageTokensThreshold) {
|
|
1486
1492
|
if (!this.isAsyncObservationEnabled()) return false;
|
|
1487
1493
|
if (record.isBufferingObservation) {
|
|
1488
1494
|
if (isOpActiveInProcess(record.id, "bufferingObservation")) return false;
|
|
@@ -1496,11 +1502,13 @@ var ObservationalMemory = class _ObservationalMemory {
|
|
|
1496
1502
|
const dbBoundary = record.lastBufferedAtTokens ?? 0;
|
|
1497
1503
|
const memBoundary = _ObservationalMemory.lastBufferedBoundary.get(bufferKey) ?? 0;
|
|
1498
1504
|
const lastBoundary = Math.max(dbBoundary, memBoundary);
|
|
1499
|
-
const
|
|
1500
|
-
const
|
|
1505
|
+
const rampPoint = messageTokensThreshold ? messageTokensThreshold - bufferTokens * 1.1 : Infinity;
|
|
1506
|
+
const effectiveBufferTokens = currentTokens >= rampPoint ? bufferTokens / 2 : bufferTokens;
|
|
1507
|
+
const currentInterval = Math.floor(currentTokens / effectiveBufferTokens);
|
|
1508
|
+
const lastInterval = Math.floor(lastBoundary / effectiveBufferTokens);
|
|
1501
1509
|
const shouldTrigger = currentInterval > lastInterval;
|
|
1502
1510
|
omDebug(
|
|
1503
|
-
`[OM:shouldTriggerAsyncObs] tokens=${currentTokens}, bufferTokens=${bufferTokens}, currentInterval=${currentInterval}, lastInterval=${lastInterval}, lastBoundary=${lastBoundary} (db=${dbBoundary}, mem=${memBoundary}), shouldTrigger=${shouldTrigger}`
|
|
1511
|
+
`[OM:shouldTriggerAsyncObs] tokens=${currentTokens}, bufferTokens=${bufferTokens}, effectiveBufferTokens=${effectiveBufferTokens}, rampPoint=${rampPoint}, currentInterval=${currentInterval}, lastInterval=${lastInterval}, lastBoundary=${lastBoundary} (db=${dbBoundary}, mem=${memBoundary}), shouldTrigger=${shouldTrigger}`
|
|
1504
1512
|
);
|
|
1505
1513
|
return shouldTrigger;
|
|
1506
1514
|
}
|
|
@@ -1761,9 +1769,17 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
1761
1769
|
}
|
|
1762
1770
|
}
|
|
1763
1771
|
if (this.observationConfig.bufferActivation !== void 0) {
|
|
1764
|
-
if (this.observationConfig.bufferActivation <= 0
|
|
1772
|
+
if (this.observationConfig.bufferActivation <= 0) {
|
|
1773
|
+
throw new Error(`observation.bufferActivation must be > 0, got ${this.observationConfig.bufferActivation}`);
|
|
1774
|
+
}
|
|
1775
|
+
if (this.observationConfig.bufferActivation > 1 && this.observationConfig.bufferActivation < 1e3) {
|
|
1776
|
+
throw new Error(
|
|
1777
|
+
`observation.bufferActivation must be <= 1 (ratio) or >= 1000 (absolute token retention), got ${this.observationConfig.bufferActivation}`
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
if (this.observationConfig.bufferActivation >= 1e3 && this.observationConfig.bufferActivation >= observationThreshold) {
|
|
1765
1781
|
throw new Error(
|
|
1766
|
-
`observation.bufferActivation
|
|
1782
|
+
`observation.bufferActivation as absolute retention (${this.observationConfig.bufferActivation}) must be less than messageTokens (${observationThreshold})`
|
|
1767
1783
|
);
|
|
1768
1784
|
}
|
|
1769
1785
|
}
|
|
@@ -2378,18 +2394,31 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
2378
2394
|
async callObserver(existingObservations, messagesToObserve, abortSignal, options) {
|
|
2379
2395
|
const agent = this.getObserverAgent();
|
|
2380
2396
|
const prompt = buildObserverPrompt(existingObservations, messagesToObserve, options);
|
|
2381
|
-
const
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2397
|
+
const doGenerate = async () => {
|
|
2398
|
+
const result2 = await this.withAbortCheck(
|
|
2399
|
+
() => agent.generate(prompt, {
|
|
2400
|
+
modelSettings: {
|
|
2401
|
+
...this.observationConfig.modelSettings
|
|
2402
|
+
},
|
|
2403
|
+
providerOptions: this.observationConfig.providerOptions,
|
|
2404
|
+
...abortSignal ? { abortSignal } : {},
|
|
2405
|
+
...options?.requestContext ? { requestContext: options.requestContext } : {}
|
|
2406
|
+
}),
|
|
2407
|
+
abortSignal
|
|
2408
|
+
);
|
|
2409
|
+
return result2;
|
|
2410
|
+
};
|
|
2411
|
+
let result = await doGenerate();
|
|
2412
|
+
let parsed = parseObserverOutput(result.text);
|
|
2413
|
+
if (parsed.degenerate) {
|
|
2414
|
+
omDebug(`[OM:callObserver] degenerate repetition detected, retrying once`);
|
|
2415
|
+
result = await doGenerate();
|
|
2416
|
+
parsed = parseObserverOutput(result.text);
|
|
2417
|
+
if (parsed.degenerate) {
|
|
2418
|
+
omDebug(`[OM:callObserver] degenerate repetition on retry, failing`);
|
|
2419
|
+
throw new Error("Observer produced degenerate output after retry");
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2393
2422
|
const usage = result.totalUsage ?? result.usage;
|
|
2394
2423
|
return {
|
|
2395
2424
|
observations: parsed.observations,
|
|
@@ -2423,18 +2452,30 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
2423
2452
|
for (const msg of allMessages) {
|
|
2424
2453
|
this.observedMessageIds.add(msg.id);
|
|
2425
2454
|
}
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2455
|
+
const doGenerate = async () => {
|
|
2456
|
+
return this.withAbortCheck(
|
|
2457
|
+
() => agent.generate(prompt, {
|
|
2458
|
+
modelSettings: {
|
|
2459
|
+
...this.observationConfig.modelSettings
|
|
2460
|
+
},
|
|
2461
|
+
providerOptions: this.observationConfig.providerOptions,
|
|
2462
|
+
...abortSignal ? { abortSignal } : {},
|
|
2463
|
+
...requestContext ? { requestContext } : {}
|
|
2464
|
+
}),
|
|
2465
|
+
abortSignal
|
|
2466
|
+
);
|
|
2467
|
+
};
|
|
2468
|
+
let result = await doGenerate();
|
|
2469
|
+
let parsed = parseMultiThreadObserverOutput(result.text);
|
|
2470
|
+
if (parsed.degenerate) {
|
|
2471
|
+
omDebug(`[OM:callMultiThreadObserver] degenerate repetition detected, retrying once`);
|
|
2472
|
+
result = await doGenerate();
|
|
2473
|
+
parsed = parseMultiThreadObserverOutput(result.text);
|
|
2474
|
+
if (parsed.degenerate) {
|
|
2475
|
+
omDebug(`[OM:callMultiThreadObserver] degenerate repetition on retry, failing`);
|
|
2476
|
+
throw new Error("Multi-thread observer produced degenerate output after retry");
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2438
2479
|
const results = /* @__PURE__ */ new Map();
|
|
2439
2480
|
for (const [threadId, threadResult] of parsed.threads) {
|
|
2440
2481
|
results.set(threadId, {
|
|
@@ -2521,11 +2562,22 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
2521
2562
|
totalUsage.totalTokens += usage.totalTokens ?? 0;
|
|
2522
2563
|
}
|
|
2523
2564
|
parsed = parseReflectorOutput(result.text);
|
|
2524
|
-
|
|
2565
|
+
if (parsed.degenerate) {
|
|
2566
|
+
omDebug(
|
|
2567
|
+
`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure`
|
|
2568
|
+
);
|
|
2569
|
+
reflectedTokens = originalTokens;
|
|
2570
|
+
} else {
|
|
2571
|
+
reflectedTokens = this.tokenCounter.countObservations(parsed.observations);
|
|
2572
|
+
}
|
|
2525
2573
|
omDebug(
|
|
2526
|
-
`[OM:callReflector] attempt #${attemptNumber} parsed: reflectedTokens=${reflectedTokens}, targetThreshold=${targetThreshold}, compressionValid=${validateCompression(reflectedTokens, targetThreshold)}, parsedObsLen=${parsed.observations?.length}`
|
|
2574
|
+
`[OM:callReflector] attempt #${attemptNumber} parsed: reflectedTokens=${reflectedTokens}, targetThreshold=${targetThreshold}, compressionValid=${validateCompression(reflectedTokens, targetThreshold)}, parsedObsLen=${parsed.observations?.length}, degenerate=${parsed.degenerate ?? false}`
|
|
2527
2575
|
);
|
|
2528
|
-
if (validateCompression(reflectedTokens, targetThreshold) || currentLevel >= maxLevel) {
|
|
2576
|
+
if (!parsed.degenerate && (validateCompression(reflectedTokens, targetThreshold) || currentLevel >= maxLevel)) {
|
|
2577
|
+
break;
|
|
2578
|
+
}
|
|
2579
|
+
if (parsed.degenerate && currentLevel >= maxLevel) {
|
|
2580
|
+
omDebug(`[OM:callReflector] degenerate output persists at maxLevel=${maxLevel}, breaking`);
|
|
2529
2581
|
break;
|
|
2530
2582
|
}
|
|
2531
2583
|
if (streamContext?.writer) {
|
|
@@ -2833,6 +2885,20 @@ ${suggestedResponse}
|
|
|
2833
2885
|
omDebug(
|
|
2834
2886
|
`[OM:threshold] activation succeeded, obsTokens=${updatedRecord.observationTokenCount}, activeObsLen=${updatedRecord.activeObservations?.length}`
|
|
2835
2887
|
);
|
|
2888
|
+
if (activationResult.suggestedContinuation || activationResult.currentTask) {
|
|
2889
|
+
const thread = await this.storage.getThreadById({ threadId });
|
|
2890
|
+
if (thread) {
|
|
2891
|
+
const newMetadata = setThreadOMMetadata(thread.metadata, {
|
|
2892
|
+
suggestedResponse: activationResult.suggestedContinuation,
|
|
2893
|
+
currentTask: activationResult.currentTask
|
|
2894
|
+
});
|
|
2895
|
+
await this.storage.updateThread({
|
|
2896
|
+
id: threadId,
|
|
2897
|
+
title: thread.title ?? "",
|
|
2898
|
+
metadata: newMetadata
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2836
2902
|
await this.maybeAsyncReflect(
|
|
2837
2903
|
updatedRecord,
|
|
2838
2904
|
updatedRecord.observationTokenCount ?? 0,
|
|
@@ -3171,6 +3237,20 @@ ${suggestedResponse}
|
|
|
3171
3237
|
_ObservationalMemory.lastBufferedBoundary.set(bufKey, 0);
|
|
3172
3238
|
this.storage.setBufferingObservationFlag(record.id, false, 0).catch(() => {
|
|
3173
3239
|
});
|
|
3240
|
+
if (activationResult.suggestedContinuation || activationResult.currentTask) {
|
|
3241
|
+
const thread = await this.storage.getThreadById({ threadId });
|
|
3242
|
+
if (thread) {
|
|
3243
|
+
const newMetadata = setThreadOMMetadata(thread.metadata, {
|
|
3244
|
+
suggestedResponse: activationResult.suggestedContinuation,
|
|
3245
|
+
currentTask: activationResult.currentTask
|
|
3246
|
+
});
|
|
3247
|
+
await this.storage.updateThread({
|
|
3248
|
+
id: threadId,
|
|
3249
|
+
title: thread.title ?? "",
|
|
3250
|
+
metadata: newMetadata
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3174
3254
|
await this.maybeReflect({
|
|
3175
3255
|
record,
|
|
3176
3256
|
observationTokens: record.observationTokenCount ?? 0,
|
|
@@ -3229,7 +3309,7 @@ ${suggestedResponse}
|
|
|
3229
3309
|
state.sealedIds = sealedIds;
|
|
3230
3310
|
const lockKey = this.getLockKey(threadId, resourceId);
|
|
3231
3311
|
if (this.isAsyncObservationEnabled() && totalPendingTokens < threshold) {
|
|
3232
|
-
const shouldTrigger = this.shouldTriggerAsyncObservation(unbufferedPendingTokens, lockKey, record);
|
|
3312
|
+
const shouldTrigger = this.shouldTriggerAsyncObservation(unbufferedPendingTokens, lockKey, record, threshold);
|
|
3233
3313
|
omDebug(
|
|
3234
3314
|
`[OM:async-obs] belowThreshold: pending=${totalPendingTokens}, unbuffered=${unbufferedPendingTokens}, threshold=${threshold}, shouldTrigger=${shouldTrigger}, isBufferingObs=${record.isBufferingObservation}, lastBufferedAt=${record.lastBufferedAtTokens}`
|
|
3235
3315
|
);
|
|
@@ -3245,7 +3325,7 @@ ${suggestedResponse}
|
|
|
3245
3325
|
);
|
|
3246
3326
|
}
|
|
3247
3327
|
} else if (this.isAsyncObservationEnabled()) {
|
|
3248
|
-
const shouldTrigger = this.shouldTriggerAsyncObservation(unbufferedPendingTokens, lockKey, record);
|
|
3328
|
+
const shouldTrigger = this.shouldTriggerAsyncObservation(unbufferedPendingTokens, lockKey, record, threshold);
|
|
3249
3329
|
omDebug(
|
|
3250
3330
|
`[OM:async-obs] atOrAboveThreshold: pending=${totalPendingTokens}, unbuffered=${unbufferedPendingTokens}, threshold=${threshold}, step=${stepNumber}, shouldTrigger=${shouldTrigger}`
|
|
3251
3331
|
);
|
|
@@ -3927,8 +4007,12 @@ ${result.observations}` : result.observations;
|
|
|
3927
4007
|
messagesToBuffer,
|
|
3928
4008
|
void 0,
|
|
3929
4009
|
// No abort signal for background ops
|
|
3930
|
-
{
|
|
4010
|
+
{ requestContext }
|
|
3931
4011
|
);
|
|
4012
|
+
if (!result.observations) {
|
|
4013
|
+
omDebug(`[OM:doAsyncBufferedObservation] empty observations returned, skipping buffer storage`);
|
|
4014
|
+
return;
|
|
4015
|
+
}
|
|
3932
4016
|
let newObservations;
|
|
3933
4017
|
if (this.scope === "resource") {
|
|
3934
4018
|
newObservations = await this.wrapWithThreadTag(threadId, result.observations);
|
|
@@ -3948,7 +4032,9 @@ ${result.observations}` : result.observations;
|
|
|
3948
4032
|
tokenCount: newTokenCount,
|
|
3949
4033
|
messageIds: newMessageIds,
|
|
3950
4034
|
messageTokens,
|
|
3951
|
-
lastObservedAt
|
|
4035
|
+
lastObservedAt,
|
|
4036
|
+
suggestedContinuation: result.suggestedContinuation,
|
|
4037
|
+
currentTask: result.currentTask
|
|
3952
4038
|
},
|
|
3953
4039
|
lastBufferedAtTime: lastObservedAt
|
|
3954
4040
|
});
|
|
@@ -4027,24 +4113,28 @@ ${bufferedObservations}`;
|
|
|
4027
4113
|
return { success: false };
|
|
4028
4114
|
}
|
|
4029
4115
|
const messageTokensThreshold = this.getMaxThreshold(this.observationConfig.messageTokens);
|
|
4116
|
+
let effectivePendingTokens = currentPendingTokens;
|
|
4030
4117
|
if (messageList) {
|
|
4031
|
-
|
|
4032
|
-
if (
|
|
4118
|
+
effectivePendingTokens = this.tokenCounter.countMessages(messageList.get.all.db());
|
|
4119
|
+
if (effectivePendingTokens < messageTokensThreshold) {
|
|
4033
4120
|
omDebug(
|
|
4034
|
-
`[OM:tryActivate] skipping activation: freshPendingTokens=${
|
|
4121
|
+
`[OM:tryActivate] skipping activation: freshPendingTokens=${effectivePendingTokens} < threshold=${messageTokensThreshold}`
|
|
4035
4122
|
);
|
|
4036
4123
|
return { success: false };
|
|
4037
4124
|
}
|
|
4038
4125
|
}
|
|
4039
|
-
const
|
|
4126
|
+
const bufferActivation = this.observationConfig.bufferActivation ?? 0.7;
|
|
4127
|
+
const activationRatio = this.resolveActivationRatio(bufferActivation, messageTokensThreshold);
|
|
4128
|
+
const forceMaxActivation = !!(this.observationConfig.blockAfter && effectivePendingTokens >= this.observationConfig.blockAfter);
|
|
4040
4129
|
omDebug(
|
|
4041
|
-
`[OM:tryActivate] swapping: freshChunks=${freshChunks.length}, activationRatio=${activationRatio}, totalChunkTokens=${freshChunks.reduce((s, c) => s + (c.tokenCount ?? 0), 0)}`
|
|
4130
|
+
`[OM:tryActivate] swapping: freshChunks=${freshChunks.length}, bufferActivation=${bufferActivation}, activationRatio=${activationRatio}, forceMax=${forceMaxActivation}, totalChunkTokens=${freshChunks.reduce((s, c) => s + (c.tokenCount ?? 0), 0)}`
|
|
4042
4131
|
);
|
|
4043
4132
|
const activationResult = await this.storage.swapBufferedToActive({
|
|
4044
4133
|
id: freshRecord.id,
|
|
4045
4134
|
activationRatio,
|
|
4046
4135
|
messageTokensThreshold,
|
|
4047
|
-
currentPendingTokens
|
|
4136
|
+
currentPendingTokens: effectivePendingTokens,
|
|
4137
|
+
forceMaxActivation
|
|
4048
4138
|
});
|
|
4049
4139
|
omDebug(
|
|
4050
4140
|
`[OM:tryActivate] swapResult: chunksActivated=${activationResult.chunksActivated}, tokensActivated=${activationResult.messageTokensActivated}, obsTokensActivated=${activationResult.observationTokensActivated}, activatedCycleIds=${activationResult.activatedCycleIds.join(",")}`
|
|
@@ -4083,7 +4173,9 @@ ${bufferedObservations}`;
|
|
|
4083
4173
|
success: true,
|
|
4084
4174
|
updatedRecord: updatedRecord ?? void 0,
|
|
4085
4175
|
messageTokensActivated: activationResult.messageTokensActivated,
|
|
4086
|
-
activatedMessageIds: activationResult.activatedMessageIds
|
|
4176
|
+
activatedMessageIds: activationResult.activatedMessageIds,
|
|
4177
|
+
suggestedContinuation: activationResult.suggestedContinuation,
|
|
4178
|
+
currentTask: activationResult.currentTask
|
|
4087
4179
|
};
|
|
4088
4180
|
}
|
|
4089
4181
|
/**
|
|
@@ -4956,5 +5048,5 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
4956
5048
|
};
|
|
4957
5049
|
|
|
4958
5050
|
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 };
|
|
4959
|
-
//# sourceMappingURL=chunk-
|
|
4960
|
-
//# sourceMappingURL=chunk-
|
|
5051
|
+
//# sourceMappingURL=chunk-DF7NDDSM.js.map
|
|
5052
|
+
//# sourceMappingURL=chunk-DF7NDDSM.js.map
|