fix(compaction): allow compaction under aws-sdk auth with no static key (#103638)

The compaction safeguard treated a successful keyless auth resolution
(ok: true with neither apiKey nor headers) as missing credentials and
cancelled compaction. Bedrock providers using auth: "aws-sdk" sign
requests with SigV4 at send time and legitimately return no static key
or header, so every message on a session large enough to need compaction
failed with a false "could not resolve request credentials" error.

Trust the registry's ok:true success signal; it already returns ok:false
when auth genuinely cannot resolve. Add regression coverage for the
keyless SDK-managed auth path.

Co-authored-by: Roy Osherove <575051+royosherove@users.noreply.github.com>
This commit is contained in:
Roy Osherove
2026-07-10 19:24:52 +03:00
committed by GitHub
parent bedcba2b9b
commit 40b3bbbba9
2 changed files with 30 additions and 9 deletions

View File

@@ -2215,6 +2215,32 @@ describe("compaction-safeguard extension model fallback", () => {
expect(retrieved?.model).toEqual(model);
});
it("proceeds with keyless SDK-managed auth (ok:true, no apiKey/headers)", async () => {
// Regression: aws-sdk/oauth providers sign requests later and resolve with
// neither apiKey nor headers. `ok: true` must be trusted so compaction runs
// instead of wedging every message with a false "no credentials" cancel.
mockSummarizeInStages.mockReset();
mockSummarizeInStages.mockResolvedValue("mock summary");
const sessionManager = stubSessionManager();
const model = createAnthropicModelFixture({ provider: "amazon-bedrock" });
setCompactionSafeguardRuntime(sessionManager, { model, recentTurnsPreserve: 0 });
const getApiKeyAndHeadersMock = vi.fn().mockResolvedValue({ ok: true });
const mockContext = createCompactionContext({ sessionManager, getApiKeyAndHeadersMock });
const compactionHandler = createCompactionHandler();
const event = createCompactionEvent({ messageText: "summarize me", tokensBefore: 1000 });
(event.preparation as { settings?: { reserveTokens: number } }).settings = {
reserveTokens: 4000,
};
const result = (await compactionHandler(event, mockContext)) as { cancel?: boolean };
expect(result.cancel).not.toBe(true);
expect(getApiKeyAndHeadersMock).toHaveBeenCalledWith(model);
expect(mockSummarizeInStages).toHaveBeenCalled();
});
it("cancels compaction when both ctx.model and runtime.model are undefined", async () => {
const sessionManager = stubSessionManager();

View File

@@ -347,15 +347,10 @@ async function resolveModelAuth(
reason: `Compaction safeguard could not resolve request credentials for ${model.provider}/${model.id}: ${requestAuth.error}`,
};
}
if (!requestAuth.apiKey && !requestAuth.headers) {
log.warn(
"Compaction safeguard: no request credentials available; cancelling compaction to preserve history.",
);
return {
ok: false,
reason: `Compaction safeguard could not resolve request credentials for ${model.provider}/${model.id}.`,
};
}
// `ok: true` is the registry's authoritative success signal; it already returns
// `ok: false` when auth cannot resolve. Do not re-derive failure from absent
// key/headers. SDK-managed modes (aws-sdk, oauth) sign the request later and
// legitimately carry neither, so gating on them wedges compaction forever.
return { ok: true, apiKey: requestAuth.apiKey, headers: requestAuth.headers };
}