mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:50:43 +00:00
fix(cli): compact persisted CLI transcripts
This commit is contained in:
@@ -73,6 +73,7 @@ type AcpRuntimeErrorsRuntime = typeof import("../acp/runtime/errors.js");
|
||||
type AcpSessionIdentifiersRuntime = typeof import("../acp/runtime/session-identifiers.js");
|
||||
type DeliveryRuntime = typeof import("./command/delivery.runtime.js");
|
||||
type SessionStoreRuntime = typeof import("./command/session-store.runtime.js");
|
||||
type CliCompactionRuntime = typeof import("./command/cli-compaction.js");
|
||||
type TranscriptResolveRuntime = typeof import("../config/sessions/transcript-resolve.runtime.js");
|
||||
type CliDepsRuntime = typeof import("../cli/deps.js");
|
||||
type ExecDefaultsRuntime = typeof import("./exec-defaults.js");
|
||||
@@ -88,6 +89,7 @@ let acpRuntimeErrorsRuntimePromise: Promise<AcpRuntimeErrorsRuntime> | undefined
|
||||
let acpSessionIdentifiersRuntimePromise: Promise<AcpSessionIdentifiersRuntime> | undefined;
|
||||
let deliveryRuntimePromise: Promise<DeliveryRuntime> | undefined;
|
||||
let sessionStoreRuntimePromise: Promise<SessionStoreRuntime> | undefined;
|
||||
let cliCompactionRuntimePromise: Promise<CliCompactionRuntime> | undefined;
|
||||
let transcriptResolveRuntimePromise: Promise<TranscriptResolveRuntime> | undefined;
|
||||
let cliDepsRuntimePromise: Promise<CliDepsRuntime> | undefined;
|
||||
let execDefaultsRuntimePromise: Promise<ExecDefaultsRuntime> | undefined;
|
||||
@@ -131,6 +133,11 @@ function loadSessionStoreRuntime(): Promise<SessionStoreRuntime> {
|
||||
return sessionStoreRuntimePromise;
|
||||
}
|
||||
|
||||
function loadCliCompactionRuntime(): Promise<CliCompactionRuntime> {
|
||||
cliCompactionRuntimePromise ??= import("./command/cli-compaction.js");
|
||||
return cliCompactionRuntimePromise;
|
||||
}
|
||||
|
||||
function loadTranscriptResolveRuntime(): Promise<TranscriptResolveRuntime> {
|
||||
transcriptResolveRuntimePromise ??= import("../config/sessions/transcript-resolve.runtime.js");
|
||||
return transcriptResolveRuntimePromise;
|
||||
@@ -874,6 +881,11 @@ async function agentCommandInternal(
|
||||
const startedAt = Date.now();
|
||||
let lifecycleEnded = false;
|
||||
const attemptExecutionRuntime = await loadAttemptExecutionRuntime();
|
||||
const runContext = resolveAgentRunContext(opts);
|
||||
const messageChannel = resolveMessageChannel(
|
||||
runContext.messageChannel,
|
||||
opts.replyChannel ?? opts.channel,
|
||||
);
|
||||
|
||||
let result: Awaited<ReturnType<AttemptExecutionRuntime["runAgentAttempt"]>>;
|
||||
let fallbackProvider = provider;
|
||||
@@ -882,11 +894,6 @@ async function agentCommandInternal(
|
||||
let liveSwitchRetries = 0;
|
||||
for (;;) {
|
||||
try {
|
||||
const runContext = resolveAgentRunContext(opts);
|
||||
const messageChannel = resolveMessageChannel(
|
||||
runContext.messageChannel,
|
||||
opts.replyChannel ?? opts.channel,
|
||||
);
|
||||
const spawnedBy = normalizedSpawned.spawnedBy ?? sessionEntry?.spawnedBy;
|
||||
const effectiveFallbacksOverride = resolveEffectiveModelFallbacks({
|
||||
cfg,
|
||||
@@ -1103,6 +1110,27 @@ async function agentCommandInternal(
|
||||
threadId: opts.threadId,
|
||||
sessionCwd: workspaceDir,
|
||||
});
|
||||
sessionEntry = await (
|
||||
await loadCliCompactionRuntime()
|
||||
).runCliTurnCompactionLifecycle({
|
||||
cfg,
|
||||
sessionId,
|
||||
sessionKey: sessionKey ?? sessionId,
|
||||
sessionEntry,
|
||||
sessionStore,
|
||||
storePath,
|
||||
sessionAgentId,
|
||||
workspaceDir,
|
||||
agentDir,
|
||||
provider: result.meta.agentMeta?.provider ?? provider,
|
||||
model: result.meta.agentMeta?.model ?? model,
|
||||
skillsSnapshot,
|
||||
messageChannel,
|
||||
agentAccountId: runContext.accountId,
|
||||
senderIsOwner: opts.senderIsOwner,
|
||||
thinkLevel: resolvedThinkLevel,
|
||||
extraSystemPrompt: opts.extraSystemPrompt,
|
||||
});
|
||||
} catch (error) {
|
||||
log.warn(
|
||||
`CLI transcript persistence failed for ${sessionKey ?? sessionId}: ${error instanceof Error ? error.message : String(error)}`,
|
||||
|
||||
@@ -205,8 +205,11 @@ export async function executePreparedCliRun(
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const basePrompt = cliSessionIdToUse
|
||||
? params.prompt
|
||||
: (context.openClawHistoryPrompt ?? params.prompt);
|
||||
let prompt = applyPluginTextReplacements(
|
||||
prependBootstrapPromptWarning(params.prompt, context.bootstrapPromptWarningLines, {
|
||||
prependBootstrapPromptWarning(basePrompt, context.bootstrapPromptWarningLines, {
|
||||
preserveExactPrompt: context.heartbeatPrompt,
|
||||
}),
|
||||
context.backendResolved.textTransforms?.input,
|
||||
@@ -270,7 +273,7 @@ export async function executePreparedCliRun(
|
||||
: undefined;
|
||||
try {
|
||||
cliBackendLog.info(
|
||||
`cli exec: provider=${params.provider} model=${context.normalizedModel} promptChars=${params.prompt.length}`,
|
||||
`cli exec: provider=${params.provider} model=${context.normalizedModel} promptChars=${basePrompt.length}`,
|
||||
);
|
||||
const logOutputText =
|
||||
isTruthyEnvValue(process.env[CLI_BACKEND_LOG_OUTPUT_ENV]) ||
|
||||
|
||||
@@ -42,7 +42,7 @@ import { redactRunIdentifier, resolveRunWorkspaceDir } from "../workspace-run.js
|
||||
import { prepareCliBundleMcpConfig } from "./bundle-mcp.js";
|
||||
import { buildSystemPrompt, normalizeCliModel } from "./helpers.js";
|
||||
import { cliBackendLog } from "./log.js";
|
||||
import { loadCliSessionHistoryMessages } from "./session-history.js";
|
||||
import { buildCliSessionHistoryPrompt, loadCliSessionHistoryMessages } from "./session-history.js";
|
||||
import type { PreparedCliRunContext, RunCliAgentParams } from "./types.js";
|
||||
|
||||
const prepareDeps = {
|
||||
@@ -259,6 +259,16 @@ export async function prepareCliRunContext(
|
||||
`cli session reset: provider=${params.provider} reason=${reusableCliSession.invalidatedReason}`,
|
||||
);
|
||||
}
|
||||
const openClawHistoryPrompt = buildCliSessionHistoryPrompt({
|
||||
messages: loadCliSessionHistoryMessages({
|
||||
sessionId: params.sessionId,
|
||||
sessionFile: params.sessionFile,
|
||||
sessionKey: params.sessionKey,
|
||||
agentId: params.agentId,
|
||||
config: params.config,
|
||||
}),
|
||||
prompt: params.prompt,
|
||||
});
|
||||
const heartbeatPrompt = resolveHeartbeatPromptForSystemPrompt({
|
||||
config: params.config,
|
||||
agentId: sessionAgentId,
|
||||
@@ -392,6 +402,7 @@ export async function prepareCliRunContext(
|
||||
systemPrompt,
|
||||
systemPromptReport,
|
||||
bootstrapPromptWarningLines: bootstrapPromptWarning.lines,
|
||||
...(openClawHistoryPrompt ? { openClawHistoryPrompt } : {}),
|
||||
heartbeatPrompt,
|
||||
authEpoch,
|
||||
authEpochVersion: CLI_AUTH_EPOCH_VERSION,
|
||||
|
||||
@@ -15,6 +15,69 @@ import {
|
||||
export const MAX_CLI_SESSION_HISTORY_FILE_BYTES = 5 * 1024 * 1024;
|
||||
export const MAX_CLI_SESSION_HISTORY_MESSAGES = MAX_AGENT_HOOK_HISTORY_MESSAGES;
|
||||
|
||||
type HistoryMessage = {
|
||||
role?: unknown;
|
||||
content?: unknown;
|
||||
};
|
||||
|
||||
function coerceHistoryText(content: unknown): string {
|
||||
if (typeof content === "string") {
|
||||
return content.trim();
|
||||
}
|
||||
if (!Array.isArray(content)) {
|
||||
return "";
|
||||
}
|
||||
return content
|
||||
.flatMap((block) => {
|
||||
if (!block || typeof block !== "object") {
|
||||
return [];
|
||||
}
|
||||
const text = (block as { text?: unknown }).text;
|
||||
return typeof text === "string" && text.trim().length > 0 ? [text.trim()] : [];
|
||||
})
|
||||
.join("\n")
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function buildCliSessionHistoryPrompt(params: {
|
||||
messages: unknown[];
|
||||
prompt: string;
|
||||
}): string | undefined {
|
||||
const renderedHistory = params.messages
|
||||
.flatMap((message) => {
|
||||
if (!message || typeof message !== "object") {
|
||||
return [];
|
||||
}
|
||||
const entry = message as HistoryMessage;
|
||||
const role =
|
||||
entry.role === "assistant" ? "Assistant" : entry.role === "user" ? "User" : undefined;
|
||||
if (!role) {
|
||||
return [];
|
||||
}
|
||||
const text = coerceHistoryText(entry.content);
|
||||
return text ? [`${role}: ${text}`] : [];
|
||||
})
|
||||
.join("\n\n")
|
||||
.trim();
|
||||
|
||||
if (!renderedHistory) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [
|
||||
"Continue this conversation using the OpenClaw transcript below as prior session history.",
|
||||
"Treat it as authoritative context for this fresh CLI session.",
|
||||
"",
|
||||
"<conversation_history>",
|
||||
renderedHistory,
|
||||
"</conversation_history>",
|
||||
"",
|
||||
"<next_user_message>",
|
||||
params.prompt,
|
||||
"</next_user_message>",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function safeRealpathSync(filePath: string): string | undefined {
|
||||
try {
|
||||
return fs.realpathSync(filePath);
|
||||
|
||||
@@ -79,6 +79,7 @@ export type PreparedCliRunContext = {
|
||||
systemPrompt: string;
|
||||
systemPromptReport: SessionSystemPromptReport;
|
||||
bootstrapPromptWarningLines: string[];
|
||||
openClawHistoryPrompt?: string;
|
||||
heartbeatPrompt?: string;
|
||||
authEpoch?: string;
|
||||
authEpochVersion: number;
|
||||
|
||||
267
src/agents/command/cli-compaction.ts
Normal file
267
src/agents/command/cli-compaction.ts
Normal file
@@ -0,0 +1,267 @@
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import { SessionManager } from "@mariozechner/pi-coding-agent";
|
||||
import type { SessionEntry } from "../../config/sessions/types.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { resolveContextEngine as resolveContextEngineImpl } from "../../context-engine/registry.js";
|
||||
import type { ContextEngine } from "../../context-engine/types.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import { buildEmbeddedCompactionRuntimeContext } from "../pi-embedded-runner/compaction-runtime-context.js";
|
||||
import { runContextEngineMaintenance as runContextEngineMaintenanceImpl } from "../pi-embedded-runner/context-engine-maintenance.js";
|
||||
import { shouldPreemptivelyCompactBeforePrompt as shouldPreemptivelyCompactBeforePromptImpl } from "../pi-embedded-runner/run/preemptive-compaction.js";
|
||||
import { resolveLiveToolResultMaxChars as resolveLiveToolResultMaxCharsImpl } from "../pi-embedded-runner/tool-result-truncation.js";
|
||||
import { createPreparedEmbeddedPiSettingsManager as createPreparedEmbeddedPiSettingsManagerImpl } from "../pi-project-settings.js";
|
||||
import { applyPiAutoCompactionGuard as applyPiAutoCompactionGuardImpl } from "../pi-settings.js";
|
||||
import type { SkillSnapshot } from "../skills.js";
|
||||
import { recordCliCompactionInStore as recordCliCompactionInStoreImpl } from "./session-store.js";
|
||||
|
||||
type SessionManagerLike = ReturnType<typeof SessionManager.open>;
|
||||
type SettingsManagerLike = {
|
||||
getCompactionReserveTokens: () => number;
|
||||
getCompactionKeepRecentTokens: () => number;
|
||||
applyOverrides: (overrides: {
|
||||
compaction: {
|
||||
reserveTokens?: number;
|
||||
keepRecentTokens?: number;
|
||||
};
|
||||
}) => void;
|
||||
setCompactionEnabled?: (enabled: boolean) => void;
|
||||
};
|
||||
type CliCompactionDeps = {
|
||||
openSessionManager: (sessionFile: string) => SessionManagerLike;
|
||||
resolveContextEngine: (cfg: OpenClawConfig) => Promise<ContextEngine>;
|
||||
createPreparedEmbeddedPiSettingsManager: (params: {
|
||||
cwd: string;
|
||||
agentDir: string;
|
||||
cfg?: OpenClawConfig;
|
||||
contextTokenBudget?: number;
|
||||
}) => SettingsManagerLike | Promise<SettingsManagerLike>;
|
||||
applyPiAutoCompactionGuard: (params: {
|
||||
settingsManager: SettingsManagerLike;
|
||||
contextEngineInfo?: ContextEngine["info"];
|
||||
}) => unknown;
|
||||
shouldPreemptivelyCompactBeforePrompt: typeof shouldPreemptivelyCompactBeforePromptImpl;
|
||||
resolveLiveToolResultMaxChars: typeof resolveLiveToolResultMaxCharsImpl;
|
||||
runContextEngineMaintenance: typeof runContextEngineMaintenanceImpl;
|
||||
recordCliCompactionInStore: typeof recordCliCompactionInStoreImpl;
|
||||
};
|
||||
|
||||
const log = createSubsystemLogger("agents/cli-compaction");
|
||||
|
||||
const cliCompactionDeps: CliCompactionDeps = {
|
||||
openSessionManager: (sessionFile: string) => SessionManager.open(sessionFile),
|
||||
resolveContextEngine: resolveContextEngineImpl,
|
||||
createPreparedEmbeddedPiSettingsManager: createPreparedEmbeddedPiSettingsManagerImpl,
|
||||
applyPiAutoCompactionGuard: applyPiAutoCompactionGuardImpl,
|
||||
shouldPreemptivelyCompactBeforePrompt: shouldPreemptivelyCompactBeforePromptImpl,
|
||||
resolveLiveToolResultMaxChars: resolveLiveToolResultMaxCharsImpl,
|
||||
runContextEngineMaintenance: runContextEngineMaintenanceImpl,
|
||||
recordCliCompactionInStore: recordCliCompactionInStoreImpl,
|
||||
};
|
||||
|
||||
export function setCliCompactionTestDeps(overrides: Partial<typeof cliCompactionDeps>): void {
|
||||
Object.assign(cliCompactionDeps, overrides);
|
||||
}
|
||||
|
||||
export function resetCliCompactionTestDeps(): void {
|
||||
Object.assign(cliCompactionDeps, {
|
||||
openSessionManager: (sessionFile: string) => SessionManager.open(sessionFile),
|
||||
resolveContextEngine: resolveContextEngineImpl,
|
||||
createPreparedEmbeddedPiSettingsManager: createPreparedEmbeddedPiSettingsManagerImpl,
|
||||
applyPiAutoCompactionGuard: applyPiAutoCompactionGuardImpl,
|
||||
shouldPreemptivelyCompactBeforePrompt: shouldPreemptivelyCompactBeforePromptImpl,
|
||||
resolveLiveToolResultMaxChars: resolveLiveToolResultMaxCharsImpl,
|
||||
runContextEngineMaintenance: runContextEngineMaintenanceImpl,
|
||||
recordCliCompactionInStore: recordCliCompactionInStoreImpl,
|
||||
});
|
||||
}
|
||||
|
||||
function resolvePositiveInteger(value: number | undefined): number | undefined {
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Math.floor(value);
|
||||
}
|
||||
|
||||
function getSessionBranchMessages(sessionManager: SessionManagerLike): AgentMessage[] {
|
||||
return sessionManager
|
||||
.getBranch()
|
||||
.flatMap((entry) =>
|
||||
entry.type === "message" && typeof entry.message === "object" && entry.message !== null
|
||||
? [entry.message]
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
function resolveSessionTokenSnapshot(sessionEntry: SessionEntry | undefined): number | undefined {
|
||||
return resolvePositiveInteger(
|
||||
sessionEntry?.totalTokensFresh === false ? undefined : sessionEntry?.totalTokens,
|
||||
);
|
||||
}
|
||||
|
||||
async function compactCliTranscript(params: {
|
||||
contextEngine: ContextEngine;
|
||||
sessionId: string;
|
||||
sessionKey: string;
|
||||
sessionFile: string;
|
||||
sessionManager: SessionManagerLike;
|
||||
cfg: OpenClawConfig;
|
||||
workspaceDir: string;
|
||||
agentDir: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
contextTokenBudget: number;
|
||||
currentTokenCount: number;
|
||||
skillsSnapshot?: SkillSnapshot;
|
||||
messageChannel?: string;
|
||||
agentAccountId?: string;
|
||||
senderIsOwner?: boolean;
|
||||
thinkLevel?: Parameters<typeof buildEmbeddedCompactionRuntimeContext>[0]["thinkLevel"];
|
||||
extraSystemPrompt?: string;
|
||||
}) {
|
||||
const runtimeContext = {
|
||||
...buildEmbeddedCompactionRuntimeContext({
|
||||
sessionKey: params.sessionKey,
|
||||
messageChannel: params.messageChannel,
|
||||
messageProvider: params.messageChannel,
|
||||
agentAccountId: params.agentAccountId,
|
||||
authProfileId: undefined,
|
||||
workspaceDir: params.workspaceDir,
|
||||
agentDir: params.agentDir,
|
||||
config: params.cfg,
|
||||
skillsSnapshot: params.skillsSnapshot,
|
||||
senderIsOwner: params.senderIsOwner,
|
||||
provider: params.provider,
|
||||
modelId: params.model,
|
||||
thinkLevel: params.thinkLevel,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
}),
|
||||
currentTokenCount: params.currentTokenCount,
|
||||
tokenBudget: params.contextTokenBudget,
|
||||
trigger: "cli_budget",
|
||||
};
|
||||
|
||||
const compactResult = await params.contextEngine.compact({
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionFile: params.sessionFile,
|
||||
tokenBudget: params.contextTokenBudget,
|
||||
currentTokenCount: params.currentTokenCount,
|
||||
force: true,
|
||||
compactionTarget: "budget",
|
||||
runtimeContext,
|
||||
});
|
||||
|
||||
if (!compactResult.compacted) {
|
||||
log.warn(
|
||||
`CLI transcript compaction did not reduce context for ${params.provider}/${params.model}: ${compactResult.reason ?? "nothing to compact"}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
await cliCompactionDeps.runContextEngineMaintenance({
|
||||
contextEngine: params.contextEngine,
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionFile: params.sessionFile,
|
||||
reason: "compaction",
|
||||
sessionManager: params.sessionManager,
|
||||
runtimeContext,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function runCliTurnCompactionLifecycle(params: {
|
||||
cfg: OpenClawConfig;
|
||||
sessionId: string;
|
||||
sessionKey: string;
|
||||
sessionEntry: SessionEntry | undefined;
|
||||
sessionStore?: Record<string, SessionEntry>;
|
||||
storePath?: string;
|
||||
sessionAgentId: string;
|
||||
workspaceDir: string;
|
||||
agentDir: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
skillsSnapshot?: SkillSnapshot;
|
||||
messageChannel?: string;
|
||||
agentAccountId?: string;
|
||||
senderIsOwner?: boolean;
|
||||
thinkLevel?: Parameters<typeof buildEmbeddedCompactionRuntimeContext>[0]["thinkLevel"];
|
||||
extraSystemPrompt?: string;
|
||||
}): Promise<SessionEntry | undefined> {
|
||||
const sessionFile = params.sessionEntry?.sessionFile;
|
||||
const contextTokenBudget = resolvePositiveInteger(params.sessionEntry?.contextTokens);
|
||||
if (!sessionFile || !contextTokenBudget) {
|
||||
return params.sessionEntry;
|
||||
}
|
||||
|
||||
const contextEngine = await cliCompactionDeps.resolveContextEngine(params.cfg);
|
||||
const sessionManager = cliCompactionDeps.openSessionManager(sessionFile);
|
||||
const settingsManager = await cliCompactionDeps.createPreparedEmbeddedPiSettingsManager({
|
||||
cwd: params.workspaceDir,
|
||||
agentDir: params.agentDir,
|
||||
cfg: params.cfg,
|
||||
contextTokenBudget,
|
||||
});
|
||||
await cliCompactionDeps.applyPiAutoCompactionGuard({
|
||||
settingsManager,
|
||||
contextEngineInfo: contextEngine.info,
|
||||
});
|
||||
|
||||
const preemptiveCompaction = cliCompactionDeps.shouldPreemptivelyCompactBeforePrompt({
|
||||
messages: getSessionBranchMessages(sessionManager),
|
||||
prompt: "",
|
||||
contextTokenBudget,
|
||||
reserveTokens: settingsManager.getCompactionReserveTokens(),
|
||||
toolResultMaxChars: cliCompactionDeps.resolveLiveToolResultMaxChars({
|
||||
contextWindowTokens: contextTokenBudget,
|
||||
cfg: params.cfg,
|
||||
agentId: params.sessionAgentId,
|
||||
}),
|
||||
});
|
||||
const tokenSnapshot = resolveSessionTokenSnapshot(params.sessionEntry);
|
||||
const currentTokenCount = Math.max(
|
||||
preemptiveCompaction.estimatedPromptTokens,
|
||||
tokenSnapshot ?? 0,
|
||||
);
|
||||
if (
|
||||
!preemptiveCompaction.shouldCompact &&
|
||||
currentTokenCount <= preemptiveCompaction.promptBudgetBeforeReserve
|
||||
) {
|
||||
return params.sessionEntry;
|
||||
}
|
||||
|
||||
const compacted = await compactCliTranscript({
|
||||
contextEngine,
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionFile,
|
||||
sessionManager,
|
||||
cfg: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
agentDir: params.agentDir,
|
||||
provider: params.provider,
|
||||
model: params.model,
|
||||
contextTokenBudget,
|
||||
currentTokenCount,
|
||||
skillsSnapshot: params.skillsSnapshot,
|
||||
messageChannel: params.messageChannel,
|
||||
agentAccountId: params.agentAccountId,
|
||||
senderIsOwner: params.senderIsOwner,
|
||||
thinkLevel: params.thinkLevel,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
});
|
||||
|
||||
if (!compacted || !params.sessionStore || !params.storePath) {
|
||||
return params.sessionEntry;
|
||||
}
|
||||
|
||||
return (
|
||||
(await cliCompactionDeps.recordCliCompactionInStore({
|
||||
provider: params.provider,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionStore: params.sessionStore,
|
||||
storePath: params.storePath,
|
||||
})) ?? params.sessionEntry
|
||||
);
|
||||
}
|
||||
@@ -202,3 +202,29 @@ export async function clearCliSessionInStore(params: {
|
||||
sessionStore[sessionKey] = persisted;
|
||||
return persisted;
|
||||
}
|
||||
|
||||
export async function recordCliCompactionInStore(params: {
|
||||
provider: string;
|
||||
sessionKey: string;
|
||||
sessionStore: Record<string, SessionEntry>;
|
||||
storePath: string;
|
||||
}): Promise<SessionEntry | undefined> {
|
||||
const { provider, sessionKey, sessionStore, storePath } = params;
|
||||
const entry = sessionStore[sessionKey];
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const next = { ...entry };
|
||||
clearCliSession(next, provider);
|
||||
next.compactionCount = (entry.compactionCount ?? 0) + 1;
|
||||
next.updatedAt = Date.now();
|
||||
|
||||
const persisted = await updateSessionStore(storePath, (store) => {
|
||||
const merged = mergeSessionEntry(store[sessionKey], next);
|
||||
store[sessionKey] = merged;
|
||||
return merged;
|
||||
});
|
||||
sessionStore[sessionKey] = persisted;
|
||||
return persisted;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user