From fc9843e0e87292eb8f581f12ee335bc94ea171be Mon Sep 17 00:00:00 2001 From: Hao Xiang Liew Date: Fri, 31 Oct 2025 17:30:49 -0700 Subject: [PATCH 1/2] feat: anthropic oauth w/ 1m context This is available with the Claude Max plans on "sonnet-4+' models Has corresponding changes in haoxiangliew/opencode --- index.mjs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/index.mjs b/index.mjs index 944b2d5..082fe4d 100644 --- a/index.mjs +++ b/index.mjs @@ -32,6 +32,44 @@ async function authorize(mode) { }; } +/** + * @param {string} token + */ +async function check1MContext(token) { + const profileResponse = await fetch( + "https://api.anthropic.com/api/oauth/profile", + { + headers: { + authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }, + ); + + if (!profileResponse.ok) return false; + + const profile = await profileResponse.json(); + const orgUuid = profile.organization?.uuid; + + if (!orgUuid) return false; + + const accessResponse = await fetch( + `https://api.anthropic.com/api/organization/${orgUuid}/claude_code_sonnet_1m_access`, + { + headers: { + "Content-Type": "application/json", + authorization: `Bearer ${token}`, + "anthropic-beta": "oauth-2025-04-20", + }, + }, + ); + + if (!accessResponse.ok) return false; + + const accessData = await accessResponse.json(); + return accessData.has_access === true; +} + /** * @param {string} code * @param {string} verifier @@ -57,11 +95,13 @@ async function exchange(code, verifier) { type: "failed", }; const json = await result.json(); + const has1MContext = await check1MContext(json.access_token); return { type: "success", refresh: json.refresh_token, access: json.access_token, expires: Date.now() + json.expires_in * 1000, + has1MContext, }; } @@ -108,6 +148,7 @@ export async function AnthropicAuthPlugin({ client }) { ); if (!response.ok) return; const json = await response.json(); + const has1MContext = await check1MContext(json.access_token); await client.auth.set({ path: { id: "anthropic", @@ -117,15 +158,24 @@ export async function AnthropicAuthPlugin({ client }) { refresh: json.refresh_token, access: json.access_token, expires: Date.now() + json.expires_in * 1000, + has1MContext, }, }); auth.access = json.access_token; + auth.has1MContext = has1MContext; } const headers = { ...init.headers, authorization: `Bearer ${auth.access}`, - "anthropic-beta": - "oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14", + "anthropic-beta": [ + "oauth-2025-04-20", + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "fine-grained-tool-streaming-2025-05-14", + auth.has1MContext && "context-1m-2025-08-07", + ] + .filter(Boolean) + .join(","), }; delete headers["x-api-key"]; return fetch(input, { From a5769d4012eafdf581b7b6eb25180263016ea5ac Mon Sep 17 00:00:00 2001 From: Hao Xiang Liew Date: Fri, 31 Oct 2025 17:42:14 -0700 Subject: [PATCH 2/2] fix: only set the 1m context header when the model is 'claude-sonnet-' --- index.mjs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/index.mjs b/index.mjs index 082fe4d..c313d55 100644 --- a/index.mjs +++ b/index.mjs @@ -164,18 +164,29 @@ export async function AnthropicAuthPlugin({ client }) { auth.access = json.access_token; auth.has1MContext = has1MContext; } + const body = (() => { + try { + return typeof init.body === "string" + ? JSON.parse(init.body) + : init.body; + } catch { + return {}; + } + })(); + const betaFeatures = [ + "oauth-2025-04-20", + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "fine-grained-tool-streaming-2025-05-14", + // Only add context-1m header if model starts with "claude-sonnet-" and has1MContext is true + auth.has1MContext && + body.model?.startsWith("claude-sonnet-") && + "context-1m-2025-08-07", + ].filter(Boolean); const headers = { ...init.headers, authorization: `Bearer ${auth.access}`, - "anthropic-beta": [ - "oauth-2025-04-20", - "claude-code-20250219", - "interleaved-thinking-2025-05-14", - "fine-grained-tool-streaming-2025-05-14", - auth.has1MContext && "context-1m-2025-08-07", - ] - .filter(Boolean) - .join(","), + "anthropic-beta": betaFeatures.join(","), }; delete headers["x-api-key"]; return fetch(input, {