From 40b3bbbba9d9adc4ecd67e508f3ceff70026aa37 Mon Sep 17 00:00:00 2001 From: Roy Osherove Date: Fri, 10 Jul 2026 19:24:52 +0300 Subject: [PATCH] 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> --- .../agent-hooks/compaction-safeguard.test.ts | 26 +++++++++++++++++++ .../agent-hooks/compaction-safeguard.ts | 13 +++------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/agents/agent-hooks/compaction-safeguard.test.ts b/src/agents/agent-hooks/compaction-safeguard.test.ts index 356a0430814e..ea157a351b53 100644 --- a/src/agents/agent-hooks/compaction-safeguard.test.ts +++ b/src/agents/agent-hooks/compaction-safeguard.test.ts @@ -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(); diff --git a/src/agents/agent-hooks/compaction-safeguard.ts b/src/agents/agent-hooks/compaction-safeguard.ts index 7e2cfc2344f5..98d0032fd5b8 100644 --- a/src/agents/agent-hooks/compaction-safeguard.ts +++ b/src/agents/agent-hooks/compaction-safeguard.ts @@ -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 }; }