From 7f2ba4d69428c7b12d1e422dae5bfc1367efbbbb Mon Sep 17 00:00:00 2001 From: hclsys Date: Sat, 9 May 2026 15:51:42 +0800 Subject: [PATCH] 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 --- src/agents/cli-runner/session-history.test.ts | 24 +++++++++++-------- src/agents/cli-runner/session-history.ts | 8 ++++++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/agents/cli-runner/session-history.test.ts b/src/agents/cli-runner/session-history.test.ts index f72f018ed8a..5c0cb051cfc 100644 --- a/src/agents/cli-runner/session-history.test.ts +++ b/src/agents/cli-runner/session-history.test.ts @@ -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 }); } diff --git a/src/agents/cli-runner/session-history.ts b/src/agents/cli-runner/session-history.ts index dca82e48b6b..8b8c2d85061 100644 --- a/src/agents/cli-runner/session-history.ts +++ b/src/agents/cli-runner/session-history.ts @@ -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;