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,