Skip to content

Commit efdb349

Browse files
hnykdaclaude
andcommitted
fix: always apply Anthropic beta headers wrapper for OAuth token support
The `createAnthropicBetaHeadersWrapper` was only applied when `resolveAnthropicBetas` returned non-empty (i.e., when `context1m` or `anthropicBeta` was set in agent extra params). When `context-1m` was configured via model-level headers instead of extra params, the wrapper was never applied, so the critical `oauth-2025-04-20` beta was never injected for OAuth tokens (`sk-ant-oat-*`). This caused Anthropic to reject all requests with HTTP 401 "OAuth authentication is currently not supported." Now the wrapper is always applied for Anthropic providers, ensuring OAuth token detection and beta injection runs unconditionally. Fixes #41444 Related: #19769, #19789 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0c7f078 commit efdb349

2 files changed

Lines changed: 35 additions & 9 deletions

File tree

src/agents/pi-embedded-runner-extraparams.test.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,7 @@ describe("applyExtraParamsToAgent", () => {
13481348
});
13491349
});
13501350

1351-
it("does not add Anthropic 1M beta header when context1m is not enabled", () => {
1351+
it("injects pi-ai default betas even when context1m is not enabled", () => {
13521352
const cfg = buildAnthropicModelConfig("anthropic/claude-opus-4-6", {
13531353
temperature: 0.2,
13541354
});
@@ -1358,7 +1358,29 @@ describe("applyExtraParamsToAgent", () => {
13581358
options: { headers: { "X-Custom": "1" } },
13591359
});
13601360

1361-
expect(headers).toEqual({ "X-Custom": "1" });
1361+
expect(headers).toEqual({
1362+
"X-Custom": "1",
1363+
"anthropic-beta": "fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14",
1364+
});
1365+
});
1366+
1367+
it("injects OAuth betas for sk-ant-oat tokens even without context1m", () => {
1368+
const cfg = buildAnthropicModelConfig("anthropic/claude-opus-4-6", {
1369+
temperature: 0.2,
1370+
});
1371+
const headers = runAnthropicHeaderCase({
1372+
cfg,
1373+
modelId: "claude-opus-4-6",
1374+
options: {
1375+
apiKey: "sk-ant-oat01-test-oauth-token", // pragma: allowlist secret
1376+
headers: { "X-Custom": "1" },
1377+
},
1378+
});
1379+
1380+
const betaHeader = headers?.["anthropic-beta"] as string;
1381+
expect(betaHeader).toContain("oauth-2025-04-20");
1382+
expect(betaHeader).toContain("claude-code-20250219");
1383+
expect(betaHeader).not.toContain("context-1m-2025-08-07");
13621384
});
13631385

13641386
it("skips context1m beta for OAuth tokens but preserves OAuth-required betas", () => {
@@ -1425,14 +1447,17 @@ describe("applyExtraParamsToAgent", () => {
14251447
});
14261448
});
14271449

1428-
it("ignores context1m for non-Opus/Sonnet Anthropic models", () => {
1450+
it("ignores context1m for non-Opus/Sonnet Anthropic models but still injects default betas", () => {
14291451
const cfg = buildAnthropicModelConfig("anthropic/claude-haiku-3-5", { context1m: true });
14301452
const headers = runAnthropicHeaderCase({
14311453
cfg,
14321454
modelId: "claude-haiku-3-5",
14331455
options: { headers: { "X-Custom": "1" } },
14341456
});
1435-
expect(headers).toEqual({ "X-Custom": "1" });
1457+
expect(headers).toEqual({
1458+
"X-Custom": "1",
1459+
"anthropic-beta": "fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14",
1460+
});
14361461
});
14371462

14381463
it("forces store=true for direct OpenAI Responses payloads", () => {

src/agents/pi-embedded-runner/extra-params.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -359,11 +359,12 @@ export function applyExtraParamsToAgent(
359359
}
360360

361361
const anthropicBetas = resolveAnthropicBetas(merged, provider, modelId);
362-
if (anthropicBetas?.length) {
363-
log.debug(
364-
`applying Anthropic beta header for ${provider}/${modelId}: ${anthropicBetas.join(",")}`,
365-
);
366-
agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, anthropicBetas);
362+
if (provider === "anthropic") {
363+
const betas = anthropicBetas ?? [];
364+
if (betas.length) {
365+
log.debug(`applying Anthropic beta header for ${provider}/${modelId}: ${betas.join(",")}`);
366+
}
367+
agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, betas);
367368
}
368369

369370
if (shouldApplySiliconFlowThinkingOffCompat({ provider, modelId, thinkingLevel })) {

0 commit comments

Comments
 (0)