fix(context-window): prioritize config over model for context window resolution

resolveContextWindowInfo now uses config > model > default priority so
explicit --context-window flag overrides model defaults. Also adds
--context-window CLI option to the run command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jiayuan Zhang 2026-02-15 21:37:02 +08:00
parent 74c0ca0ddc
commit 40a2e8ae55
3 changed files with 32 additions and 13 deletions

View file

@ -28,6 +28,7 @@ type RunOptions = {
runLog?: boolean;
toolsAllow?: string[];
toolsDeny?: string[];
contextWindow?: number;
help?: boolean;
};
@ -49,6 +50,7 @@ ${cyan("Options:")}
${yellow("--session")} ID Session ID for persistence
${yellow("--debug")} Enable debug logging
${yellow("--run-log")} Enable structured run logging (run-log.jsonl)
${yellow("--context-window")} N Override context window token count
${yellow("--help")}, -h Show this help
${cyan("Tools Configuration:")}
@ -141,6 +143,11 @@ function parseArgs(argv: string[]): { opts: RunOptions; prompt: string } {
opts.toolsDeny = value?.split(",").map((s) => s.trim()) ?? [];
continue;
}
if (arg === "--context-window") {
const value = args.shift();
opts.contextWindow = value ? parseInt(value, 10) : undefined;
continue;
}
if (arg === "--") {
promptParts.push(...args);
break;
@ -213,6 +220,7 @@ export async function runCommand(args: string[]): Promise<void> {
debug: opts.debug,
enableRunLog,
tools: toolsConfig,
contextWindowTokens: opts.contextWindow,
});
const sessionDir = join(DATA_DIR, "sessions", agent.sessionId);

View file

@ -24,15 +24,15 @@ describe("guard", () => {
});
describe("resolveContextWindowInfo", () => {
it("should prioritize model context window", () => {
it("should prioritize config over model (explicit override wins)", () => {
const result = resolveContextWindowInfo({
modelContextWindow: 100_000,
configContextTokens: 50_000,
defaultTokens: 200_000,
});
expect(result.tokens).toBe(100_000);
expect(result.source).toBe("model");
expect(result.tokens).toBe(50_000);
expect(result.source).toBe("config");
});
it("should fall back to config when model is undefined", () => {
@ -105,13 +105,23 @@ describe("guard", () => {
expect(result.source).toBe("config");
});
it("should floor decimal values", () => {
it("should floor decimal values from model", () => {
const result = resolveContextWindowInfo({
modelContextWindow: 100_000.9,
});
expect(result.tokens).toBe(100_000);
});
it("should use model when config is not provided", () => {
const result = resolveContextWindowInfo({
modelContextWindow: 100_000,
defaultTokens: 200_000,
});
expect(result.tokens).toBe(100_000);
expect(result.source).toBe("model");
});
});
describe("evaluateContextWindowGuard", () => {

View file

@ -27,28 +27,29 @@ function normalizePositiveInt(value: unknown): number | null {
/**
* Resolve context window information
*
* Priority: model > config > default
* Priority: config > model > default
* (Explicit config override always wins allows capping context for testing/cost control)
*/
export function resolveContextWindowInfo(params: {
/** Model's contextWindow property */
modelContextWindow?: number | undefined;
/** Context tokens specified in config */
/** Context tokens specified in config (explicit override, highest priority) */
configContextTokens?: number | undefined;
/** Default value */
defaultTokens?: number | undefined;
}): ContextWindowInfo {
// 1. Try getting from model
const fromModel = normalizePositiveInt(params.modelContextWindow);
if (fromModel) {
return { tokens: fromModel, source: "model" };
}
// 2. Try getting from config
// 1. Explicit config override always wins
const fromConfig = normalizePositiveInt(params.configContextTokens);
if (fromConfig) {
return { tokens: fromConfig, source: "config" };
}
// 2. Try getting from model
const fromModel = normalizePositiveInt(params.modelContextWindow);
if (fromModel) {
return { tokens: fromModel, source: "model" };
}
// 3. Use default value
return {
tokens: Math.floor(params.defaultTokens ?? DEFAULT_CONTEXT_TOKENS),