Skip to content

Commit b5aa1d0

Browse files
committed
fix(compaction): respect Model.contextWindow; size Codebase Auto correctly
User report: CLI compacting at 80–100k tokens on Codebase Auto routes that have 200k of headroom. Two stacked bugs: 1. CompactionEngine ignored the resolved Model's contextWindow field entirely. It read modelId (a string) and regex-matched against a built-in table — IDs like "MiniMax-M2.7" or proxy-routed names missed every pattern and silently fell back to 128k. 2. buildProxiedConfig synthesized the Codebase Auto model by spreading a Groq llama-3.3-70b template, which carries contextWindow: 128_000. Even after fix #1, the synthesized model was lying about its true window. Now: CompactionEngine takes an explicit contextWindow option that agent.ts passes from model.contextWindow. buildProxiedConfig sets contextWindow: 200_000 on the Codebase Auto path and uses a guessContextWindow helper for explicit-provider synthesis so Gemini routes get 1M, GPT-5 gets 400k, Claude/proxy-default gets 200k. Threshold math: old = 128_000 * 0.75 = 96_000 (matches the report). New on Codebase Auto = 200_000 * 0.75 = 150_000.
1 parent 0e5ee25 commit b5aa1d0

5 files changed

Lines changed: 49 additions & 5 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codebase-cli",
3-
"version": "2.0.0-pre.57",
3+
"version": "2.0.0-pre.58",
44
"description": "Codebase CLI — a TypeScript coding agent on the pi-mono runtime. OAuth-aware, any LLM provider, single install.",
55
"keywords": [
66
"ai",

src/agent/agent.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,11 @@ export function createAgent(opts: CreateAgentOptions = {}): AgentBundle {
175175
smartModel: glueModels.smart,
176176
getApiKey,
177177
});
178-
const compaction = new CompactionEngine({ glue, modelId: model.id });
178+
// Pass model.contextWindow explicitly so proxy-synthesized models
179+
// (Codebase Auto, custom in-house IDs) get the real window instead of
180+
// the regex-based fallback in tokens.ts, which would otherwise lock
181+
// them at 128k and trigger compaction at ~96k on a 200k-context route.
182+
const compaction = new CompactionEngine({ glue, modelId: model.id, contextWindow: model.contextWindow });
179183
const compactionMonitor = new CompactionMonitor();
180184
const sessions = new SessionStore({ cwd });
181185
const resumed = opts.resume ? sessions.load(model.id) : null;

src/agent/config.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,13 @@ function buildProxiedConfig(
202202
// `model.id` only, so this cast is safe.
203203
provider: "codebase" as Model<string>["provider"],
204204
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
205+
// Codebase Auto routes to large-context models on the backend
206+
// (Claude Sonnet 4 default, open-weight alternates also 128k+).
207+
// The Groq llama template's 128k contextWindow was leaking
208+
// through and triggering compaction at ~96k tokens on routes
209+
// that have 200k of headroom. Set explicitly so the compaction
210+
// engine reads the right value.
211+
contextWindow: 200_000,
205212
};
206213
return { model, apiKey: accessToken, source: "proxy" };
207214
}
@@ -230,10 +237,31 @@ function buildProxiedConfig(
230237
baseUrl: proxyBase,
231238
provider: explicitProvider as Model<string>["provider"],
232239
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
240+
// Same reasoning as Codebase Auto: synthesized proxy models point
241+
// at large-context backends. Without overriding contextWindow the
242+
// Groq llama template's 128k leaks through and the compaction
243+
// engine triggers ~30% earlier than it should.
244+
contextWindow: guessContextWindow(modelId, 200_000),
233245
};
234246
return { model: synthesized, apiKey: accessToken, source: "proxy" };
235247
}
236248

249+
/**
250+
* Best-effort context-window guess for synthesized proxy models whose
251+
* IDs pi-ai doesn't know natively. Pattern-matches a few common families
252+
* so we don't gimp a 1M-context Gemini model to 200k; everything else
253+
* defaults to the supplied fallback (200k, matching Claude Sonnet 4 /
254+
* GPT-5 / most open-weight large models the proxy routes to).
255+
*/
256+
function guessContextWindow(modelId: string, fallback: number): number {
257+
const id = modelId.toLowerCase();
258+
if (id.startsWith("gemini-")) return 1_000_000;
259+
if (id.startsWith("gpt-5")) return 400_000;
260+
if (id.startsWith("claude-")) return 200_000;
261+
if (id.startsWith("llama-3-3-70b") || id.startsWith("llama-3.3-70b")) return 128_000;
262+
return fallback;
263+
}
264+
237265
/**
238266
* OpenAI Chat-Completions compatible custom endpoint. Used by
239267
* MiniMax in-house, Qwen in-house, Groq custom URLs, Ollama, vLLM,

src/compaction/engine.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,37 @@ Be concrete. Reference paths, function names, commit subjects when you have them
2525
export interface CompactionEngineOptions {
2626
glue: GlueClient;
2727
modelId: string;
28+
/**
29+
* Authoritative context-window size from the resolved model. When set,
30+
* this is used verbatim and the modelId-based regex fallback is
31+
* bypassed entirely. Codebase Auto + proxy-synthesized models need
32+
* this — their IDs (e.g. "MiniMax-M2.7") don't match any built-in
33+
* regex and would otherwise fall back to 128k, triggering compaction
34+
* at ~96k against a model that has 200k of headroom.
35+
*/
36+
contextWindow?: number;
2837
thresholdRatio?: number;
2938
keepRecent?: number;
3039
}
3140

3241
export class CompactionEngine {
3342
private readonly glue: GlueClient;
3443
private readonly modelId: string;
44+
private readonly explicitContextWindow: number | undefined;
3545
private readonly thresholdRatio: number;
3646
private readonly keepRecent: number;
3747

3848
constructor(options: CompactionEngineOptions) {
3949
this.glue = options.glue;
4050
this.modelId = options.modelId;
51+
this.explicitContextWindow = options.contextWindow;
4152
this.thresholdRatio = options.thresholdRatio ?? DEFAULT_THRESHOLD;
4253
this.keepRecent = options.keepRecent ?? DEFAULT_KEEP_RECENT;
4354
}
4455

4556
threshold(): number {
46-
return Math.floor(contextWindow(this.modelId) * this.thresholdRatio);
57+
const window = this.explicitContextWindow ?? contextWindow(this.modelId);
58+
return Math.floor(window * this.thresholdRatio);
4759
}
4860

4961
needsCompaction(messages: AgentMessage[]): boolean {

0 commit comments

Comments
 (0)