diff --git a/CHANGELOG.md b/CHANGELOG.md index a0b57cc1b45..43f30946ed3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -254,6 +254,7 @@ Docs: https://docs.openclaw.ai - Models/default provider fallback: when the hardcoded default provider is removed from `models.providers`, resolve defaults from configured providers instead of reporting stale removed-provider defaults in status output. (#38947) Thanks @davidemanuelDEV. - Agents/cache-trace stability: guard stable stringify against circular references in trace payloads so near-limit payloads no longer crash with `Maximum call stack size exceeded`; adds regression coverage. (#38935) Thanks @MumuTW. - Extensions/diffs CI stability: add `headers` to the `localReq` test helper in `extensions/diffs/index.test.ts` so forwarding-hint checks no longer crash with `req.headers` undefined. (supersedes #39063) Thanks @Shennng. +- Agents/compaction thresholding: apply `agents.defaults.contextTokens` cap to the model passed into embedded run and `/compact` session creation so auto-compaction thresholds use the effective context window, not native model max context. (#39099) Thanks @MumuTW. ## 2026.3.2 diff --git a/src/agents/pi-embedded-runner/compact.hooks.test.ts b/src/agents/pi-embedded-runner/compact.hooks.test.ts index ce8b9e0f696..9745071654d 100644 --- a/src/agents/pi-embedded-runner/compact.hooks.test.ts +++ b/src/agents/pi-embedded-runner/compact.hooks.test.ts @@ -173,6 +173,7 @@ vi.mock("../date-time.js", () => ({ vi.mock("../defaults.js", () => ({ DEFAULT_MODEL: "fake-model", DEFAULT_PROVIDER: "openai", + DEFAULT_CONTEXT_TOKENS: 128_000, })); vi.mock("../utils.js", () => ({ diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 92bf4b97f23..a3d02596886 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -376,6 +376,20 @@ export async function compactEmbeddedPiSessionDirect( sessionId: params.sessionId, warn: makeBootstrapWarn({ sessionLabel, warn: (message) => log.warn(message) }), }); + // Apply contextTokens cap to model so pi-coding-agent's auto-compaction + // threshold uses the effective limit, not the native context window. + const ctxInfo = resolveContextWindowInfo({ + cfg: params.config, + provider, + modelId, + modelContextWindow: model.contextWindow, + defaultTokens: DEFAULT_CONTEXT_TOKENS, + }); + const effectiveModel = + ctxInfo.tokens < (model.contextWindow ?? Infinity) + ? { ...model, contextWindow: ctxInfo.tokens } + : model; + const runAbortController = new AbortController(); const toolsRaw = createOpenClawCodingTools({ exec: { @@ -398,7 +412,7 @@ export async function compactEmbeddedPiSessionDirect( abortSignal: runAbortController.signal, modelProvider: model.provider, modelId, - modelContextWindowTokens: model.contextWindow, + modelContextWindowTokens: ctxInfo.tokens, modelAuthMode: resolveModelAuthMode(model.provider, params.config), }); const tools = sanitizeToolsForGoogle({ @@ -594,7 +608,7 @@ export async function compactEmbeddedPiSessionDirect( agentDir, authStorage, modelRegistry, - model, + model: effectiveModel, thinkingLevel: mapThinkingLevel(params.thinkLevel), tools: builtInTools, customTools, diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index c1d1d414c49..80ef934d63e 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -375,6 +375,12 @@ export async function runEmbeddedPiAgent( modelContextWindow: model.contextWindow, defaultTokens: DEFAULT_CONTEXT_TOKENS, }); + // Apply contextTokens cap to model so pi-coding-agent's auto-compaction + // threshold uses the effective limit, not the native context window. + const effectiveModel = + ctxInfo.tokens < (model.contextWindow ?? Infinity) + ? { ...model, contextWindow: ctxInfo.tokens } + : model; const ctxGuard = evaluateContextWindowGuard({ info: ctxInfo, warnBelowTokens: CONTEXT_WINDOW_WARN_BELOW_TOKENS, @@ -866,7 +872,7 @@ export async function runEmbeddedPiAgent( disableTools: params.disableTools, provider, modelId, - model, + model: effectiveModel, authProfileId: lastProfileId, authProfileIdSource: lockedProfileId ? "user" : "auto", authStorage,