diff --git a/extensions/codex/src/app-server/compact.test.ts b/extensions/codex/src/app-server/compact.test.ts index fd5699ba9e73..cff6517f7c96 100644 --- a/extensions/codex/src/app-server/compact.test.ts +++ b/extensions/codex/src/app-server/compact.test.ts @@ -1712,6 +1712,71 @@ describe("maybeCompactCodexAppServerSession", () => { warn.mockRestore(); }); + it("bounds ignored compaction override warnings through the compact entry point", async () => { + const warn = vi.spyOn(embeddedAgentLog, "warn").mockImplementation(() => undefined); + const info = vi.spyOn(embeddedAgentLog, "info").mockImplementation(() => undefined); + + async function runWithIgnoredOverrides(index: number): Promise { + const agentId = `cap-${index}`; + await maybeCompactCodexAppServerSession({ + sessionId: `session-${index}`, + sessionKey: `agent:${agentId}:session-${index}`, + sessionFile: path.join(tempDir, `${agentId}.jsonl`), + workspaceDir: tempDir, + trigger: "budget", + config: { + agents: { + list: [ + { + id: agentId, + compaction: { + model: "openai/gpt-5.4-mini", + provider: "custom-summary", + }, + }, + ], + }, + }, + }); + } + + // Fill exactly the advertised 4,096-entry cap: every distinct key warns once. + for (let index = 0; index < 4_096; index += 1) { + await runWithIgnoredOverrides(index); + } + expect(warn).toHaveBeenCalledTimes(4_096); + + // At exactly 4,096 entries the oldest key is still retained, so a duplicate stays suppressed. + await runWithIgnoredOverrides(0); + expect(warn).toHaveBeenCalledTimes(4_096); + + // The 4,097th distinct key pushes past the cap and evicts the LRU entry. The duplicate + // check on key 0 refreshed its recency, so the evicted key is 1. + await runWithIgnoredOverrides(4_096); + expect(warn).toHaveBeenCalledTimes(4_097); + + // The evicted key warns again on reappearance. + await runWithIgnoredOverrides(1); + expect(warn).toHaveBeenCalledTimes(4_098); + + // A recent duplicate stays suppressed. + await runWithIgnoredOverrides(4_096); + expect(warn).toHaveBeenCalledTimes(4_098); + expect(warn.mock.calls.at(-1)).toEqual([ + "ignoring OpenClaw compaction overrides for Codex app-server compaction; Codex uses native server-side compaction", + { + sessionId: "session-1", + sessionKey: "agent:cap-1:session-1", + ignoredConfig: [ + "agents.list.cap-1.compaction.model", + "agents.list.cap-1.compaction.provider", + ], + }, + ]); + info.mockRestore(); + warn.mockRestore(); + }); + it("warns when active agent compaction overrides are ignored", async () => { const warn = vi.spyOn(embeddedAgentLog, "warn").mockImplementation(() => undefined); const fake = createFakeCodexClient(); diff --git a/extensions/codex/src/app-server/compact.ts b/extensions/codex/src/app-server/compact.ts index 3ecb41eadb9a..92f9da96c290 100644 --- a/extensions/codex/src/app-server/compact.ts +++ b/extensions/codex/src/app-server/compact.ts @@ -8,6 +8,7 @@ import { type EmbeddedAgentCompactResult, } from "openclaw/plugin-sdk/agent-harness-runtime"; import { resolveAgentDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime"; +import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime"; import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue"; import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime"; import { isIncognitoSessionKey } from "../incognito-session.js"; @@ -42,7 +43,9 @@ import { } from "./shared-client.js"; import { resumeCodexAppServerThread } from "./thread-resume.js"; -const warnedIgnoredCompactionOverrides = new Set(); +// ttlMs: 0 retains keys until the 4,096-entry LRU cap evicts them, after which a +// previously suppressed warning can intentionally emit again. +const warnedIgnoredCompactionOverrides = createDedupeCache({ ttlMs: 0, maxSize: 4096 }); const codexNativeCompactionQueue = new KeyedAsyncQueue(); const CODEX_NATIVE_COMPACTION_INTERRUPT_GRACE_MS = 30_000; type CodexAppServerCompactOptions = { @@ -350,10 +353,9 @@ function warnIfIgnoringOpenClawCompactionOverrides( return; } const warningKey = ignoredConfig.join("\0"); - if (warnedIgnoredCompactionOverrides.has(warningKey)) { + if (warnedIgnoredCompactionOverrides.check(warningKey)) { return; } - warnedIgnoredCompactionOverrides.add(warningKey); embeddedAgentLog.warn( "ignoring OpenClaw compaction overrides for Codex app-server compaction; Codex uses native server-side compaction", {