From 76f31ae3d080ca9ec60df81563cc10393a472c79 Mon Sep 17 00:00:00 2001 From: "Joe.Skinner" Date: Wed, 18 Feb 2026 16:39:33 +0000 Subject: [PATCH 1/4] Fix Claude Code auth in sandbox by pre-seeding apiKeyHelper config --- src/inspect_swe/_claude_code/claude_code.py | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/inspect_swe/_claude_code/claude_code.py b/src/inspect_swe/_claude_code/claude_code.py index 136f064..5cd253e 100644 --- a/src/inspect_swe/_claude_code/claude_code.py +++ b/src/inspect_swe/_claude_code/claude_code.py @@ -221,6 +221,17 @@ async def execute(state: AgentState) -> AgentState: state=state, ) else: + # Pre-seed Claude Code config files in the sandbox. + # Claude Code >=2.1 ignores ANTHROPIC_API_KEY and + # ANTHROPIC_AUTH_TOKEN env vars, falling into an OAuth + # flow that silently fails and exits with rc=0. + # Providing an apiKeyHelper in settings.json supplies the + # API key through a mechanism Claude Code does honour. + api_key = agent_env.get( + "ANTHROPIC_AUTH_TOKEN", "sk-ant-api03-dummy" + ) + await _seed_claude_config(sbox, api_key, user, cwd) + # execute the agent (track debug output) debug_output: list[str] = [] agent_prompt = prompt @@ -312,6 +323,38 @@ async def execute(state: AgentState) -> AgentState: return agent_with(execute, name=name, description=description) +async def _seed_claude_config( + sbox: Any, + api_key: str, + user: str | None, + cwd: str | None, +) -> None: + """Pre-seed Claude Code config files in the sandbox. + + Writes ~/.claude/settings.json with an apiKeyHelper that echoes the + provided API key, and ~/.claude.json with onboarding flags. This + ensures Claude Code authenticates via the bridge proxy rather than + attempting an OAuth flow. + """ + await sbox.exec( + cmd=[ + "bash", + "-c", + 'mkdir -p "$HOME/.claude"' + " && echo '" + '{"apiKeyHelper": "echo ' + api_key + '",' + ' "hasCompletedOnboarding": true,' + ' "bypassPermissionsModeAccepted": true}' + "' > \"$HOME/.claude/settings.json\"" + " && echo '" + '{"hasCompletedOnboarding":true,"bypassPermissionsModeAccepted":true}' + "' > \"$HOME/.claude.json\"", + ], + user=user, + cwd=cwd, + ) + + def resolve_mcp_servers( mcp_servers: Sequence[MCPServerConfig], ) -> tuple[list[str], list[str]]: From ac4d931f3e98c82a6c4872a857eeb6457c20e245 Mon Sep 17 00:00:00 2001 From: "Joe.Skinner" Date: Wed, 18 Feb 2026 17:10:10 +0000 Subject: [PATCH 2/4] Simplify fix, only pre-seed apiKeyHelper in sandbox --- src/inspect_swe/_claude_code/claude_code.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/inspect_swe/_claude_code/claude_code.py b/src/inspect_swe/_claude_code/claude_code.py index 5cd253e..0fe0b6a 100644 --- a/src/inspect_swe/_claude_code/claude_code.py +++ b/src/inspect_swe/_claude_code/claude_code.py @@ -221,14 +221,13 @@ async def execute(state: AgentState) -> AgentState: state=state, ) else: - # Pre-seed Claude Code config files in the sandbox. - # Claude Code >=2.1 ignores ANTHROPIC_API_KEY and - # ANTHROPIC_AUTH_TOKEN env vars, falling into an OAuth - # flow that silently fails and exits with rc=0. - # Providing an apiKeyHelper in settings.json supplies the - # API key through a mechanism Claude Code does honour. + # Claude Code 2.1.37 reports "has Authorization header: false" + # despite ANTHROPIC_AUTH_TOKEN being set in the environment, + # then enters an OAuth flow that silently fails (rc=0, no + # output). Providing an apiKeyHelper in settings.json + # supplies a key through a path that does work. api_key = agent_env.get( - "ANTHROPIC_AUTH_TOKEN", "sk-ant-api03-dummy" + "ANTHROPIC_AUTH_TOKEN", "dummy-key-for-bridge" ) await _seed_claude_config(sbox, api_key, user, cwd) From 5b484bbb45afa4db751729c70cd8ac845e4a85c2 Mon Sep 17 00:00:00 2001 From: "Joe.Skinner" Date: Wed, 18 Feb 2026 17:15:29 +0000 Subject: [PATCH 3/4] Add changes that should have been in last commit --- src/inspect_swe/_claude_code/claude_code.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/inspect_swe/_claude_code/claude_code.py b/src/inspect_swe/_claude_code/claude_code.py index 0fe0b6a..0c868a6 100644 --- a/src/inspect_swe/_claude_code/claude_code.py +++ b/src/inspect_swe/_claude_code/claude_code.py @@ -328,12 +328,11 @@ async def _seed_claude_config( user: str | None, cwd: str | None, ) -> None: - """Pre-seed Claude Code config files in the sandbox. + """Write ~/.claude/settings.json with an apiKeyHelper. - Writes ~/.claude/settings.json with an apiKeyHelper that echoes the - provided API key, and ~/.claude.json with onboarding flags. This - ensures Claude Code authenticates via the bridge proxy rather than - attempting an OAuth flow. + Claude Code 2.1.37 does not use ANTHROPIC_AUTH_TOKEN from the + environment for API requests. Providing an apiKeyHelper in + settings.json supplies the key through a path it does use. """ await sbox.exec( cmd=[ @@ -341,13 +340,8 @@ async def _seed_claude_config( "-c", 'mkdir -p "$HOME/.claude"' " && echo '" - '{"apiKeyHelper": "echo ' + api_key + '",' - ' "hasCompletedOnboarding": true,' - ' "bypassPermissionsModeAccepted": true}' - "' > \"$HOME/.claude/settings.json\"" - " && echo '" - '{"hasCompletedOnboarding":true,"bypassPermissionsModeAccepted":true}' - "' > \"$HOME/.claude.json\"", + '{"apiKeyHelper": "echo ' + api_key + '"}' + "' > \"$HOME/.claude/settings.json\"", ], user=user, cwd=cwd, From c52f7940bbb0923bcafd48a42cf1003e3eb3e01f Mon Sep 17 00:00:00 2001 From: "Joe.Skinner" Date: Wed, 18 Feb 2026 17:24:36 +0000 Subject: [PATCH 4/4] Move apiKeyHelper config seeding before centaur/non-centaur branch so both paths get the fix --- src/inspect_swe/_claude_code/claude_code.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/inspect_swe/_claude_code/claude_code.py b/src/inspect_swe/_claude_code/claude_code.py index 0c868a6..2eda90b 100644 --- a/src/inspect_swe/_claude_code/claude_code.py +++ b/src/inspect_swe/_claude_code/claude_code.py @@ -212,6 +212,16 @@ async def execute(state: AgentState) -> AgentState: "IS_SANDBOX": "1", } | (env or {}) + # Claude Code 2.1.37 reports "has Authorization header: false" + # despite ANTHROPIC_AUTH_TOKEN being set in the environment, + # then enters an OAuth flow that silently fails (rc=0, no + # output). Providing an apiKeyHelper in settings.json + # supplies a key through a path that does work. + api_key = agent_env.get( + "ANTHROPIC_AUTH_TOKEN", "dummy-key-for-bridge" + ) + await _seed_claude_config(sbox, api_key, user, cwd) + # centaur mode uses human_cli with custom instructions and bash rc if centaur: await run_claude_code_centaur( @@ -221,16 +231,6 @@ async def execute(state: AgentState) -> AgentState: state=state, ) else: - # Claude Code 2.1.37 reports "has Authorization header: false" - # despite ANTHROPIC_AUTH_TOKEN being set in the environment, - # then enters an OAuth flow that silently fails (rc=0, no - # output). Providing an apiKeyHelper in settings.json - # supplies a key through a path that does work. - api_key = agent_env.get( - "ANTHROPIC_AUTH_TOKEN", "dummy-key-for-bridge" - ) - await _seed_claude_config(sbox, api_key, user, cwd) - # execute the agent (track debug output) debug_output: list[str] = [] agent_prompt = prompt