diff --git a/plugin/core/claude/sdk-adapter.ts b/plugin/core/claude/sdk-adapter.ts index 96d1349..7467320 100644 --- a/plugin/core/claude/sdk-adapter.ts +++ b/plugin/core/claude/sdk-adapter.ts @@ -158,10 +158,29 @@ export async function* dispatchToClaude( // detached:true puts claude in its own process group. Claude's Bash tool // occasionally leaks shells/ssh that keep claude alive past end-of-turn; when // that happens we SIGKILL the whole group rather than wait forever. + // Sanitize NODE_OPTIONS before spawning. Claude Code is a Node CLI; if + // the parent gateway runs with `NODE_OPTIONS=--inspect=...:9229`, every + // child Node process — including claude — tries to bind the same inspector + // port, fails (EADDRINUSE), and exits SILENTLY (no stdout, no stderr). + // Bridge then sees an empty stream and reports `claude did not return a + // session_id` with no useful diagnostic. Strip any --inspect* / + // --inspect-brk* / --debug* flag from NODE_OPTIONS; keep everything else + // (e.g. --max-old-space-size) in case operators depend on it. + const childEnv: NodeJS.ProcessEnv = { ...process.env }; + if (childEnv.NODE_OPTIONS) { + const filtered = childEnv.NODE_OPTIONS + .split(/\s+/) + .filter((tok) => tok && !tok.startsWith("--inspect") && !tok.startsWith("--debug")) + .join(" ") + .trim(); + if (filtered) childEnv.NODE_OPTIONS = filtered; + else delete childEnv.NODE_OPTIONS; + } + const child = spawn("claude", args, { cwd: workspace, stdio: ["ignore", "pipe", "pipe"], - env: { ...process.env }, + env: childEnv, detached: true, }); diff --git a/plugin/core/gemini/sdk-adapter.ts b/plugin/core/gemini/sdk-adapter.ts index d3d935e..ab71436 100644 --- a/plugin/core/gemini/sdk-adapter.ts +++ b/plugin/core/gemini/sdk-adapter.ts @@ -156,10 +156,24 @@ export async function* dispatchToGemini( args.push("--resume", resumeSessionId); } + // Sanitize NODE_OPTIONS before spawning — same reason as the claude + // adapter: gemini-cli is a Node binary; inheriting a parent + // `NODE_OPTIONS=--inspect=...:9229` makes every child silently EADDRINUSE. + const childEnv: NodeJS.ProcessEnv = { ...process.env }; + if (childEnv.NODE_OPTIONS) { + const filtered = childEnv.NODE_OPTIONS + .split(/\s+/) + .filter((tok) => tok && !tok.startsWith("--inspect") && !tok.startsWith("--debug")) + .join(" ") + .trim(); + if (filtered) childEnv.NODE_OPTIONS = filtered; + else delete childEnv.NODE_OPTIONS; + } + const child = spawn("gemini", args, { cwd: workspace, stdio: ["ignore", "pipe", "pipe"], - env: { ...process.env }, + env: childEnv, }); const stderrLines: string[] = [];