fix: fall back to built-in compaction when model/apiKey unavailable

When both ctx.model and runtime.model are undefined, or when the API key
is missing, return undefined instead of { cancel: true } so the session
falls back to truncation-based compaction rather than blocking the lane.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Joey Krug 2026-03-14 12:45:58 -04:00
parent 62afc4b514
commit 3cf454f7ea
2 changed files with 33 additions and 8 deletions

View File

@ -1469,7 +1469,7 @@ describe("compaction-safeguard extension model fallback", () => {
apiKey: null,
});
expect(result).toEqual({ cancel: true });
expect(result).toBeUndefined();
// KEY ASSERTION: Prove the fallback path was exercised
// The handler should have called getApiKey with runtime.model (via ctx.model ?? runtime?.model)
@ -1495,11 +1495,38 @@ describe("compaction-safeguard extension model fallback", () => {
apiKey: null,
});
expect(result).toEqual({ cancel: true });
expect(result).toBeUndefined();
// Verify early return: getApiKey should NOT have been called when both models are missing
expect(getApiKeyMock).not.toHaveBeenCalled();
});
it("falls back to built-in compaction when model is undefined (returns undefined, not cancel)", async () => {
const sessionManager = stubSessionManager();
// Do NOT set runtime model
const mockEvent = createCompactionEvent({ messageText: "test", tokensBefore: 500 });
const { result, getApiKeyMock } = await runCompactionScenario({
sessionManager,
event: mockEvent,
apiKey: null,
});
expect(result).toBeUndefined();
expect(getApiKeyMock).not.toHaveBeenCalled();
});
it("falls back to built-in compaction when API key is missing (returns undefined, not cancel)", async () => {
const sessionManager = stubSessionManager();
const model = createAnthropicModelFixture();
setCompactionSafeguardRuntime(sessionManager, { model });
const mockEvent = createCompactionEvent({ messageText: "test", tokensBefore: 500 });
const { result, getApiKeyMock } = await runCompactionScenario({
sessionManager,
event: mockEvent,
apiKey: null,
});
expect(result).toBeUndefined();
expect(getApiKeyMock).toHaveBeenCalledWith(model);
});
});
describe("compaction-safeguard double-compaction guard", () => {
@ -1542,7 +1569,7 @@ describe("compaction-safeguard double-compaction guard", () => {
event: mockEvent,
apiKey: null,
});
expect(result).toEqual({ cancel: true });
expect(result).toBeUndefined();
expect(getApiKeyMock).toHaveBeenCalled();
});
});

View File

@ -740,15 +740,13 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
"was not called and model was not passed through runtime registry.",
);
}
return { cancel: true };
return undefined;
}
const apiKey = await ctx.modelRegistry.getApiKey(model);
if (!apiKey) {
log.warn(
"Compaction safeguard: no API key available; cancelling compaction to preserve history.",
);
return { cancel: true };
log.warn("Compaction safeguard: no API key available; falling back to built-in compaction.");
return undefined;
}
try {