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:
can1357
2026-06-29 05:04:35 +02:00
parent 717a3a9d64
commit ca9f2847e6
@@ -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(),