(
parent: NodeJS.ProcessEnv,
extras: { [key: string]: string } = {},
cwd?: string,
)
| 65 | ] as const; |
| 66 | |
| 67 | export function buildSpawnEnv( |
| 68 | parent: NodeJS.ProcessEnv, |
| 69 | extras: { [key: string]: string } = {}, |
| 70 | cwd?: string, |
| 71 | ): { [key: string]: string } { |
| 72 | const out: { [key: string]: string } = {}; |
| 73 | for (const [k, v] of Object.entries(parent)) { |
| 74 | if (typeof v !== 'string') continue; |
| 75 | if (shouldStrip(k)) continue; |
| 76 | out[k] = v; |
| 77 | } |
| 78 | // Announce ourselves honestly so well-behaved TUI apps can detect us. |
| 79 | out['TERM'] = 'xterm-256color'; |
| 80 | out['COLORTERM'] = 'truecolor'; |
| 81 | out['TERM_PROGRAM'] = 'openalice-workspaces'; |
| 82 | out['TERM_PROGRAM_VERSION'] = SELF_VERSION; |
| 83 | if (!out['LANG']) out['LANG'] = 'en_US.UTF-8'; |
| 84 | if (!out['LC_CTYPE'] && !out['LC_ALL']) out['LC_CTYPE'] = 'en_US.UTF-8'; |
| 85 | // Override PWD to match the spawn cwd. PTY spawn does chdir() to `cwd`, |
| 86 | // but env PWD is just the parent's PWD passed verbatim. Claude Code CLI |
| 87 | // selects its `~/.claude/projects/<projectKey>/` from $PWD (not from |
| 88 | // process.cwd()), so without this override claude writes the workspace's |
| 89 | // session jsonl into the backend's projectKey — mixing it with whatever |
| 90 | // happens to be running there. On resume, `--continue` looks in the |
| 91 | // workspace's own projectKey, finds it empty, and exits 1 → PTY respawn |
| 92 | // loop into the circuit breaker. Verified end-to-end against the |
| 93 | // `path.trace` log: pre-fix `envPWD` was the OpenAlice repo root while |
| 94 | // `spawnCwd` was the workspace dir. |
| 95 | if (cwd) out['PWD'] = cwd; |
| 96 | // Caller-supplied per-session env (e.g. AQ_WS_ID, AQ_LAUNCHER_REPO_ROOT) |
| 97 | // wins over the inherited env so .mcp.json `${VAR}` expansion at Claude |
| 98 | // startup resolves to the right values. |
| 99 | for (const [k, v] of Object.entries(extras)) { |
| 100 | out[k] = v; |
| 101 | } |
| 102 | out['PATH'] = buildCliPath(out); |
| 103 | return out; |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Build a PATH suitable for launching user-installed agent CLIs from a GUI app. |
no test coverage detected