From 62fdb7533d44ec9bc79346bda0bd1d73bab1b672 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Fri, 26 Jun 2026 21:09:21 -0400 Subject: [PATCH] fix(anthropic): don't inherit an outer Claude Code session for gateway auth (#700) neutralizeInheritedAgentSession strips inherited CLAUDE* env so the SDK bearer-auths to the gateway instead of an OAuth 401. Only matters when the wizard runs inside another Claude Code session (the test harness); no-op in production. Co-Authored-By: Claude Opus 4.8 --- src/lib/agent/agent-interface.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/lib/agent/agent-interface.ts b/src/lib/agent/agent-interface.ts index 22c7d539..828820b7 100644 --- a/src/lib/agent/agent-interface.ts +++ b/src/lib/agent/agent-interface.ts @@ -567,6 +567,33 @@ export function wizardCanUseTool( }; } +/** + * When the wizard itself runs inside another agent (e.g. a Claude Code session + * or CI harness), the parent's `CLAUDE*` env vars advertise an active agent + * session with its own OAuth identity. Inherited by the SDK subprocess, they + * push it onto that OAuth path instead of bearer-authenticating to the gateway + * — a 401. Drop every inherited `CLAUDE*` var except the two the wizard sets + * itself, so the child authenticates fresh from the gateway token. A no-op in a + * plain terminal where none are set. Returns an undefined-valued map; the spawn + * treats undefined as "unset". + */ +export function neutralizeInheritedAgentSession(): Record { + const wizardOwned = new Set([ + 'CLAUDE_CODE_OAUTH_TOKEN', + 'CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS', + ]); + const out: Record = {}; + for (const key of Object.keys(process.env)) { + if ( + (key.startsWith('CLAUDE') || key === 'CLAUDECODE') && + !wizardOwned.has(key) + ) { + out[key] = undefined; + } + } + return out; +} + /** * Initialize agent configuration for the LLM gateway */ @@ -967,6 +994,9 @@ export async function runAgent( }, env: { ...process.env, + // Drop an outer agent's inherited Claude Code session identity so the + // SDK bearer-authenticates to the gateway instead of its OAuth path. + ...neutralizeInheritedAgentSession(), // Drop any shell ANTHROPIC_API_KEY so it can't override the wizard's // OAuth gateway token. ANTHROPIC_API_KEY: undefined,