test(coding-agent): fixed snapcompact non-ascii auto-fallback fixture
The auto-snapcompact non-ASCII fallback test seeded a single CJK turn with a 245k-token usage. claude-sonnet-4-5's 200k context window made that an input overflow, so the recovery path dropped the assistant turn and left nothing to summarize; the default 20k keep-recent window also kept both tiny messages. Either way prepareCompaction returned undefined, the renderability preflight never ran, and the strategy stayed "snapcompact" instead of downgrading to context-full. Derive the synthetic prompt size from the active model's context window (above the auto-compaction threshold, below the window) and force keepRecentTokens=1 so the CJK history is always summarized and scanned, regardless of model metadata.
This commit is contained in:
@@ -39,13 +39,19 @@ async function createHarness(tempDir: TempDir, authStorage: AuthStorage, options
|
||||
const firstKeptEntryId = sessionManager.getBranch()[0]?.id;
|
||||
if (!firstKeptEntryId) throw new Error("Expected seeded branch entry");
|
||||
|
||||
const settings = Settings.isolated({
|
||||
"compaction.strategy": "snapcompact",
|
||||
// Force a 1-token recent window so the post-turn cut always splits off the
|
||||
// last turn and summarizes the seeded (CJK) history. With the default
|
||||
// 20k window the cut keeps both tiny messages, leaving nothing for
|
||||
// snapcompact's renderability preflight to scan.
|
||||
"compaction.keepRecentTokens": 1,
|
||||
modelRoles: { vision: "aimlapi/claude-sonnet-4-5-20250929" },
|
||||
});
|
||||
const session = new AgentSession({
|
||||
agent,
|
||||
sessionManager,
|
||||
settings: Settings.isolated({
|
||||
"compaction.strategy": "snapcompact",
|
||||
modelRoles: { vision: "aimlapi/claude-sonnet-4-5-20250929" },
|
||||
}),
|
||||
settings,
|
||||
modelRegistry,
|
||||
});
|
||||
vi.spyOn(compactionModule, "compact").mockResolvedValue({
|
||||
@@ -66,6 +72,16 @@ async function createHarness(tempDir: TempDir, authStorage: AuthStorage, options
|
||||
});
|
||||
|
||||
const triggerThreshold = () => {
|
||||
// Prompt tokens above the auto-compaction threshold but below the model's
|
||||
// context window: post-turn maintenance must run a threshold compaction,
|
||||
// NOT the overflow recovery path (which drops the just-ended turn before
|
||||
// snapcompact's renderability preflight can scan it, leaving nothing to
|
||||
// summarize). Derived from the live window so the fixture survives model
|
||||
// metadata changes (claude-sonnet-4-5's 200k window is narrower than the
|
||||
// vision-role qwen's, so a fixed count would overflow one of them).
|
||||
const contextWindow = activeModel.contextWindow ?? 0;
|
||||
const thresholdTokens = compactionModule.resolveThresholdTokens(contextWindow, settings.getGroup("compaction"));
|
||||
const promptTokens = contextWindow > 0 ? Math.floor((thresholdTokens + contextWindow) / 2) : 246_000;
|
||||
const assistantMsg = {
|
||||
role: "assistant" as const,
|
||||
content: [{ type: "text" as const, text: "Done." }],
|
||||
@@ -74,11 +90,11 @@ async function createHarness(tempDir: TempDir, authStorage: AuthStorage, options
|
||||
model: activeModel.id,
|
||||
stopReason: "stop" as const,
|
||||
usage: {
|
||||
input: 245000,
|
||||
output: 1000,
|
||||
input: promptTokens,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
totalTokens: 246000,
|
||||
totalTokens: promptTokens,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
|
||||
Reference in New Issue
Block a user