@mastra/memory 1.37.0 → 1.37.1-alpha.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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-multi-user-threads.md +1 -1
- package/dist/docs/references/docs-memory-observational-memory.md +17 -15
- package/dist/docs/references/reference-memory-listThreads.md +1 -1
- package/dist/docs/references/reference-memory-observational-memory.md +3 -3
- package/dist/docs/references/reference-processors-token-limiter-processor.md +2 -2
- package/dist/docs/references/reference-vectors-libsql.md +1 -1
- package/dist/docs/references/reference-vectors-mongodb.md +2 -1
- package/dist/docs/references/reference-vectors-pg.md +16 -11
- package/dist/docs/references/reference-vectors-upstash.md +10 -4
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/processors/index.cjs +1 -1
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/buffering-coordinator.d.ts +25 -0
- package/dist/processors/observational-memory/buffering-coordinator.d.ts.map +1 -1
- package/dist/processors/observational-memory/commit-queue.d.ts +46 -0
- package/dist/processors/observational-memory/commit-queue.d.ts.map +1 -0
- package/dist/processors/observational-memory/extraction-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts +7 -4
- package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/base.d.ts +93 -6
- package/dist/processors/observational-memory/observation-strategies/base.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/resource-scoped.d.ts +20 -3
- package/dist/processors/observational-memory/observation-strategies/resource-scoped.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/sync.d.ts +10 -3
- package/dist/processors/observational-memory/observation-strategies/sync.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/types.d.ts +30 -0
- package/dist/processors/observational-memory/observation-strategies/types.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/step.d.ts +1 -1
- package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +30 -6
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts +8 -0
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-runner.d.ts +1 -1
- package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/processor.d.ts.map +1 -1
- package/dist/processors/observational-memory/record-lineage.d.ts +21 -0
- package/dist/processors/observational-memory/record-lineage.d.ts.map +1 -0
- package/dist/processors/observational-memory/reflector-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/reflector-runner.d.ts +9 -0
- package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +10 -8
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/dist/processors/observational-memory/working-memory-extractor.d.ts.map +1 -1
- package/dist/{src-D-jA6eRb.cjs → src-BcRCGtUY.cjs} +1062 -302
- package/dist/src-BcRCGtUY.cjs.map +1 -0
- package/dist/{src-B9qhQmNI.js → src-DjDnBIS0.js} +1063 -303
- package/dist/src-DjDnBIS0.js.map +1 -0
- package/package.json +3 -3
- package/dist/src-B9qhQmNI.js.map +0 -1
- package/dist/src-D-jA6eRb.cjs.map +0 -1
|
@@ -19,7 +19,7 @@ import { join } from "path";
|
|
|
19
19
|
import { createTool } from "@mastra/core/tools";
|
|
20
20
|
import { MASTRA_THREAD_ID_KEY, RequestContext } from "@mastra/core/request-context";
|
|
21
21
|
import { modelSupportsAttachments, modelSupportsTemperature, resolveModelConfig } from "@mastra/core/llm";
|
|
22
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
22
|
+
import { AsyncLocalStorage, AsyncResource } from "async_hooks";
|
|
23
23
|
import { createHash } from "crypto";
|
|
24
24
|
import probeImageSizeSync from "probe-image-size/sync.js";
|
|
25
25
|
import { isStandardSchemaWithJSON as isStandardSchemaWithJSON$1, toStandardSchema as toStandardSchema$1 } from "@mastra/core/schema";
|
|
@@ -19230,8 +19230,12 @@ function formatOmError(error) {
|
|
|
19230
19230
|
function isAbortError(error, abortSignal) {
|
|
19231
19231
|
return abortSignal?.aborted === true || hasAbortInChain(error);
|
|
19232
19232
|
}
|
|
19233
|
+
function hasEmptyObjectValue(object, extractor) {
|
|
19234
|
+
const value = object[extractor.slug];
|
|
19235
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
19236
|
+
}
|
|
19233
19237
|
function shouldRetryEmptyStructuredObject(object, extractors) {
|
|
19234
|
-
return Object.keys(object).length === 0
|
|
19238
|
+
return extractors.some((extractor) => extractor.retryStructuredExtractionOnEmptyObject && (Object.keys(object).length === 0 || hasEmptyObjectValue(object, extractor)));
|
|
19235
19239
|
}
|
|
19236
19240
|
async function extractStructuredValues(opts) {
|
|
19237
19241
|
const structuredExtractors = (opts.extractors ?? []).filter((extractor) => extractor.mode === "structured");
|
|
@@ -19305,17 +19309,28 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
|
|
|
19305
19309
|
}
|
|
19306
19310
|
}
|
|
19307
19311
|
if (retryEmptyObject) try {
|
|
19308
|
-
|
|
19312
|
+
const fallbackJsonPromptInjection = coreFeatures.has("json-prompt-injection:inline") ? "inline" : true;
|
|
19313
|
+
object = {
|
|
19314
|
+
...object,
|
|
19315
|
+
...await streamWithStructuredOutput(fallbackJsonPromptInjection)
|
|
19316
|
+
};
|
|
19309
19317
|
} catch (fallbackError) {
|
|
19310
19318
|
if (isAbortError(fallbackError, opts.abortSignal)) throw fallbackError;
|
|
19311
19319
|
const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
19312
|
-
return {
|
|
19320
|
+
if (Object.keys(object).length === 0) return {
|
|
19313
19321
|
values,
|
|
19314
19322
|
failures: structuredExtractors.map((extractor) => ({
|
|
19315
19323
|
slug: extractor.slug,
|
|
19316
19324
|
error: message
|
|
19317
19325
|
}))
|
|
19318
19326
|
};
|
|
19327
|
+
for (const extractor of structuredExtractors) if (extractor.retryStructuredExtractionOnEmptyObject && hasEmptyObjectValue(object, extractor)) {
|
|
19328
|
+
failures.push({
|
|
19329
|
+
slug: extractor.slug,
|
|
19330
|
+
error: message
|
|
19331
|
+
});
|
|
19332
|
+
delete object[extractor.slug];
|
|
19333
|
+
}
|
|
19319
19334
|
}
|
|
19320
19335
|
for (const extractor of structuredExtractors) {
|
|
19321
19336
|
const value = object[extractor.slug];
|
|
@@ -20557,7 +20572,7 @@ Group related observations (like tool sequences) by indenting:
|
|
|
20557
20572
|
* -> applied fix, tests now pass
|
|
20558
20573
|
* ✅ Tests passing, auth issue resolved
|
|
20559
20574
|
|
|
20560
|
-
Group observations by date,
|
|
20575
|
+
Group observations by date. Within a date, list observations in the order the events happened (not by importance), each with 24-hour time.
|
|
20561
20576
|
|
|
20562
20577
|
<observations>
|
|
20563
20578
|
Date: Dec 4, 2025
|
|
@@ -20930,8 +20945,8 @@ function extractToolResultAttachments(result, counter, attachmentFilter) {
|
|
|
20930
20945
|
attachments
|
|
20931
20946
|
};
|
|
20932
20947
|
}
|
|
20933
|
-
function formatObserverPartLine(title, body, time, previousTime) {
|
|
20934
|
-
const timeLabel = time && time !== previousTime ? `(${time})` : "";
|
|
20948
|
+
function formatObserverPartLine(title, body, time, previousTime, alwaysShowTime) {
|
|
20949
|
+
const timeLabel = time && (alwaysShowTime || time !== previousTime) ? `(${time})` : "";
|
|
20935
20950
|
if (!title) return timeLabel ? `${timeLabel}: ${body}` : body;
|
|
20936
20951
|
return `${title}${timeLabel ? ` ${timeLabel}` : ""}: ${body}`;
|
|
20937
20952
|
}
|
|
@@ -20956,7 +20971,7 @@ function formatObserverLines(lines, context = {}) {
|
|
|
20956
20971
|
previousDate = line.date;
|
|
20957
20972
|
previousTime = void 0;
|
|
20958
20973
|
}
|
|
20959
|
-
output.push(formatObserverPartLine(line.title, line.body, line.time, previousTime));
|
|
20974
|
+
output.push(formatObserverPartLine(line.title, line.body, line.time, previousTime, line.alwaysShowTime));
|
|
20960
20975
|
previousTime = line.time || previousTime;
|
|
20961
20976
|
}
|
|
20962
20977
|
return {
|
|
@@ -21057,14 +21072,15 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
|
|
|
21057
21072
|
const unavailableUrls = getUnavailableAttachmentUrls(msg);
|
|
21058
21073
|
let lines = [];
|
|
21059
21074
|
const temporalGapText = isTemporalGapMarker(msg) ? getTemporalGapMarkerText(msg) : void 0;
|
|
21060
|
-
const pushLine = (title, body, createdAt) => {
|
|
21075
|
+
const pushLine = (title, body, createdAt, opts) => {
|
|
21061
21076
|
if (!body) return;
|
|
21062
21077
|
const normalizedCreatedAt = normalizeObserverCreatedAt(createdAt) ?? messageCreatedAt;
|
|
21063
21078
|
lines.push({
|
|
21064
21079
|
date: formatObserverDate(normalizedCreatedAt, timeZone),
|
|
21065
21080
|
time: formatObserverTime(normalizedCreatedAt, timeZone),
|
|
21066
21081
|
title,
|
|
21067
|
-
body
|
|
21082
|
+
body,
|
|
21083
|
+
alwaysShowTime: opts?.tool === true
|
|
21068
21084
|
});
|
|
21069
21085
|
};
|
|
21070
21086
|
if (temporalGapText) pushLine("", temporalGapText, messageCreatedAt);
|
|
@@ -21081,7 +21097,7 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
|
|
|
21081
21097
|
const exchange = toolFormatting.exchanges.get(inv.toolCallId);
|
|
21082
21098
|
if (exchange && occurrence === exchange.firstOccurrence && exchange.signature && !toolFormatting.emittedCalls.has(inv.toolCallId)) {
|
|
21083
21099
|
toolFormatting.emittedCalls.add(inv.toolCallId);
|
|
21084
|
-
pushLine(`Tool Call ${exchange.signature.toolName}`, formatObserverToolArguments(exchange.signature.args, maxLen), partCreatedAt);
|
|
21100
|
+
pushLine(`Tool Call ${exchange.signature.toolName}`, formatObserverToolArguments(exchange.signature.args, maxLen), partCreatedAt, { tool: true });
|
|
21085
21101
|
}
|
|
21086
21102
|
if (inv.state === "result") {
|
|
21087
21103
|
const { value: resultForObserver } = resolveToolResultValue(part, inv.result);
|
|
@@ -21091,17 +21107,17 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
|
|
|
21091
21107
|
toolFormatting.emittedOutcomes.add(inv.toolCallId);
|
|
21092
21108
|
const hasResult = resultWithoutAttachments !== void 0 && (typeof resultWithoutAttachments !== "string" || resultWithoutAttachments.trim().length > 0);
|
|
21093
21109
|
const body = inv.isError ? hasResult ? formatToolResultForObserver(resultWithoutAttachments, { maxTokens: maxToolResultTokens }) : inv.errorText?.trim() ? inv.errorText : "Tool execution failed" : hasResult ? formatToolResultForObserver(resultWithoutAttachments, { maxTokens: maxToolResultTokens }) : "[empty result]";
|
|
21094
|
-
pushLine(`${inv.isError ? "Tool Error" : "Tool Result"} ${inv.toolName}`, maybeTruncate(body, maxLen), partCreatedAt);
|
|
21110
|
+
pushLine(`${inv.isError ? "Tool Error" : "Tool Result"} ${inv.toolName}`, maybeTruncate(body, maxLen), partCreatedAt, { tool: true });
|
|
21095
21111
|
}
|
|
21096
21112
|
return;
|
|
21097
21113
|
}
|
|
21098
21114
|
if (exchange?.terminal?.occurrence === occurrence && !toolFormatting.emittedOutcomes.has(inv.toolCallId)) {
|
|
21099
21115
|
toolFormatting.emittedOutcomes.add(inv.toolCallId);
|
|
21100
|
-
if (inv.state === "output-error") pushLine(`Tool Error ${inv.toolName}`, maybeTruncate(inv.errorText?.trim() ? inv.errorText : "Tool execution failed", maxLen), partCreatedAt);
|
|
21101
|
-
else if (inv.state === "output-denied") pushLine(`Tool Denied ${inv.toolName}`, maybeTruncate(inv.approval?.reason?.trim() ? inv.approval.reason : "Tool call was not approved by the user", maxLen), partCreatedAt);
|
|
21116
|
+
if (inv.state === "output-error") pushLine(`Tool Error ${inv.toolName}`, maybeTruncate(inv.errorText?.trim() ? inv.errorText : "Tool execution failed", maxLen), partCreatedAt, { tool: true });
|
|
21117
|
+
else if (inv.state === "output-denied") pushLine(`Tool Denied ${inv.toolName}`, maybeTruncate(inv.approval?.reason?.trim() ? inv.approval.reason : "Tool call was not approved by the user", maxLen), partCreatedAt, { tool: true });
|
|
21102
21118
|
return;
|
|
21103
21119
|
}
|
|
21104
|
-
if (!exchange) pushLine(`Tool Call ${inv.toolName}`, formatObserverToolArguments(inv.args, maxLen), partCreatedAt);
|
|
21120
|
+
if (!exchange) pushLine(`Tool Call ${inv.toolName}`, formatObserverToolArguments(inv.args, maxLen), partCreatedAt, { tool: true });
|
|
21105
21121
|
return;
|
|
21106
21122
|
}
|
|
21107
21123
|
const partType = part.type;
|
|
@@ -21256,13 +21272,17 @@ function surroundObserverHistory(history, contextPrompt, taskInstructions) {
|
|
|
21256
21272
|
function buildMultiThreadObserverTaskInstructions(includeThreadTitle) {
|
|
21257
21273
|
return `## Your Task\n\nExtract new observations from each thread. Output your observations grouped by thread using <thread id="..."> tags inside your <observations> block. Each thread block should contain that thread's observations, current-task, suggested-response${includeThreadTitle ? ", and thread-title" : ""}, in the format specified in your instructions.`;
|
|
21258
21274
|
}
|
|
21275
|
+
function buildTruncationNotice(wasTruncated) {
|
|
21276
|
+
if (!wasTruncated) return "";
|
|
21277
|
+
return "Previous observations were truncated for context budget reasons.\nThe main agent still has full memory context outside this observer window.\n\n";
|
|
21278
|
+
}
|
|
21259
21279
|
function buildMultiThreadObserverContextPrompt(existingObservations, threadOrder, priorMetadataByThread, wasTruncated, includeThreadTitle, extractors = []) {
|
|
21260
21280
|
let prompt = "";
|
|
21261
21281
|
if (existingObservations) {
|
|
21262
21282
|
prompt += `## Previous Observations\n\n${existingObservations}\n\n---\n\n`;
|
|
21263
21283
|
prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
|
|
21264
21284
|
}
|
|
21265
|
-
|
|
21285
|
+
prompt += buildTruncationNotice(wasTruncated);
|
|
21266
21286
|
const threadMetadataLines = threadOrder?.map((threadId) => {
|
|
21267
21287
|
const metadata = priorMetadataByThread?.get(threadId);
|
|
21268
21288
|
const extractorPriorLines = buildExtractorPriorLines(extractors, metadata?.extracted);
|
|
@@ -21276,10 +21296,6 @@ function buildMultiThreadObserverContextPrompt(existingObservations, threadOrder
|
|
|
21276
21296
|
}).filter(Boolean).join("\n");
|
|
21277
21297
|
if (threadMetadataLines) {
|
|
21278
21298
|
prompt += `## Prior Thread Metadata\n\n${threadMetadataLines}\n\n`;
|
|
21279
|
-
if (hasTruncatedObservations) {
|
|
21280
|
-
prompt += `Previous observations were truncated for context budget reasons.\n`;
|
|
21281
|
-
prompt += `The main agent still has full memory context outside this observer window.\n`;
|
|
21282
|
-
}
|
|
21283
21299
|
prompt += `Use each thread's prior current-task, suggested-response${includeThreadTitle ? ", and thread-title" : ""} as continuity hints, then update them based on that thread's new messages.\n\n---\n\n`;
|
|
21284
21300
|
}
|
|
21285
21301
|
return prompt;
|
|
@@ -21322,7 +21338,7 @@ function parseMultiThreadObserverOutput(output, extractors = []) {
|
|
|
21322
21338
|
threadTitle = threadTitleMatch[1].trim();
|
|
21323
21339
|
observations = observations.replace(/<thread-title>[\s\S]*?<\/thread-title>/i, "");
|
|
21324
21340
|
}
|
|
21325
|
-
observations = sanitizeObservationLines(observations.trim());
|
|
21341
|
+
observations = sortObservationsByTime(sanitizeObservationLines(observations.trim()));
|
|
21326
21342
|
threads.set(threadId, {
|
|
21327
21343
|
observations,
|
|
21328
21344
|
currentTask: currentTask || getStringExtractedValue$1(parsedExtractedValues.values, "current-task"),
|
|
@@ -21362,7 +21378,7 @@ function buildObserverContextPrompt(existingObservations, options) {
|
|
|
21362
21378
|
prompt += `## Previous Observations\n\n${existingObservations}\n\n---\n\n`;
|
|
21363
21379
|
prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
|
|
21364
21380
|
}
|
|
21365
|
-
|
|
21381
|
+
prompt += buildTruncationNotice(options?.wasTruncated);
|
|
21366
21382
|
const priorMetadataLines = [];
|
|
21367
21383
|
if (options?.priorCurrentTask) priorMetadataLines.push(`- prior current-task: ${options.priorCurrentTask}`);
|
|
21368
21384
|
if (options?.priorSuggestedResponse) priorMetadataLines.push(`- prior suggested-response: ${options.priorSuggestedResponse}`);
|
|
@@ -21370,10 +21386,6 @@ function buildObserverContextPrompt(existingObservations, options) {
|
|
|
21370
21386
|
priorMetadataLines.push(...buildExtractorPriorLines(options?.extractors ?? [], options?.priorExtractedValues));
|
|
21371
21387
|
if (priorMetadataLines.length > 0) {
|
|
21372
21388
|
prompt += `## Prior Thread Metadata\n\n${priorMetadataLines.join("\n")}\n\n`;
|
|
21373
|
-
if (hasTruncatedObservations) {
|
|
21374
|
-
prompt += `Previous observations were truncated for context budget reasons.\n`;
|
|
21375
|
-
prompt += `The main agent still has full memory context outside this observer window.\n`;
|
|
21376
|
-
}
|
|
21377
21389
|
const titleHint = options?.includeThreadTitle ? ", and thread-title" : "";
|
|
21378
21390
|
prompt += `Use the prior current-task, suggested-response${titleHint} as continuity hints, then update them based on the new messages.\n\n---\n\n`;
|
|
21379
21391
|
}
|
|
@@ -21404,7 +21416,7 @@ function parseObserverOutput(output, extractors = []) {
|
|
|
21404
21416
|
const parsedExtractedValues = parseExtractedValues(output, inlineExtractors);
|
|
21405
21417
|
const parsed = parseMemorySectionXml(stripExtractorSections(output, inlineExtractors));
|
|
21406
21418
|
return {
|
|
21407
|
-
observations: sanitizeObservationLines(parsed.observations || ""),
|
|
21419
|
+
observations: sortObservationsByTime(sanitizeObservationLines(parsed.observations || "")),
|
|
21408
21420
|
currentTask: parsed.currentTask || getStringExtractedValue$1(parsedExtractedValues.values, "current-task"),
|
|
21409
21421
|
suggestedContinuation: parsed.suggestedResponse || getStringExtractedValue$1(parsedExtractedValues.values, "suggested-response"),
|
|
21410
21422
|
threadTitle: parsed.threadTitle || getStringExtractedValue$1(parsedExtractedValues.values, "thread-title"),
|
|
@@ -21498,6 +21510,68 @@ function sanitizeObservationLines(observations) {
|
|
|
21498
21510
|
}
|
|
21499
21511
|
return changed ? lines.join("\n") : observations;
|
|
21500
21512
|
}
|
|
21513
|
+
const OBSERVATION_TIME = /^[*-]\s+(?:\S+\s+)?\((\d{1,2}):(\d{2})\s*([AaPp])?\.?(?:[Mm]\.?)?\)/;
|
|
21514
|
+
/** Minutes since midnight for each item, or undefined when the group's times can't be ordered safely. */
|
|
21515
|
+
function observationMinutes(items) {
|
|
21516
|
+
const times = items.map((item) => OBSERVATION_TIME.exec(item[0]));
|
|
21517
|
+
if (times.some((t) => !t)) return void 0;
|
|
21518
|
+
const matches = times;
|
|
21519
|
+
const marked = matches.filter((m) => m[3]).length;
|
|
21520
|
+
if (marked > 0 && marked < matches.length) return void 0;
|
|
21521
|
+
if (marked === matches.length) return matches.map((m) => Number(m[1]) % 12 * 60 + (/p/i.test(m[3]) ? 720 : 0) + Number(m[2]));
|
|
21522
|
+
const hours = matches.map((m) => m[1]);
|
|
21523
|
+
const twelveHour = hours.some((h) => /^[1-9]$/.test(h));
|
|
21524
|
+
const twentyFourHour = hours.some((h) => /^(0\d|1[3-9]|2\d)$/.test(h));
|
|
21525
|
+
if (twelveHour && (twentyFourHour || new Set(hours).size > 1)) return void 0;
|
|
21526
|
+
return matches.map((m) => Number(m[1]) * 60 + Number(m[2]));
|
|
21527
|
+
}
|
|
21528
|
+
/**
|
|
21529
|
+
* Put the top-level observations of each "Date:" group in time order, keeping indented
|
|
21530
|
+
* sub-items with their parent. The Observer writes a time on every observation but tends
|
|
21531
|
+
* to list a user's message ahead of the earlier events it follows, which can make a
|
|
21532
|
+
* superseded state read as the latest one. Groups are left as written when any
|
|
21533
|
+
* observation has no time or the times are ambiguous.
|
|
21534
|
+
*/
|
|
21535
|
+
function sortObservationsByTime(observations) {
|
|
21536
|
+
if (!observations) return observations;
|
|
21537
|
+
const output = [];
|
|
21538
|
+
let group;
|
|
21539
|
+
const flush = () => {
|
|
21540
|
+
if (!group) return;
|
|
21541
|
+
const minutes = group.sortable && group.items.length > 1 ? observationMinutes(group.items) : void 0;
|
|
21542
|
+
const items = minutes ? group.items.map((item, i) => ({
|
|
21543
|
+
item,
|
|
21544
|
+
minute: minutes[i]
|
|
21545
|
+
})).sort((a, b) => a.minute - b.minute).map((x) => x.item) : group.items;
|
|
21546
|
+
output.push(...group.header, ...items.flat(), ...group.trailing);
|
|
21547
|
+
group = void 0;
|
|
21548
|
+
};
|
|
21549
|
+
for (const line of observations.split("\n")) if (/^\s*Date:\s/.test(line)) {
|
|
21550
|
+
flush();
|
|
21551
|
+
group = {
|
|
21552
|
+
header: [line],
|
|
21553
|
+
items: [],
|
|
21554
|
+
trailing: [],
|
|
21555
|
+
sortable: true
|
|
21556
|
+
};
|
|
21557
|
+
} else if (!group) output.push(line);
|
|
21558
|
+
else if (/^[*-]\s/.test(line)) {
|
|
21559
|
+
group.items.at(-1)?.push(...group.trailing);
|
|
21560
|
+
group.trailing = [];
|
|
21561
|
+
group.items.push([line]);
|
|
21562
|
+
} else if (group.items.length === 0) group.header.push(line);
|
|
21563
|
+
else if (line.trim() === "") group.trailing.push(line);
|
|
21564
|
+
else if (/^\s/.test(line)) {
|
|
21565
|
+
group.items.at(-1).push(...group.trailing, line);
|
|
21566
|
+
group.trailing = [];
|
|
21567
|
+
} else {
|
|
21568
|
+
group.sortable = false;
|
|
21569
|
+
group.items.at(-1).push(...group.trailing, line);
|
|
21570
|
+
group.trailing = [];
|
|
21571
|
+
}
|
|
21572
|
+
flush();
|
|
21573
|
+
return output.join("\n");
|
|
21574
|
+
}
|
|
21501
21575
|
function analyzeDegenerateRepetition(text) {
|
|
21502
21576
|
const lines = collapseBoundedLineRuns(text.split("\n"));
|
|
21503
21577
|
const windowText = lines.filter((line) => line.trim().length >= MIN_DUPLICATE_LINE_CHARS && line.length <= MAX_OBSERVATION_LINE_CHARS).join("\n");
|
|
@@ -22040,7 +22114,7 @@ var ObserverRunner = class {
|
|
|
22040
22114
|
/**
|
|
22041
22115
|
* Call the Observer agent for multiple threads in a single batched request.
|
|
22042
22116
|
*/
|
|
22043
|
-
async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext, timeZone, mainAgent, currentModel) {
|
|
22117
|
+
async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext, timeZone, mainAgent, currentModel, wasTruncated) {
|
|
22044
22118
|
const contextFor = (threadId) => ({
|
|
22045
22119
|
threadId,
|
|
22046
22120
|
...hookContext
|
|
@@ -22051,7 +22125,7 @@ var ObserverRunner = class {
|
|
|
22051
22125
|
if (filtered.length > 0) messagesByThread.set(threadId, filtered);
|
|
22052
22126
|
}
|
|
22053
22127
|
const threadOrder = allThreadOrder.filter((threadId) => messagesByThread.has(threadId));
|
|
22054
|
-
const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone, mainAgent, currentModel);
|
|
22128
|
+
const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone, mainAgent, currentModel, wasTruncated);
|
|
22055
22129
|
for (const threadId of allThreadOrder) {
|
|
22056
22130
|
const threadResult = output.results.get(threadId);
|
|
22057
22131
|
if (!threadResult) {
|
|
@@ -22062,7 +22136,7 @@ var ObserverRunner = class {
|
|
|
22062
22136
|
}
|
|
22063
22137
|
return output;
|
|
22064
22138
|
}
|
|
22065
|
-
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone, mainAgent, currentModel) {
|
|
22139
|
+
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone, mainAgent, currentModel, wasTruncated) {
|
|
22066
22140
|
if (threadOrder.length === 0) return { results: /* @__PURE__ */ new Map() };
|
|
22067
22141
|
const inputTokens = Array.from(messagesByThread.values()).reduce((total, messages) => total + this.tokenCounter.countMessages(messages), 0);
|
|
22068
22142
|
let resolvedModel;
|
|
@@ -22108,7 +22182,8 @@ var ObserverRunner = class {
|
|
|
22108
22182
|
priorThreadTitle: priorMetadataByThread?.get(threadId)?.threadTitle,
|
|
22109
22183
|
priorExtractedValues: priorMetadataByThread?.get(threadId)?.extracted,
|
|
22110
22184
|
model: resolvedModel.model,
|
|
22111
|
-
timeZone
|
|
22185
|
+
timeZone,
|
|
22186
|
+
wasTruncated
|
|
22112
22187
|
});
|
|
22113
22188
|
results.set(threadId, {
|
|
22114
22189
|
observations: threadResult.observations,
|
|
@@ -22134,7 +22209,7 @@ var ObserverRunner = class {
|
|
|
22134
22209
|
const agent = this.createAgent(resolvedModel.model, true, void 0, activeExtractors);
|
|
22135
22210
|
const internalRequestContext = withOmInternalThreadId(requestContext, agent.id);
|
|
22136
22211
|
const multiThreadAttachmentFilter = this.resolveAttachmentFilter(resolvedModel.model, requestContext);
|
|
22137
|
-
const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread,
|
|
22212
|
+
const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, wasTruncated, this.observationConfig.threadTitle, activeExtractors, {
|
|
22138
22213
|
attachmentFilter: multiThreadAttachmentFilter,
|
|
22139
22214
|
timeZone
|
|
22140
22215
|
})];
|
|
@@ -22148,6 +22223,7 @@ var ObserverRunner = class {
|
|
|
22148
22223
|
metadata: {
|
|
22149
22224
|
omThreadCount: threadOrder.length,
|
|
22150
22225
|
omPreviousObserverTokens: this.observationConfig.previousObserverTokens,
|
|
22226
|
+
omWasTruncated: wasTruncated ?? false,
|
|
22151
22227
|
omThreadTitleEnabled: this.observationConfig.threadTitle,
|
|
22152
22228
|
...resolvedModel.selectedThreshold !== void 0 ? { omSelectedThreshold: resolvedModel.selectedThreshold } : {},
|
|
22153
22229
|
...resolvedModel.routingStrategy ? { omRoutingStrategy: resolvedModel.routingStrategy } : {},
|
|
@@ -24080,6 +24156,9 @@ async function validateAgainstConfiguredSchema(schema, value) {
|
|
|
24080
24156
|
}).join("; ");
|
|
24081
24157
|
throw new Error(`Working memory update does not match the configured schema, so it was not saved: ${details}`);
|
|
24082
24158
|
}
|
|
24159
|
+
function isEmptyPlainObject(value) {
|
|
24160
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
24161
|
+
}
|
|
24083
24162
|
async function getWorkingMemoryDetails(context) {
|
|
24084
24163
|
const memory = context.memory;
|
|
24085
24164
|
const memoryConfig = parseMemoryRequestContext(context.requestContext)?.memoryConfig;
|
|
@@ -24127,8 +24206,9 @@ var WorkingMemoryExtractor = class extends Extractor {
|
|
|
24127
24206
|
const configuredSchema = memory.getMergedThreadConfig(memoryConfig ?? {}).workingMemory?.schema;
|
|
24128
24207
|
let document = current;
|
|
24129
24208
|
if (configuredSchema) {
|
|
24130
|
-
if (current === null) return;
|
|
24209
|
+
if (current === null || isEmptyPlainObject(current)) return;
|
|
24131
24210
|
document = await validateAgainstConfiguredSchema(configuredSchema, current);
|
|
24211
|
+
if (document === null || isEmptyPlainObject(document)) return;
|
|
24132
24212
|
}
|
|
24133
24213
|
const workingMemory = typeof document === "string" ? document : JSON.stringify(document) ?? "";
|
|
24134
24214
|
if (!workingMemory.trim()) return;
|
|
@@ -26416,6 +26496,82 @@ function resolveAutoModelId(mainModelId, options) {
|
|
|
26416
26496
|
return pick ? `${prefix}${pick}` : mainModelId;
|
|
26417
26497
|
}
|
|
26418
26498
|
//#endregion
|
|
26499
|
+
//#region src/processors/observational-memory/commit-queue.ts
|
|
26500
|
+
/** How long an op may hold its key before later ops on that key stop waiting for it. */
|
|
26501
|
+
const OM_COMMIT_STALL_MS = 6e4;
|
|
26502
|
+
/** The per-thread / per-resource key shared by the commit queue and `BufferingCoordinator`. */
|
|
26503
|
+
function getOMLockKey(scope, threadId, resourceId) {
|
|
26504
|
+
if (scope === "resource" && resourceId) return `resource:${resourceId}`;
|
|
26505
|
+
return `thread:${threadId ?? "unknown"}`;
|
|
26506
|
+
}
|
|
26507
|
+
const REGISTRY = Symbol.for("@mastra/memory/om-commit-queue");
|
|
26508
|
+
const { queues, heldKeys } = globalThis[REGISTRY] ??= {
|
|
26509
|
+
queues: /* @__PURE__ */ new Map(),
|
|
26510
|
+
heldKeys: new AsyncLocalStorage()
|
|
26511
|
+
};
|
|
26512
|
+
function holdsKey(key) {
|
|
26513
|
+
const token = heldKeys.getStore()?.get(key);
|
|
26514
|
+
return token !== void 0 && queues.get(key)?.running === token;
|
|
26515
|
+
}
|
|
26516
|
+
function runNext(key, queue) {
|
|
26517
|
+
const next = queue.reflection.shift() ?? queue.normal.shift();
|
|
26518
|
+
if (!next) {
|
|
26519
|
+
queue.running = null;
|
|
26520
|
+
queues.delete(key);
|
|
26521
|
+
return;
|
|
26522
|
+
}
|
|
26523
|
+
next();
|
|
26524
|
+
}
|
|
26525
|
+
/**
|
|
26526
|
+
* Runs `op` once every earlier op on `key` has settled (reflection-priority ops first among those
|
|
26527
|
+
* still waiting) and resolves or rejects with its result. A rejection reaches only this caller.
|
|
26528
|
+
*
|
|
26529
|
+
* Enqueueing on a key from inside an op that holds the same key, directly or through nested ops
|
|
26530
|
+
* on other keys, would deadlock, so it rejects.
|
|
26531
|
+
*/
|
|
26532
|
+
function runOMCommit(key, op, { priority = "normal" } = {}) {
|
|
26533
|
+
if (holdsKey(key)) return Promise.reject(/* @__PURE__ */ new Error(`Re-entrant observational memory commit on "${key}": an op cannot enqueue on the key it holds.`));
|
|
26534
|
+
return new Promise((resolve, reject) => {
|
|
26535
|
+
let queue = queues.get(key);
|
|
26536
|
+
if (!queue) {
|
|
26537
|
+
queue = {
|
|
26538
|
+
running: null,
|
|
26539
|
+
reflection: [],
|
|
26540
|
+
normal: []
|
|
26541
|
+
};
|
|
26542
|
+
queues.set(key, queue);
|
|
26543
|
+
}
|
|
26544
|
+
const owner = queue;
|
|
26545
|
+
const start = AsyncResource.bind(() => {
|
|
26546
|
+
const token = Symbol(key);
|
|
26547
|
+
owner.running = token;
|
|
26548
|
+
const held = new Map(heldKeys.getStore());
|
|
26549
|
+
held.set(key, token);
|
|
26550
|
+
let released = false;
|
|
26551
|
+
const release = () => {
|
|
26552
|
+
if (released) return;
|
|
26553
|
+
released = true;
|
|
26554
|
+
clearTimeout(stallTimer);
|
|
26555
|
+
runNext(key, owner);
|
|
26556
|
+
};
|
|
26557
|
+
const stallTimer = setTimeout(() => {
|
|
26558
|
+
console.warn(`[observational-memory] Commit on "${key}" has not settled after ${OM_COMMIT_STALL_MS}ms; releasing the key so later commits can run. The stalled storage call is still pending.`);
|
|
26559
|
+
release();
|
|
26560
|
+
}, OM_COMMIT_STALL_MS);
|
|
26561
|
+
stallTimer.unref?.();
|
|
26562
|
+
heldKeys.run(held, () => Promise.resolve().then(op)).then((value) => {
|
|
26563
|
+
release();
|
|
26564
|
+
resolve(value);
|
|
26565
|
+
}, (error) => {
|
|
26566
|
+
release();
|
|
26567
|
+
reject(error);
|
|
26568
|
+
});
|
|
26569
|
+
});
|
|
26570
|
+
(priority === "reflection" ? owner.reflection : owner.normal).push(start);
|
|
26571
|
+
if (owner.running === null) runNext(key, owner);
|
|
26572
|
+
});
|
|
26573
|
+
}
|
|
26574
|
+
//#endregion
|
|
26419
26575
|
//#region src/processors/observational-memory/operation-registry.ts
|
|
26420
26576
|
const activeOps = /* @__PURE__ */ new Map();
|
|
26421
26577
|
function opKey(recordId, op) {
|
|
@@ -26455,11 +26611,37 @@ var BufferingCoordinator = class BufferingCoordinator {
|
|
|
26455
26611
|
*/
|
|
26456
26612
|
static asyncBufferingOps = /* @__PURE__ */ new Map();
|
|
26457
26613
|
/**
|
|
26614
|
+
* Observation buffer ops that may still append a buffered chunk.
|
|
26615
|
+
* Key format: "obs:{lockKey}"
|
|
26616
|
+
* Value: Promise that resolves once the op has persisted its chunk (or ended without one)
|
|
26617
|
+
*
|
|
26618
|
+
* Above the threshold→blockAfter band, activation waits (bounded) on this phase so the chunk
|
|
26619
|
+
* activates instead of being re-observed by the sync pass. It is not needed for safety: storage
|
|
26620
|
+
* keeps a chunk appended during activation. An op stays in asyncBufferingOps through its
|
|
26621
|
+
* post-persist work (indexing, thread title), which activation never waits for.
|
|
26622
|
+
*/
|
|
26623
|
+
static pendingChunkWrites = /* @__PURE__ */ new Map();
|
|
26624
|
+
/**
|
|
26458
26625
|
* Track the last token boundary at which we started buffering.
|
|
26459
26626
|
* Key format: "obs:{lockKey}" or "refl:{lockKey}"
|
|
26460
26627
|
*/
|
|
26461
26628
|
static lastBufferedBoundary = /* @__PURE__ */ new Map();
|
|
26462
26629
|
/**
|
|
26630
|
+
* The buffer op that set the observation boundary, if one did. Any other write (an activation
|
|
26631
|
+
* reset, another op) replaces or clears it, so an op that outlived it never writes its boundary
|
|
26632
|
+
* back. Key format: "obs:{lockKey}"
|
|
26633
|
+
*/
|
|
26634
|
+
static observationBoundaryOwners = /* @__PURE__ */ new Map();
|
|
26635
|
+
/** Set the observation boundary; `owner` is the buffer op setting it, if any. */
|
|
26636
|
+
static setObservationBoundary(bufferKey, tokens, owner) {
|
|
26637
|
+
BufferingCoordinator.lastBufferedBoundary.set(bufferKey, tokens);
|
|
26638
|
+
if (owner) BufferingCoordinator.observationBoundaryOwners.set(bufferKey, owner);
|
|
26639
|
+
else BufferingCoordinator.observationBoundaryOwners.delete(bufferKey);
|
|
26640
|
+
}
|
|
26641
|
+
static ownsObservationBoundary(bufferKey, owner) {
|
|
26642
|
+
return BufferingCoordinator.observationBoundaryOwners.get(bufferKey) === owner;
|
|
26643
|
+
}
|
|
26644
|
+
/**
|
|
26463
26645
|
* Track the timestamp cursor for buffered messages.
|
|
26464
26646
|
* Key format: "obs:{lockKey}"
|
|
26465
26647
|
*/
|
|
@@ -26475,8 +26657,7 @@ var BufferingCoordinator = class BufferingCoordinator {
|
|
|
26475
26657
|
this.scope = opts.scope;
|
|
26476
26658
|
}
|
|
26477
26659
|
getLockKey(threadId, resourceId) {
|
|
26478
|
-
|
|
26479
|
-
return `thread:${threadId ?? "unknown"}`;
|
|
26660
|
+
return getOMLockKey(this.scope, threadId, resourceId);
|
|
26480
26661
|
}
|
|
26481
26662
|
isAsyncObservationEnabled() {
|
|
26482
26663
|
return this.observationConfig.bufferTokens !== void 0 && this.observationConfig.bufferTokens > 0;
|
|
@@ -26494,6 +26675,19 @@ var BufferingCoordinator = class BufferingCoordinator {
|
|
|
26494
26675
|
return BufferingCoordinator.asyncBufferingOps.has(bufferKey);
|
|
26495
26676
|
}
|
|
26496
26677
|
/**
|
|
26678
|
+
* Mark an observation buffer op as able to append a chunk. Call the returned release
|
|
26679
|
+
* function once it no longer can; releasing more than once is a no-op.
|
|
26680
|
+
*/
|
|
26681
|
+
static trackChunkWrite(bufferKey) {
|
|
26682
|
+
let resolve;
|
|
26683
|
+
const pending = new Promise((r) => resolve = r);
|
|
26684
|
+
BufferingCoordinator.pendingChunkWrites.set(bufferKey, pending);
|
|
26685
|
+
return () => {
|
|
26686
|
+
if (BufferingCoordinator.pendingChunkWrites.get(bufferKey) === pending) BufferingCoordinator.pendingChunkWrites.delete(bufferKey);
|
|
26687
|
+
resolve();
|
|
26688
|
+
};
|
|
26689
|
+
}
|
|
26690
|
+
/**
|
|
26497
26691
|
* Clean up static maps for a thread/resource to prevent memory leaks.
|
|
26498
26692
|
*/
|
|
26499
26693
|
cleanupStaticMaps(threadId, resourceId, activatedMessageIds) {
|
|
@@ -26502,10 +26696,12 @@ var BufferingCoordinator = class BufferingCoordinator {
|
|
|
26502
26696
|
const reflBufKey = this.getReflectionBufferKey(lockKey);
|
|
26503
26697
|
if (activatedMessageIds) {
|
|
26504
26698
|
BufferingCoordinator.lastBufferedBoundary.delete(obsBufKey);
|
|
26699
|
+
BufferingCoordinator.observationBoundaryOwners.delete(obsBufKey);
|
|
26505
26700
|
BufferingCoordinator.lastBufferedAtTime.delete(obsBufKey);
|
|
26506
26701
|
} else {
|
|
26507
26702
|
BufferingCoordinator.lastBufferedAtTime.delete(obsBufKey);
|
|
26508
26703
|
BufferingCoordinator.lastBufferedBoundary.delete(obsBufKey);
|
|
26704
|
+
BufferingCoordinator.observationBoundaryOwners.delete(obsBufKey);
|
|
26509
26705
|
BufferingCoordinator.lastBufferedBoundary.delete(reflBufKey);
|
|
26510
26706
|
BufferingCoordinator.asyncBufferingOps.delete(obsBufKey);
|
|
26511
26707
|
BufferingCoordinator.asyncBufferingOps.delete(reflBufKey);
|
|
@@ -26520,7 +26716,7 @@ var BufferingCoordinator = class BufferingCoordinator {
|
|
|
26520
26716
|
if (record.isBufferingObservation) {
|
|
26521
26717
|
if (isOpActiveInProcess(record.id, "bufferingObservation")) return false;
|
|
26522
26718
|
omDebug(`[OM:shouldTriggerAsyncObs] isBufferingObservation=true but stale, clearing`);
|
|
26523
|
-
storage
|
|
26719
|
+
if (storage) runOMCommit(lockKey, () => storage.setBufferingObservationFlag(record.id, false)).catch(() => {});
|
|
26524
26720
|
}
|
|
26525
26721
|
const bufferKey = this.getObservationBufferKey(lockKey);
|
|
26526
26722
|
if (this.isAsyncBufferingInProgress(bufferKey)) return false;
|
|
@@ -26540,7 +26736,7 @@ var BufferingCoordinator = class BufferingCoordinator {
|
|
|
26540
26736
|
* Await any in-flight async buffering operations for a given thread/resource.
|
|
26541
26737
|
*/
|
|
26542
26738
|
static async awaitBuffering(threadId, resourceId, scope, timeoutMs = 3e4) {
|
|
26543
|
-
const lockKey = scope
|
|
26739
|
+
const lockKey = getOMLockKey(scope, threadId, resourceId);
|
|
26544
26740
|
const obsKey = `obs:${lockKey}`;
|
|
26545
26741
|
const reflKey = `refl:${lockKey}`;
|
|
26546
26742
|
const promises = [];
|
|
@@ -26838,6 +27034,36 @@ function createThreadUpdateMarker(params) {
|
|
|
26838
27034
|
}
|
|
26839
27035
|
};
|
|
26840
27036
|
}
|
|
27037
|
+
//#endregion
|
|
27038
|
+
//#region src/processors/observational-memory/record-lineage.ts
|
|
27039
|
+
/**
|
|
27040
|
+
* The current head of `origin`'s lineage (the generations reflections have created from it),
|
|
27041
|
+
* or `null` when that lineage is gone.
|
|
27042
|
+
*
|
|
27043
|
+
* Use this instead of reading the head by thread/resource when committing work that started
|
|
27044
|
+
* from `origin`. Clearing a record (`om.clear`, `Memory.deleteThread`) deletes every generation
|
|
27045
|
+
* of the thread/resource, and the thread can then get a new, unrelated record. Work produced
|
|
27046
|
+
* from the cleared messages must never be committed to it.
|
|
27047
|
+
*
|
|
27048
|
+
* Generation ids are never reused and clearing deletes all of them, so the head belongs to
|
|
27049
|
+
* `origin`'s lineage iff `origin` still exists.
|
|
27050
|
+
*/
|
|
27051
|
+
async function getLineageHead(storage, origin) {
|
|
27052
|
+
const head = await storage.getObservationalMemory(origin.threadId, origin.resourceId);
|
|
27053
|
+
if (!head || head.id === origin.id) return head;
|
|
27054
|
+
if (head.generationCount <= origin.generationCount) return null;
|
|
27055
|
+
return (await storage.getObservationalMemoryHistory(origin.threadId, origin.resourceId, head.generationCount - origin.generationCount + 3)).some((record) => record.id === origin.id) ? head : null;
|
|
27056
|
+
}
|
|
27057
|
+
/**
|
|
27058
|
+
* The generation `id` of `origin`'s lineage, or `null` when that lineage is gone or `id` is not
|
|
27059
|
+
* part of it. Unlike {@link getLineageHead}, this keeps naming `id` after later reflections have
|
|
27060
|
+
* superseded it, e.g. to report which generation a write landed on.
|
|
27061
|
+
*/
|
|
27062
|
+
async function getLineageRecord(storage, origin, id) {
|
|
27063
|
+
const head = await getLineageHead(storage, origin);
|
|
27064
|
+
if (!head || head.id === id) return head;
|
|
27065
|
+
return (await storage.getObservationalMemoryHistory(origin.threadId, origin.resourceId, head.generationCount - origin.generationCount + 3)).find((record) => record.id === id) ?? null;
|
|
27066
|
+
}
|
|
26841
27067
|
function isRecord$1(value) {
|
|
26842
27068
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
26843
27069
|
}
|
|
@@ -26984,6 +27210,39 @@ function calculateProjectedMessageRemoval(chunks, bufferActivation, messageToken
|
|
|
26984
27210
|
//#region src/processors/observational-memory/observation-strategies/base.ts
|
|
26985
27211
|
/** Module-level xxhash singleton — loaded once, shared across all strategy instances. */
|
|
26986
27212
|
const hasherPromise = xxhash();
|
|
27213
|
+
/** Recompose-and-retry rounds for an observation commit that hit a retired or changed head. */
|
|
27214
|
+
const MAX_HEAD_COMMIT_RETRIES = 3;
|
|
27215
|
+
const OBSERVATION_LIFECYCLE_MARKERS = /* @__PURE__ */ new Set([
|
|
27216
|
+
"data-om-observation-start",
|
|
27217
|
+
"data-om-observation-end",
|
|
27218
|
+
"data-om-observation-failed"
|
|
27219
|
+
]);
|
|
27220
|
+
function isOmPart(part) {
|
|
27221
|
+
return String(part?.type ?? "").startsWith("data-om-");
|
|
27222
|
+
}
|
|
27223
|
+
/**
|
|
27224
|
+
* Index in `message` right after this cycle's start marker (and any OM parts directly after it),
|
|
27225
|
+
* or -1 when the message doesn't carry it.
|
|
27226
|
+
*/
|
|
27227
|
+
function afterOwnStartMarker(message, marker) {
|
|
27228
|
+
const cycleId = marker.data?.cycleId;
|
|
27229
|
+
const startType = marker.type.startsWith("data-om-buffering-") ? "data-om-buffering-start" : "data-om-observation-start";
|
|
27230
|
+
if (!cycleId || marker.type === startType) return -1;
|
|
27231
|
+
const parts = message.content?.parts ?? [];
|
|
27232
|
+
const start = parts.findIndex((part) => part?.type === startType && part?.data?.cycleId === cycleId);
|
|
27233
|
+
if (start === -1) return -1;
|
|
27234
|
+
let index = start + 1;
|
|
27235
|
+
while (index < parts.length && isOmPart(parts[index])) index++;
|
|
27236
|
+
return index;
|
|
27237
|
+
}
|
|
27238
|
+
/** True when the message has parts no completed observation boundary covers (OM markers aside). */
|
|
27239
|
+
function hasUnobservedContent(message) {
|
|
27240
|
+
return getUnobservedParts(message).some((part) => !isOmPart(part));
|
|
27241
|
+
}
|
|
27242
|
+
function isObservationLifecycleMarker(marker) {
|
|
27243
|
+
const operationType = marker.data?.operationType;
|
|
27244
|
+
return OBSERVATION_LIFECYCLE_MARKERS.has(marker.type) && operationType !== "reflection";
|
|
27245
|
+
}
|
|
26987
27246
|
/**
|
|
26988
27247
|
* Abstract base class for observation strategies.
|
|
26989
27248
|
*
|
|
@@ -27033,17 +27292,35 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
27033
27292
|
const fresh = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
27034
27293
|
if (fresh?.lastObservedAt && record.lastObservedAt && fresh.lastObservedAt > record.lastObservedAt) return { observed: false };
|
|
27035
27294
|
}
|
|
27036
|
-
const { messages, existingObservations } = await this.prepare();
|
|
27295
|
+
const { messages, existingObservations, contextRecord } = await this.prepare();
|
|
27296
|
+
if (messages.length === 0) return { observed: false };
|
|
27037
27297
|
const observationMessages = stripSubconsciousSignals(messages);
|
|
27038
27298
|
await this.emitStartMarkers(cycleId);
|
|
27039
|
-
const
|
|
27040
|
-
const
|
|
27041
|
-
|
|
27299
|
+
const observerContext = this.deps.prepareObserverContext(existingObservations, contextRecord ?? record);
|
|
27300
|
+
const output = await this.observe(observerContext.context ?? "", observationMessages, observerContext.wasTruncated);
|
|
27301
|
+
let processed = await this.process(output, existingObservations);
|
|
27302
|
+
let committedRecord = record;
|
|
27303
|
+
const outcome = await this.persist(processed);
|
|
27304
|
+
this.settleObservationCommit(outcome?.status === "committed" && !outcome.alreadyCovered);
|
|
27305
|
+
if (outcome?.status === "not-committed") {
|
|
27306
|
+
omDebug(`[OM:observe] cycle ${cycleId} not committed: ${outcome.reason}`);
|
|
27307
|
+
await this.emitFailedMarkers(cycleId, /* @__PURE__ */ new Error(`Observation not committed: ${outcome.reason}`));
|
|
27308
|
+
return {
|
|
27309
|
+
observed: false,
|
|
27310
|
+
usage: output.usage,
|
|
27311
|
+
providerMetadata: output.providerMetadata
|
|
27312
|
+
};
|
|
27313
|
+
}
|
|
27314
|
+
if (outcome?.status === "committed") {
|
|
27315
|
+
processed = outcome.processed;
|
|
27316
|
+
committedRecord = outcome.record;
|
|
27317
|
+
}
|
|
27042
27318
|
await this.emitEndMarkers(cycleId, processed);
|
|
27043
27319
|
if (this.needsReflection) await this.deps.reflector.maybeReflect({
|
|
27044
27320
|
record: {
|
|
27045
|
-
...
|
|
27046
|
-
activeObservations: processed.observations
|
|
27321
|
+
...committedRecord,
|
|
27322
|
+
activeObservations: processed.observations,
|
|
27323
|
+
observationTokenCount: processed.observationTokens
|
|
27047
27324
|
},
|
|
27048
27325
|
observationTokens: processed.observationTokens,
|
|
27049
27326
|
threadId,
|
|
@@ -27079,7 +27356,8 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
27079
27356
|
threadId
|
|
27080
27357
|
}
|
|
27081
27358
|
};
|
|
27082
|
-
|
|
27359
|
+
const observed = this.getObservedMessagesForThread(threadId);
|
|
27360
|
+
await (observed?.length ? this.persistMarkerToObservedMessage(failedMarkerForStorage, observed, threadId, this.opts.resourceId) : this.persistMarkerToStorage(failedMarkerForStorage, threadId, this.opts.resourceId)).catch(() => {});
|
|
27083
27361
|
if (abortSignal?.aborted) throw error;
|
|
27084
27362
|
omError("[OM] Observation failed", error);
|
|
27085
27363
|
return {
|
|
@@ -27106,6 +27384,11 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
27106
27384
|
transient: true
|
|
27107
27385
|
}).catch(() => {});
|
|
27108
27386
|
const markerThreadId = marker.data?.threadId ?? this.opts.threadId;
|
|
27387
|
+
const observed = isObservationLifecycleMarker(marker) ? this.getObservedMessagesForThread(markerThreadId) : void 0;
|
|
27388
|
+
if (observed?.length) {
|
|
27389
|
+
await this.persistMarkerToObservedMessage(marker, observed, markerThreadId, this.opts.resourceId);
|
|
27390
|
+
return;
|
|
27391
|
+
}
|
|
27109
27392
|
if (!await this.persistMarkerToMessage(marker, this.opts.messageList, markerThreadId, this.opts.resourceId)) await this.persistMarkerToStorage(marker, markerThreadId, this.opts.resourceId);
|
|
27110
27393
|
}
|
|
27111
27394
|
getObservationMarkerConfig() {
|
|
@@ -27189,6 +27472,76 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
27189
27472
|
}
|
|
27190
27473
|
return `${existingObservations}${lastObservedAt ? ObservationStrategy.createMessageBoundary(lastObservedAt) : "\n\n"}${newThreadSection}`;
|
|
27191
27474
|
}
|
|
27475
|
+
/**
|
|
27476
|
+
* Commit composed observations to the current head generation.
|
|
27477
|
+
*
|
|
27478
|
+
* The write is conditional on the head text the observations were composed from. When the
|
|
27479
|
+
* target generation was retired by a reflection, or another writer changed the text, the
|
|
27480
|
+
* observations are recomposed against the fresh head and the commit is retried (bounded).
|
|
27481
|
+
* When the fresh head's `observedMessageIds` already include every message this cycle observed
|
|
27482
|
+
* (another instance observed them first), nothing is written and `alreadyCovered` is set, so
|
|
27483
|
+
* callers advance cursors and markers without adding a second copy of the same observations.
|
|
27484
|
+
* The cursor alone is not proof: an activation can move it past a message it never covered.
|
|
27485
|
+
* Returns null when no commit landed — callers must then leave cursors, markers, and the live
|
|
27486
|
+
* context untouched.
|
|
27487
|
+
*/
|
|
27488
|
+
async commitActiveObservationsToHead(opts) {
|
|
27489
|
+
let { processed, composedFrom, target } = opts;
|
|
27490
|
+
for (let attempt = 0; attempt <= MAX_HEAD_COMMIT_RETRIES; attempt++) {
|
|
27491
|
+
const result = await this.runCommit(async () => {
|
|
27492
|
+
const head = await getLineageHead(this.storage, target);
|
|
27493
|
+
if (!head) return null;
|
|
27494
|
+
if (head.id !== target.id || (head.activeObservations ?? "") !== composedFrom) {
|
|
27495
|
+
if (this.headCoversCycle(head, opts.cycleMessageIds)) {
|
|
27496
|
+
omDebug(`[OM:observe] head ${head.id} already covers this cycle's messages; skipping the duplicate commit`);
|
|
27497
|
+
target = head;
|
|
27498
|
+
return {
|
|
27499
|
+
applied: true,
|
|
27500
|
+
alreadyCovered: true
|
|
27501
|
+
};
|
|
27502
|
+
}
|
|
27503
|
+
target = head;
|
|
27504
|
+
composedFrom = head.activeObservations ?? "";
|
|
27505
|
+
processed = await opts.recompose(head);
|
|
27506
|
+
}
|
|
27507
|
+
const input = {
|
|
27508
|
+
id: target.id,
|
|
27509
|
+
observations: processed.observations,
|
|
27510
|
+
tokenCount: processed.observationTokens,
|
|
27511
|
+
lastObservedAt: processed.lastObservedAt,
|
|
27512
|
+
observedMessageIds: processed.observedMessageIds,
|
|
27513
|
+
expectedActiveObservations: composedFrom
|
|
27514
|
+
};
|
|
27515
|
+
return typeof this.storage.commitActiveObservations === "function" ? await this.storage.commitActiveObservations(input) : (await this.storage.updateActiveObservations(input), { applied: true });
|
|
27516
|
+
});
|
|
27517
|
+
if (!result) return null;
|
|
27518
|
+
if ("alreadyCovered" in result) return {
|
|
27519
|
+
processed: {
|
|
27520
|
+
...processed,
|
|
27521
|
+
observations: target.activeObservations ?? "",
|
|
27522
|
+
observationTokens: target.observationTokenCount
|
|
27523
|
+
},
|
|
27524
|
+
record: target,
|
|
27525
|
+
alreadyCovered: true
|
|
27526
|
+
};
|
|
27527
|
+
if (result.applied) return {
|
|
27528
|
+
processed,
|
|
27529
|
+
record: target
|
|
27530
|
+
};
|
|
27531
|
+
omDebug(`[OM:observe] commit to ${target.id} not applied (${result.reason}); retrying against the head`);
|
|
27532
|
+
}
|
|
27533
|
+
return null;
|
|
27534
|
+
}
|
|
27535
|
+
headCoversCycle(head, cycleMessageIds) {
|
|
27536
|
+
if (cycleMessageIds.length === 0) return false;
|
|
27537
|
+
const headIds = new Set(Array.isArray(head.observedMessageIds) ? head.observedMessageIds : []);
|
|
27538
|
+
return cycleMessageIds.every((id) => headIds.has(id));
|
|
27539
|
+
}
|
|
27540
|
+
/** Runs one storage commit in this thread's (or resource's) slot of the process-wide commit queue. */
|
|
27541
|
+
runCommit(op) {
|
|
27542
|
+
const { threadId, resourceId, record } = this.opts;
|
|
27543
|
+
return runOMCommit(getOMLockKey(this.scope, record.threadId ?? threadId, record.resourceId ?? resourceId), op);
|
|
27544
|
+
}
|
|
27192
27545
|
async indexObservationGroups(observations, threadId, resourceId, observedAt, recordId) {
|
|
27193
27546
|
if (!resourceId || !this.deps.onIndexObservations) return;
|
|
27194
27547
|
const groups = parseObservationGroups(observations);
|
|
@@ -27207,30 +27560,133 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
27207
27560
|
})));
|
|
27208
27561
|
}
|
|
27209
27562
|
/**
|
|
27563
|
+
* Messages this cycle observed for `threadId`, or undefined when the strategy does not
|
|
27564
|
+
* place observation lifecycle markers.
|
|
27565
|
+
*/
|
|
27566
|
+
getObservedMessagesForThread(_threadId) {}
|
|
27567
|
+
/**
|
|
27568
|
+
* Persist an observation start/end/failed marker without hiding unobserved content. A completed
|
|
27569
|
+
* end marker tells `getUnobservedMessages` that everything before it in that message is observed
|
|
27570
|
+
* (whatever thread the marker names), so it must only follow content this cycle observed.
|
|
27571
|
+
* Placement, in order:
|
|
27572
|
+
* 1. the live MessageList's newest assistant message (this turn's response or its seed), when the
|
|
27573
|
+
* list belongs to the marker's thread and the cycle observed that message or it has no
|
|
27574
|
+
* unobserved content;
|
|
27575
|
+
* 2. the newest observed assistant message; a stored copy (no live list, or another instance's
|
|
27576
|
+
* message) gets the marker right after the parts this cycle observed (end/failed markers right
|
|
27577
|
+
* after this cycle's start marker), so parts added while the Observer ran stay unobserved;
|
|
27578
|
+
* 3. the newest stored assistant message that is not newer than the observed range, only when it
|
|
27579
|
+
* has nothing unobserved (or already carries this cycle's start marker); otherwise no marker,
|
|
27580
|
+
* and the thread cursor alone records the cycle.
|
|
27581
|
+
* Lifecycle markers never go on user messages.
|
|
27582
|
+
*/
|
|
27583
|
+
async persistMarkerToObservedMessage(marker, observed, threadId, resourceId) {
|
|
27584
|
+
const observedIds = new Set(observed.map((m) => m.id));
|
|
27585
|
+
const list = threadId === this.opts.threadId ? this.opts.messageList : void 0;
|
|
27586
|
+
const listMessages = list ? getObservableMessages(list) : [];
|
|
27587
|
+
const live = [...listMessages].reverse().find((m) => m.role === "assistant");
|
|
27588
|
+
if (live && (observedIds.has(live.id) || !hasUnobservedContent(live))) {
|
|
27589
|
+
await this.attachMarker(marker, live, live.content.parts.length, threadId, resourceId);
|
|
27590
|
+
return;
|
|
27591
|
+
}
|
|
27592
|
+
const target = [...observed].reverse().find((m) => m.role === "assistant");
|
|
27593
|
+
if (target) {
|
|
27594
|
+
const fromList = listMessages.find((m) => m.id === target.id);
|
|
27595
|
+
if (fromList) {
|
|
27596
|
+
await this.attachMarker(marker, fromList, fromList.content.parts.length, threadId, resourceId);
|
|
27597
|
+
return;
|
|
27598
|
+
}
|
|
27599
|
+
try {
|
|
27600
|
+
const stored = (await this.storage.listMessagesById({ messageIds: [target.id] })).messages[0];
|
|
27601
|
+
if (stored?.content?.parts && Array.isArray(stored.content.parts)) {
|
|
27602
|
+
const afterStart = afterOwnStartMarker(stored, marker);
|
|
27603
|
+
const index = afterStart !== -1 ? afterStart : this.insertionAfterObserved(stored, target);
|
|
27604
|
+
await this.attachMarker(marker, stored, index, threadId, resourceId);
|
|
27605
|
+
}
|
|
27606
|
+
} catch (e) {
|
|
27607
|
+
omDebug(`[OM:persistMarkerToObservedMessage] failed to load marker target: ${e}`);
|
|
27608
|
+
}
|
|
27609
|
+
return;
|
|
27610
|
+
}
|
|
27611
|
+
const observedTimes = observed.filter((m) => m.createdAt).map((m) => new Date(m.createdAt).getTime());
|
|
27612
|
+
if (observedTimes.length === 0) return;
|
|
27613
|
+
await this.persistMarkerToStorage(marker, threadId, resourceId, {
|
|
27614
|
+
notAfter: new Date(Math.max(...observedTimes)),
|
|
27615
|
+
onlyIfObserved: true
|
|
27616
|
+
});
|
|
27617
|
+
}
|
|
27618
|
+
/** What the cycle observed of each message, captured when it placed its first marker there. */
|
|
27619
|
+
observedParts = /* @__PURE__ */ new Map();
|
|
27620
|
+
/**
|
|
27621
|
+
* Index in `stored` right after the parts the cycle observed in `observed` (and any OM markers
|
|
27622
|
+
* directly after them). Parts are only ever appended, so the observed message's non-marker part
|
|
27623
|
+
* count locates them, counted from the start of the message or, when `observed` is the trimmed
|
|
27624
|
+
* copy `getUnobservedMessages` returns for an already marked message, from just after the stored
|
|
27625
|
+
* copy's last completed boundary. Captured at the cycle's first marker, before the Observer runs,
|
|
27626
|
+
* because `observed` may be a live reference that grows meanwhile. Falls back to the end when the
|
|
27627
|
+
* stored copy has fewer parts than were observed.
|
|
27628
|
+
*/
|
|
27629
|
+
insertionAfterObserved(stored, observed) {
|
|
27630
|
+
if (!this.observedParts.has(observed.id)) this.observedParts.set(observed.id, {
|
|
27631
|
+
count: (observed.content?.parts ?? []).filter((part) => !isOmPart(part)).length,
|
|
27632
|
+
observedIsTrimmed: findLastCompletedObservationBoundary(observed) === -1
|
|
27633
|
+
});
|
|
27634
|
+
const { count, observedIsTrimmed } = this.observedParts.get(observed.id);
|
|
27635
|
+
const parts = stored.content.parts;
|
|
27636
|
+
let index = observedIsTrimmed ? findLastCompletedObservationBoundary(stored) + 1 : 0;
|
|
27637
|
+
let seen = 0;
|
|
27638
|
+
while (index < parts.length && seen < count) {
|
|
27639
|
+
if (!isOmPart(parts[index])) seen++;
|
|
27640
|
+
index++;
|
|
27641
|
+
}
|
|
27642
|
+
if (seen < count) return parts.length;
|
|
27643
|
+
while (index < parts.length && isOmPart(parts[index])) index++;
|
|
27644
|
+
return index;
|
|
27645
|
+
}
|
|
27646
|
+
async attachMarker(marker, msg, index, threadId, resourceId) {
|
|
27647
|
+
const parts = msg.content?.parts;
|
|
27648
|
+
if (!parts || !Array.isArray(parts)) return;
|
|
27649
|
+
const markerData = marker.data;
|
|
27650
|
+
if (!(markerData?.cycleId && parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) parts.splice(index, 0, marker);
|
|
27651
|
+
try {
|
|
27652
|
+
await this.messageHistory.persistMessages({
|
|
27653
|
+
messages: [msg],
|
|
27654
|
+
threadId,
|
|
27655
|
+
resourceId
|
|
27656
|
+
});
|
|
27657
|
+
} catch (e) {
|
|
27658
|
+
omDebug(`[OM:persistMarkerToObservedMessage] failed to save marker to DB: ${e}`);
|
|
27659
|
+
}
|
|
27660
|
+
}
|
|
27661
|
+
/**
|
|
27210
27662
|
* Persist a marker to the last assistant message in storage.
|
|
27211
27663
|
* Fetches messages directly from the DB so it works even when
|
|
27212
27664
|
* no MessageList is available (e.g. async buffering ops).
|
|
27213
27665
|
*/
|
|
27214
|
-
async persistMarkerToStorage(marker, threadId, resourceId) {
|
|
27666
|
+
async persistMarkerToStorage(marker, threadId, resourceId, opts) {
|
|
27215
27667
|
try {
|
|
27216
|
-
const
|
|
27668
|
+
const assistants = ((await this.storage.listMessages({
|
|
27217
27669
|
threadId,
|
|
27218
27670
|
perPage: 20,
|
|
27219
27671
|
orderBy: {
|
|
27220
27672
|
field: "createdAt",
|
|
27221
27673
|
direction: "DESC"
|
|
27222
|
-
}
|
|
27223
|
-
|
|
27224
|
-
|
|
27225
|
-
|
|
27226
|
-
|
|
27227
|
-
|
|
27228
|
-
|
|
27229
|
-
|
|
27230
|
-
|
|
27231
|
-
|
|
27232
|
-
|
|
27233
|
-
|
|
27674
|
+
},
|
|
27675
|
+
...opts?.notAfter ? { filter: { dateRange: { end: opts.notAfter } } } : {}
|
|
27676
|
+
}))?.messages ?? []).filter((msg) => msg?.role === "assistant" && Array.isArray(msg.content?.parts));
|
|
27677
|
+
const startMessage = opts?.onlyIfObserved || opts?.onCycleStartMessage ? assistants.find((m) => afterOwnStartMarker(m, marker) !== -1) : void 0;
|
|
27678
|
+
const msg = opts?.onCycleStartMessage ? startMessage : startMessage ?? assistants[0];
|
|
27679
|
+
if (!msg) return;
|
|
27680
|
+
const markerData = marker.data;
|
|
27681
|
+
const alreadyPresent = markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId);
|
|
27682
|
+
const afterStart = afterOwnStartMarker(msg, marker);
|
|
27683
|
+
if (opts?.onlyIfObserved && afterStart === -1 && hasUnobservedContent(msg)) return;
|
|
27684
|
+
if (!alreadyPresent) msg.content.parts.splice(afterStart !== -1 ? afterStart : msg.content.parts.length, 0, marker);
|
|
27685
|
+
await this.messageHistory.persistMessages({
|
|
27686
|
+
messages: [msg],
|
|
27687
|
+
threadId,
|
|
27688
|
+
resourceId
|
|
27689
|
+
});
|
|
27234
27690
|
} catch (e) {
|
|
27235
27691
|
omDebug(`[OM:persistMarkerToStorage] failed to save marker to DB: ${e}`);
|
|
27236
27692
|
}
|
|
@@ -27292,10 +27748,15 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
27292
27748
|
tokensToObserve = 0;
|
|
27293
27749
|
observerResult;
|
|
27294
27750
|
priorExtractedValues;
|
|
27751
|
+
/** Head text the processed observations were composed from (the commit's expected text). */
|
|
27752
|
+
composedFrom = "";
|
|
27295
27753
|
constructor(deps, opts) {
|
|
27296
27754
|
super(deps, opts);
|
|
27297
27755
|
this.lastMessage = opts.messages[opts.messages.length - 1];
|
|
27298
27756
|
}
|
|
27757
|
+
getObservedMessagesForThread(threadId) {
|
|
27758
|
+
return threadId === this.opts.threadId ? this.opts.messages : void 0;
|
|
27759
|
+
}
|
|
27299
27760
|
get needsLock() {
|
|
27300
27761
|
return true;
|
|
27301
27762
|
}
|
|
@@ -27330,9 +27791,11 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
27330
27791
|
}
|
|
27331
27792
|
}
|
|
27332
27793
|
this.tokensToObserve = await this.tokenCounter.countMessagesAsync(messages);
|
|
27794
|
+
const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
27333
27795
|
return {
|
|
27334
27796
|
messages,
|
|
27335
|
-
existingObservations:
|
|
27797
|
+
existingObservations: freshRecord?.activeObservations ?? record.activeObservations ?? "",
|
|
27798
|
+
contextRecord: freshRecord
|
|
27336
27799
|
};
|
|
27337
27800
|
}
|
|
27338
27801
|
async emitStartMarkers(cycleId) {
|
|
@@ -27350,11 +27813,12 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
27350
27813
|
await this.streamMarker(startMarker);
|
|
27351
27814
|
}
|
|
27352
27815
|
}
|
|
27353
|
-
async observe(
|
|
27816
|
+
async observe(observerContext, messages, wasTruncated) {
|
|
27354
27817
|
const thread = await this.storage.getThreadById({ threadId: this.opts.threadId });
|
|
27355
27818
|
const omMeta = thread ? getThreadOMMetadata(thread.metadata) : void 0;
|
|
27356
27819
|
this.priorExtractedValues = getPriorExtractedValues(omMeta, this.observationConfig.extractors);
|
|
27357
|
-
const result = await this.deps.observer.call(
|
|
27820
|
+
const result = await this.deps.observer.call(observerContext, messages, this.opts.abortSignal, {
|
|
27821
|
+
wasTruncated,
|
|
27358
27822
|
requestContext: this.opts.requestContext,
|
|
27359
27823
|
observabilityContext: this.opts.observabilityContext,
|
|
27360
27824
|
priorCurrentTask: omMeta?.currentTask,
|
|
@@ -27400,20 +27864,14 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
27400
27864
|
}
|
|
27401
27865
|
async process(output, existingObservations) {
|
|
27402
27866
|
const { record, threadId, messages } = this.opts;
|
|
27403
|
-
|
|
27404
|
-
const
|
|
27405
|
-
const newObservations = await this.wrapObservations(output.observations, existingObservations, threadId, lastObservedAt, messageRange);
|
|
27406
|
-
const observationTokens = this.tokenCounter.countObservations(newObservations);
|
|
27407
|
-
const cycleObservationTokens = this.tokenCounter.countObservations(output.observations);
|
|
27408
|
-
const newMessageIds = messages.map((m) => m.id);
|
|
27409
|
-
const existingIds = record.observedMessageIds ?? [];
|
|
27410
|
-
const observedMessageIds = [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])];
|
|
27867
|
+
this.composedFrom = existingObservations;
|
|
27868
|
+
const processed = await this.compose(output, existingObservations, record);
|
|
27411
27869
|
this.deps.emitDebugEvent({
|
|
27412
27870
|
type: "observation_complete",
|
|
27413
27871
|
timestamp: /* @__PURE__ */ new Date(),
|
|
27414
27872
|
threadId,
|
|
27415
27873
|
resourceId: record.resourceId ?? "",
|
|
27416
|
-
observations:
|
|
27874
|
+
observations: processed.observations,
|
|
27417
27875
|
rawObserverOutput: output.observations,
|
|
27418
27876
|
previousObservations: record.activeObservations,
|
|
27419
27877
|
messages: messages.map((m) => ({
|
|
@@ -27422,11 +27880,23 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
27422
27880
|
})),
|
|
27423
27881
|
usage: output.usage
|
|
27424
27882
|
});
|
|
27883
|
+
return processed;
|
|
27884
|
+
}
|
|
27885
|
+
/** Build the observations to commit on top of `existingObservations` (the head text). */
|
|
27886
|
+
async compose(output, existingObservations, baseRecord) {
|
|
27887
|
+
const { threadId, messages } = this.opts;
|
|
27888
|
+
const lastObservedAt = this.getMaxMessageTimestamp(messages);
|
|
27889
|
+
const messageRange = this.retrieval ? buildMessageRange(messages) : void 0;
|
|
27890
|
+
const newObservations = await this.wrapObservations(output.observations, existingObservations, threadId, lastObservedAt, messageRange);
|
|
27891
|
+
const observationTokens = this.tokenCounter.countObservations(newObservations);
|
|
27892
|
+
const cycleObservationTokens = this.tokenCounter.countObservations(output.observations);
|
|
27893
|
+
const newMessageIds = messages.map((m) => m.id);
|
|
27894
|
+
const existingIds = baseRecord.observedMessageIds ?? [];
|
|
27425
27895
|
return {
|
|
27426
27896
|
observations: newObservations,
|
|
27427
27897
|
observationTokens,
|
|
27428
27898
|
cycleObservationTokens,
|
|
27429
|
-
observedMessageIds,
|
|
27899
|
+
observedMessageIds: [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])],
|
|
27430
27900
|
lastObservedAt,
|
|
27431
27901
|
suggestedContinuation: output.suggestedContinuation,
|
|
27432
27902
|
currentTask: output.currentTask,
|
|
@@ -27438,12 +27908,27 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
27438
27908
|
}
|
|
27439
27909
|
async persist(processed) {
|
|
27440
27910
|
const { record, threadId, resourceId, messages } = this.opts;
|
|
27441
|
-
const
|
|
27442
|
-
if (!
|
|
27911
|
+
const liveRecord = await getLineageHead(this.storage, record);
|
|
27912
|
+
if (!liveRecord) {
|
|
27443
27913
|
omDebug(`[OM:sync-obs] skipping persist for thread ${threadId}: observational memory record is gone`);
|
|
27444
|
-
return
|
|
27914
|
+
return {
|
|
27915
|
+
status: "not-committed",
|
|
27916
|
+
reason: "the observational memory record was cleared"
|
|
27917
|
+
};
|
|
27445
27918
|
}
|
|
27446
|
-
|
|
27919
|
+
const committed = await this.commitActiveObservationsToHead({
|
|
27920
|
+
processed,
|
|
27921
|
+
composedFrom: this.composedFrom,
|
|
27922
|
+
target: liveRecord,
|
|
27923
|
+
recompose: (head) => this.compose(this.observerResult, head.activeObservations ?? "", head),
|
|
27924
|
+
cycleMessageIds: messages.map((m) => m.id)
|
|
27925
|
+
});
|
|
27926
|
+
if (!committed) return {
|
|
27927
|
+
status: "not-committed",
|
|
27928
|
+
reason: "the observational memory head kept changing during the commit"
|
|
27929
|
+
};
|
|
27930
|
+
processed = committed.processed;
|
|
27931
|
+
const thread = committed.alreadyCovered ? null : await this.storage.getThreadById({ threadId });
|
|
27447
27932
|
if (thread) {
|
|
27448
27933
|
const oldTitle = thread.title?.trim();
|
|
27449
27934
|
const newTitle = resolveThreadTitleUpdate(thread, processed.threadTitle);
|
|
@@ -27465,23 +27950,20 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
27465
27950
|
...shouldUpdateThreadTitle ? { title: newTitle } : {},
|
|
27466
27951
|
metadata: newMetadata
|
|
27467
27952
|
});
|
|
27468
|
-
if (shouldUpdateThreadTitle)
|
|
27953
|
+
if (shouldUpdateThreadTitle) await this.streamMarker(createThreadUpdateMarker({
|
|
27469
27954
|
cycleId: this.cycleId ?? crypto.randomUUID(),
|
|
27470
27955
|
threadId,
|
|
27471
27956
|
oldTitle,
|
|
27472
27957
|
newTitle
|
|
27473
|
-
});
|
|
27958
|
+
}));
|
|
27474
27959
|
}
|
|
27475
|
-
if (
|
|
27476
|
-
|
|
27477
|
-
|
|
27478
|
-
|
|
27479
|
-
|
|
27480
|
-
|
|
27481
|
-
|
|
27482
|
-
});
|
|
27483
|
-
await this.indexObservationGroups(processed.observations, threadId, resourceId, processed.lastObservedAt, record.id);
|
|
27484
|
-
return true;
|
|
27960
|
+
if (!committed.alreadyCovered) await this.indexObservationGroups(processed.observations, threadId, resourceId, processed.lastObservedAt, committed.record.id);
|
|
27961
|
+
return {
|
|
27962
|
+
status: "committed",
|
|
27963
|
+
processed,
|
|
27964
|
+
record: committed.record,
|
|
27965
|
+
alreadyCovered: committed.alreadyCovered
|
|
27966
|
+
};
|
|
27485
27967
|
}
|
|
27486
27968
|
async emitEndMarkers(cycleId, processed) {
|
|
27487
27969
|
const actualTokensObserved = await this.tokenCounter.countMessagesAsync(this.opts.messages);
|
|
@@ -27525,6 +28007,8 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
27525
28007
|
startedAt;
|
|
27526
28008
|
cycleId;
|
|
27527
28009
|
priorExtractedValues;
|
|
28010
|
+
/** The generation the chunk was written to (the head when the target was retired). */
|
|
28011
|
+
persistedRecordId;
|
|
27528
28012
|
constructor(deps, opts) {
|
|
27529
28013
|
super(deps, opts);
|
|
27530
28014
|
this.cycleId = opts.cycleId;
|
|
@@ -27551,12 +28035,13 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
27551
28035
|
};
|
|
27552
28036
|
}
|
|
27553
28037
|
async emitStartMarkers(_cycleId) {}
|
|
27554
|
-
async observe(
|
|
28038
|
+
async observe(observerContext, messages, wasTruncated) {
|
|
27555
28039
|
const thread = await this.storage.getThreadById({ threadId: this.opts.threadId });
|
|
27556
28040
|
const omMeta = thread ? getThreadOMMetadata(thread.metadata) : void 0;
|
|
27557
28041
|
this.priorExtractedValues = getPriorExtractedValues(omMeta, this.observationConfig.extractors);
|
|
27558
|
-
const result = await this.deps.observer.call(
|
|
28042
|
+
const result = await this.deps.observer.call(observerContext, messages, void 0, {
|
|
27559
28043
|
skipContinuationHints: true,
|
|
28044
|
+
wasTruncated,
|
|
27560
28045
|
requestContext: this.opts.requestContext,
|
|
27561
28046
|
observabilityContext: this.opts.observabilityContext,
|
|
27562
28047
|
priorExtractedValues: this.priorExtractedValues,
|
|
@@ -27630,71 +28115,124 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
27630
28115
|
};
|
|
27631
28116
|
}
|
|
27632
28117
|
async persist(processed) {
|
|
27633
|
-
if (!processed.observations) return
|
|
28118
|
+
if (!processed.observations) return;
|
|
27634
28119
|
const { record, threadId, resourceId, messages } = this.opts;
|
|
27635
|
-
|
|
28120
|
+
const liveRecord = await getLineageHead(this.storage, record);
|
|
28121
|
+
if (!liveRecord) {
|
|
27636
28122
|
omDebug(`[OM:asyncBuffer] skipping persist for thread ${threadId}: observational memory record is gone`);
|
|
27637
|
-
return
|
|
28123
|
+
return {
|
|
28124
|
+
status: "not-committed",
|
|
28125
|
+
reason: "the observational memory record was cleared"
|
|
28126
|
+
};
|
|
27638
28127
|
}
|
|
27639
28128
|
const messageTokens = await this.tokenCounter.countMessagesAsync(messages);
|
|
27640
|
-
|
|
27641
|
-
|
|
27642
|
-
|
|
27643
|
-
|
|
27644
|
-
|
|
27645
|
-
|
|
27646
|
-
|
|
27647
|
-
|
|
27648
|
-
|
|
27649
|
-
|
|
27650
|
-
|
|
27651
|
-
|
|
27652
|
-
|
|
27653
|
-
|
|
27654
|
-
|
|
27655
|
-
|
|
27656
|
-
|
|
28129
|
+
let appendAttempts = 0;
|
|
28130
|
+
let targetId = record.id;
|
|
28131
|
+
const appendResult = await withRetry(() => {
|
|
28132
|
+
appendAttempts++;
|
|
28133
|
+
return this.runCommit(async () => {
|
|
28134
|
+
const head = await getLineageHead(this.storage, record);
|
|
28135
|
+
if (!head) return null;
|
|
28136
|
+
targetId = head.id;
|
|
28137
|
+
const input = {
|
|
28138
|
+
id: targetId,
|
|
28139
|
+
chunk: {
|
|
28140
|
+
cycleId: this.cycleId,
|
|
28141
|
+
observations: processed.observations,
|
|
28142
|
+
tokenCount: processed.observationTokens,
|
|
28143
|
+
messageIds: processed.observedMessageIds,
|
|
28144
|
+
messageTokens,
|
|
28145
|
+
lastObservedAt: processed.lastObservedAt,
|
|
28146
|
+
suggestedContinuation: processed.suggestedContinuation,
|
|
28147
|
+
currentTask: processed.currentTask,
|
|
28148
|
+
threadTitle: processed.threadTitle,
|
|
28149
|
+
extractedValues: processed.extractedValues,
|
|
28150
|
+
extractionFailures: processed.extractionFailures
|
|
28151
|
+
},
|
|
28152
|
+
lastBufferedAtTime: processed.lastObservedAt
|
|
28153
|
+
};
|
|
28154
|
+
return typeof this.storage.appendBufferedObservations === "function" ? this.storage.appendBufferedObservations(input) : this.storage.updateBufferedObservations(input).then(() => ({
|
|
28155
|
+
persisted: true,
|
|
28156
|
+
recordId: input.id
|
|
28157
|
+
}));
|
|
28158
|
+
});
|
|
28159
|
+
}, {
|
|
27657
28160
|
label: "persist-buffered-observations",
|
|
27658
28161
|
abortSignal: this.opts.abortSignal
|
|
27659
28162
|
});
|
|
27660
|
-
|
|
27661
|
-
|
|
27662
|
-
|
|
27663
|
-
|
|
27664
|
-
|
|
27665
|
-
|
|
27666
|
-
|
|
27667
|
-
|
|
27668
|
-
|
|
27669
|
-
|
|
27670
|
-
|
|
27671
|
-
|
|
27672
|
-
|
|
27673
|
-
|
|
27674
|
-
|
|
27675
|
-
|
|
27676
|
-
|
|
27677
|
-
|
|
27678
|
-
|
|
27679
|
-
|
|
27680
|
-
|
|
27681
|
-
|
|
27682
|
-
|
|
27683
|
-
|
|
27684
|
-
|
|
27685
|
-
|
|
27686
|
-
|
|
27687
|
-
|
|
27688
|
-
|
|
28163
|
+
if (appendResult === null) {
|
|
28164
|
+
omDebug(`[OM:asyncBuffer] skipping persist for thread ${threadId}: observational memory record was cleared`);
|
|
28165
|
+
return {
|
|
28166
|
+
status: "not-committed",
|
|
28167
|
+
reason: "the observational memory record was cleared"
|
|
28168
|
+
};
|
|
28169
|
+
}
|
|
28170
|
+
let committedRecord = liveRecord;
|
|
28171
|
+
if (!appendResult.persisted) {
|
|
28172
|
+
const head = appendAttempts > 1 ? await getLineageHead(this.storage, record) : null;
|
|
28173
|
+
if (!(!!head && (getBufferedChunks(head).some((chunk) => chunk.cycleId === this.cycleId) || head.activeObservations.includes(processed.observations)))) return {
|
|
28174
|
+
status: "not-committed",
|
|
28175
|
+
reason: "the buffered chunk was already observed"
|
|
28176
|
+
};
|
|
28177
|
+
this.persistedRecordId = head.id;
|
|
28178
|
+
committedRecord = head;
|
|
28179
|
+
} else this.persistedRecordId = appendResult.recordId;
|
|
28180
|
+
await this.emitBufferingEndMarker(processed);
|
|
28181
|
+
await this.opts.onBufferedChunkPersisted?.();
|
|
28182
|
+
if (committedRecord.id !== this.persistedRecordId) {
|
|
28183
|
+
const landedOn = await getLineageRecord(this.storage, record, this.persistedRecordId);
|
|
28184
|
+
if (!landedOn) return {
|
|
28185
|
+
status: "not-committed",
|
|
28186
|
+
reason: "the observational memory record was cleared"
|
|
28187
|
+
};
|
|
28188
|
+
committedRecord = landedOn;
|
|
28189
|
+
}
|
|
28190
|
+
try {
|
|
28191
|
+
await this.indexObservationGroups(processed.observations, threadId, resourceId, processed.lastObservedAt, this.persistedRecordId);
|
|
28192
|
+
const candidateTitle = processed.threadTitle?.trim();
|
|
28193
|
+
const hasValidThreadTitle = !!candidateTitle && candidateTitle.length >= 3;
|
|
28194
|
+
if (hasValidThreadTitle || processed.extractedValues) {
|
|
28195
|
+
const thread = await this.storage.getThreadById({ threadId });
|
|
28196
|
+
if (thread) {
|
|
28197
|
+
const oldTitle = thread.title?.trim();
|
|
28198
|
+
const newTitle = resolveThreadTitleUpdate(thread, candidateTitle);
|
|
28199
|
+
const shouldUpdateThreadTitle = newTitle !== void 0;
|
|
28200
|
+
const previousOmMetadata = getThreadOMMetadata(thread.metadata);
|
|
28201
|
+
const metadataUpdate = buildThreadMetadataFromExtractedValues(processed.extractors ?? this.observationConfig.extractors, processed.extractedValues);
|
|
28202
|
+
const newMetadata = setThreadOMMetadata(thread.metadata, {
|
|
28203
|
+
...hasValidThreadTitle || metadataUpdate.threadTitle ? { threadTitle: metadataUpdate.threadTitle ?? processed.threadTitle } : {},
|
|
28204
|
+
extracted: {
|
|
28205
|
+
...previousOmMetadata?.extracted ?? {},
|
|
28206
|
+
...metadataUpdate.extracted ?? {}
|
|
28207
|
+
}
|
|
27689
28208
|
});
|
|
27690
|
-
await this.
|
|
28209
|
+
await this.storage.patchThread({
|
|
28210
|
+
id: threadId,
|
|
28211
|
+
...shouldUpdateThreadTitle ? { title: newTitle } : {},
|
|
28212
|
+
metadata: newMetadata
|
|
28213
|
+
});
|
|
28214
|
+
if (shouldUpdateThreadTitle) {
|
|
28215
|
+
const marker = createThreadUpdateMarker({
|
|
28216
|
+
cycleId: this.cycleId,
|
|
28217
|
+
threadId,
|
|
28218
|
+
oldTitle,
|
|
28219
|
+
newTitle
|
|
28220
|
+
});
|
|
28221
|
+
await this.streamMarker(marker);
|
|
28222
|
+
}
|
|
27691
28223
|
}
|
|
27692
28224
|
}
|
|
28225
|
+
} catch (error) {
|
|
28226
|
+
omError("[OM] Post-buffer indexing or thread update failed; the buffered chunk is kept", error);
|
|
27693
28227
|
}
|
|
27694
|
-
return
|
|
28228
|
+
return {
|
|
28229
|
+
status: "committed",
|
|
28230
|
+
processed,
|
|
28231
|
+
record: committedRecord
|
|
28232
|
+
};
|
|
27695
28233
|
}
|
|
27696
|
-
async emitEndMarkers(
|
|
27697
|
-
|
|
28234
|
+
async emitEndMarkers() {}
|
|
28235
|
+
async emitBufferingEndMarker(processed) {
|
|
27698
28236
|
const { record, threadId, messages } = this.opts;
|
|
27699
28237
|
const tokensBuffered = await this.tokenCounter.countMessagesAsync(messages);
|
|
27700
28238
|
const totalBufferedTokens = getBufferedChunks(await this.storage.getObservationalMemory(record.threadId, record.resourceId)).reduce((sum, c) => sum + (c.tokenCount ?? 0), 0) || processed.observationTokens;
|
|
@@ -27704,7 +28242,7 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
27704
28242
|
startedAt: this.startedAt,
|
|
27705
28243
|
tokensBuffered,
|
|
27706
28244
|
bufferedTokens: totalBufferedTokens,
|
|
27707
|
-
recordId: record.id,
|
|
28245
|
+
recordId: this.persistedRecordId ?? record.id,
|
|
27708
28246
|
threadId,
|
|
27709
28247
|
observations: processed.observations,
|
|
27710
28248
|
extractedValues: processed.extractedValues,
|
|
@@ -27714,7 +28252,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
27714
28252
|
...endMarker,
|
|
27715
28253
|
transient: true
|
|
27716
28254
|
}).catch(() => {});
|
|
27717
|
-
await this.persistMarkerToStorage(endMarker, threadId, record.resourceId ?? void 0
|
|
28255
|
+
await this.persistMarkerToStorage(endMarker, threadId, record.resourceId ?? void 0, {
|
|
28256
|
+
notAfter: new Date(this.startedAt),
|
|
28257
|
+
onCycleStartMessage: true
|
|
28258
|
+
});
|
|
27718
28259
|
}
|
|
27719
28260
|
async emitFailedMarkers(_cycleId, error) {
|
|
27720
28261
|
const { record, threadId, messages } = this.opts;
|
|
@@ -27733,7 +28274,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
27733
28274
|
...failedMarker,
|
|
27734
28275
|
transient: true
|
|
27735
28276
|
}).catch(() => {});
|
|
27736
|
-
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0
|
|
28277
|
+
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0, {
|
|
28278
|
+
notAfter: new Date(this.startedAt),
|
|
28279
|
+
onCycleStartMessage: true
|
|
28280
|
+
});
|
|
27737
28281
|
}
|
|
27738
28282
|
};
|
|
27739
28283
|
//#endregion
|
|
@@ -27756,10 +28300,17 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
27756
28300
|
lastBatchProviderMetadata;
|
|
27757
28301
|
observationResults = [];
|
|
27758
28302
|
priorMetadataByThread = /* @__PURE__ */ new Map();
|
|
28303
|
+
/** Each observed thread's wrapped section from the last composition. */
|
|
28304
|
+
threadSections = /* @__PURE__ */ new Map();
|
|
28305
|
+
/** Head text the processed observations were composed from (the commit's expected text). */
|
|
28306
|
+
composedFrom = "";
|
|
27759
28307
|
constructor(deps, opts) {
|
|
27760
28308
|
super(deps, opts);
|
|
27761
28309
|
this.resourceId = opts.resourceId;
|
|
27762
28310
|
}
|
|
28311
|
+
getObservedMessagesForThread(threadId) {
|
|
28312
|
+
return this.threadsWithMessages.get(threadId);
|
|
28313
|
+
}
|
|
27763
28314
|
get needsLock() {
|
|
27764
28315
|
return true;
|
|
27765
28316
|
}
|
|
@@ -27846,10 +28397,12 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
27846
28397
|
content: typeof m.content === "string" ? m.content : JSON.stringify(m.content)
|
|
27847
28398
|
}))
|
|
27848
28399
|
});
|
|
27849
|
-
const
|
|
28400
|
+
const freshRecord = await this.storage.getObservationalMemory(null, this.resourceId);
|
|
28401
|
+
const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
|
|
27850
28402
|
return {
|
|
27851
28403
|
messages: Array.from(this.threadsWithMessages.values()).flat(),
|
|
27852
|
-
existingObservations
|
|
28404
|
+
existingObservations,
|
|
28405
|
+
contextRecord: freshRecord
|
|
27853
28406
|
};
|
|
27854
28407
|
}
|
|
27855
28408
|
async emitStartMarkers(cycleId) {
|
|
@@ -27873,7 +28426,7 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
27873
28426
|
}
|
|
27874
28427
|
}
|
|
27875
28428
|
}
|
|
27876
|
-
async observe(
|
|
28429
|
+
async observe(observerContext, _messages, wasTruncated) {
|
|
27877
28430
|
const maxTokensPerBatch = this.observationConfig.maxTokensPerBatch ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.maxTokensPerBatch;
|
|
27878
28431
|
const orderedThreadIds = this.threadOrder.filter((tid) => this.threadsWithMessages.has(tid));
|
|
27879
28432
|
const batches = [];
|
|
@@ -27899,10 +28452,10 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
27899
28452
|
}
|
|
27900
28453
|
if (currentBatch.threadIds.length > 0) batches.push(currentBatch);
|
|
27901
28454
|
const batchResults = await Promise.all(batches.map(async (batch) => {
|
|
27902
|
-
return this.deps.observer.callMultiThread(
|
|
28455
|
+
return this.deps.observer.callMultiThread(observerContext, batch.threadMap, batch.threadIds, this.opts.abortSignal, this.opts.requestContext, this.priorMetadataByThread, this.opts.observabilityContext, void 0, {
|
|
27903
28456
|
resourceId: this.opts.resourceId,
|
|
27904
28457
|
trigger: this.opts.trigger
|
|
27905
|
-
}, this.opts.record.observedTimezone, this.opts.agent, this.opts.currentModel);
|
|
28458
|
+
}, this.opts.record.observedTimezone, this.opts.agent, this.opts.currentModel, wasTruncated);
|
|
27906
28459
|
}));
|
|
27907
28460
|
for (const batchResult of batchResults) {
|
|
27908
28461
|
for (const [threadId, result] of batchResult.results) this.multiThreadResults.set(threadId, result);
|
|
@@ -27969,14 +28522,38 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
27969
28522
|
}
|
|
27970
28523
|
});
|
|
27971
28524
|
}
|
|
28525
|
+
this.composedFrom = existingObservations;
|
|
28526
|
+
const processed = await this.compose(existingObservations, record);
|
|
28527
|
+
for (const [index, { threadId, threadMessages, result }] of this.observationResults.entries()) this.deps.emitDebugEvent({
|
|
28528
|
+
type: "observation_complete",
|
|
28529
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
28530
|
+
threadId,
|
|
28531
|
+
resourceId: this.resourceId,
|
|
28532
|
+
observations: this.threadSections.get(threadId) ?? "",
|
|
28533
|
+
rawObserverOutput: result.observations,
|
|
28534
|
+
previousObservations: record.activeObservations,
|
|
28535
|
+
messages: threadMessages.map((m) => ({
|
|
28536
|
+
role: m.role,
|
|
28537
|
+
content: typeof m.content === "string" ? m.content : JSON.stringify(m.content)
|
|
28538
|
+
})),
|
|
28539
|
+
usage: index === 0 && this.totalBatchUsage.totalTokens > 0 ? this.totalBatchUsage : void 0
|
|
28540
|
+
});
|
|
28541
|
+
return processed;
|
|
28542
|
+
}
|
|
28543
|
+
/**
|
|
28544
|
+
* Merge every observed thread's section into `existingObservations` (the head text). Resource
|
|
28545
|
+
* scope rewrites an existing same-day `<thread>` section in place, so the result is not an
|
|
28546
|
+
* append of the head text.
|
|
28547
|
+
*/
|
|
28548
|
+
async compose(existingObservations, baseRecord) {
|
|
27972
28549
|
let currentObservations = existingObservations;
|
|
27973
28550
|
let cycleObservationTokens = 0;
|
|
27974
28551
|
const threadMetadataUpdates = [];
|
|
27975
|
-
for (const
|
|
27976
|
-
const { threadId, threadMessages, result } = obsResult;
|
|
28552
|
+
for (const { threadId, threadMessages, result } of this.observationResults) {
|
|
27977
28553
|
cycleObservationTokens += this.tokenCounter.countObservations(result.observations);
|
|
27978
28554
|
const messageRange = this.retrieval ? buildMessageRange(threadMessages) : void 0;
|
|
27979
28555
|
const threadSection = await this.wrapWithThreadTag(threadId, result.observations, messageRange);
|
|
28556
|
+
this.threadSections.set(threadId, threadSection);
|
|
27980
28557
|
const threadLastObservedAt = this.getMaxMessageTimestamp(threadMessages);
|
|
27981
28558
|
currentObservations = this.replaceOrAppendThreadSection(currentObservations, threadId, threadSection, threadLastObservedAt);
|
|
27982
28559
|
threadMetadataUpdates.push({
|
|
@@ -27990,26 +28567,11 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
27990
28567
|
extractors: result.extractors,
|
|
27991
28568
|
lastObservedMessageCursor: getLastObservedMessageCursor(threadMessages)
|
|
27992
28569
|
});
|
|
27993
|
-
const isFirstThread = this.observationResults.indexOf(obsResult) === 0;
|
|
27994
|
-
this.deps.emitDebugEvent({
|
|
27995
|
-
type: "observation_complete",
|
|
27996
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
27997
|
-
threadId,
|
|
27998
|
-
resourceId: this.resourceId,
|
|
27999
|
-
observations: threadSection,
|
|
28000
|
-
rawObserverOutput: result.observations,
|
|
28001
|
-
previousObservations: record.activeObservations,
|
|
28002
|
-
messages: threadMessages.map((m) => ({
|
|
28003
|
-
role: m.role,
|
|
28004
|
-
content: typeof m.content === "string" ? m.content : JSON.stringify(m.content)
|
|
28005
|
-
})),
|
|
28006
|
-
usage: isFirstThread && this.totalBatchUsage.totalTokens > 0 ? this.totalBatchUsage : void 0
|
|
28007
|
-
});
|
|
28008
28570
|
}
|
|
28009
28571
|
const observedMessages = this.observationResults.flatMap((r) => r.threadMessages);
|
|
28010
28572
|
const lastObservedAt = this.getMaxMessageTimestamp(observedMessages);
|
|
28011
28573
|
const newMessageIds = observedMessages.map((m) => m.id);
|
|
28012
|
-
const existingIds =
|
|
28574
|
+
const existingIds = baseRecord.observedMessageIds ?? [];
|
|
28013
28575
|
const observedMessageIds = [.../* @__PURE__ */ new Set([...existingIds, ...newMessageIds])];
|
|
28014
28576
|
const observationTokens = this.tokenCounter.countObservations(currentObservations);
|
|
28015
28577
|
return {
|
|
@@ -28023,8 +28585,25 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
28023
28585
|
}
|
|
28024
28586
|
async persist(processed) {
|
|
28025
28587
|
const { record, resourceId } = this.opts;
|
|
28588
|
+
const head = await getLineageHead(this.storage, record);
|
|
28589
|
+
if (!head) return {
|
|
28590
|
+
status: "not-committed",
|
|
28591
|
+
reason: "the observational memory record was cleared"
|
|
28592
|
+
};
|
|
28593
|
+
const committed = await this.commitActiveObservationsToHead({
|
|
28594
|
+
processed,
|
|
28595
|
+
composedFrom: this.composedFrom,
|
|
28596
|
+
target: head,
|
|
28597
|
+
recompose: (fresh) => this.compose(fresh.activeObservations ?? "", fresh),
|
|
28598
|
+
cycleMessageIds: this.observationResults.flatMap((r) => r.threadMessages.map((m) => m.id))
|
|
28599
|
+
});
|
|
28600
|
+
if (!committed) return {
|
|
28601
|
+
status: "not-committed",
|
|
28602
|
+
reason: "the observational memory head kept changing during the commit"
|
|
28603
|
+
};
|
|
28604
|
+
processed = committed.processed;
|
|
28026
28605
|
const threadUpdateMarkers = [];
|
|
28027
|
-
if (processed.threadMetadataUpdates) for (const update of processed.threadMetadataUpdates) {
|
|
28606
|
+
if (processed.threadMetadataUpdates && !committed.alreadyCovered) for (const update of processed.threadMetadataUpdates) {
|
|
28028
28607
|
const thread = await this.storage.getThreadById({ threadId: update.threadId });
|
|
28029
28608
|
if (thread) {
|
|
28030
28609
|
const oldTitle = thread.title?.trim();
|
|
@@ -28057,15 +28636,13 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
28057
28636
|
}
|
|
28058
28637
|
}
|
|
28059
28638
|
for (const marker of threadUpdateMarkers) await this.streamMarker(marker);
|
|
28060
|
-
await this.
|
|
28061
|
-
|
|
28062
|
-
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
}
|
|
28067
|
-
if (resourceId) await Promise.all(this.observationResults.map(({ threadId, threadMessages, result }) => this.indexObservationGroups(result.observations, threadId, resourceId, this.getMaxMessageTimestamp(threadMessages), record.id)));
|
|
28068
|
-
return true;
|
|
28639
|
+
if (resourceId && !committed.alreadyCovered) await Promise.all(this.observationResults.map(({ threadId, threadMessages, result }) => this.indexObservationGroups(result.observations, threadId, resourceId, this.getMaxMessageTimestamp(threadMessages), committed.record.id)));
|
|
28640
|
+
return {
|
|
28641
|
+
status: "committed",
|
|
28642
|
+
processed,
|
|
28643
|
+
record: committed.record,
|
|
28644
|
+
alreadyCovered: committed.alreadyCovered
|
|
28645
|
+
};
|
|
28069
28646
|
}
|
|
28070
28647
|
async emitEndMarkers(cycleId, processed) {
|
|
28071
28648
|
for (const obsResult of this.observationResults) {
|
|
@@ -28124,7 +28701,8 @@ ObservationStrategy.create = ((om, opts) => {
|
|
|
28124
28701
|
observedMessageIds: om.observedMessageIds,
|
|
28125
28702
|
obscureThreadIds: om.getObscureThreadIds(),
|
|
28126
28703
|
onIndexObservations: om.onIndexObservations,
|
|
28127
|
-
emitDebugEvent: (e) => om.emitDebugEvent(e)
|
|
28704
|
+
emitDebugEvent: (e) => om.emitDebugEvent(e),
|
|
28705
|
+
prepareObserverContext: (observations, record) => om.prepareObserverContext(observations, record)
|
|
28128
28706
|
};
|
|
28129
28707
|
if (opts.cycleId) return new AsyncBufferObservationStrategy(deps, opts);
|
|
28130
28708
|
if (deps.scope === "resource" && opts.resourceId) return new ResourceScopedObservationStrategy(deps, opts);
|
|
@@ -28256,8 +28834,8 @@ var ObservationStep = class {
|
|
|
28256
28834
|
let reflected = false;
|
|
28257
28835
|
let didThresholdCleanup = false;
|
|
28258
28836
|
let observerExchange;
|
|
28837
|
+
const step0Messages = this.stepNumber === 0 ? getObservableMessages(messageList) : [];
|
|
28259
28838
|
if (this.stepNumber === 0) {
|
|
28260
|
-
const step0Messages = getObservableMessages(messageList);
|
|
28261
28839
|
const activation = await om.activate({
|
|
28262
28840
|
threadId,
|
|
28263
28841
|
resourceId,
|
|
@@ -28314,7 +28892,17 @@ var ObservationStep = class {
|
|
|
28314
28892
|
record: this.turn.record,
|
|
28315
28893
|
messages: getObservableMessages(messageList)
|
|
28316
28894
|
});
|
|
28317
|
-
if (statusSnapshot.
|
|
28895
|
+
if (statusSnapshot.inAsyncObservationBand && !statusSnapshot.canActivate && !hasIncompleteToolCalls) {
|
|
28896
|
+
await this.turn.refreshRecord();
|
|
28897
|
+
statusSnapshot = await om.getStatus({
|
|
28898
|
+
threadId,
|
|
28899
|
+
resourceId,
|
|
28900
|
+
record: this.turn.record,
|
|
28901
|
+
messages: getObservableMessages(messageList)
|
|
28902
|
+
});
|
|
28903
|
+
}
|
|
28904
|
+
const activateBeforeBuffering = statusSnapshot.inAsyncObservationBand && statusSnapshot.canActivate && !hasIncompleteToolCalls;
|
|
28905
|
+
if (statusSnapshot.shouldBuffer && !activateBeforeBuffering) {
|
|
28318
28906
|
const allMessages = getObservableMessages(messageList);
|
|
28319
28907
|
const unobservedMessages = om.getUnobservedMessages(allMessages, statusSnapshot.record);
|
|
28320
28908
|
const candidates = om.getUnobservedMessages(unobservedMessages, statusSnapshot.record, { excludeBuffered: true });
|
|
@@ -28328,8 +28916,10 @@ var ObservationStep = class {
|
|
|
28328
28916
|
omDebug(`[OM:buffer] onBufferChunkSealed hook failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
28329
28917
|
}
|
|
28330
28918
|
if (this.turn.memory) await this.turn.memory.persistMessages(safeCandidates);
|
|
28331
|
-
messageList.
|
|
28332
|
-
|
|
28919
|
+
const inputIds = new Set(messageList.get.input.db().map((msg) => msg.id));
|
|
28920
|
+
const sealedOutput = safeCandidates.filter((msg) => !inputIds.has(msg.id));
|
|
28921
|
+
messageList.removeByIds(sealedOutput.map((msg) => msg.id));
|
|
28922
|
+
for (const msg of sealedOutput) messageList.add(msg, "memory");
|
|
28333
28923
|
}
|
|
28334
28924
|
if (!deferred) {
|
|
28335
28925
|
om.trackBackgroundWork(om.buffer({
|
|
@@ -28351,7 +28941,8 @@ var ObservationStep = class {
|
|
|
28351
28941
|
buffered = true;
|
|
28352
28942
|
}
|
|
28353
28943
|
}
|
|
28354
|
-
const
|
|
28944
|
+
const bandDefersObservation = statusSnapshot.inAsyncObservationBand && !statusSnapshot.canActivate;
|
|
28945
|
+
const willObserveNow = statusSnapshot.shouldObserve && !hasIncompleteToolCalls && !bandDefersObservation;
|
|
28355
28946
|
/** In-flight message ids the step-0 cleanup must never remove from live context. */
|
|
28356
28947
|
let step0PreserveIds;
|
|
28357
28948
|
if (this.stepNumber > 0 || willObserveNow) {
|
|
@@ -28393,7 +28984,7 @@ var ObservationStep = class {
|
|
|
28393
28984
|
}
|
|
28394
28985
|
if (willObserveNow) {
|
|
28395
28986
|
const preObsGeneration = this.turn.record.generationCount;
|
|
28396
|
-
const obsResult = await this.runThresholdObservation();
|
|
28987
|
+
const obsResult = await this.runThresholdObservation(statusSnapshot.inAsyncObservationBand);
|
|
28397
28988
|
observerExchange = obsResult.observerExchange;
|
|
28398
28989
|
if (obsResult.succeeded) {
|
|
28399
28990
|
observed = true;
|
|
@@ -28466,12 +29057,12 @@ var ObservationStep = class {
|
|
|
28466
29057
|
/**
|
|
28467
29058
|
* Run the full threshold observation pipeline:
|
|
28468
29059
|
* waitForBuffering → re-check → activate buffered chunks → reflect → observe
|
|
28469
|
-
* (sync fallback
|
|
29060
|
+
* (sync fallback at blockAfter when async buffering is enabled)
|
|
28470
29061
|
*/
|
|
28471
|
-
async runThresholdObservation() {
|
|
29062
|
+
async runThresholdObservation(inAsyncObservationBand) {
|
|
28472
29063
|
const { threadId, resourceId, messageList } = this.turn;
|
|
28473
29064
|
const om = this.turn.om;
|
|
28474
|
-
await om.waitForBuffering(threadId, resourceId);
|
|
29065
|
+
if (!inAsyncObservationBand) await om.waitForBuffering(threadId, resourceId);
|
|
28475
29066
|
await this.turn.refreshRecord();
|
|
28476
29067
|
const observableMessages = this.seededResponseMessage ? getObservableMessages(messageList).filter((msg) => msg.id !== this.turn.responseMessageId) : getObservableMessages(messageList);
|
|
28477
29068
|
let status = await om.getStatus({
|
|
@@ -28500,7 +29091,16 @@ var ObservationStep = class {
|
|
|
28500
29091
|
messageList
|
|
28501
29092
|
});
|
|
28502
29093
|
this.turn.setRecord(activation.record);
|
|
28503
|
-
if (!activation.activated)
|
|
29094
|
+
if (!activation.activated) {
|
|
29095
|
+
await this.turn.refreshRecord();
|
|
29096
|
+
status = await om.getStatus({
|
|
29097
|
+
threadId,
|
|
29098
|
+
resourceId,
|
|
29099
|
+
record: this.turn.record,
|
|
29100
|
+
messages: pendingMessages
|
|
29101
|
+
});
|
|
29102
|
+
break;
|
|
29103
|
+
}
|
|
28504
29104
|
activated = true;
|
|
28505
29105
|
const ids = new Set(activation.activatedMessageIds ?? []);
|
|
28506
29106
|
activatedMessageIds.push(...ids);
|
|
@@ -28512,11 +29112,17 @@ var ObservationStep = class {
|
|
|
28512
29112
|
messages: pendingMessages
|
|
28513
29113
|
});
|
|
28514
29114
|
}
|
|
29115
|
+
if (status.shouldObserve && status.canActivate) return {
|
|
29116
|
+
succeeded: activated,
|
|
29117
|
+
record: this.turn.record,
|
|
29118
|
+
activatedMessageIds: activated ? activatedMessageIds : void 0,
|
|
29119
|
+
cleanupMessageIds: activatedMessageIds
|
|
29120
|
+
};
|
|
28515
29121
|
if (activated) {
|
|
28516
|
-
const
|
|
29122
|
+
const preReflectionRecord = this.turn.record;
|
|
28517
29123
|
await om.reflector.maybeReflect({
|
|
28518
|
-
record:
|
|
28519
|
-
observationTokens:
|
|
29124
|
+
record: preReflectionRecord,
|
|
29125
|
+
observationTokens: preReflectionRecord.observationTokenCount ?? 0,
|
|
28520
29126
|
threadId,
|
|
28521
29127
|
writer: this.turn.writer,
|
|
28522
29128
|
messageList,
|
|
@@ -28531,6 +29137,14 @@ var ObservationStep = class {
|
|
|
28531
29137
|
}),
|
|
28532
29138
|
trigger: "turn-sync"
|
|
28533
29139
|
});
|
|
29140
|
+
await this.turn.refreshRecord();
|
|
29141
|
+
const postActivationRecord = this.turn.record;
|
|
29142
|
+
status = await om.getStatus({
|
|
29143
|
+
threadId,
|
|
29144
|
+
resourceId,
|
|
29145
|
+
record: postActivationRecord,
|
|
29146
|
+
messages: pendingMessages
|
|
29147
|
+
});
|
|
28534
29148
|
if (!status.shouldObserve) return {
|
|
28535
29149
|
succeeded: true,
|
|
28536
29150
|
record: postActivationRecord,
|
|
@@ -28538,6 +29152,12 @@ var ObservationStep = class {
|
|
|
28538
29152
|
cleanupMessageIds: activatedMessageIds
|
|
28539
29153
|
};
|
|
28540
29154
|
}
|
|
29155
|
+
if (status.inAsyncObservationBand || status.canActivate) return {
|
|
29156
|
+
succeeded: activated,
|
|
29157
|
+
record: this.turn.record,
|
|
29158
|
+
activatedMessageIds: activated ? activatedMessageIds : void 0,
|
|
29159
|
+
cleanupMessageIds: activatedMessageIds
|
|
29160
|
+
};
|
|
28541
29161
|
let obsResult;
|
|
28542
29162
|
try {
|
|
28543
29163
|
obsResult = await om.observe({
|
|
@@ -28889,13 +29509,18 @@ Take the existing observations and rewrite them to make it easier to continue in
|
|
|
28889
29509
|
|
|
28890
29510
|
IMPORTANT: your reflections are THE ENTIRETY of the assistants memory. Any information you do not add to your reflections will be immediately forgotten. Make sure you do not leave out anything. Your reflections must assume the assistant knows nothing - your reflections are the ENTIRE memory system.
|
|
28891
29511
|
|
|
29512
|
+
The assistant relies on this memory to answer precise questions about what the user said, when they said it, and how things changed over time.
|
|
29513
|
+
|
|
28892
29514
|
When consolidating observations:
|
|
28893
|
-
-
|
|
28894
|
-
-
|
|
29515
|
+
- Keep every concrete fact: names, places, dates, numbers, amounts, durations, titles, versions, and who said what. Merge duplicates, but never drop specifics
|
|
29516
|
+
- Keep every dated statement with its date, and retain the timestamps that matter (start times, completion times, significant events)
|
|
29517
|
+
- When the user changed their mind or a fact was updated, keep both the old and new values with their dates and say which is current
|
|
29518
|
+
- Keep what the user explicitly said they have never done, do not know, or refused, and keep every stated preference, rule, and instruction with its meaning intact
|
|
29519
|
+
- Prefer many short factual lines over prose summaries. Do not replace facts with generalities like "discussed travel plans"
|
|
28895
29520
|
- Combine related items where it makes sense (e.g., "agent called view tool 5 times on file x")
|
|
28896
29521
|
- Preserve ✅ completion markers — they are memory signals that tell the assistant what is already resolved and help prevent repeated work
|
|
28897
29522
|
- Preserve the concrete resolved outcome captured by ✅ markers so the assistant knows what exactly is done
|
|
28898
|
-
- Condense older observations more
|
|
29523
|
+
- Condense the wording of older observations more than recent ones, but keep their facts
|
|
28899
29524
|
|
|
28900
29525
|
CRITICAL: USER ASSERTIONS vs QUESTIONS
|
|
28901
29526
|
- "User stated: X" = authoritative assertion (user told us something about themselves)
|
|
@@ -29177,6 +29802,25 @@ async function withAbortCheck(fn, abortSignal) {
|
|
|
29177
29802
|
* size while still letting them fire sooner than a threshold activation.
|
|
29178
29803
|
*/
|
|
29179
29804
|
const EARLY_ACTIVATION_SIZE_FLOOR_RATIO = .75;
|
|
29805
|
+
/** A reflection commit that created nothing because the reflected record is no longer the head. */
|
|
29806
|
+
var ReflectionNotAppliedError = class extends Error {
|
|
29807
|
+
constructor(recordId, headId) {
|
|
29808
|
+
super(`Reflection not applied: record ${recordId} is no longer the current generation (head is ${headId})`);
|
|
29809
|
+
this.name = "ReflectionNotAppliedError";
|
|
29810
|
+
}
|
|
29811
|
+
};
|
|
29812
|
+
/**
|
|
29813
|
+
* Whether a reflection commit created the generation the caller asked for. Storage creates
|
|
29814
|
+
* nothing (and returns the current head or the stored record) when the reflected record was
|
|
29815
|
+
* already retired or its observations were rewritten since the reflection read them.
|
|
29816
|
+
*
|
|
29817
|
+
* Adapters that predate `newRecordId` ignore it and do not report `supersededBy`; for those a
|
|
29818
|
+
* returned record other than the reflected one means a new generation was created.
|
|
29819
|
+
*/
|
|
29820
|
+
function isReflectionApplied(returned, newRecordId, reflectedRecordId) {
|
|
29821
|
+
if (returned.id === newRecordId) return true;
|
|
29822
|
+
return returned.supersededBy === void 0 && returned.id !== reflectedRecordId;
|
|
29823
|
+
}
|
|
29180
29824
|
/**
|
|
29181
29825
|
* Runs the Reflector agent for compressing observations.
|
|
29182
29826
|
* Handles synchronous reflection, async buffered reflection, and activation.
|
|
@@ -29540,14 +30184,14 @@ var ReflectorRunner = class {
|
|
|
29540
30184
|
* Perform async buffered reflection — reflects observations and stores to bufferedReflection.
|
|
29541
30185
|
* Does NOT create a new generation or update activeObservations.
|
|
29542
30186
|
*/
|
|
29543
|
-
async doAsyncBufferedReflection(record,
|
|
30187
|
+
async doAsyncBufferedReflection(record, bufferKey, writer, requestContext, observabilityContext, priorExtractedValues, mainAgent, sendSignal, trigger, currentModel) {
|
|
29544
30188
|
const currentRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId) ?? record;
|
|
29545
30189
|
const observationTokens = currentRecord.observationTokenCount ?? 0;
|
|
29546
30190
|
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(currentRecord));
|
|
29547
30191
|
const bufferActivation = this.reflectionConfig.bufferActivation ?? .5;
|
|
29548
30192
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29549
30193
|
const cycleId = `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
29550
|
-
BufferingCoordinator.reflectionBufferCycleIds.set(
|
|
30194
|
+
BufferingCoordinator.reflectionBufferCycleIds.set(bufferKey, cycleId);
|
|
29551
30195
|
const allLines = (currentRecord.activeObservations ?? "").split("\n");
|
|
29552
30196
|
const totalLines = allLines.length;
|
|
29553
30197
|
const avgTokensPerLine = totalLines > 0 ? observationTokens / totalLines : 0;
|
|
@@ -29586,13 +30230,24 @@ var ReflectorRunner = class {
|
|
|
29586
30230
|
await persistThreadExtractedValues(this.storage, this.reflectionConfig.extractors, currentRecord.threadId ?? void 0, reflectResult.extractedValues);
|
|
29587
30231
|
const reflectionTokenCount = this.tokenCounter.countObservations(reflectResult.observations);
|
|
29588
30232
|
omDebug(`[OM:reflect] doAsyncBufferedReflection: reflector returned ${reflectionTokenCount} tokens (${reflectResult.observations?.length} chars), saving to recordId=${currentRecord.id}`);
|
|
29589
|
-
await this.
|
|
29590
|
-
id
|
|
29591
|
-
|
|
29592
|
-
|
|
29593
|
-
|
|
29594
|
-
|
|
29595
|
-
|
|
30233
|
+
if (!await runOMCommit(this.buffering.getLockKey(currentRecord.threadId, currentRecord.resourceId), async () => {
|
|
30234
|
+
if ((await this.storage.getObservationalMemory(currentRecord.threadId, currentRecord.resourceId))?.id !== currentRecord.id) return false;
|
|
30235
|
+
await this.storage.updateBufferedReflection({
|
|
30236
|
+
id: currentRecord.id,
|
|
30237
|
+
reflection: reflectResult.observations,
|
|
30238
|
+
tokenCount: reflectionTokenCount,
|
|
30239
|
+
inputTokenCount: sliceTokenEstimate,
|
|
30240
|
+
reflectedObservationLineCount
|
|
30241
|
+
});
|
|
30242
|
+
return true;
|
|
30243
|
+
})) {
|
|
30244
|
+
omDebug(`[OM:reflect] doAsyncBufferedReflection: record ${currentRecord.id} is no longer the head; discarding the buffered reflection`);
|
|
30245
|
+
BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
|
|
30246
|
+
return {
|
|
30247
|
+
usage: reflectResult.usage,
|
|
30248
|
+
providerMetadata: reflectResult.providerMetadata
|
|
30249
|
+
};
|
|
30250
|
+
}
|
|
29596
30251
|
omDebug(`[OM:reflect] doAsyncBufferedReflection: bufferedReflection saved with lineCount=${reflectedObservationLineCount}`);
|
|
29597
30252
|
if (writer) {
|
|
29598
30253
|
const endMarker = createBufferingEndMarker({
|
|
@@ -29671,10 +30326,16 @@ var ReflectorRunner = class {
|
|
|
29671
30326
|
}
|
|
29672
30327
|
}
|
|
29673
30328
|
omDebug(`[OM:reflect] tryActivateBufferedReflection: activating, beforeTokens=${beforeTokens}, combinedTokenCount=${combinedTokenCount}, reflectedLineCount=${reflectedLineCount}, unreflectedLines=${unreflectedLines.length}`);
|
|
29674
|
-
|
|
30329
|
+
const newRecordId = crypto.randomUUID();
|
|
30330
|
+
const swapped = await runOMCommit(lockKey, () => this.storage.swapBufferedReflectionToActive({
|
|
29675
30331
|
currentRecord: freshRecord,
|
|
29676
|
-
tokenCount: combinedTokenCount
|
|
29677
|
-
|
|
30332
|
+
tokenCount: combinedTokenCount,
|
|
30333
|
+
newRecordId
|
|
30334
|
+
}), { priority: "reflection" });
|
|
30335
|
+
if (!isReflectionApplied(swapped, newRecordId, freshRecord.id)) {
|
|
30336
|
+
omDebug(`[OM:reflect] tryActivateBufferedReflection: buffered reflection on ${freshRecord.id} not applied (head is ${swapped.id})`);
|
|
30337
|
+
return { status: "not-applied" };
|
|
30338
|
+
}
|
|
29678
30339
|
if (committedContext) await this.notifyReflectionCommitted({
|
|
29679
30340
|
...committedContext,
|
|
29680
30341
|
observations: allLines.slice(0, reflectedLineCount).join("\n").trim(),
|
|
@@ -29729,7 +30390,8 @@ var ReflectorRunner = class {
|
|
|
29729
30390
|
* @internal Used by observation strategies. Do not call directly.
|
|
29730
30391
|
*/
|
|
29731
30392
|
async maybeReflect(opts) {
|
|
29732
|
-
const { record, observationTokens, writer, abortSignal, mainAgent, sendSignal, sendStateSignal, messageList, currentModel, reflectionHooks, trigger, requestContext, observabilityContext, lastActivityAt, threadId: requestedThreadId } = opts;
|
|
30393
|
+
const { record: callerRecord, observationTokens, writer, abortSignal, mainAgent, sendSignal, sendStateSignal, messageList, currentModel, reflectionHooks, trigger, requestContext, observabilityContext, lastActivityAt, threadId: requestedThreadId } = opts;
|
|
30394
|
+
const record = { ...callerRecord };
|
|
29733
30395
|
const lockKey = this.buffering.getLockKey(record.threadId, record.resourceId);
|
|
29734
30396
|
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
29735
30397
|
const priorExtractedValues = await getThreadExtractedValues(this.storage, this.reflectionConfig.extractors, requestedThreadId ?? record.threadId);
|
|
@@ -29783,7 +30445,7 @@ var ReflectorRunner = class {
|
|
|
29783
30445
|
abortSignal,
|
|
29784
30446
|
observabilityContext
|
|
29785
30447
|
});
|
|
29786
|
-
if (activationResult.status === "activated") return;
|
|
30448
|
+
if (activationResult.status === "activated" || activationResult.status === "not-applied") return;
|
|
29787
30449
|
if (activationResult.status === "suppressed") {
|
|
29788
30450
|
omDebug(`[OM:reflect] skipping sync fallback / re-buffer after suppressed early ${activationMetadata.triggeredBy} activation (reason=${activationResult.reason})`);
|
|
29789
30451
|
return;
|
|
@@ -29866,13 +30528,19 @@ var ReflectorRunner = class {
|
|
|
29866
30528
|
reflectResult.observations = await applyTextTransform(this.hooks, "afterReflection", reflectResult.observations, hookContext);
|
|
29867
30529
|
reflectionUsage = reflectResult.usage;
|
|
29868
30530
|
reflectionProviderMetadata = reflectResult.providerMetadata;
|
|
29869
|
-
await persistThreadExtractedValues(this.storage, this.reflectionConfig.extractors, record.threadId ?? void 0, reflectResult.extractedValues);
|
|
29870
30531
|
const reflectionTokenCount = this.tokenCounter.countObservations(reflectResult.observations);
|
|
29871
|
-
|
|
30532
|
+
const newRecordId = crypto.randomUUID();
|
|
30533
|
+
const committedRecord = await runOMCommit(this.buffering.getLockKey(record.threadId, record.resourceId), () => this.storage.createReflectionGeneration({
|
|
29872
30534
|
currentRecord: record,
|
|
29873
30535
|
reflection: reflectResult.observations,
|
|
29874
|
-
tokenCount: reflectionTokenCount
|
|
29875
|
-
|
|
30536
|
+
tokenCount: reflectionTokenCount,
|
|
30537
|
+
newRecordId
|
|
30538
|
+
}), { priority: "reflection" });
|
|
30539
|
+
if (!isReflectionApplied(committedRecord, newRecordId, record.id)) {
|
|
30540
|
+
omDebug(`[OM:reflect] reflection of ${record.id} not applied (head is ${committedRecord.id})`);
|
|
30541
|
+
throw new ReflectionNotAppliedError(record.id, committedRecord.id);
|
|
30542
|
+
}
|
|
30543
|
+
await persistThreadExtractedValues(this.storage, this.reflectionConfig.extractors, record.threadId ?? void 0, reflectResult.extractedValues);
|
|
29876
30544
|
if (reflectionTokenCount >= reflectThreshold) this.syncReflectionSuppression.set(lockKey, reflectionTokenCount);
|
|
29877
30545
|
else this.syncReflectionSuppression.delete(lockKey);
|
|
29878
30546
|
await this.notifyReflectionCommitted({
|
|
@@ -29952,6 +30620,7 @@ var ReflectorRunner = class {
|
|
|
29952
30620
|
failureKind: failedMarker.data.failureKind,
|
|
29953
30621
|
error: reflectionError.message
|
|
29954
30622
|
});
|
|
30623
|
+
if (error instanceof ReflectionNotAppliedError) return;
|
|
29955
30624
|
if (lifecycleError !== void 0 || abortSignal?.aborted || this.reflectionConfig.failurePolicy !== "continue" || !isOmModelExecutionError(error) || error.failureKind !== "reflector-model") throw error;
|
|
29956
30625
|
this.syncReflectionSuppression.set(lockKey, observationTokens);
|
|
29957
30626
|
omError("[OM] Reflection failed", error);
|
|
@@ -30295,6 +30964,8 @@ function writeProcessInputStepReproCapture(params) {
|
|
|
30295
30964
|
* Returns the parts from the latest step of a message (after the last step-start marker).
|
|
30296
30965
|
* If no step-start marker exists, returns all parts.
|
|
30297
30966
|
*/
|
|
30967
|
+
/** Re-reads of the head after an activation swap hit a generation retired by a reflection. */
|
|
30968
|
+
const MAX_ACTIVATION_RETIRED_RETRIES = 3;
|
|
30298
30969
|
function getLatestStepParts(parts) {
|
|
30299
30970
|
for (let i = parts.length - 1; i >= 0; i--) if (parts[i]?.type === "step-start") return parts.slice(i + 1);
|
|
30300
30971
|
return parts;
|
|
@@ -30488,6 +31159,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
30488
31159
|
reflector;
|
|
30489
31160
|
/** Buffering state coordinator — manages static maps and buffering lifecycle. */
|
|
30490
31161
|
buffering;
|
|
31162
|
+
/** Unresolved observation blockAfter (multiplier or absolute); resolved per record. */
|
|
31163
|
+
observationBlockAfterSetting;
|
|
30491
31164
|
shouldObscureThreadIds = false;
|
|
30492
31165
|
hasher = xxhash();
|
|
30493
31166
|
mastra;
|
|
@@ -30623,6 +31296,7 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
30623
31296
|
else throw new Error("shareTokenBudget requires async buffering to be disabled (this is a temporary limitation). Add observation: { bufferTokens: false } to your config:\n\n observationalMemory: {\n shareTokenBudget: true,\n observation: { bufferTokens: false },\n }\n\nAsync buffering is enabled by default — this opt-out is only needed when using shareTokenBudget.");
|
|
30624
31297
|
const observationActivateAfterIdle = config.observation?.activateAfterIdle ?? config.activateAfterIdle;
|
|
30625
31298
|
const observationActivateAfterIdlePath = config.observation?.activateAfterIdle !== void 0 ? "observation.activateAfterIdle" : "activateAfterIdle";
|
|
31299
|
+
this.observationBlockAfterSetting = asyncBufferingDisabled ? void 0 : config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0);
|
|
30626
31300
|
this.observationConfig = {
|
|
30627
31301
|
model: observationModel,
|
|
30628
31302
|
maxRetries: config.observation?.maxRetries ?? RETRY_CONFIG.maxRetries,
|
|
@@ -30643,8 +31317,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
30643
31317
|
bufferActivation: asyncBufferingDisabled ? void 0 : config.observation?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferActivation,
|
|
30644
31318
|
activateAfterIdle: parseActivationTTLConfig(observationActivateAfterIdle, observationActivateAfterIdlePath),
|
|
30645
31319
|
activateOnProviderChange: config.observation?.activateOnProviderChange ?? config.activateOnProviderChange ?? false,
|
|
30646
|
-
blockAfter:
|
|
30647
|
-
previousObserverTokens: config.observation?.previousObserverTokens ??
|
|
31320
|
+
blockAfter: resolveBlockAfter(this.observationBlockAfterSetting, config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens),
|
|
31321
|
+
previousObserverTokens: config.observation?.previousObserverTokens ?? false,
|
|
30648
31322
|
instruction: config.observation?.instruction,
|
|
30649
31323
|
threadTitle: config.observation?.threadTitle ?? false,
|
|
30650
31324
|
observeAttachments: config.observation?.observeAttachments ?? [...OBSERVATIONAL_MEMORY_DEFAULTS.observation.observeAttachments],
|
|
@@ -30953,6 +31627,13 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
30953
31627
|
return this.observationConfig.messageTokens;
|
|
30954
31628
|
}
|
|
30955
31629
|
/**
|
|
31630
|
+
* Resolve the observation blockAfter for a record. A multiplier scales the record's
|
|
31631
|
+
* effective messageTokens (per-record overrides included); an absolute value is used as-is.
|
|
31632
|
+
*/
|
|
31633
|
+
getEffectiveObservationBlockAfter(record) {
|
|
31634
|
+
return resolveBlockAfter(this.observationBlockAfterSetting, this.getEffectiveMessageTokens(record));
|
|
31635
|
+
}
|
|
31636
|
+
/**
|
|
30956
31637
|
* Resolve the effective reflection observationTokens for a record.
|
|
30957
31638
|
* Only explicit per-record overrides (stored under `_overrides`) win;
|
|
30958
31639
|
* the initial config snapshot is ignored so instance-level changes
|
|
@@ -31066,18 +31747,21 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
31066
31747
|
* the latest user input that triggered it.
|
|
31067
31748
|
* @internal Used by observation strategies and the reflector. Do not call directly.
|
|
31068
31749
|
*/
|
|
31069
|
-
async persistMarkerToStorage(marker, threadId, resourceId) {
|
|
31750
|
+
async persistMarkerToStorage(marker, threadId, resourceId, opts) {
|
|
31070
31751
|
try {
|
|
31071
|
-
const
|
|
31752
|
+
const markerData = marker.data;
|
|
31753
|
+
const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
|
|
31754
|
+
const preferred = [...opts?.preferMessages ?? []].reverse().find((m) => m.role === targetRole && (!m.threadId || m.threadId === threadId));
|
|
31755
|
+
const preferredStored = preferred ? (await this.storage.listMessagesById({ messageIds: [preferred.id] })).messages : [];
|
|
31756
|
+
const messages = (preferredStored.length ? { messages: preferredStored } : await this.storage.listMessages({
|
|
31072
31757
|
threadId,
|
|
31073
31758
|
perPage: 20,
|
|
31074
31759
|
orderBy: {
|
|
31075
31760
|
field: "createdAt",
|
|
31076
31761
|
direction: "DESC"
|
|
31077
|
-
}
|
|
31762
|
+
},
|
|
31763
|
+
...opts?.notAfter ? { filter: { dateRange: { end: opts.notAfter } } } : {}
|
|
31078
31764
|
}))?.messages ?? [];
|
|
31079
|
-
const markerData = marker.data;
|
|
31080
|
-
const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
|
|
31081
31765
|
for (const msg of messages) if (msg?.role === targetRole && msg.content?.parts && Array.isArray(msg.content.parts)) {
|
|
31082
31766
|
if (!(markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) msg.content.parts.push(marker);
|
|
31083
31767
|
await this.messageHistory.persistMessages({
|
|
@@ -31164,7 +31848,7 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
31164
31848
|
*/
|
|
31165
31849
|
createUnobservedMessage(message) {
|
|
31166
31850
|
const unobservedParts = getUnobservedParts(message);
|
|
31167
|
-
if (unobservedParts.
|
|
31851
|
+
if (unobservedParts.every((part) => part?.type?.startsWith("data-om-"))) return null;
|
|
31168
31852
|
return {
|
|
31169
31853
|
...message,
|
|
31170
31854
|
content: {
|
|
@@ -31536,17 +32220,17 @@ ${formattedMessages}
|
|
|
31536
32220
|
async startAsyncBufferedObservation(record, threadId, unobservedMessages, lockKey, writer, contextWindowTokens, requestContext, observabilityContext) {
|
|
31537
32221
|
const bufferKey = this.buffering.getObservationBufferKey(lockKey);
|
|
31538
32222
|
const currentTokens = contextWindowTokens ?? await this.tokenCounter.countMessagesAsync(unobservedMessages) + (record.pendingMessageTokens ?? 0);
|
|
31539
|
-
BufferingCoordinator.
|
|
32223
|
+
BufferingCoordinator.setObservationBoundary(bufferKey, currentTokens);
|
|
31540
32224
|
registerOp(record.id, "bufferingObservation");
|
|
31541
|
-
this.storage.setBufferingObservationFlag(record.id, true, currentTokens).catch((err) => {
|
|
32225
|
+
runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, true, currentTokens)).catch((err) => {
|
|
31542
32226
|
omError("[OM] Failed to set buffering observation flag", err);
|
|
31543
32227
|
});
|
|
31544
32228
|
const asyncOp = this.runAsyncBufferedObservation(record, threadId, unobservedMessages, bufferKey, writer, requestContext, observabilityContext).catch((err) => {
|
|
31545
32229
|
omError("[OM] async buffering observation failed", err);
|
|
31546
32230
|
}).finally(() => {
|
|
31547
|
-
BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
|
|
32231
|
+
if (BufferingCoordinator.asyncBufferingOps.get(bufferKey) === asyncOp) BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
|
|
31548
32232
|
unregisterOp(record.id, "bufferingObservation");
|
|
31549
|
-
this.storage.setBufferingObservationFlag(record.id, false).catch((err) => {
|
|
32233
|
+
runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, false)).catch((err) => {
|
|
31550
32234
|
omError("[OM] Failed to clear buffering observation flag", err);
|
|
31551
32235
|
});
|
|
31552
32236
|
});
|
|
@@ -31561,6 +32245,14 @@ ${formattedMessages}
|
|
|
31561
32245
|
if (existingOp) try {
|
|
31562
32246
|
await existingOp;
|
|
31563
32247
|
} catch {}
|
|
32248
|
+
const releaseChunkWrite = BufferingCoordinator.trackChunkWrite(bufferKey);
|
|
32249
|
+
try {
|
|
32250
|
+
await this.bufferObservationChunk(record, threadId, unobservedMessages, bufferKey, releaseChunkWrite, writer, requestContext, observabilityContext);
|
|
32251
|
+
} finally {
|
|
32252
|
+
releaseChunkWrite();
|
|
32253
|
+
}
|
|
32254
|
+
}
|
|
32255
|
+
async bufferObservationChunk(record, threadId, unobservedMessages, bufferKey, releaseChunkWrite, writer, requestContext, observabilityContext) {
|
|
31564
32256
|
const freshRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
31565
32257
|
if (!freshRecord) return;
|
|
31566
32258
|
let bufferCursor = BufferingCoordinator.lastBufferedAtTime.get(bufferKey) ?? freshRecord.lastBufferedAtTime ?? null;
|
|
@@ -31601,13 +32293,16 @@ ${formattedMessages}
|
|
|
31601
32293
|
threadIds: [threadId],
|
|
31602
32294
|
config: this.getObservationMarkerConfig()
|
|
31603
32295
|
});
|
|
31604
|
-
await this.persistMarkerToStorage(startMarker, threadId, freshRecord.resourceId ?? void 0
|
|
32296
|
+
await this.persistMarkerToStorage(startMarker, threadId, freshRecord.resourceId ?? void 0, {
|
|
32297
|
+
notAfter: new Date(startedAt),
|
|
32298
|
+
preferMessages: messagesToBuffer
|
|
32299
|
+
});
|
|
31605
32300
|
if (writer) writer.custom({
|
|
31606
32301
|
...startMarker,
|
|
31607
32302
|
transient: true
|
|
31608
32303
|
}).catch(() => {});
|
|
31609
32304
|
omDebug(`[OM:bufferInput] cycleId=${cycleId}, msgCount=${messagesToBuffer.length}, msgTokens=${tokensToBuffer}, ids=${messagesToBuffer.map((m) => `${m.id?.slice(0, 8)}@${m.createdAt ? new Date(m.createdAt).toISOString() : "none"}`).join(",")}`);
|
|
31610
|
-
|
|
32305
|
+
await this.runBufferedObservationCycle({
|
|
31611
32306
|
threadId,
|
|
31612
32307
|
resourceId: freshRecord.resourceId ?? void 0,
|
|
31613
32308
|
trigger: "async-buffer"
|
|
@@ -31621,12 +32316,13 @@ ${formattedMessages}
|
|
|
31621
32316
|
writer,
|
|
31622
32317
|
requestContext,
|
|
31623
32318
|
observabilityContext,
|
|
31624
|
-
trigger: "async-buffer"
|
|
31625
|
-
|
|
31626
|
-
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
|
|
32319
|
+
trigger: "async-buffer",
|
|
32320
|
+
onBufferedChunkPersisted: async () => {
|
|
32321
|
+
const maxTs = this.getMaxMessageTimestamp(messagesToBuffer);
|
|
32322
|
+
BufferingCoordinator.lastBufferedAtTime.set(bufferKey, new Date(maxTs.getTime() + 1));
|
|
32323
|
+
releaseChunkWrite();
|
|
32324
|
+
}
|
|
32325
|
+
}).run());
|
|
31630
32326
|
}
|
|
31631
32327
|
/**
|
|
31632
32328
|
* Trigger async buffered observation if the token count has crossed a new interval.
|
|
@@ -31811,8 +32507,8 @@ ${formattedMessages}
|
|
|
31811
32507
|
const { threadId, resourceId, recordId, activatedMessageIds } = opts;
|
|
31812
32508
|
const lockKey = this.buffering.getLockKey(threadId, resourceId);
|
|
31813
32509
|
const bufKey = this.buffering.getObservationBufferKey(lockKey);
|
|
31814
|
-
BufferingCoordinator.
|
|
31815
|
-
await this.storage.setBufferingObservationFlag(recordId, false, 0).catch(() => {});
|
|
32510
|
+
BufferingCoordinator.setObservationBoundary(bufKey, 0);
|
|
32511
|
+
await runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(recordId, false, 0)).catch(() => {});
|
|
31816
32512
|
if (activatedMessageIds && activatedMessageIds.length > 0) this.buffering.cleanupStaticMaps(threadId, resourceId, activatedMessageIds);
|
|
31817
32513
|
}
|
|
31818
32514
|
/**
|
|
@@ -31971,9 +32667,13 @@ ${formattedMessages}
|
|
|
31971
32667
|
* @example
|
|
31972
32668
|
* ```ts
|
|
31973
32669
|
* const status = await om.getStatus({ threadId });
|
|
31974
|
-
* if (status.shouldObserve) {
|
|
32670
|
+
* if (status.shouldObserve && status.canActivate) {
|
|
32671
|
+
* await om.activate({ threadId });
|
|
32672
|
+
* } else if (status.shouldObserve && !status.inAsyncObservationBand) {
|
|
32673
|
+
* // At/above blockAfter (or async buffering disabled): observe synchronously
|
|
31975
32674
|
* await om.observe({ threadId });
|
|
31976
32675
|
* } else if (status.shouldBuffer) {
|
|
32676
|
+
* // Below the threshold, or in the threshold→blockAfter band: buffer in the background
|
|
31977
32677
|
* await om.buffer({ threadId });
|
|
31978
32678
|
* }
|
|
31979
32679
|
* if (status.shouldReflect) {
|
|
@@ -32003,8 +32703,10 @@ ${formattedMessages}
|
|
|
32003
32703
|
const bufferedChunkCount = bufferedChunks.length;
|
|
32004
32704
|
const bufferedChunkTokens = bufferedChunks.reduce((sum, chunk) => sum + (chunk.messageTokens ?? 0), 0);
|
|
32005
32705
|
const asyncObservationEnabled = this.buffering.isAsyncObservationEnabled();
|
|
32706
|
+
const observationBlockAfter = asyncObservationEnabled ? this.getEffectiveObservationBlockAfter(record) : void 0;
|
|
32707
|
+
const inAsyncObservationBand = observationBlockAfter !== void 0 && pendingTokens >= threshold && pendingTokens < observationBlockAfter;
|
|
32006
32708
|
let shouldBuffer = false;
|
|
32007
|
-
if (asyncObservationEnabled && pendingTokens < threshold) {
|
|
32709
|
+
if (asyncObservationEnabled && (pendingTokens < threshold || inAsyncObservationBand)) {
|
|
32008
32710
|
const lockKey = this.buffering.getLockKey(threadId, resourceId);
|
|
32009
32711
|
shouldBuffer = this.buffering.shouldTriggerAsyncObservation(pendingTokens, lockKey, record, this.storage, threshold);
|
|
32010
32712
|
}
|
|
@@ -32029,6 +32731,8 @@ ${formattedMessages}
|
|
|
32029
32731
|
canActivate,
|
|
32030
32732
|
asyncObservationEnabled,
|
|
32031
32733
|
asyncReflectionEnabled: this.buffering.isAsyncReflectionEnabled(),
|
|
32734
|
+
observationBlockAfter,
|
|
32735
|
+
inAsyncObservationBand,
|
|
32032
32736
|
scope: this.scope
|
|
32033
32737
|
};
|
|
32034
32738
|
}
|
|
@@ -32155,24 +32859,27 @@ ${formattedMessages}
|
|
|
32155
32859
|
const currentTokens = opts.pendingTokens ?? record.pendingMessageTokens ?? 0;
|
|
32156
32860
|
const lockKey = this.buffering.getLockKey(threadId, resourceId);
|
|
32157
32861
|
const bufferKey = this.buffering.getObservationBufferKey(lockKey);
|
|
32158
|
-
|
|
32159
|
-
|
|
32862
|
+
const boundaryOwner = Symbol("observation-boundary");
|
|
32863
|
+
BufferingCoordinator.setObservationBoundary(bufferKey, currentTokens, boundaryOwner);
|
|
32160
32864
|
const existingOp = BufferingCoordinator.asyncBufferingOps.get(bufferKey);
|
|
32865
|
+
let resolveOp;
|
|
32866
|
+
const opPromise = new Promise((resolve) => {
|
|
32867
|
+
resolveOp = resolve;
|
|
32868
|
+
});
|
|
32869
|
+
BufferingCoordinator.asyncBufferingOps.set(bufferKey, opPromise);
|
|
32870
|
+
if (record.isBufferingObservation) await runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, false)).catch(() => {});
|
|
32161
32871
|
if (existingOp) try {
|
|
32162
32872
|
await existingOp;
|
|
32163
32873
|
} catch {}
|
|
32164
|
-
|
|
32874
|
+
const opRecordId = record.id;
|
|
32875
|
+
registerOp(opRecordId, "bufferingObservation");
|
|
32876
|
+
const releaseChunkWrite = BufferingCoordinator.trackChunkWrite(bufferKey);
|
|
32877
|
+
const ownsStartBoundary = BufferingCoordinator.ownsObservationBoundary(bufferKey, boundaryOwner);
|
|
32165
32878
|
inMemoryRecord.isBufferingObservation = true;
|
|
32166
|
-
inMemoryRecord.lastBufferedAtTokens = currentTokens;
|
|
32167
|
-
this.storage.setBufferingObservationFlag(record.id, true, currentTokens).catch((err) => {
|
|
32879
|
+
if (ownsStartBoundary) inMemoryRecord.lastBufferedAtTokens = currentTokens;
|
|
32880
|
+
runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, true, ownsStartBoundary ? currentTokens : void 0)).catch((err) => {
|
|
32168
32881
|
omError("[OM] Failed to set buffering observation flag", err);
|
|
32169
32882
|
});
|
|
32170
|
-
let resolveOp;
|
|
32171
|
-
const opPromise = new Promise((resolve) => {
|
|
32172
|
-
resolveOp = resolve;
|
|
32173
|
-
});
|
|
32174
|
-
BufferingCoordinator.asyncBufferingOps.set(bufferKey, opPromise);
|
|
32175
|
-
record = await this.storage.getObservationalMemory(record.threadId, record.resourceId) ?? record;
|
|
32176
32883
|
const setBufferingState = (isBufferingObservation, lastBufferedAtTokens) => {
|
|
32177
32884
|
inMemoryRecord.isBufferingObservation = isBufferingObservation;
|
|
32178
32885
|
record.isBufferingObservation = isBufferingObservation;
|
|
@@ -32183,6 +32890,7 @@ ${formattedMessages}
|
|
|
32183
32890
|
};
|
|
32184
32891
|
let flagCleared = false;
|
|
32185
32892
|
try {
|
|
32893
|
+
record = await this.storage.getObservationalMemory(record.threadId, record.resourceId) ?? record;
|
|
32186
32894
|
let candidateMessages;
|
|
32187
32895
|
if (opts.messages) candidateMessages = this.getUnobservedMessages(opts.messages, record, { excludeBuffered: true });
|
|
32188
32896
|
else {
|
|
@@ -32222,12 +32930,29 @@ ${formattedMessages}
|
|
|
32222
32930
|
threadIds: [threadId],
|
|
32223
32931
|
config: this.getObservationMarkerConfig()
|
|
32224
32932
|
});
|
|
32225
|
-
await this.persistMarkerToStorage(startMarker, threadId, record.resourceId ?? void 0
|
|
32933
|
+
await this.persistMarkerToStorage(startMarker, threadId, record.resourceId ?? void 0, {
|
|
32934
|
+
notAfter: new Date(startedAt),
|
|
32935
|
+
preferMessages: candidateMessages
|
|
32936
|
+
});
|
|
32226
32937
|
const writer = opts.writer;
|
|
32227
32938
|
if (writer) writer.custom({
|
|
32228
32939
|
...startMarker,
|
|
32229
32940
|
transient: true
|
|
32230
32941
|
}).catch(() => {});
|
|
32942
|
+
let chunkRecorded = false;
|
|
32943
|
+
const recordBufferedChunk = async () => {
|
|
32944
|
+
if (chunkRecorded) return;
|
|
32945
|
+
chunkRecorded = true;
|
|
32946
|
+
const ownsBoundary = BufferingCoordinator.ownsObservationBoundary(bufferKey, boundaryOwner);
|
|
32947
|
+
if (ownsBoundary) BufferingCoordinator.setObservationBoundary(bufferKey, newTokens, boundaryOwner);
|
|
32948
|
+
await runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, false, ownsBoundary ? newTokens : void 0)).catch(() => {});
|
|
32949
|
+
flagCleared = true;
|
|
32950
|
+
const stillOwnsBoundary = ownsBoundary && BufferingCoordinator.ownsObservationBoundary(bufferKey, boundaryOwner);
|
|
32951
|
+
setBufferingState(false, stillOwnsBoundary ? newTokens : void 0);
|
|
32952
|
+
const maxTimestamp = this.getMaxMessageTimestamp(candidateMessages);
|
|
32953
|
+
BufferingCoordinator.lastBufferedAtTime.set(bufferKey, new Date(maxTimestamp.getTime() + 1));
|
|
32954
|
+
releaseChunkWrite();
|
|
32955
|
+
};
|
|
32231
32956
|
if (!(await this.runBufferedObservationCycle({
|
|
32232
32957
|
threadId,
|
|
32233
32958
|
resourceId: record.resourceId ?? resourceId,
|
|
@@ -32246,7 +32971,8 @@ ${formattedMessages}
|
|
|
32246
32971
|
requestContext,
|
|
32247
32972
|
currentModel: opts.currentModel,
|
|
32248
32973
|
observabilityContext,
|
|
32249
|
-
trigger: "async-buffer"
|
|
32974
|
+
trigger: "async-buffer",
|
|
32975
|
+
onBufferedChunkPersisted: recordBufferedChunk
|
|
32250
32976
|
}).run()))?.observed) return {
|
|
32251
32977
|
buffered: false,
|
|
32252
32978
|
record
|
|
@@ -32266,13 +32992,7 @@ ${formattedMessages}
|
|
|
32266
32992
|
newTokens
|
|
32267
32993
|
}
|
|
32268
32994
|
});
|
|
32269
|
-
await
|
|
32270
|
-
flagCleared = true;
|
|
32271
|
-
setBufferingState(false, newTokens);
|
|
32272
|
-
BufferingCoordinator.lastBufferedBoundary.set(bufferKey, newTokens);
|
|
32273
|
-
const maxTimestamp = this.getMaxMessageTimestamp(candidateMessages);
|
|
32274
|
-
const cursor = new Date(maxTimestamp.getTime() + 1);
|
|
32275
|
-
BufferingCoordinator.lastBufferedAtTime.set(bufferKey, cursor);
|
|
32995
|
+
await recordBufferedChunk();
|
|
32276
32996
|
return {
|
|
32277
32997
|
buffered: true,
|
|
32278
32998
|
record: await this.storage.getObservationalMemory(record.threadId, record.resourceId) ?? record
|
|
@@ -32284,12 +33004,13 @@ ${formattedMessages}
|
|
|
32284
33004
|
record
|
|
32285
33005
|
};
|
|
32286
33006
|
} finally {
|
|
32287
|
-
|
|
32288
|
-
|
|
33007
|
+
releaseChunkWrite();
|
|
33008
|
+
unregisterOp(opRecordId, "bufferingObservation");
|
|
33009
|
+
if (BufferingCoordinator.asyncBufferingOps.get(bufferKey) === opPromise) BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
|
|
32289
33010
|
resolveOp();
|
|
32290
33011
|
if (!flagCleared) {
|
|
32291
33012
|
setBufferingState(false);
|
|
32292
|
-
await this.storage.setBufferingObservationFlag(record.id, false).catch(() => {});
|
|
33013
|
+
await runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, false)).catch(() => {});
|
|
32293
33014
|
}
|
|
32294
33015
|
}
|
|
32295
33016
|
}
|
|
@@ -32326,8 +33047,8 @@ ${formattedMessages}
|
|
|
32326
33047
|
const currentContextTokens = this.tokenCounter.countMessages(unobserved);
|
|
32327
33048
|
if (currentContextTokens < dbBoundary * .5) {
|
|
32328
33049
|
omDebug(`[OM:activate] resetting stale lastBufferedBoundary: dbBoundary=${dbBoundary}, currentContextTokens=${currentContextTokens}`);
|
|
32329
|
-
BufferingCoordinator.
|
|
32330
|
-
await this.storage.setBufferingObservationFlag(record.id, false, 0).catch(() => {});
|
|
33050
|
+
BufferingCoordinator.setObservationBoundary(bufKey, 0);
|
|
33051
|
+
await runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, false, 0)).catch(() => {});
|
|
32331
33052
|
}
|
|
32332
33053
|
}
|
|
32333
33054
|
}
|
|
@@ -32373,39 +33094,63 @@ ${formattedMessages}
|
|
|
32373
33094
|
livePendingTokens ??= status.pendingTokens;
|
|
32374
33095
|
}
|
|
32375
33096
|
}
|
|
32376
|
-
|
|
32377
|
-
|
|
32378
|
-
|
|
32379
|
-
const
|
|
32380
|
-
|
|
32381
|
-
|
|
32382
|
-
|
|
33097
|
+
const obsBufferKey = this.buffering.getObservationBufferKey(this.buffering.getLockKey(threadId, resourceId));
|
|
33098
|
+
const pendingChunkWrite = BufferingCoordinator.pendingChunkWrites.get(obsBufferKey);
|
|
33099
|
+
if (pendingChunkWrite) {
|
|
33100
|
+
const blockAfter = this.buffering.isAsyncObservationEnabled() ? this.getEffectiveObservationBlockAfter(record) : void 0;
|
|
33101
|
+
const threshold = calculateDynamicThreshold(this.getEffectiveMessageTokens(record), record.observationTokenCount ?? 0);
|
|
33102
|
+
if (activationTriggeredBy === "threshold" && !(livePendingTokens !== void 0 && blockAfter !== void 0 && livePendingTokens >= threshold && livePendingTokens < blockAfter)) {
|
|
33103
|
+
let timeoutId;
|
|
33104
|
+
await Promise.race([pendingChunkWrite, new Promise((resolve) => timeoutId = setTimeout(resolve, 6e4))]);
|
|
33105
|
+
clearTimeout(timeoutId);
|
|
33106
|
+
}
|
|
33107
|
+
}
|
|
33108
|
+
const commitKey = this.buffering.getLockKey(threadId, resourceId);
|
|
33109
|
+
let freshRecord = null;
|
|
33110
|
+
let freshChunks = [];
|
|
33111
|
+
let activationResult;
|
|
33112
|
+
for (let attempt = 0; attempt <= MAX_ACTIVATION_RETIRED_RETRIES; attempt++) {
|
|
33113
|
+
const swapped = await runOMCommit(commitKey, async () => {
|
|
33114
|
+
const head = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
33115
|
+
if (!head) return null;
|
|
33116
|
+
const chunks = getBufferedChunks(head);
|
|
33117
|
+
if (!chunks.length) return null;
|
|
33118
|
+
const messageTokensThreshold = getMaxThreshold(this.getEffectiveMessageTokens(head));
|
|
33119
|
+
const activationRatio = resolveActivationRatio(this.observationConfig.bufferActivation ?? .7, messageTokensThreshold);
|
|
33120
|
+
const totalChunkMessageTokens = chunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
|
|
33121
|
+
const currentPendingTokens = livePendingTokens ?? (head.pendingMessageTokens || totalChunkMessageTokens);
|
|
33122
|
+
const blockAfter = this.getEffectiveObservationBlockAfter(head);
|
|
33123
|
+
const forceMaxActivation = !!(blockAfter && currentPendingTokens >= blockAfter);
|
|
33124
|
+
if (head.pendingMessageTokens !== currentPendingTokens) await this.storage.setPendingMessageTokens(head.id, currentPendingTokens);
|
|
33125
|
+
return {
|
|
33126
|
+
head,
|
|
33127
|
+
chunks,
|
|
33128
|
+
result: await this.storage.swapBufferedToActive({
|
|
33129
|
+
id: head.id,
|
|
33130
|
+
activationRatio,
|
|
33131
|
+
messageTokensThreshold,
|
|
33132
|
+
currentPendingTokens,
|
|
33133
|
+
forceMaxActivation,
|
|
33134
|
+
bufferedChunks: chunks
|
|
33135
|
+
})
|
|
33136
|
+
};
|
|
33137
|
+
});
|
|
33138
|
+
if (!swapped) return {
|
|
33139
|
+
activated: false,
|
|
33140
|
+
record
|
|
33141
|
+
};
|
|
33142
|
+
freshRecord = swapped.head;
|
|
33143
|
+
freshChunks = swapped.chunks;
|
|
33144
|
+
activationResult = swapped.result;
|
|
33145
|
+
if (!activationResult.retired) break;
|
|
33146
|
+
omDebug(`[OM:activate] record ${freshRecord.id} was retired by a reflection; retrying on the head`);
|
|
32383
33147
|
}
|
|
32384
|
-
|
|
32385
|
-
if (!freshRecord) return {
|
|
33148
|
+
if (!freshRecord || !activationResult || activationResult.retired) return {
|
|
32386
33149
|
activated: false,
|
|
32387
|
-
record
|
|
32388
|
-
};
|
|
32389
|
-
const freshChunks = getBufferedChunks(freshRecord);
|
|
32390
|
-
if (!freshChunks.length) return {
|
|
32391
|
-
activated: false,
|
|
32392
|
-
record
|
|
33150
|
+
record: await this.getRecord(threadId, resourceId) ?? record
|
|
32393
33151
|
};
|
|
32394
|
-
const
|
|
32395
|
-
|
|
32396
|
-
const totalChunkMessageTokens = freshChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
|
|
32397
|
-
const currentPendingTokens = livePendingTokens ?? (freshRecord.pendingMessageTokens || totalChunkMessageTokens);
|
|
32398
|
-
const forceMaxActivation = !!(this.observationConfig.blockAfter && currentPendingTokens >= this.observationConfig.blockAfter);
|
|
32399
|
-
if (freshRecord.pendingMessageTokens !== currentPendingTokens) await this.storage.setPendingMessageTokens(freshRecord.id, currentPendingTokens);
|
|
32400
|
-
const activationResult = await this.storage.swapBufferedToActive({
|
|
32401
|
-
id: freshRecord.id,
|
|
32402
|
-
activationRatio,
|
|
32403
|
-
messageTokensThreshold,
|
|
32404
|
-
currentPendingTokens,
|
|
32405
|
-
forceMaxActivation,
|
|
32406
|
-
bufferedChunks: freshChunks
|
|
32407
|
-
});
|
|
32408
|
-
await this.storage.setBufferingObservationFlag(freshRecord.id, false).catch(() => {});
|
|
33152
|
+
const swappedRecordId = freshRecord.id;
|
|
33153
|
+
await runOMCommit(commitKey, () => this.storage.setBufferingObservationFlag(swappedRecordId, false)).catch(() => {});
|
|
32409
33154
|
unregisterOp(freshRecord.id, "bufferingObservation");
|
|
32410
33155
|
const postSwapRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
32411
33156
|
if (opts.writer && postSwapRecord && activationResult.activatedCycleIds.length > 0) {
|
|
@@ -32650,7 +33395,7 @@ ${formattedMessages}
|
|
|
32650
33395
|
* ```
|
|
32651
33396
|
*/
|
|
32652
33397
|
async reflect(threadId, resourceId, prompt, requestContext, observabilityContext) {
|
|
32653
|
-
const record = await this.getOrCreateRecord(threadId, resourceId);
|
|
33398
|
+
const record = { ...await this.getOrCreateRecord(threadId, resourceId) };
|
|
32654
33399
|
if (!record.activeObservations) return {
|
|
32655
33400
|
reflected: false,
|
|
32656
33401
|
record,
|
|
@@ -32695,11 +33440,23 @@ ${formattedMessages}
|
|
|
32695
33440
|
const reflectResult = await this.reflector.call(await applyTextTransform(this.hooks, "beforeReflection", record.activeObservations, hookContext), prompt, void 0, reflectThreshold, void 0, void 0, void 0, requestContext, priorExtractedValues, observabilityContext, void 0);
|
|
32696
33441
|
reflectResult.observations = await applyTextTransform(this.hooks, "afterReflection", reflectResult.observations, hookContext);
|
|
32697
33442
|
const reflectionTokenCount = this.tokenCounter.countObservations(reflectResult.observations);
|
|
32698
|
-
|
|
33443
|
+
const newRecordId = crypto.randomUUID();
|
|
33444
|
+
const committedRecord = await runOMCommit(this.buffering.getLockKey(threadId, resourceId), () => this.storage.createReflectionGeneration({
|
|
32699
33445
|
currentRecord: record,
|
|
32700
33446
|
reflection: reflectResult.observations,
|
|
32701
|
-
tokenCount: reflectionTokenCount
|
|
32702
|
-
|
|
33447
|
+
tokenCount: reflectionTokenCount,
|
|
33448
|
+
newRecordId
|
|
33449
|
+
}), { priority: "reflection" });
|
|
33450
|
+
reflectionUsage = reflectResult.usage;
|
|
33451
|
+
reflectionProviderMetadata = reflectResult.providerMetadata;
|
|
33452
|
+
if (!isReflectionApplied(committedRecord, newRecordId, record.id)) {
|
|
33453
|
+
omDebug(`[OM:reflect] manual reflection of ${record.id} not applied (head is ${committedRecord.id})`);
|
|
33454
|
+
return {
|
|
33455
|
+
reflected: false,
|
|
33456
|
+
record: await this.getOrCreateRecord(threadId, resourceId),
|
|
33457
|
+
usage: reflectResult.usage
|
|
33458
|
+
};
|
|
33459
|
+
}
|
|
32703
33460
|
if (thread && reflectResult.extractedValues) {
|
|
32704
33461
|
const metadataUpdate = buildThreadMetadataFromExtractedValues(this.reflectionConfig.extractors, reflectResult.extractedValues);
|
|
32705
33462
|
const newMetadata = setThreadOMMetadata(thread.metadata, {
|
|
@@ -32788,11 +33545,13 @@ ${formattedMessages}
|
|
|
32788
33545
|
*/
|
|
32789
33546
|
async updateRecordConfig(threadId, resourceId, config) {
|
|
32790
33547
|
const ids = this.getStorageIds(threadId, resourceId);
|
|
32791
|
-
|
|
32792
|
-
|
|
32793
|
-
|
|
32794
|
-
|
|
32795
|
-
|
|
33548
|
+
await runOMCommit(this.buffering.getLockKey(threadId, resourceId), async () => {
|
|
33549
|
+
const record = await this.storage.getObservationalMemory(ids.threadId, ids.resourceId);
|
|
33550
|
+
if (!record) throw new Error(`No observational memory record found for thread ${ids.threadId}`);
|
|
33551
|
+
await this.storage.updateObservationalMemoryConfig({
|
|
33552
|
+
id: record.id,
|
|
33553
|
+
config: { _overrides: config }
|
|
33554
|
+
});
|
|
32796
33555
|
});
|
|
32797
33556
|
}
|
|
32798
33557
|
/**
|
|
@@ -33186,7 +33945,7 @@ var ObservationalMemoryProcessor = class {
|
|
|
33186
33945
|
});
|
|
33187
33946
|
const finalTotalPending = ctx.status.pendingTokens;
|
|
33188
33947
|
try {
|
|
33189
|
-
await this.engine.getStorage().setPendingMessageTokens(turnRecord.id, finalTotalPending);
|
|
33948
|
+
await runOMCommit(this.engine.buffering.getLockKey(threadId, resourceId), () => this.engine.getStorage().setPendingMessageTokens(turnRecord.id, finalTotalPending));
|
|
33190
33949
|
this.turn.patchRecord({ pendingMessageTokens: finalTotalPending });
|
|
33191
33950
|
} catch {}
|
|
33192
33951
|
if (reproCaptureEnabled) writeProcessInputStepReproCapture({
|
|
@@ -35521,6 +36280,7 @@ Notes:
|
|
|
35521
36280
|
cloned.id = crypto.randomUUID();
|
|
35522
36281
|
cloned.createdAt = now;
|
|
35523
36282
|
cloned.updatedAt = now;
|
|
36283
|
+
cloned.supersededBy = null;
|
|
35524
36284
|
await memoryStore.insertObservationalMemoryRecord(cloned);
|
|
35525
36285
|
}
|
|
35526
36286
|
/**
|
|
@@ -35814,4 +36574,4 @@ Notes:
|
|
|
35814
36574
|
//#endregion
|
|
35815
36575
|
export { renderSubconsciousActivity as $, buildObserverSystemPrompt as A, OBSERVATION_CONTEXT_INSTRUCTIONS as B, WorkingMemoryExtractor as C, TokenCounter as D, summarizeConversation as E, parseObserverOutput as F, Subconscious as G, OBSERVATION_CONTEXT_PROMPT_THREAD as H, injectAnchorIds as I, SubconsciousRemindExtractor as J, KnowledgeSemanticIndexCoordinator as K, parseAnchorId as L, formatMessagesForObserver as M, hasCurrentTaskSection as N, OBSERVER_SYSTEM_PROMPT as O, optimizeObservationsForContext as P, publishSubconsciousActivity as Q, stripEphemeralAnchorIds as R, wrapInObservationGroup as S, SUMMARIZE_THREAD_DEFAULTS as T, OBSERVATION_CONTINUATION_HINT as U, OBSERVATION_CONTEXT_PROMPT as V, getObservationContextPrompt as W, SUBCONSCIOUS_ACTIVITY_STATE_ID as X, ModelByInputTokens as Y, buildSubconsciousActivitySnapshot as Z, deriveObservationGroupProvenance as _, extractWorkingMemoryContent as a, renderObservationGroupsForReflection as b, getObservationsAsOf as c, AUTO_MODEL_BY_PROVIDER as d, Extractor as et, resolveAutoModelId as f, combineObservationGroupRanges as g, WorkingMemoryStateProcessor as h, WorkingMemory as i, extractCurrentTask as j, buildObserverPrompt as k, ObservationalMemoryProcessor as l, WORKING_MEMORY_STATE_PROCESSOR_ID as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, WORKING_MEMORY_STATE_ID as p, StaleKnowledgeSemanticIndexError as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, ObservationalMemory as u, parseObservationGroups as v, deepMergeWorkingMemory as w, stripObservationGroups as x, reconcileObservationGroupsFromReflection as y, OBSERVATIONAL_MEMORY_DEFAULTS as z };
|
|
35816
36576
|
|
|
35817
|
-
//# sourceMappingURL=src-
|
|
36577
|
+
//# sourceMappingURL=src-DjDnBIS0.js.map
|