diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bf9f5e..e942d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `claude_read_skill` path traversal: added `canonicalize()` + `starts_with()` validation to prevent reading arbitrary files via crafted skill paths (lib.rs) ### Fixed +- Sidecar env var stripping now whitelists `CLAUDE_CODE_EXPERIMENTAL_*` vars (both Rust sidecar.rs and JS agent-runner.ts) — previously all `CLAUDE*` vars were stripped, blocking feature flags like agent teams from reaching the SDK (sidecar.rs, agent-runner.ts) - E2E terminal tab tests: scoped selectors to `.tab-bar .tab-title` (was `.tab-title` which matched project tabs), used `browser.execute()` for DOM text reads to avoid stale element issues (bterminal.test.ts) - E2E wdio.conf.js: added `wdio:enforceWebDriverClassic: true` to disable BiDi negotiation (wdio v9 injects `webSocketUrl:true` which tauri-driver rejects), removed unnecessary `browserName: 'wry'`, fixed binary path to Cargo workspace target dir (`v2/target/debug/` not `v2/src-tauri/target/debug/`) - E2E consolidated to single spec file: Tauri creates one app session per spec file; multiple files caused "invalid session id" on 2nd+ file (wdio.conf.js, bterminal.test.ts) diff --git a/v2/bterminal-core/src/sidecar.rs b/v2/bterminal-core/src/sidecar.rs index bc6e5f6..34a851b 100644 --- a/v2/bterminal-core/src/sidecar.rs +++ b/v2/bterminal-core/src/sidecar.rs @@ -66,10 +66,13 @@ impl SidecarManager { log::info!("Starting sidecar: {} {}", cmd.program, cmd.args.join(" ")); - // Build a clean environment stripping all CLAUDE* vars to prevent - // the SDK from detecting nesting when BTerminal is launched from a Claude Code terminal + // Build a clean environment stripping CLAUDE* vars to prevent + // the SDK from detecting nesting when BTerminal is launched from a Claude Code terminal. + // Whitelist CLAUDE_CODE_EXPERIMENTAL_* so feature flags (e.g. agent teams) pass through. let clean_env: Vec<(String, String)> = std::env::vars() - .filter(|(k, _)| !k.starts_with("CLAUDE")) + .filter(|(k, _)| { + !k.starts_with("CLAUDE") || k.starts_with("CLAUDE_CODE_EXPERIMENTAL_") + }) .collect(); let mut child = Command::new(&cmd.program) diff --git a/v2/sidecar/agent-runner.ts b/v2/sidecar/agent-runner.ts index f8b4125..4d648fd 100644 --- a/v2/sidecar/agent-runner.ts +++ b/v2/sidecar/agent-runner.ts @@ -83,10 +83,13 @@ async function handleQuery(msg: QueryMessage) { const controller = new AbortController(); - // Strip CLAUDE* env vars to prevent nesting detection by the spawned CLI + // Strip CLAUDE* and ANTHROPIC_* env vars to prevent nesting detection by the spawned CLI. + // Whitelist CLAUDE_CODE_EXPERIMENTAL_* so feature flags (e.g. agent teams) pass through. const cleanEnv: Record = {}; for (const [key, value] of Object.entries(process.env)) { - if (!key.startsWith('CLAUDE') && !key.startsWith('ANTHROPIC_')) { + if (key.startsWith('CLAUDE_CODE_EXPERIMENTAL_')) { + cleanEnv[key] = value; + } else if (!key.startsWith('CLAUDE') && !key.startsWith('ANTHROPIC_')) { cleanEnv[key] = value; } }