openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,143 lines • 56.4 kB
JavaScript
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import { t as createLazyImportLoader } from "./lazy-promise-DGqyc4Y4.js";
import { n as isAbortError } from "./abort-signal-D2k14JsD.js";
import { y as resolveDefaultAgentId } from "./agent-scope-config-DcbEhP0R.js";
import { f as resolveAgentIdFromSessionKey, l as isUnscopedSessionKeySentinel } from "./session-key-BnWWjqNc.js";
import { t as isIncognitoSessionKey } from "./incognito-session-key-BwpD1Lwd.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import { a as resolveMergedModelProviderModels, r as resolveMergedModelProviderConfig } from "./model-provider-config-DX4Bzd5F.js";
import { c as normalizeProviderId, u as normalizeStaticProviderModelId } from "./model-ref-shared-Dz7QU0Lx.js";
import { o as resolveSessionStorePathCore } from "./paths-CXdaYWF_.js";
import "./defaults-CdX9UGcX.js";
import { c as resolveContextConfigProviderForRuntime, m as resolveModelExtraParamSources } from "./openai-routing-Ex_3E9TF.js";
import { p as parseNonNegativeByteSize } from "./zod-schema-DnirJMRA.js";
import { r as logVerbose } from "./globals-CTaGxEqj.js";
import { a as clearAgentRunContext, h as registerAgentRunContext } from "./agent-run-registry-CKYKdfNd.js";
import { a as hasNonzeroUsage, c as normalizeUsage, t as deriveContextPromptTokens } from "./usage-CtmV8Xxq.js";
import { t as resolveEffectiveCompactionReserveTokens } from "./agent-compaction-constants-CzVH4jGZ.js";
import { s as readRecentSessionTranscriptActiveEvents, u as readSessionTranscriptActiveStats, xt as updateSessionEntry } from "./session-accessor-YsytfDtG.js";
import { E as resolveSessionStorePathForScope } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import { u as selectSessionTranscriptLeafControlledPath } from "./transcript-tree-BH69pMSi.js";
import { o as resolveFreshSessionTotalTokens } from "./types-BHV0IaPK.js";
import { h as resolveSessionPinnedHarnessId } from "./agent-harness-session-key-BOz3yx0-.js";
import { i as resolveCliBackendConfig } from "./cli-backends-Bs_a4MW0.js";
import { i as isCliRuntimeAliasForProvider } from "./model-runtime-aliases-BJ2qM8I_.js";
import { i as resolveSessionRuntimeOverrideForProvider, r as resolvePersistedSessionRuntimeId } from "./session-runtime-compat-DNAICzi6.js";
import { o as resolveEffectiveAgentRuntime } from "./thinking-runtime-BiFbTVdI.js";
import { v as resolveMemoryFlushPlan } from "./memory-state-BanUXlB7.js";
import { u as estimateMessagesTokens } from "./compaction-planning-DWqFldf6.js";
import { s as isCliProvider } from "./model-selection-di2kjKCB.js";
import { o as prepareSystemAgentRunAdmission } from "./admitted-run-context-CHY5SdVF.js";
import { t as formatTokenCount } from "./token-format-D942KbWN.js";
import "./sessions-9nxpeTwt.js";
import { i as isRenderablePayload } from "./reply-payloads-OWAnL0iD.js";
import { i as resolveSandboxConfigForAgent } from "./config-RoLkL_H5.js";
import { n as resolveSandboxRuntimeStatus } from "./runtime-status-6c8Jb1Kg.js";
import { _t as isBenignCompactionSkipResult, f as readPostCompactionContext, v as createDeferredEmbeddedRunLifecycleManager } from "./builtin-openclaw-B_H1oNzF.js";
import { o as resolveBootstrapWarningSignaturesSeen } from "./bootstrap-budget-7_XA_4pm.js";
import "./sandbox-A2KOznlu.js";
import "./helpers-CkZ0XN7O.js";
import { o as readSessionMessagesAsync } from "./session-transcript-readers-CYDRQsH5.js";
import { a as resolveContextTokensForModel } from "./context-B0EuZFRZ.js";
import { c as refreshQueuedFollowupSession } from "./settings-jmDsm5RI.js";
import { r as createToolResultPromptProjectionState } from "./session-prompt-state-ClEGMz2c.js";
import "./queue-BpaiO3X2.js";
import { n as incrementCompactionCount } from "./session-updates-DdrrbgwI.js";
import { i as runEmbeddedAgentEntry, n as invalidateTurnCompactionContext, r as recordTurnCompaction } from "./agent-runner-compaction-accounting-CztfzYyv.js";
import { c as resolveRunThinkingLevelForFallbackCandidate, l as resolveModelFallbackOptions, t as buildEmbeddedRunExecutionParams } from "./agent-runner-utils-4NqM1iMb.js";
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
import { resolveAnthropicServerCompactionPlan } from "@openclaw/ai/internal/anthropic";
import { resolveOpenAIResponsesServerCompactionPlan } from "@openclaw/ai/internal/openai-responses-payload-policy";
//#region src/auto-reply/reply/memory-flush.ts
function resolveMemoryFlushContextWindowTokens(params) {
return resolveContextTokensForModel({
cfg: params.cfg,
provider: params.provider,
model: params.modelId,
allowAsyncLoad: false
}) ?? 2e5;
}
function resolveMaxActiveTranscriptBytes(cfg) {
const parsed = parseNonNegativeByteSize(cfg?.agents?.defaults?.compaction?.maxActiveTranscriptBytes);
return typeof parsed === "number" && parsed > 0 ? parsed : void 0;
}
function resolvePositiveTokenCount(value) {
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
}
/** Resolves the blocking threshold using the selected reserve and server floor. */
function resolveCompactionThreshold(params) {
const contextWindow = Math.max(1, Math.floor(params.contextWindowTokens));
const reserveTokens = Math.max(0, Math.floor(params.reserveTokensFloor));
return Math.max(0, contextWindow - reserveTokens, Math.floor(params.minimumThresholdTokens ?? 0));
}
function resolveResponsesServerCompactionThreshold(params) {
const provider = params.provider?.trim();
const modelId = params.modelId?.trim();
if (!provider || !modelId) return;
const normalizedProvider = normalizeProviderId(provider);
const normalizeModelId = (value) => normalizeStaticProviderModelId(normalizedProvider, value).trim().toLowerCase();
const providerConfig = resolveMergedModelProviderConfig(params.cfg, provider);
const configuredModel = resolveMergedModelProviderModels({
models: providerConfig?.models,
normalizeModelId
}).get(normalizeModelId(modelId));
const { defaultParams, modelParams } = resolveModelExtraParamSources({
config: params.cfg,
provider,
modelId
});
const extraParams = {
...defaultParams,
...modelParams
};
if (normalizedProvider === "anthropic") return resolveAnthropicServerCompactionPlan({
provider,
api: configuredModel?.api ?? providerConfig?.api ?? "anthropic-messages",
baseUrl: configuredModel?.baseUrl ?? providerConfig?.baseUrl,
contextWindow: configuredModel?.contextWindow ?? resolveMemoryFlushContextWindowTokens({
cfg: params.cfg,
provider,
modelId
})
}, extraParams).threshold;
const defaultOpenAIBaseUrl = normalizedProvider === "openai" ? "https://api.openai.com/v1" : void 0;
const activeContextTokens = resolveMemoryFlushContextWindowTokens({
cfg: params.cfg,
provider,
modelId
});
return resolveOpenAIResponsesServerCompactionPlan({
provider,
api: configuredModel?.api ?? providerConfig?.api ?? (normalizedProvider === "openai" ? "openai-responses" : void 0),
baseUrl: configuredModel?.baseUrl ?? providerConfig?.baseUrl ?? defaultOpenAIBaseUrl,
compat: configuredModel?.compat,
contextTokens: configuredModel?.contextTokens ?? activeContextTokens,
contextWindow: configuredModel?.contextWindow ?? activeContextTokens
}, extraParams).threshold;
}
function resolveMaintenanceGateState(params) {
if (!params.entry) return null;
const totalTokens = resolvePositiveTokenCount(params.tokenCount) ?? resolveFreshSessionTotalTokens(params.entry);
if (!totalTokens || totalTokens <= 0) return null;
const threshold = params.threshold;
return threshold > 0 ? {
entry: params.entry,
totalTokens,
threshold
} : null;
}
function shouldRunMemoryFlush(params) {
const state = resolveMaintenanceGateState(params);
if (!state || state.totalTokens < state.threshold) return false;
if (hasAlreadyFlushedForCurrentCompaction(state.entry)) return false;
return true;
}
function shouldRunPreflightCompaction(params) {
const state = resolveMaintenanceGateState(params);
return Boolean(state && state.totalTokens >= state.threshold);
}
/**
* Returns true when a memory flush has already been performed for the current
* compaction cycle. This prevents repeated flush runs within the same cycle —
* important for both the token-based and transcript-size–based trigger paths.
*/
function hasAlreadyFlushedForCurrentCompaction(entry) {
const compactionCount = entry.compactionCount ?? 0;
const lastFlushAt = entry.memoryFlush?.compactionCount;
return typeof lastFlushAt === "number" && lastFlushAt === compactionCount;
}
//#endregion
//#region src/auto-reply/reply/agent-runner-memory.ts
/** Preflight compaction and memory flush helpers for agent runner sessions. */
const MAX_VISIBLE_MEMORY_FLUSH_ERROR_CHARS = 600;
const MAX_FLUSH_FAILURES = 3;
const MAX_FLUSH_ERROR_LENGTH = 200;
const preflightCompactionLog = createSubsystemLogger("auto-reply/preflight-compaction");
const embeddedAgentRuntimeLoader = createLazyImportLoader(() => import("./embedded-agent-CUhbhIMN.js"));
const toolResultTruncationRuntimeLoader = createLazyImportLoader(() => import("./tool-result-truncation-D1rCMIMx.js"));
async function compactEmbeddedAgentSession(...args) {
return await (await embeddedAgentRuntimeLoader.load()).compactEmbeddedAgentSession(...args);
}
async function runEmbeddedAgent(params) {
return await (await embeddedAgentRuntimeLoader.load()).runEmbeddedAgent(params);
}
async function ensureMemoryFlushTargetFile(params) {
const workspaceDir = normalizeOptionalString(params.workspaceDir);
const relativePath = normalizeOptionalString(params.relativePath);
if (!workspaceDir || !relativePath || path.isAbsolute(relativePath)) throw new Error("Invalid memory flush target path");
const workspaceRoot = path.resolve(workspaceDir);
const targetPath = path.resolve(workspaceRoot, relativePath);
const targetRelativePath = path.relative(workspaceRoot, targetPath);
if (!targetRelativePath || targetRelativePath.startsWith("..") || path.isAbsolute(targetRelativePath)) throw new Error("Memory flush target path must stay inside the workspace");
await fs.promises.mkdir(path.dirname(targetPath), { recursive: true });
await (await fs.promises.open(targetPath, "a")).close();
}
function hasMatchingTranscriptByteCompactionLatch(entry, activeBytes, maxBytes) {
const latch = entry.transcriptByteCompactionLatch;
return latch?.sessionId === entry.sessionId && latch.maxBytes === maxBytes && activeBytes >= latch.activeBytes && activeBytes - latch.activeBytes < maxBytes;
}
function estimatePromptTokensForMemoryFlush(prompt) {
const trimmed = normalizeOptionalString(prompt);
if (!trimmed) return;
const tokens = estimateMessagesTokens([{
role: "user",
content: trimmed,
timestamp: Date.now()
}]);
if (!Number.isFinite(tokens) || tokens <= 0) return;
return Math.ceil(tokens);
}
function resolveEffectivePromptTokens(basePromptTokens, lastOutputTokens, promptTokenEstimate) {
const base = Math.max(0, basePromptTokens ?? 0);
const output = Math.max(0, lastOutputTokens ?? 0);
const estimate = Math.max(0, promptTokenEstimate ?? 0);
return base + output + estimate;
}
function resolveMemoryFlushModelFallbackOptions(run, model, configOverride = run.config) {
const options = resolveModelFallbackOptions(run, configOverride);
const override = normalizeOptionalString(model);
if (!override) return options;
const slashIdx = override.indexOf("/");
if (slashIdx > 0) {
const overrideProvider = override.slice(0, slashIdx).trim();
const overrideModel = override.slice(slashIdx + 1).trim();
if (overrideProvider && overrideModel) return {
...options,
provider: overrideProvider,
model: overrideModel,
requestedRouteResolution: "raw",
fallbacksOverride: []
};
}
return {
...options,
model: override,
requestedRouteResolution: "raw",
fallbacksOverride: []
};
}
function followupUsesCliRuntime(params, runtimeId) {
const provider = params.followupRun.run.provider;
if (params.agentHarnessId) return isCliRuntimeAliasForProvider({
provider,
runtime: params.agentHarnessId,
cfg: params.cfg
});
if (isCliProvider(provider, params.cfg)) return true;
return [resolvePersistedSessionRuntimeId(params.sessionEntry), runtimeId].some((runtime) => isCliRuntimeAliasForProvider({
provider,
runtime,
cfg: params.cfg
}));
}
function resolveFollowupAgentRuntimeId(params) {
if (params.agentHarnessId) return params.agentHarnessId;
const matchingSessionEntry = params.sessionEntry?.sessionId === params.followupRun.run.sessionId ? params.sessionEntry : void 0;
return resolveEffectiveAgentRuntime({
cfg: params.cfg,
provider: params.followupRun.run.provider,
modelId: params.followupRun.run.model,
agentId: params.followupRun.run.agentId ?? resolveDefaultAgentId(params.cfg),
sessionKey: params.sessionKey ?? params.followupRun.run.sessionKey,
sessionEntry: matchingSessionEntry
});
}
function followupOwnsNativeCompaction(params, runtimeId) {
return resolveCliBackendConfig(runtimeId, params.cfg, { agentId: params.followupRun.run.agentId })?.ownsNativeCompaction === true;
}
function resolveVisibleMemoryFlushErrorPayloads(payloads) {
return (payloads ?? []).filter((payload) => payload.isError === true && isRenderablePayload(payload));
}
function buildVisibleMemoryFlushFailure(payloads) {
const message = payloads.map((payload) => normalizeOptionalString(payload.text)).filter((text) => Boolean(text)).join("\n");
return new Error(message || "Memory flush returned an error response");
}
function buildMemoryFlushErrorPayload(err) {
if (isAbortError(err)) return;
const message = normalizeOptionalString(formatErrorMessage(err));
if (!message) return;
const visibleText = message.startsWith("⚠️") ? message : `⚠️ ${message}`;
return {
text: visibleText.length > MAX_VISIBLE_MEMORY_FLUSH_ERROR_CHARS ? `${truncateUtf16Safe(visibleText, 599)}…` : visibleText,
isError: true
};
}
function truncateMemoryFlushErrorMessage(err) {
const message = normalizeOptionalString(formatErrorMessage(err)) || String(err);
return message.length > MAX_FLUSH_ERROR_LENGTH ? `${truncateUtf16Safe(message, 199)}…` : message;
}
function hasUsableProviderPromptUsage(usage) {
return typeof usage?.promptTokens === "number" && Number.isFinite(usage.promptTokens) && usage.promptTokens > 0;
}
function isUnavailableContextBarrier(usage) {
if (usage.contextUsage?.state !== "unavailable") return false;
return [
usage.input,
usage.output,
usage.cacheRead,
usage.cacheWrite,
usage.total
].every((value) => !(typeof value === "number" && value > 0));
}
const TRANSCRIPT_OUTPUT_READ_BUFFER_TOKENS = 8192;
const SQLITE_USAGE_TAIL_MAX_EVENTS = 512;
function deriveTranscriptUsageSnapshot(snapshot) {
const usage = snapshot?.usage;
if (!usage) return;
const promptTokens = deriveContextPromptTokens({ lastCallUsage: usage });
const outputRaw = usage.output;
const outputTokens = typeof outputRaw === "number" && Number.isFinite(outputRaw) && outputRaw > 0 ? outputRaw : void 0;
if (!(typeof promptTokens === "number") && !(typeof outputTokens === "number")) return;
return {
promptTokens,
outputTokens,
trailingMessages: snapshot.trailingMessages
};
}
function readLatestNonzeroUsageSnapshotFromTranscriptEvents(events) {
const activeEvents = selectSessionTranscriptLeafControlledPath(events) ?? events;
const trailingMessages = [];
for (const event of activeEvents.toReversed()) {
if (!event || typeof event !== "object" || Array.isArray(event)) continue;
const record = event;
if (record.type === "compaction" || record.type === "reset") return;
const message = record.message && typeof record.message === "object" && !Array.isArray(record.message) ? record.message : void 0;
const rawUsage = message?.usage ?? record.usage;
if (message?.api === "cli" && rawUsage && rawUsage.contextUsage === void 0) return;
const usage = normalizeUsage(rawUsage);
if (usage && isUnavailableContextBarrier(usage)) return;
if (usage && hasNonzeroUsage(usage)) return {
usage,
trailingMessages: trailingMessages.toReversed()
};
if (message) trailingMessages.push(message);
}
}
function readActiveTurnTaintFromTranscriptEvents(events) {
const activeEvents = selectSessionTranscriptLeafControlledPath(events) ?? events;
for (const event of activeEvents.toReversed()) {
if (!event || typeof event !== "object" || Array.isArray(event)) continue;
const message = event.message;
if (!message || typeof message !== "object" || Array.isArray(message)) continue;
const record = message;
if (record.role === "user") return {
boundaryFound: true,
tainted: false
};
const metadata = record["__openclaw"];
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) continue;
const openClaw = metadata;
if (openClaw.turnTainted === true || openClaw.resultContentSource === "network") return {
boundaryFound: false,
tainted: true
};
}
return {
boundaryFound: false,
tainted: false
};
}
function readSqliteSessionLogSnapshot(scope, options) {
const snapshot = {};
try {
if (options.includeByteSize) {
const stats = readSessionTranscriptActiveStats(scope);
snapshot.byteSize = stats.sizeBytes;
snapshot.eventCount = stats.eventCount;
}
if (options.includeUsage || options.includeTurnTaint) {
const events = readRecentSessionTranscriptActiveEvents(scope, options.usageEventLimit ?? SQLITE_USAGE_TAIL_MAX_EVENTS);
if (options.includeUsage) snapshot.usage = deriveTranscriptUsageSnapshot(readLatestNonzeroUsageSnapshotFromTranscriptEvents(events));
if (options.includeTurnTaint) {
const scan = readActiveTurnTaintFromTranscriptEvents(events);
snapshot.turnTainted = scan.tainted || !scan.boundaryFound && events.length >= SQLITE_USAGE_TAIL_MAX_EVENTS;
}
}
} catch {
if (options.includeTurnTaint) snapshot.turnTainted = true;
return snapshot;
}
return snapshot;
}
async function appendPostCompactionRefreshPrompt(params) {
const refreshPrompt = await readPostCompactionContext(params.followupRun.run.workspaceDir, {
cfg: params.cfg,
agentId: params.followupRun.run.agentId
});
if (!refreshPrompt) return;
const existingPrompt = normalizeOptionalString(params.followupRun.run.extraSystemPrompt);
if (existingPrompt?.includes(refreshPrompt)) return;
params.followupRun.run.extraSystemPrompt = [existingPrompt, refreshPrompt].filter(Boolean).join("\n\n");
}
function readSessionLogSnapshot(params) {
const agentId = params.agentId ?? resolveAgentIdFromSessionKey(params.sessionKey);
if (params.sessionId && params.storePath && agentId) return readSqliteSessionLogSnapshot({
agentId,
sessionId: params.sessionId,
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
storePath: params.storePath
}, params);
return params.includeTurnTaint ? { turnTainted: true } : {};
}
async function estimateProviderPromptTokensFromMessages(messages, contextWindowTokens) {
if (messages.length === 0) return 0;
const { truncateOversizedToolResultsInMessages } = await toolResultTruncationRuntimeLoader.load();
const projected = truncateOversizedToolResultsInMessages(messages, contextWindowTokens, void 0, void 0, createToolResultPromptProjectionState()).messages;
const tokens = estimateMessagesTokens(projected);
return Number.isFinite(tokens) && tokens >= 0 ? Math.ceil(tokens) : void 0;
}
async function estimatePromptTokensFromSessionTranscript(params) {
const sessionId = normalizeOptionalString(params.sessionId);
if (!sessionId) return;
try {
const snapshot = readSessionLogSnapshot({
agentId: params.agentId,
sessionId,
sessionKey: params.sessionKey,
storePath: params.storePath,
includeByteSize: true,
includeUsage: true
});
let usage = snapshot.usage;
if (!hasUsableProviderPromptUsage(usage) && typeof snapshot.eventCount === "number" && snapshot.eventCount > SQLITE_USAGE_TAIL_MAX_EVENTS) usage = readSessionLogSnapshot({
agentId: params.agentId,
sessionId,
sessionKey: params.sessionKey,
storePath: params.storePath,
includeByteSize: false,
includeUsage: true,
usageEventLimit: snapshot.eventCount
}).usage;
const normalizedOutputTokens = typeof usage?.outputTokens === "number" && Number.isFinite(usage.outputTokens) && usage.outputTokens > 0 ? Math.ceil(usage.outputTokens) : void 0;
if (hasUsableProviderPromptUsage(usage)) {
const trailingMessages = usage.trailingMessages;
const trailingTokens = await estimateProviderPromptTokensFromMessages(trailingMessages, params.contextWindowTokens);
if (trailingTokens === void 0) return;
return {
promptTokens: Math.ceil(usage.promptTokens) + trailingTokens,
promptTokenSource: trailingMessages.length > 0 ? "provider_usage_plus_prompt_projection" : "provider_usage",
outputTokens: normalizedOutputTokens,
transcriptByteSize: snapshot.byteSize
};
}
const estimatedTokens = await estimateProviderPromptTokensFromMessages(await readSessionMessagesAsync({
agentId: params.agentId ?? resolveAgentIdFromSessionKey(params.sessionKey),
sessionId,
sessionKey: params.sessionKey,
storePath: params.storePath
}, {
mode: "full",
reason: "preflight-compaction-estimate"
}), params.contextWindowTokens);
if (estimatedTokens === void 0) return;
return {
promptTokens: estimatedTokens,
promptTokenSource: "prompt_projection",
promptIncludesOutput: true,
outputTokens: normalizedOutputTokens,
transcriptByteSize: snapshot.byteSize
};
} catch {
return;
}
}
/** Compacts session context before a reply or after a completed direct command. */
async function runSessionCompactionIfNeeded(params) {
const assertActive = () => {
params.abortSignal?.throwIfAborted();
if (params.authorize?.() === false) throw new Error("Session compaction maintenance is no longer active");
};
if (!params.sessionKey) return params.sessionEntry;
let entry = params.sessionEntry ?? (params.sessionKey ? params.sessionStore?.[params.sessionKey] : void 0);
if (!entry?.sessionId) return entry ?? params.sessionEntry;
const runtimeParams = {
cfg: params.cfg,
followupRun: params.followupRun,
sessionEntry: entry,
sessionKey: params.sessionKey,
agentHarnessId: params.agentHarnessId
};
assertActive();
const runtimeId = resolveFollowupAgentRuntimeId(runtimeParams);
const isCli = followupUsesCliRuntime(runtimeParams, runtimeId);
const ownsNativeCompaction = followupOwnsNativeCompaction(runtimeParams, runtimeId);
if (params.isHeartbeat || isCli || ownsNativeCompaction) return entry ?? params.sessionEntry;
const isCodexRuntime = normalizeLowercaseStringOrEmpty(runtimeId) === "codex";
const compactionSessionKey = params.sessionKey ?? params.followupRun.run.sessionKey;
if (!compactionSessionKey) return entry ?? params.sessionEntry;
const configuredAgentId = params.followupRun.run.agentId ?? resolveDefaultAgentId(params.cfg);
const compactionAgentId = isUnscopedSessionKeySentinel(compactionSessionKey) ? configuredAgentId : resolveAgentIdFromSessionKey(compactionSessionKey, configuredAgentId);
const compactionStorePath = resolveSessionStorePathForScope({
agentId: compactionAgentId,
sessionKey: compactionSessionKey,
storePath: params.storePath ?? resolveSessionStorePathCore(params.cfg.session?.store, { agentId: compactionAgentId })
});
const compactionStore = params.sessionStore ?? { [compactionSessionKey]: entry };
const contextWindowTokens = resolveMemoryFlushContextWindowTokens({
cfg: params.cfg,
provider: resolveContextConfigProviderForRuntime({
provider: params.followupRun.run.provider,
runtimeId,
config: params.cfg
}),
modelId: params.followupRun.run.model ?? params.defaultModel
});
const reserveTokensFloor = resolveMemoryFlushPlan({
cfg: params.cfg,
contextWindowTokens
})?.reserveTokensFloor ?? resolveEffectiveCompactionReserveTokens({
contextTokenBudget: contextWindowTokens,
reserveTokens: 2e4
});
const freshPersistedTokens = resolveFreshSessionTotalTokens(entry);
const promptTokenEstimate = estimatePromptTokensForMemoryFlush(params.promptForEstimate ?? params.followupRun.prompt);
const responsesServerCompactionThreshold = resolveResponsesServerCompactionThreshold({
cfg: params.cfg,
provider: params.followupRun.run.provider,
modelId: params.followupRun.run.model ?? params.defaultModel
});
const threshold = resolveCompactionThreshold({
contextWindowTokens,
reserveTokensFloor,
minimumThresholdTokens: responsesServerCompactionThreshold
});
const freshNeedsOutputRead = typeof freshPersistedTokens === "number" && typeof promptTokenEstimate === "number" && threshold > 0 && freshPersistedTokens + promptTokenEstimate >= threshold - TRANSCRIPT_OUTPUT_READ_BUFFER_TOKENS;
const maxActiveTranscriptBytes = resolveMaxActiveTranscriptBytes(params.cfg);
const shouldCheckActiveTranscriptBytes = typeof maxActiveTranscriptBytes === "number";
const transcriptUsageTokens = isCodexRuntime || typeof freshPersistedTokens === "number" && !freshNeedsOutputRead ? void 0 : await estimatePromptTokensFromSessionTranscript({
agentId: compactionAgentId,
sessionId: entry.sessionId,
sessionKey: compactionSessionKey,
storePath: compactionStorePath,
contextWindowTokens
});
const transcriptSizeSnapshot = shouldCheckActiveTranscriptBytes && transcriptUsageTokens?.transcriptByteSize === void 0 ? readSessionLogSnapshot({
agentId: compactionAgentId,
sessionId: entry.sessionId,
sessionKey: compactionSessionKey,
storePath: compactionStorePath,
includeByteSize: true,
includeUsage: false
}) : void 0;
const activeTranscriptBytes = transcriptUsageTokens?.transcriptByteSize ?? transcriptSizeSnapshot?.byteSize;
const exceedsTranscriptByteThreshold = typeof activeTranscriptBytes === "number" && typeof maxActiveTranscriptBytes === "number" && activeTranscriptBytes >= maxActiveTranscriptBytes;
let transcriptByteCompactionLatched = !isCodexRuntime && exceedsTranscriptByteThreshold && hasMatchingTranscriptByteCompactionLatch(entry, activeTranscriptBytes, maxActiveTranscriptBytes);
const latch = entry.transcriptByteCompactionLatch;
if (latch !== void 0 && (typeof maxActiveTranscriptBytes !== "number" || latch.sessionId !== entry.sessionId || latch.maxBytes !== maxActiveTranscriptBytes || typeof activeTranscriptBytes === "number" && !transcriptByteCompactionLatched)) {
const compactionCount = await incrementCompactionCount({
agentId: compactionAgentId,
amount: 0,
expectedSession: entry,
sessionEntry: entry,
sessionKey: compactionSessionKey,
sessionStore: compactionStore,
storePath: compactionStorePath
});
assertActive();
if (compactionCount === void 0) throw new Error("Session changed before byte-compaction progress could be cleared");
entry = compactionStore[compactionSessionKey] ?? entry;
transcriptByteCompactionLatched = !isCodexRuntime && exceedsTranscriptByteThreshold && hasMatchingTranscriptByteCompactionLatch(entry, activeTranscriptBytes, maxActiveTranscriptBytes);
}
const shouldCompactByTranscriptBytes = exceedsTranscriptByteThreshold && !transcriptByteCompactionLatched;
if (isCodexRuntime && !shouldCompactByTranscriptBytes) {
logVerbose(`preflightCompaction skipped: sessionKey=${params.sessionKey} runtime=codex reason=codex_native_auto_compaction activeTranscriptBytes=${activeTranscriptBytes ?? "undefined"} maxActiveTranscriptBytes=${maxActiveTranscriptBytes ?? "undefined"}`);
return entry ?? params.sessionEntry;
}
const transcriptPromptTokens = transcriptUsageTokens?.promptTokens;
const transcriptOutputTokens = transcriptUsageTokens?.outputTokens;
const transcriptEstimateOutputTokens = transcriptUsageTokens?.promptIncludesOutput ? void 0 : transcriptOutputTokens;
const usageProjectedTokenCount = typeof transcriptPromptTokens === "number" ? resolveEffectivePromptTokens(transcriptPromptTokens, transcriptEstimateOutputTokens, promptTokenEstimate) : void 0;
const freshProjectedTokenCount = typeof freshPersistedTokens === "number" ? resolveEffectivePromptTokens(freshPersistedTokens, transcriptOutputTokens, promptTokenEstimate) : void 0;
const projectedTokenCount = Math.max(usageProjectedTokenCount ?? 0, freshProjectedTokenCount ?? 0);
const tokenCountForCompaction = Number.isFinite(projectedTokenCount) && projectedTokenCount > 0 ? projectedTokenCount : void 0;
logVerbose(`preflightCompaction check: sessionKey=${params.sessionKey} tokenCount=${tokenCountForCompaction ?? freshPersistedTokens ?? "undefined"} contextWindow=${contextWindowTokens} threshold=${threshold} responsesServerCompactionThreshold=${responsesServerCompactionThreshold ?? "undefined"} isHeartbeat=${params.isHeartbeat} isCli=${isCli} persistedFresh=${entry?.totalTokensFresh === true} transcriptPromptTokens=${transcriptPromptTokens ?? "undefined"} transcriptPromptSource=${transcriptUsageTokens?.promptTokenSource ?? "undefined"} promptTokensEst=${promptTokenEstimate ?? "undefined"} activeTranscriptBytes=${activeTranscriptBytes ?? "undefined"} maxActiveTranscriptBytes=${maxActiveTranscriptBytes ?? "undefined"} sizeTrigger=${shouldCompactByTranscriptBytes} sizeTriggerLatched=${transcriptByteCompactionLatched}`);
if (!(shouldRunPreflightCompaction({
entry,
tokenCount: tokenCountForCompaction,
threshold
}) || shouldCompactByTranscriptBytes)) return entry ?? params.sessionEntry;
const compactionTrigger = shouldCompactByTranscriptBytes ? "transcript_bytes" : "tokens";
logVerbose(`preflightCompaction triggered: sessionKey=${params.sessionKey} tokenCount=${tokenCountForCompaction ?? freshPersistedTokens ?? "undefined"} threshold=${threshold} trigger=${compactionTrigger} activeTranscriptBytes=${activeTranscriptBytes ?? "undefined"} maxActiveTranscriptBytes=${maxActiveTranscriptBytes ?? "undefined"}`);
assertActive();
params.onCompactionStart?.();
const notifyCompaction = async (phase, text) => {
try {
if (text) await params.onCompactionNotice?.(phase, text);
else await params.onCompactionNotice?.(phase);
} catch (err) {
logVerbose(`preflightCompaction notice delivery failed: ${String(err)}`);
}
};
let startedCompactionNotice = false;
let terminalCompactionNoticeSent = false;
const notifyStartCompaction = async () => {
startedCompactionNotice = true;
await notifyCompaction("start");
};
const notifyTerminalCompaction = async (phase, text) => {
terminalCompactionNoticeSent = true;
await notifyCompaction(phase, text);
};
let expectedSession = entry;
try {
await notifyStartCompaction();
assertActive();
const result = await compactEmbeddedAgentSession({
sessionId: entry.sessionId,
sessionKey: compactionSessionKey,
sessionTarget: {
agentId: compactionAgentId,
sessionId: entry.sessionId,
sessionKey: compactionSessionKey,
storePath: compactionStorePath
},
sandboxSessionKey: params.runtimePolicySessionKey,
allowGatewaySubagentBinding: true,
messageChannel: params.followupRun.run.messageProvider,
clientCaps: params.followupRun.run.clientCaps,
conversationToolPolicy: params.followupRun.run.conversationToolPolicy,
groupId: entry.groupId ?? params.followupRun.run.groupId,
groupChannel: entry.groupChannel ?? params.followupRun.run.groupChannel,
groupSpace: entry.space ?? params.followupRun.run.groupSpace,
senderId: params.followupRun.run.senderId,
senderName: params.followupRun.run.senderName,
senderUsername: params.followupRun.run.senderUsername,
senderE164: params.followupRun.run.senderE164,
inputProvenance: params.followupRun.run.inputProvenance,
sessionFile: compactionSessionKey,
workspaceDir: params.followupRun.run.workspaceDir,
cwd: params.followupRun.run.cwd,
agentDir: params.followupRun.run.agentDir,
config: params.cfg,
agentAccountId: params.followupRun.run.agentAccountId,
conversationRoutePeerId: params.followupRun.run.conversationRoutePeerId,
chatType: params.followupRun.run.chatType,
skillsSnapshot: entry.skillsSnapshot ?? params.followupRun.run.skillsSnapshot,
provider: params.followupRun.run.provider,
model: params.followupRun.run.model,
authProfileId: params.followupRun.run.authProfileId,
authProfileIdSource: params.followupRun.run.authProfileIdSource,
sessionEntry: entry,
agentHarnessId: params.agentHarnessId ?? (entry.sessionId === params.followupRun.run.sessionId ? entry.modelSelectionLocked === true ? resolvePersistedSessionRuntimeId(entry) : runtimeId : void 0),
modelSelectionLocked: entry.modelSelectionLocked === true,
thinkLevel: params.followupRun.run.thinkLevel,
bashElevated: params.followupRun.run.bashElevated,
trigger: "budget",
force: true,
forcePreflight: true,
preflightRequired: true,
preflightCompactionTrigger: compactionTrigger,
deferOwningContextEngineCompaction: false,
contextTokenBudget: contextWindowTokens,
currentTokenCount: tokenCountForCompaction ?? freshPersistedTokens,
ownerNumbers: params.followupRun.run.ownerNumbers,
abortSignal: params.abortSignal
}, {
assertActive,
onCommitted: (accepted) => {
expectedSession = accepted.entry;
entry = accepted.entry;
compactionStore[compactionSessionKey] = accepted.entry;
params.onCompactionCommitted?.(accepted);
}
});
if (!result?.ok) {
assertActive();
const reason = result?.reason ?? "not_compacted";
if (result && isBenignCompactionSkipResult(result)) {
await notifyTerminalCompaction("skipped");
logVerbose(`preflightCompaction skipped: sessionKey=${params.sessionKey} reason=${reason}`);
return entry ?? params.sessionEntry;
}
await notifyTerminalCompaction("incomplete");
logVerbose(`preflightCompaction failed: sessionKey=${params.sessionKey} reason=${reason}`);
throw new Error(`Preflight compaction required but failed: ${reason}`);
}
if (!result.compacted) {
assertActive();
const reason = normalizeOptionalString(result.reason) ?? "not_compacted";
if (isBenignCompactionSkipResult(result)) {
await notifyTerminalCompaction("skipped");
logVerbose(`preflightCompaction skipped: sessionKey=${params.sessionKey} reason=${reason}`);
return entry ?? params.sessionEntry;
}
await notifyTerminalCompaction("incomplete");
logVerbose(`preflightCompaction failed: sessionKey=${params.sessionKey} reason=${reason}`);
throw new Error(`Preflight compaction required but failed: ${reason}`);
}
const postCompactionBytes = !isCodexRuntime && compactionTrigger === "transcript_bytes" && typeof maxActiveTranscriptBytes === "number" ? readSessionLogSnapshot({
agentId: compactionAgentId,
sessionId: entry.sessionId,
sessionKey: compactionSessionKey,
storePath: compactionStorePath,
includeByteSize: true,
includeUsage: false
}).byteSize : void 0;
const transcriptByteCompactionLatch = typeof postCompactionBytes === "number" && typeof maxActiveTranscriptBytes === "number" && postCompactionBytes >= maxActiveTranscriptBytes ? {
activeBytes: postCompactionBytes,
sessionId: entry.sessionId,
maxBytes: maxActiveTranscriptBytes
} : void 0;
const compactionCount = await incrementCompactionCount({
agentId: compactionAgentId,
sessionEntry: entry,
sessionStore: compactionStore,
sessionKey: compactionSessionKey,
storePath: compactionStorePath,
tokensAfter: result.result?.tokensAfter,
compactionKind: result.compactionKind,
expectedSession,
transcriptByteCompactionLatch
});
assertActive();
if (compactionCount === void 0) throw new Error("Session changed before compaction maintenance could be recorded");
entry = compactionStore[compactionSessionKey] ?? entry;
if (transcriptByteCompactionLatch) preflightCompactionLog.warn("byte-triggered compaction left the active transcript above its limit; suppressing repeats until it grows by another threshold", {
sessionKey: compactionSessionKey,
activeTranscriptBytes: transcriptByteCompactionLatch.activeBytes,
maxActiveTranscriptBytes: transcriptByteCompactionLatch.maxBytes
});
await appendPostCompactionRefreshPrompt({
cfg: params.cfg,
followupRun: params.followupRun
});
assertActive();
await notifyTerminalCompaction("end", result.compactionKind === "server-endpoint" && typeof result.result?.tokensBefore === "number" && typeof result.result.tokensAfter === "number" ? `🧹 Server-side compaction complete (${formatTokenCount(result.result.tokensBefore)} → ${formatTokenCount(result.result.tokensAfter)})` : void 0);
assertActive();
entry = compactionStore[compactionSessionKey] ?? entry;
if (entry) {
const previousSessionId = params.followupRun.run.sessionId;
params.followupRun.run.sessionId = entry.sessionId;
params.onSessionIdChanged?.(entry.sessionId);
const queueKey = params.followupRun.run.sessionKey ?? params.sessionKey;
if (queueKey) {
params.followupRun.run.sessionFile = queueKey;
refreshQueuedFollowupSession({
key: queueKey,
previousSessionId,
nextSessionId: entry.sessionId,
nextSessionFile: queueKey
});
}
}
return entry ?? params.sessionEntry;
} catch (err) {
if (startedCompactionNotice && !terminalCompactionNoticeSent && !params.abortSignal?.aborted) await notifyCompaction("incomplete");
throw err;
}
}
/** Runs pre-compaction memory flush when transcript state warrants it. */
async function runMemoryFlushIfNeeded(params) {
const abortSignal = params.replyOperation?.abortSignal ?? params.abortSignal;
const updateSessionId = (sessionId) => {
params.replyOperation?.updateSessionId(sessionId);
params.onSessionIdChanged?.(sessionId);
};
const memoryFlushWritable = (() => {
if (!params.sessionKey) return true;
const runtime = resolveSandboxRuntimeStatus({
cfg: params.cfg,
agentId: params.followupRun.run.agentId,
sessionKey: params.sessionKey,
classificationSessionKey: params.runtimePolicySessionKey
});
const workspaceAccess = runtime.workspaceAccess ?? resolveSandboxConfigForAgent(params.cfg, runtime.classificationAgentId).workspaceAccess;
return !runtime.sandboxed || workspaceAccess === "rw";
})();
let entry = params.sessionEntry ?? (params.sessionKey ? params.sessionStore?.[params.sessionKey] : void 0);
if (entry?.incognito === true || isIncognitoSessionKey(params.sessionKey)) return {
sessionEntry: entry,
outcome: "skipped"
};
const runtimeParams = {
cfg: params.cfg,
followupRun: params.followupRun,
sessionEntry: entry,
sessionKey: params.sessionKey
};
const runtimeId = resolveFollowupAgentRuntimeId(runtimeParams);
const isCli = followupUsesCliRuntime(runtimeParams, runtimeId) || followupOwnsNativeCompaction(runtimeParams, runtimeId);
if (!(memoryFlushWritable && !params.isHeartbeat && !isCli)) return {
sessionEntry: entry ?? params.sessionEntry,
outcome: "skipped"
};
const flushRunId = crypto.randomUUID();
let flushRunRegistered = false;
let activeSessionEntry = entry ?? params.sessionEntry;
const activeSessionStore = params.sessionStore ?? {};
const recordFailure = (error) => recordMemoryFlushFailure(error, params, activeSessionEntry);
const contextWindowTokens = resolveMemoryFlushContextWindowTokens({
cfg: params.cfg,
provider: resolveContextConfigProviderForRuntime({
provider: params.followupRun.run.provider,
runtimeId,
config: params.cfg
}),
modelId: params.followupRun.run.model ?? params.defaultModel
});
let memoryFlushPlan;
try {
memoryFlushPlan = resolveMemoryFlushPlan({
cfg: params.cfg,
contextWindowTokens
});
} catch (error) {
return await recordFailure(error);
}
if (!memoryFlushPlan) return {
sessionEntry: activeSessionEntry,
outcome: "skipped"
};
const promptTokenEstimate = estimatePromptTokensForMemoryFlush(params.promptForEstimate ?? params.followupRun.prompt);
const persistedPromptTokensRaw = entry?.totalTokens;
const persistedPromptTokens = typeof persistedPromptTokensRaw === "number" && Number.isFinite(persistedPromptTokensRaw) && persistedPromptTokensRaw > 0 ? persistedPromptTokensRaw : void 0;
const hasFreshPersistedPromptTokens = resolveFreshSessionTotalTokens(entry) !== void 0;
const flushThreshold = Math.max(0, resolveCompactionThreshold({
contextWindowTokens,
reserveTokensFloor: memoryFlushPlan.reserveTokensFloor
}) - Math.max(0, Math.floor(memoryFlushPlan.softThresholdTokens)));
const shouldReadTranscriptForOutput = entry && hasFreshPersistedPromptTokens && typeof promptTokenEstimate === "number" && Number.isFinite(promptTokenEstimate) && flushThreshold > 0 && (persistedPromptTokens ?? 0) + promptTokenEstimate >= flushThreshold - TRANSCRIPT_OUTPUT_READ_BUFFER_TOKENS;
const shouldReadTranscript = Boolean(entry && (!hasFreshPersistedPromptTokens || shouldReadTranscriptForOutput));
const forceFlushTranscriptBytes = memoryFlushPlan.forceFlushTranscriptBytes;
const shouldCheckTranscriptSizeForForcedFlush = Boolean(entry && Number.isFinite(forceFlushTranscriptBytes) && forceFlushTranscriptBytes > 0);
const shouldReadTurnTaint = Boolean(entry);
const sessionLogSnapshot = shouldReadTranscript || shouldCheckTranscriptSizeForForcedFlush || shouldReadTurnTaint ? readSessionLogSnapshot({
agentId: params.followupRun.run.agentId,
sessionId: params.followupRun.run.sessionId,
sessionKey: params.sessionKey ?? params.followupRun.run.sessionKey,
storePath: params.storePath,
includeByteSize: shouldCheckTranscriptSizeForForcedFlush,
includeTurnTaint: shouldReadTurnTaint,
includeUsage: shouldReadTranscript
}) : void 0;
const transcriptByteSize = sessionLogSnapshot?.byteSize;
const shouldForceFlushByTranscriptSize = typeof transcriptByteSize === "number" && transcriptByteSize >= forceFlushTranscriptBytes;
const transcriptUsageSnapshot = sessionLogSnapshot?.usage;
const transcriptPromptTokens = transcriptUsageSnapshot?.promptTokens;
const transcriptOutputTokens = transcriptUsageSnapshot?.outputTokens;
const hasReliableTranscriptPromptTokens = typeof transcriptPromptTokens === "number" && Number.isFinite(transcriptPromptTokens) && transcriptPromptTokens > 0;
if (entry && hasReliableTranscriptPromptTokens && (!hasFreshPersistedPromptTokens || (transcriptPromptTokens ?? 0) > (persistedPromptTokens ?? 0))) {
const nextEntry = {
...entry,
totalTokens: transcriptPromptTokens,
totalTokensFresh: true,
totalTokensVersion: 1
};
entry = nextEntry;
if (params.sessionKey && params.sessionStore) params.sessionStore[params.sessionKey] = nextEntry;
if (params.storePath && params.sessionKey) try {
const updatedEntry = await updateSessionEntry({
storePath: params.storePath,
sessionKey: params.sessionKey
}, () => ({
totalTokens: transcriptPromptTokens,
totalTokensFresh: true,
totalTokensVersion: 1
}), {
skipMaintenance: true,
takeCacheOwnership: true
});
if (updatedEntry) {
entry = updatedEntry;
if (params.sessionStore) params.sessionStore[params.sessionKey] = updatedEntry;
}
} catch (err) {
logVerbose(`failed to persist derived prompt totalTokens: ${String(err)}`);
}
}
const promptTokensSnapshot = Math.max(hasFreshPersistedPromptTokens ? persistedPromptTokens ?? 0 : 0, hasReliableTranscriptPromptTokens ? transcriptPromptTokens ?? 0 : 0);
const projectedTokenCount = promptTokensSnapshot > 0 && (hasFreshPersistedPromptTokens || hasReliableTranscriptPromptTokens) ? resolveEffectivePromptTokens(promptTokensSnapshot, transcriptOutputTokens, promptTokenEstimate) : void 0;
const tokenCountForFlush = typeof projectedTokenCount === "number" && Number.isFinite(projectedTokenCount) && projectedTokenCount > 0 ? projectedTokenCount : void 0;
logVerbose(`memoryFlush check: sessionKey=${params.sessionKey} tokenCount=${tokenCountForFlush ?? "undefined"} contextWindow=${contextWindowTokens} threshold=${flushThreshold} isHeartbeat=${params.isHeartbeat} isCli=${isCli} memoryFlushWritable=${memoryFlushWritable} compactionCount=${entry?.compactionCount ?? 0} memoryFlushCompactionCount=${entry?.memoryFlush?.compactionCount ?? "undefined"} persistedPromptTokens=${persistedPromptTokens ?? "undefined"} persistedFresh=${entry?.totalTokensFresh === true} promptTokensEst=${promptTokenEstimate ?? "undefined"} transcriptPromptTokens=${transcriptPromptTokens ?? "undefined"} transcriptOutputTokens=${transcriptOutputTokens ?? "undefined"} projectedTokenCount=${projectedTokenCount ?? "undefined"} transcriptBytes=${transcriptByteSize ?? "undefined"} forceFlushTranscriptBytes=${forceFlushTranscriptBytes} forceFlushByTranscriptSize=${shouldForceFlushByTranscriptSize}`);
if (!(shouldRunMemoryFlush({
entry,
tokenCount: tokenCountForFlush,
threshold: flushThreshold
}) || shouldForceFlushByTranscriptSize && entry != null && !hasAlreadyFlushedForCurrentCompaction(entry))) return {
sessionEntry: entry ?? params.sessionEntry,
outcome: "skipped"
};
logVerbose(`memoryFlush triggered: sessionKey=${params.sessionKey} tokenCount=${tokenCountForFlush ?? "undefined"} threshold=${flushThreshold}`);
activeSessionEntry = entry ?? params.sessionEntry;
params.replyOperation?.setPhase("memory_flushing");
let bootstrapPromptWarningSignaturesSeen = resolveBootstrapWarningSignaturesSeen(activeSessionEntry?.systemPromptReport ?? (params.sessionKey ? activeSessionStore?.[params.sessionKey]?.systemPromptReport : void 0));
const prepareMemoryFlushAttempt = async () => {
const plan = resolveMemoryFlushPlan({
cfg: params.cfg,
nowMs: Date.now(),
contextWindowTokens
});
if (!plan) return null;
const writePath = plan.relativePath;
await ensureMemoryFlushTargetFile({
workspaceDir: params.followupRun.run.workspaceDir,
relativePath: writePath
});
return {
plan,
writePath,
systemPrompt: [params.followupRun.run.extraSystemPrompt, plan.systemPrompt].filter(Boolean).join("\n\n"),
selection: resolveMemoryFlushModelFallbackOptions(params.followupRun.run, plan.model, params.cfg),
preparedRunAdmission: prepareSystemAgentRunAdmission(params.cfg, flushRunId, params.followupRun.run.agentId, "auto-reply.memory-flush")
};
};
let preparedAttempt;
try {
preparedAttempt = await prepareMemoryFlushAttempt();
} catch (error) {
return await recordFailure(error);
}
if (!preparedAttempt) return {
sessionEntry: activeSessionEntry,
outcome: "skipped"
};
const { plan: activeMemoryFlushPlan, writePath: memoryFlushWritePath, systemPrompt: flushSystemPrompt, selection, preparedRunAdmission } = preparedAttempt;
const deferredLifecycle = createDeferredEmbeddedRunLifecycleManager({
runId: flushRunId,
sessionId: activeSessionEntry?.sessionId ?? params.followupRun.run.sessionId,
sessionKey: params.sessionKey,
sessionFile: params.followupRun.run.sessionFile,
abortSignal
});
const flushedCompactionCount = activeSessionEntry?.compactionCount ?? 0;
const compaction = {
count: 0,
durable: []
};
let visibleErrorPayloads = [];
try {
if (params.sessionKey) {
registerAgentRunContext(flushRunId, {
sessionKey: params.sessionKey,
...activeSessionEntry?.sessionId ? { sessionId: activeSessionEntry.sessionId } : {},
verboseLevel: params.resolvedVerboseLevel,
isControlUiVisible: false,
projectSessionActive: false,
projectSessionLifecycle: false,
projectSessionMessages: false
});
flushRunRegistered = true;
}
try {
await runEmbeddedAgentEntry({
selection: {
cfg: selection.cfg,
provider: selection.provider,
model: selection.model,
requestedRouteResolution: selection.requestedRouteResolution,
agentDir: selection.agentDir,
fallbacksOverride: selection.fallbacksOverride,
userLockedAuthProfileId: params.followupRun.run.authProfileIdSource === "user" ? params.followupRun.run.authProfileId : void 0
},
identity: {
runId: flushRunId,
agentId: params.followupRun.run.agentId,
sessionId: activeSessionEntry?.sessionId ?? params.followupRun.run.sessionId,
sessionKey: selection.sessionKey,
lane: "main"
},
harness: {
workspaceDir: params.followupRun.run.workspaceDir,
sessionKey: params.runtimePolicySessionKey ?? params.followupRun.run.runtimePolicySessionKey ?? params.sessionKey,
preparation: { kind: "direct" },
resolveRuntimeOverride: (provider) => resolveSessionRuntimeOverrideForProvider({
provider,
entry: activeSessionEntry,
cfg: params.cfg
})
},
behavior: { kind: "maintenance" },
sessionOverride: { kind: "preserve" },
abortSignal: deferredLifecycle.signal,
runCandidate: async (provider, model, runOptions) => {
invalidateTurnCompactionContext(compaction);
const sessionRuntimeOverride = resolveSessionRuntimeOverrideForProvider({
provider,
entry: activeSessionEntry,
cfg: params.cfg
});
const candidateThinkLevel = resolveRunThinkingLevelForFallbackCandidate({
cfg: params.cfg,
provider,
modelId: model,
run: params.followupRun.run,
catalog: params.followupRun.run.thinkingCatalog,
agentId: params.followupRun.run.agentId,
sessionKey: params.runtimePolicySessionKey ?? params.followupRun.run.runtimePolicySessionKey ?? params.sessionKey,
sessionEntry: activeSessionEntry,
agentRuntime: sessionRuntimeOverride
});
const { embeddedContext, senderContext, runBaseParams } = await buildEmbeddedRunExecutionParams({
run: {
...params.followupRun.run,
thinkLevel: candidateThinkLevel
},
replyRoute: params.followupRun,
sessionCtx: params.sessionCtx,
hasRepliedRef: params.opts?.hasRepliedRef,
provider,
model,
runId: flushRunId,
allowTransientCooldownProbe: runOptions.allowTransientCooldownProbe
});
const result = await runEmbeddedAgent({
preparedRunAdmission,
...embeddedContext,
...senderContext,
...runBaseParams,
agentHarnessId: resolveSessionPinnedHarnessId(activeSessionEntry),
agentHarnessRuntimeOverride: sessionRuntimeOverride,
sandboxSessionKey: params.runtimePolicySessionKey,
allowGatewaySubagentBinding: true,
silentExpected: true,
allowEmptyAssistantReplyAsSilent: true,
terminalReplyExpectation: "optional",
trigger: "memory",
memoryFlushWritePath,
initialTurnTainted: !params.followupRun.run.senderIsOwner || sessionLogSnapshot?.turnTainted === true,
prompt: activeMemoryFlushPlan.prompt,
transcriptPrompt: "",
extraSystemPrompt: flushSystemPrompt,
isFinalFallbackAttempt: runOptions.isFinalFallbackAttempt,
bootstrapPromptWarningSignaturesSeen,
bootstrapPromptWarningSignature: bootstrapPromptWarningSignaturesSeen[bootstrapPromptWarningSignaturesSeen.length - 1],
abortSignal: deferredLifecycle.signal,
onCompactionAccounting: (fact) => {
if (fact) {
recordTurnCompaction(compaction, fact);
if (fact.kind === "durable" && !deferredLifecycle.signal.aborted) {
params.followupRun.run.sessionId = fact.target.sessionId;
params.followupRun.run.sessionFile = fact.target.sessionKey;
}
}
},
onDeferredLifecycleOwner: deferredLifecycle.adopt,
onDeferredLifecycleAbort: deferredLifecycle.abort,
replyOperation: params.replyOperation,
contextEngineLogicalTurnLease: runOptions.contextEngineLogicalTurnLease,
onContextEngineTurnCandidate: runOptions.onContextEngineTurnCandidate
});
visibleErrorPayloads = resolveVisibleMemoryFlushErrorPayloads(result.payloads);
bootstrapPromptWarningSignaturesSeen = resolveBootstrapWarningSignaturesSeen(result.meta?.systemPromptReport);
return result;
}
});
} finally {
for (const fact of compaction.durable) {
const previousSessionId = activeSessionEntry?.sessionId ?? params.followupRun.run.sessionId;
if (await incrementCompactionCount({
agentId: fact.target.agentId,
sessionEntry: activeSessionEntry,
sessionStore: activeSessionStore,
sessionKey: fact.target.sessionKey,
storePath: fact.target.storePath,
expectedSession: fact.target,
amount: fact.count,
tokensAfter: fact.currentContextSnapshot?.tokens
}) === void 0) continue;
activeSessionEntry = activeSessionStore[fact.target.sessionKey] ?? activeSessionEntry;
if (activeSessionEntry?.sessionId === fact.target.sessionId) {
params.followupRun.run.sessionId = activeSessionEntry.sessionId;
params.followupRun.run.sessionFile = fact.target.sessionKey;
refreshQueuedFollowupSession({
key: fact.target.sessionKey,
previousSessionId,
nextSessionId: activeSessionEntry.sessionId,
nextSessionFile: fact.target.sessionKey
});
updateSessionId(activeSessionEntry.sessionId);
}
}
}
deferredLifecycle.signal.throwIfAborted();
if (visibleErrorPayloads.length > 0) throw buildVisibleMemoryFlushFailure(visibleErrorPayloads);
if (params.storePath && params.sessionKey) try {
const updatedEntry = await updateSessionEntry({
storePath: params.storePath,
sessionKey: params.sessionKey
}, async () => ({ memoryFlush: {
kind: "succeeded",
compactionCount: flushedCompactionCount
} }), {
skipMaintenance: true,
takeCacheOwnership: true
});
if (updatedEntry) {
activeSessionEntry = updatedEntry;
params.followupRun.run.sessionId = updatedEntry.sessionId;
updateSessionId(updatedEntry.sessionId);
const refreshedSessionKey = params.sessionKey ?? params.followupRun.run.sessionKey;
if (refreshedSessionKey) params.followupRun.run.sessionFile = refreshedSessionKey;
}
} catch (err) {
logVerbose(`failed to persist memory flush metadata: ${String(err)}`);
}
return {
sessionEntry: activeSessionEntry,
outcome: "completed"
};
} catch (error) {
return await recordFailure(error);
} finally {
await deferredLifecycle.complete();
if (flushRunRegistered) clearAgentRunContext(flushRunId);
preparedRunAdmission.close();
}
}
async function recordMemoryFlushFailure(error, run, initialSessionEntry) {
let sessionEntry = initialSessionEntry;
let outcome = "failed";
const truncatedError = truncateMemoryFlushErrorMessage(error);
const { sessionKey, storePath } = run;
if (!isAbortError(error) && storePath && sessionKey) try {
const adoptEntry = (entry) => {
if (entry) {
sessionEntry = entry;
if (run.sessionStore) run.sessionStore[sessionKey] = entry;
}
};
const updateEntry = (update) => updateSessionEntry({
storePath,
sessionKey
}, update, {
skipMaintenance: true,
takeCacheOwnership: true
});
const failedEntry = await updateEntry(async (currentEntry) => ({ memoryFlush: {
kind: "failed",
...currentEntry.memoryFlush?.compactionCount !== void 0 ? { compactionCount: currentEntry.memoryFlush.compactionCount } : {},
failureCount: (currentEntry.memoryFlush?.kind === "failed" ? currentEntry.memoryFlush.failureCount : 0) + 1
} }));
adoptEntry(failedEntry);
const failureCount = failedEntry?.memoryFlush?.kind === "failed" ? failedEntry.memoryFlush.failureCount : 0;
logVerbose(`memory flush failed (attempt ${failureCount}/${MAX_FLUSH_FAILURES}): ${truncatedError}`);
if (failedEntry && failureCount >= MAX_FLUSH_FAILURES) {
outcome = "exhausted";
logVerbose(`memory flush exhausted: skipping flush for this compaction cycle after ${failureCount} consecutive failures`);
adoptEntry(await updateEntry(async (currentEntry) => ({ memoryFlush: {
kind: "succeeded",
compactionCount: currentEntry.compactionCount ?? 0
} })));
run.onVisibleErrorPayloads?.([{
text: `⚠️ Memory flush failed after ${MAX_FLUSH_FAILURES} attempts; skipping for this cycle. It will retry after the next compaction.`,
isError: true
}]);
}
} catch (persistError) {
logVerbose(`failed to persist memory flush failure metadata: ${String(persistError)}`);
}
else logVerbose(`memory flush run failed: ${String(error)}`);
const visibleErrorPayload = buildMemoryFlushErrorPayload(error);
if (visibleErrorPayload) run.onVisibleErrorPayloads?.([visibleErrorPayload]);
return {
sessionEntry,
outcome
};
}
//#endregion
export { runSessionCompactionIfNeeded as n, runMemoryFlushIfNeeded as t };