fix: compaction safeguard falls through when ctx.model is unavailable#4223
fix: compaction safeguard falls through when ctx.model is unavailable#4223hanxiao wants to merge 5 commits intoopenclaw:mainfrom
Conversation
When extensionRunner.initialize() is not called (e.g. in embedded runner mode), ctx.model resolves to undefined because the default getModel returns undefined. Previously this caused the safeguard to return a fallback summary with no actual content, effectively discarding all conversation history on every compaction. Instead of returning an empty fallback, return undefined so the built-in compaction code in AgentSession handles summarization. The built-in path correctly accesses the model via this.model (AgentSession.agent.state.model) and produces a proper summary. Same fix applied to the apiKey null case -- falling through is safer than producing an empty summary that causes cascading re-compactions.
|
Nice find on the root cause! I ran into the same issue and took a slightly different approach — instead of falling back to built-in compaction, I initialized In // Fix: Initialize extension runner's getModel so extensions can access the model.
const sessionAny = activeSession as any;
if (sessionAny._extensionRunner) {
sessionAny._extensionRunner.getModel = () => activeSession.model;
}And the same in This makes Both approaches solve the immediate problem — just wanted to share in case the maintainers prefer fixing the root cause vs. the fallback approach. |
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks @SpacePlushy! Implemented your suggestion in commits 8afaa14 and f3e0aef. Added
const sessionAny = activeSession as any;
if (sessionAny._extensionRunner) {
sessionAny._extensionRunner.getModel = () => activeSession.model;
}This fixes the root cause so |
bfc1ccb to
f92900f
Compare
|
Thanks for digging into this root cause. I’m closing this as a duplicate of #17864, which is now the canonical PR for the Your investigation and implementation materially advanced the final fix path. You will be included in final credits |
Problem
When Clawdbot runs in embedded mode,
extensionRunner.initialize()is never called. This meansExtensionRunner.getModelstays at its default() => undefined.When the compaction safeguard extension runs, it reads
ctx.modelwhich callsgetModel()→ returnsundefined. The safeguard then returns a fallback summary with no actual content:This effectively discards all conversation history on every compaction. It also causes cascading re-compactions: the empty summary is only ~50 tokens, so the next tool call immediately fills the context again, triggering another compaction, which again produces an empty summary.
In a single session I observed 22 consecutive compactions, all with empty summaries.
Root Cause
ExtensionRunner.getModeldefaults to() => undefinedand is only set to the correct getter duringinitialize(). The three built-in pi-coding-agent modes (interactive, print, rpc) all callinitialize(), but Clawdbot's embedded runner (compact.js,run/attempt.js) does not.In
AgentSession._runAutoCompaction(), the model and API key are correctly validated viathis.modelbefore emitting thesession_before_compactevent. So the model is available -- just not through the extension context'sctx.modelgetter.Fix
When
ctx.modelorapiKeyis unavailable, returnundefinedinstead of the fallback summary. This lets the built-in compaction code inAgentSessionhandle summarization, which correctly accesses the model viathis.model(agent.state.model) and produces a proper summary.Alternative
The deeper fix would be to ensure
extensionRunner.initialize()is called in the embedded runner flow, but that's a larger change. This fix is safe and minimal -- the built-in compaction path is well-tested and produces correct summaries.Greptile Overview
Greptile Summary
This PR adjusts the
session_before_compactcompaction-safeguard extension so it no longer emits an “empty fallback” compaction result whenctx.modelor its API key is unavailable (notably in embedded runner mode whereextensionRunner.initialize()is not invoked). Instead it returnsundefined, allowingAgentSession’s built-in compaction path to run with a valid model sourced from the session state, preventing repeated compactions caused by near-empty summaries.Confidence Score: 5/5
(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!
Context used:
dashboard- CLAUDE.md (source)dashboard- AGENTS.md (source)