diff --git a/docs/gateway/cli-backends.md b/docs/gateway/cli-backends.md index 9dc2af073835..a289e0912b32 100644 --- a/docs/gateway/cli-backends.md +++ b/docs/gateway/cli-backends.md @@ -376,20 +376,16 @@ For CLIs that emit Claude Code stream-json compatible JSONL, set Some CLI backends run an agent that compacts its **own** transcript, so OpenClaw must not run its safeguard summarizer against them - doing so fights the backend's own -compaction and can hard-fail the turn. **Codex** (its app-server owns automatic -compaction) and **Claude Code** (`claude-cli`) both work this way, and both **opt out -of OpenClaw compaction**: +compaction and can hard-fail the turn. -- **Codex** routes to its native-harness compaction endpoint (matched by the session's - `agentHarnessId`). -- **`claude-cli`** has no harness endpoint - Claude Code compacts internally - so it - declares `ownsNativeCompaction: true`, and OpenClaw returns a no-op from the - compaction path. +`claude-cli` has no harness endpoint - Claude Code compacts internally - so it declares +`ownsNativeCompaction: true`, and OpenClaw returns a no-op from the compaction path. +Native-harness sessions such as Codex keep routing to their harness compaction endpoint +instead. -Either way OpenClaw **defers and never compacts these sessions.** Because the backend -owns compaction, the old stopgap of setting `contextTokens: 1_000_000` purely to keep -OpenClaw's safeguard from firing on a claude-cli session is **no longer needed** - the -opt-out replaces it. +Because the backend owns compaction, the old stopgap of setting +`contextTokens: 1_000_000` purely to keep OpenClaw's safeguard from firing on a +claude-cli session is **no longer needed** - the opt-out replaces it. ```typescript api.registerCliBackend({ id: "my-cli", ownsNativeCompaction: true /* ... */ }); @@ -398,8 +394,7 @@ api.registerCliBackend({ id: "my-cli", ownsNativeCompaction: true /* ... */ }); Only declare `ownsNativeCompaction` for a backend that genuinely owns its compaction: it must reliably bound its own transcript as it nears its context window and persist a resumable session (e.g. `--resume` / `--session-id`); otherwise a deferred session can -stay over budget. (A session whose `agentHarnessId` matches the provider still routes to -the harness endpoint - the no-op applies only when there is no harness endpoint.) +stay over budget. Matching `agentHarnessId` sessions still route to the harness endpoint. ## Bundle MCP overlays diff --git a/docs/plugins/cli-backend-plugins.md b/docs/plugins/cli-backend-plugins.md index 5b3fb7b07293..4819703477fb 100644 --- a/docs/plugins/cli-backend-plugins.md +++ b/docs/plugins/cli-backend-plugins.md @@ -217,10 +217,9 @@ backend hook can express the behavior. If your backend runs an agent that compacts its **own** transcript, set `ownsNativeCompaction: true` so OpenClaw's safeguard summarizer never runs against its -sessions - the CLI compaction lifecycle returns a no-op and the turn proceeds. This is -the **same opt-out Codex uses** (its app-server owns automatic compaction); `claude-cli` -declares it because Claude Code compacts internally with no harness endpoint. It also -removes any need to inflate `contextTokens` just to keep the safeguard from firing. +sessions - the CLI compaction lifecycle returns a no-op and the turn proceeds. `claude-cli` +declares it because Claude Code compacts internally with no harness endpoint. Native-harness +sessions such as Codex keep routing to their harness compaction endpoint instead. **Only declare it when all of the following hold**, or a deferred over-budget session can stay over budget / go stale (OpenClaw no longer rescues it): @@ -228,9 +227,8 @@ stay over budget / go stale (OpenClaw no longer rescues it): - the backend reliably compacts or bounds its own transcript as it nears its window; - it persists a resumable session so the compacted state survives turns (e.g. `--resume` / `--session-id`); -- it is **not** a native-harness compaction session - a session whose `agentHarnessId` - matches the provider routes to the harness endpoint instead; this no-op applies only - when there is no harness endpoint. +- it is not a native-harness compaction session - matching `agentHarnessId` sessions + route to the harness endpoint instead. ## MCP tool bridge diff --git a/src/agents/command/cli-compaction.test.ts b/src/agents/command/cli-compaction.test.ts index d00a29ae5af7..d086e45a5aba 100644 --- a/src/agents/command/cli-compaction.test.ts +++ b/src/agents/command/cli-compaction.test.ts @@ -76,12 +76,6 @@ describe("runCliTurnCompactionLifecycle", () => { beforeEach(async () => { tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-cli-compaction-")); - // Default backends to non-owning so the context-engine compaction-path tests - // exercise that path. On current main resolveCliBackendConfig("claude-cli") - // resolves the (now ownsNativeCompaction) backend even in unit tests, which - // would otherwise route every claude-cli compaction test through the #88315 - // defer no-op. The ownsNativeCompaction-specific tests override this with an - // owning backend to exercise the defer. setCliCompactionTestDeps({ resolveCliBackendConfig: () => null }); }); diff --git a/src/agents/command/cli-compaction.ts b/src/agents/command/cli-compaction.ts index ce5cbce0d50e..e9031f3f6266 100644 --- a/src/agents/command/cli-compaction.ts +++ b/src/agents/command/cli-compaction.ts @@ -13,6 +13,7 @@ import { applyAgentAutoCompactionGuard as applyAgentAutoCompactionGuardImpl, resolveEffectiveCompactionMode, } from "../agent-settings.js"; +import { resolveCliBackendConfig as resolveCliBackendConfigImpl } from "../cli-backends.js"; import { classifyCompactionReason } from "../embedded-agent-runner/compact-reasons.js"; import { buildEmbeddedCompactionRuntimeContext } from "../embedded-agent-runner/compaction-runtime-context.js"; import { @@ -29,7 +30,6 @@ import { ensureSelectedAgentHarnessPlugin as ensureSelectedAgentHarnessPluginImp import { maybeCompactAgentHarnessSession as maybeCompactAgentHarnessSessionImpl } from "../harness/selection.js"; import type { AgentMessage } from "../runtime/index.js"; import { SessionManager } from "../sessions/session-manager.js"; -import { resolveCliBackendConfig as resolveCliBackendConfigImpl } from "../cli-backends.js"; import { clearCliSessionInStore as clearCliSessionInStoreImpl, recordCliCompactionInStore as recordCliCompactionInStoreImpl, @@ -529,18 +529,12 @@ export async function runCliTurnCompactionLifecycle(params: { return params.sessionEntry; } - // When the backend declares native compaction ownership but has no harness - // compaction endpoint (e.g. claude-cli — Claude Code compacts its own - // transcript internally), skip both native-harness and context-engine - // compaction. The backend will handle it; OpenClaw returns a no-op. const resolvedBackend = cliCompactionDeps.resolveCliBackendConfig(params.provider, params.cfg); if ( resolvedBackend?.ownsNativeCompaction && !isNativeHarnessCompactionSession(params.sessionEntry, params.provider) ) { - log.info( - `CLI backend "${params.provider}" owns native compaction — deferring to backend`, - ); + log.info(`CLI backend "${params.provider}" owns native compaction — deferring to backend`); return params.sessionEntry; } diff --git a/src/plugins/cli-backend.types.ts b/src/plugins/cli-backend.types.ts index 7e4c691747fc..e154d0b67125 100644 --- a/src/plugins/cli-backend.types.ts +++ b/src/plugins/cli-backend.types.ts @@ -83,21 +83,8 @@ export type CliBackendPlugin = { */ contextEngineHostCapabilities?: readonly ContextEngineHostCapability[]; /** - * When true, the backend manages its own transcript compaction lifecycle - * (e.g. Claude Code's internal auto-compaction). OpenClaw skips its safeguard - * summarizer and returns a no-op from the CLI compaction path instead of - * fighting the backend's own compaction or hard-failing the turn. - * - * Safety contract - only declare this when ALL of the following hold, or an - * over-budget session can stay over budget / go stale (OpenClaw no longer - * rescues it): - * - the backend reliably compacts or bounds its own transcript as it nears - * its context window; - * - it persists a resumable session so the compacted state survives across - * turns (e.g. `--resume` / `--session-id`); - * - it is NOT a native-harness compaction session - a session whose - * `agentHarnessId` matches the provider still routes to the harness - * endpoint, so this no-op applies only when there is no harness endpoint. + * Backend-owned compaction for non-harness CLI sessions. + * Set only when the backend bounds its own transcript and persists resumable state. */ ownsNativeCompaction?: boolean; /**