fix(cli-runner): reseed from raw tail when session has no compaction (#79713)

loadCliSessionReseedMessages returned [] for any session that had never
been compacted, making the historyPrompt fallback silently inert. On
short sessions (the common case for Discord/Telegram DMs), this meant
every CLI invalidation (idle timeout, system-prompt change) restarted
the agent with no prior context.

Return limitAgentHookHistoryMessages(rawTail) instead, mirroring the
same cap already applied to the post-compaction tail messages.  The
rival opt-in config approach (#77583) stalled on a missing CliBackend
schema key; always-on is simpler and matches the issue author's suggested
fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hclsys
2026-05-09 15:51:42 +08:00
committed by Peter Steinberger
parent 14e9c064ee
commit 7f2ba4d694
2 changed files with 21 additions and 11 deletions

View File

@@ -226,24 +226,28 @@ describe("loadCliSessionReseedMessages", () => {
vi.unstubAllEnvs();
});
it("does not reseed fresh CLI sessions from raw transcript history before compaction", async () => {
it("reseeds fresh CLI sessions from raw message tail when no compaction exists", async () => {
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-cli-state-"));
vi.stubEnv("OPENCLAW_STATE_DIR", stateDir);
const sessionFile = createSessionTranscript({
rootDir: stateDir,
sessionId: "session-no-compaction",
messages: ["raw secret", "large context"],
messages: ["first message", "second message"],
});
try {
expect(
await loadCliSessionReseedMessages({
sessionId: "session-no-compaction",
sessionFile,
sessionKey: "agent:main:main",
agentId: "main",
}),
).toStrictEqual([]);
const reseed = await loadCliSessionReseedMessages({
sessionId: "session-no-compaction",
sessionFile,
sessionKey: "agent:main:main",
agentId: "main",
});
expect(reseed).toHaveLength(2);
expect(reseed[0]).toMatchObject({ role: "user", content: "first message" });
expect(reseed[1]).toMatchObject({ role: "user", content: "second message" });
expect(buildCliSessionHistoryPrompt({ messages: reseed, prompt: "next" })).toContain(
"first message",
);
} finally {
fs.rmSync(stateDir, { recursive: true, force: true });
}

View File

@@ -197,7 +197,13 @@ export async function loadCliSessionReseedMessages(params: {
return candidate.type === "compaction" && typeof candidate.summary === "string";
});
if (latestCompactionIndex < 0) {
return [];
// Session never compacted — return raw message tail so historyPrompt is
// not silently inert on short sessions invalidated before compaction (#79713).
const rawTail = entries.flatMap((entry) => {
const candidate = entry as HistoryEntry;
return candidate.type === "message" ? [candidate.message] : [];
});
return limitAgentHookHistoryMessages(rawTail, MAX_CLI_SESSION_HISTORY_MESSAGES);
}
const compaction = entries[latestCompactionIndex] as HistoryEntry;