diff --git a/.github/workflows/smoke-codex.yml b/.github/workflows/smoke-codex.yml index 929a8884..bdc66ba1 100644 --- a/.github/workflows/smoke-codex.yml +++ b/.github/workflows/smoke-codex.yml @@ -119,10 +119,10 @@ jobs: BRAINTRUST_API_KEY: smoke-key BRAINTRUST_API_URL: http://127.0.0.1:53999 BRAINTRUST_APP_URL: http://127.0.0.1:53999 - BT_DAEMON_CONFIG: ${{ runner.temp }}/bt-daemon-config.json + BRAINTRUST_DAEMON_CONFIG: ${{ runner.temp }}/bt-daemon-config.json MOCK_COLLECTOR_OUT: ${{ runner.temp }}/mock-summary.json run: | - printf '%s\n' '{"trace_to_braintrust":true,"route":{"destination":{"type":"project_logs","project_name":"trace-codex-smoke"},"flush_mode":"flush_on_turn_end"}}' > "$BT_DAEMON_CONFIG" + printf '%s\n' '{"trace_to_braintrust":true,"route":{"destination":{"type":"project_logs","project_name":"trace-codex-smoke"},"flush_mode":"flush_on_turn_end"}}' > "$BRAINTRUST_DAEMON_CONFIG" python3 .github/scripts/mock-braintrust.py > "${{ runner.temp }}/mock-collector.log" 2>&1 & collector_pid=$! trap 'kill "$collector_pid" 2>/dev/null || true' EXIT diff --git a/.github/workflows/smoke-cursor.yml b/.github/workflows/smoke-cursor.yml index 1fe0b78d..828ad79f 100644 --- a/.github/workflows/smoke-cursor.yml +++ b/.github/workflows/smoke-cursor.yml @@ -105,10 +105,10 @@ jobs: - name: Run a real traced Cursor session env: CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }} - BT_DAEMON_CONFIG: ${{ runner.temp }}/bt-daemon-config.json - BT_DAEMON_SOCKET: ${{ runner.temp }}/cursor-daemon.sock + BRAINTRUST_DAEMON_CONFIG: ${{ runner.temp }}/bt-daemon-config.json + BRAINTRUST_DAEMON_SOCKET: ${{ runner.temp }}/cursor-daemon.sock BT_DAEMON_BIN: ${{ github.workspace }}/bt-daemon/target/debug/bt-daemon - BT_BIN: ${{ github.workspace }}/src/plugins/cursor/test/bt-standalone-wrapper.sh + BRAINTRUST_BT_BIN: ${{ github.workspace }}/src/plugins/cursor/test/bt-standalone-wrapper.sh BRAINTRUST_API_KEY: smoke-key BRAINTRUST_API_URL: http://127.0.0.1:53999 BRAINTRUST_APP_URL: http://127.0.0.1:53999 @@ -117,13 +117,13 @@ jobs: set -euo pipefail cargo build --manifest-path bt-daemon/Cargo.toml --features cli mkdir -p "${RUNNER_TEMP}/cursor-bin" - install -m 755 "$BT_BIN" "${RUNNER_TEMP}/cursor-bin/bt" + install -m 755 "$BRAINTRUST_BT_BIN" "${RUNNER_TEMP}/cursor-bin/bt" export PATH="${RUNNER_TEMP}/cursor-bin:$PATH" - printf '%s\n' '{"trace_to_braintrust":true,"route":{"destination":{"type":"project_logs","project_name":"trace-cursor-smoke"},"flush_mode":"flush_on_turn_end"}}' > "$BT_DAEMON_CONFIG" + printf '%s\n' '{"trace_to_braintrust":true,"route":{"destination":{"type":"project_logs","project_name":"trace-cursor-smoke"},"flush_mode":"flush_on_turn_end"}}' > "$BRAINTRUST_DAEMON_CONFIG" python3 .github/scripts/mock-braintrust.py > "${RUNNER_TEMP}/mock-collector.log" 2>&1 & collector_pid=$! trap 'kill "$collector_pid" 2>/dev/null || true' EXIT - "$BT_DAEMON_BIN" serve --socket "$BT_DAEMON_SOCKET" --data-dir "${RUNNER_TEMP}/cursor-state" --idle-timeout-secs 0 & + "$BT_DAEMON_BIN" serve --socket "$BRAINTRUST_DAEMON_SOCKET" --data-dir "${RUNNER_TEMP}/cursor-state" --idle-timeout-secs 0 & daemon_pid=$! trap 'kill "$daemon_pid" "$collector_pid" 2>/dev/null || true' EXIT for attempt in $(seq 1 50); do diff --git a/AGENTS.md b/AGENTS.md index 68497833..42601f8f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,6 +50,13 @@ process APIs. These helpers prevent Windows console flashes. Foreground commands and daemon detachment have explicit exceptions; do not reuse them for background work. Clippy and Oxlint enforce use of these helpers in CI. +User-facing environment variables must use the `BRAINTRUST_` prefix (for +example `BRAINTRUST_DAEMON_SOCKET`), matching `bt` and the Braintrust SDKs. Do +not add new `BT_*` names. Read them through `bt-daemon/src/env.rs`; +`DEPRECATED_BT_ENV_VARS` there only keeps old names working. +`BT_TRACE_MANAGED_RUN_ID` and `BT_TRACE_INVOCATION_SETTINGS` remain the +internal protocol between `bt trace run` and its child processes. + ## Versioning and distribution Versioning is per distribution. Claude, Codex, Cursor, and Grok plugins carry diff --git a/bt-daemon/README.md b/bt-daemon/README.md index 61d291a8..bf3aa57b 100644 --- a/bt-daemon/README.md +++ b/bt-daemon/README.md @@ -70,7 +70,12 @@ Each coding agent reads an independent non-credential `braintrust.json` file: - Grok: `~/.grok/braintrust.json` - Antigravity: `~/.gemini/config/braintrust.json` -`BT_DAEMON_CONFIG` can override the path for isolated tests and managed hosts. +`BRAINTRUST_DAEMON_CONFIG` can override the path for isolated tests and managed +hosts. + +Tracing env vars use the `BRAINTRUST_` prefix. The old `BT_` names (for example +`BT_DAEMON_CONFIG`) are deprecated but still work; the `BRAINTRUST_` name wins +when both are set. See [`config.json.example`](config.json.example). `trace_to_braintrust` controls enablement and `route` stores the selected profile, organization, typed @@ -256,13 +261,13 @@ default suite; see the [test harness guide](tests/support/README.md). From the monorepo root, in a Unix shell: ```bash -export BT_DAEMON_SOCKET=/tmp/btd.sock BT_DAEMON_DATA_DIR=/tmp/btd BT_DAEMON_CONFIG=/tmp/btd-config.json -printf '%s\n' '{"trace_to_braintrust":true,"route":{"destination":{"type":"project_logs","project_name":"debug"}}}' > "$BT_DAEMON_CONFIG" +export BRAINTRUST_DAEMON_SOCKET=/tmp/btd.sock BRAINTRUST_DAEMON_DATA_DIR=/tmp/btd BRAINTRUST_DAEMON_CONFIG=/tmp/btd-config.json +printf '%s\n' '{"trace_to_braintrust":true,"route":{"destination":{"type":"project_logs","project_name":"debug"}}}' > "$BRAINTRUST_DAEMON_CONFIG" cargo build --manifest-path bt-daemon/Cargo.toml --features cli --locked --bin bt-daemon echo '{"session_id":"s1","hook_event_name":"SessionStart"}' | ./bt-daemon/target/debug/bt-daemon hook --source debug echo '{"session_id":"s1","hook_event_name":"Stop"}' | ./bt-daemon/target/debug/bt-daemon hook --source debug ./bt-daemon/target/debug/bt-daemon status -# Inspect journal/ and spans/ under $BT_DAEMON_DATA_DIR +# Inspect journal/ and spans/ under $BRAINTRUST_DAEMON_DATA_DIR ``` The first `hook` spawns the daemon detached; it idles out after 5 minutes. diff --git a/bt-daemon/docs/protocol.md b/bt-daemon/docs/protocol.md index bb0a010d..0f74ea4a 100644 --- a/bt-daemon/docs/protocol.md +++ b/bt-daemon/docs/protocol.md @@ -6,11 +6,11 @@ front-end and the standalone test binary. Hook clients use an agent-specific non-credential `braintrust.json` file. Codex, Claude Code, OpenCode, and Pi therefore retain independent persistent profile, -organization, and destination selections. `$BT_DAEMON_CONFIG` may explicitly -override the file path. The hook front-end applies `trace_to_braintrust` and the -stored `route` before sending an event. A managed run sets -`BT_TRACE_INVOCATION_SETTINGS` for only its child process tree, keeping global -agent configuration and generated hook commands unchanged. +organization, and destination selections. `$BRAINTRUST_DAEMON_CONFIG` may +explicitly override the file path. The hook front-end applies +`trace_to_braintrust` and the stored `route` before sending an event. A managed +run sets `BT_TRACE_INVOCATION_SETTINGS` for only its child process tree, keeping +global agent configuration and generated hook commands unchanged. The profile is optional and defaults through `bt`. Credentials and backend URLs are resolved and refreshed inside the daemon and never enter this file. @@ -19,8 +19,8 @@ URLs are resolved and refreshed inside the daemon and never enter this file. ## Transport - **Unix domain socket (Linux/macOS).** Default path resolution (first match wins): - 1. `--socket ` flag / `BT_DAEMON_SOCKET` env (explicit override; used by - tests to sandbox a daemon per test). + 1. `--socket ` flag / `BRAINTRUST_DAEMON_SOCKET` env (explicit + override; used by tests to sandbox a daemon per test). 2. `$XDG_RUNTIME_DIR/braintrust/daemon.sock` if `XDG_RUNTIME_DIR` is set. 3. `$HOME/.braintrust/run/daemon.sock`. The containing directory is created mode `0700`. macOS caps `sockaddr_un` @@ -30,7 +30,7 @@ URLs are resolved and refreshed inside the daemon and never enter this file. so `\n` is an unambiguous frame delimiter. Max line length is bounded (default 64 MiB) to cap memory on a malformed/huge payload; over-length lines are a protocol error and close the connection. -- **Windows named pipe.** `--socket` / `BT_DAEMON_SOCKET` may provide an +- **Windows named pipe.** `--socket` / `BRAINTRUST_DAEMON_SOCKET` may provide an explicit full pipe name. Otherwise the daemon uses `\\.\pipe\braintrust-bt-daemon-`, where the suffix is derived from the Windows domain and user name so concurrent users do not share a diff --git a/bt-daemon/src/env.rs b/bt-daemon/src/env.rs new file mode 100644 index 00000000..3789d9b1 --- /dev/null +++ b/bt-daemon/src/env.rs @@ -0,0 +1,132 @@ +//! User-facing environment variables use the `BRAINTRUST_` prefix, matching +//! `bt` and the Braintrust SDKs. Read them through [`var_os`] so that the +//! deprecated `BT_` names keep working until support is removed. + +use std::ffi::{OsStr, OsString}; + +/// Pre-`BRAINTRUST_` names of tracing env vars. Each `BT_` is still +/// honored as an alias for `BRAINTRUST_` until support is removed in a +/// major release. Do not add entries: new env vars must use the `BRAINTRUST_` +/// prefix. +/// +/// `BT_TRACE_MANAGED_RUN_ID` and `BT_TRACE_INVOCATION_SETTINGS` are not listed: +/// they are the internal protocol `bt trace run` uses to configure its child +/// process tree, read by separately released plugins, and are unchanged. +pub(crate) const DEPRECATED_BT_ENV_VARS: &[&str] = &[ + "BT_ANTIGRAVITY_CONFIG_DIR", + "BT_CURSOR_CONFIG_DIR", + "BT_CURSOR_PLUGIN_DIR", + "BT_DAEMON_CONFIG", + "BT_DAEMON_DATA_DIR", + "BT_DAEMON_SOCKET", + "BT_TRACE_OPENCODE_PLUGIN_SPEC", + "BT_TRACE_PI_PLUGIN_SPEC", +]; + +pub(crate) fn canonical_env_name(deprecated: &str) -> String { + let name = deprecated.strip_prefix("BT_").unwrap_or(deprecated); + format!("BRAINTRUST_{name}") +} + +/// The deprecated `BT_` alias of `canonical`, if it has one. +pub(crate) fn deprecated_env_name(canonical: &str) -> Option<&'static str> { + DEPRECATED_BT_ENV_VARS + .iter() + .copied() + .find(|deprecated| canonical_env_name(deprecated) == canonical) +} + +/// Read `canonical`, falling back to its deprecated `BT_` alias. The canonical +/// name wins when both are set. +pub(crate) fn var_os(canonical: &str) -> Option { + std::env::var_os(canonical) + .or_else(|| deprecated_env_name(canonical).and_then(std::env::var_os)) +} + +pub(crate) fn var(canonical: &str) -> Option { + var_os(canonical).and_then(|value| value.into_string().ok()) +} + +/// Pass every resolved value to a child process under both names, so plugins +/// released before the rename read the same value instead of a stale or +/// missing deprecated one. +pub(crate) fn mirror_aliases(command: &mut tokio::process::Command) { + for deprecated in DEPRECATED_BT_ENV_VARS { + let canonical = canonical_env_name(deprecated); + if let Some(value) = var_os(&canonical) { + command.env(&canonical, &value).env(deprecated, &value); + } + } +} + +/// Set `canonical` and its deprecated alias to the same value on a child +/// process, so plugins released before the rename still read it. +pub(crate) fn set_with_alias( + command: &mut tokio::process::Command, + canonical: &str, + value: impl AsRef, +) { + command.env(canonical, value.as_ref()); + if let Some(deprecated) = deprecated_env_name(canonical) { + command.env(deprecated, value.as_ref()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::BTreeSet; + + #[test] + fn canonical_env_name_swaps_prefix() { + assert_eq!( + canonical_env_name("BT_DAEMON_SOCKET"), + "BRAINTRUST_DAEMON_SOCKET" + ); + assert_eq!( + deprecated_env_name("BRAINTRUST_DAEMON_SOCKET"), + Some("BT_DAEMON_SOCKET") + ); + assert_eq!(deprecated_env_name("BRAINTRUST_API_KEY"), None); + } + + /// New user-facing env vars must use the `BRAINTRUST_` prefix. Every + /// `BT_*` name in the crate must be a deprecated alias or internal protocol, + /// and every deprecated alias must map to a name the crate reads. + #[test] + fn env_vars_use_braintrust_prefix() { + const INTERNAL: &[&str] = &[ + "BT_TRACE_INVOCATION_SETTINGS", + "BT_TRACE_MANAGED_RUN_ID", + // Fallback for `BRAINTRUST_BT_BIN` in the Cursor hook launchers. + "BT_BIN", + ]; + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); + let mut sources = String::new(); + let mut pending = vec![root]; + while let Some(path) = pending.pop() { + if path.is_dir() { + pending.extend(std::fs::read_dir(&path).unwrap().map(|e| e.unwrap().path())); + } else if path.extension().is_some_and(|ext| ext == "rs") && !path.ends_with("env.rs") { + sources.push_str(&std::fs::read_to_string(&path).unwrap()); + } + } + let pattern = regex::Regex::new(r"\bBT_[A-Z0-9_]+\b").unwrap(); + let unexpected: BTreeSet<_> = pattern + .find_iter(&sources) + .map(|m| m.as_str()) + .filter(|name| !DEPRECATED_BT_ENV_VARS.contains(name) && !INTERNAL.contains(name)) + .collect(); + assert!( + unexpected.is_empty(), + "env vars must use the BRAINTRUST_ prefix: {unexpected:?}" + ); + for deprecated in DEPRECATED_BT_ENV_VARS { + let canonical = canonical_env_name(deprecated); + assert!( + sources.contains(&format!("\"{canonical}\"")), + "{deprecated} maps to {canonical}, which nothing reads" + ); + } + } +} diff --git a/bt-daemon/src/lib.rs b/bt-daemon/src/lib.rs index 9c586f5a..2df4e679 100644 --- a/bt-daemon/src/lib.rs +++ b/bt-daemon/src/lib.rs @@ -19,6 +19,7 @@ mod command_output; mod correlation; mod delivery_ledger; mod dispatch; +mod env; mod hook; mod ids; mod journal; diff --git a/bt-daemon/src/managed_run/mod.rs b/bt-daemon/src/managed_run/mod.rs index 9d2461c8..1f3716f4 100644 --- a/bt-daemon/src/managed_run/mod.rs +++ b/bt-daemon/src/managed_run/mod.rs @@ -17,7 +17,7 @@ use crate::client::{flush_managed_run_in, shutdown_daemon}; use crate::hook::MANAGED_RUN_ID_ENV; use crate::route::resolve_span_plugin_paths; use crate::wire::{self, SessionRoute}; -use crate::{paths, settings, subprocess}; +use crate::{env, paths, settings, subprocess}; const MANAGED_RUN_FLUSH_TIMEOUT_MS: u64 = 10_000; @@ -94,7 +94,7 @@ pub async fn run_traced( // Only create our own boundary when environment auth would otherwise use // the ambient shared daemon. let isolate_daemon = route.auth.effective_source() == wire::AuthSource::Environment - && std::env::var_os(paths::SOCKET_ENV).is_none(); + && env::var_os(paths::SOCKET_ENV).is_none(); let isolated_runtime = isolate_daemon .then(|| ManagedRunRuntime::new(&managed_run_id)) .transpose()?; @@ -106,10 +106,10 @@ pub async fn run_traced( .env("_BT_TRACE_MANAGED_RUN", "1") .env(MANAGED_RUN_ID_ENV, &managed_run_id) .env(settings::INVOCATION_SETTINGS_ENV, invocation_settings); + env::mirror_aliases(&mut command); if let Some(runtime) = &isolated_runtime { - command - .env(paths::SOCKET_ENV, &runtime.socket) - .env(paths::DATA_DIR_ENV, runtime.temp_dir.path()); + env::set_with_alias(&mut command, paths::SOCKET_ENV, &runtime.socket); + env::set_with_alias(&mut command, paths::DATA_DIR_ENV, runtime.temp_dir.path()); } command.envs(injection.env); let _guards = injection.guards; diff --git a/bt-daemon/src/managed_run/opencode.rs b/bt-daemon/src/managed_run/opencode.rs index f5bb3092..9e658672 100644 --- a/bt-daemon/src/managed_run/opencode.rs +++ b/bt-daemon/src/managed_run/opencode.rs @@ -18,8 +18,8 @@ fn opencode_managed_config(existing: Option<&str>) -> anyhow::Result { .or_insert_with(|| serde_json::Value::Array(Vec::new())) .as_array_mut() .ok_or_else(|| anyhow::anyhow!("OPENCODE_CONFIG_CONTENT.plugin must be an array"))?; - let plugin = std::env::var("BT_TRACE_OPENCODE_PLUGIN_SPEC") - .unwrap_or_else(|_| "@braintrust/trace-opencode/tracing".to_string()); + let plugin = crate::env::var("BRAINTRUST_TRACE_OPENCODE_PLUGIN_SPEC") + .unwrap_or_else(|| "@braintrust/trace-opencode/tracing".to_string()); if !plugins.iter().any(|value| value.as_str() == Some(&plugin)) { plugins.push(serde_json::Value::String(plugin)); } diff --git a/bt-daemon/src/managed_run/pi.rs b/bt-daemon/src/managed_run/pi.rs index 50f01f11..85f09d91 100644 --- a/bt-daemon/src/managed_run/pi.rs +++ b/bt-daemon/src/managed_run/pi.rs @@ -16,7 +16,7 @@ impl ManagedRun for Pi { _hook_command: &RunHookCommand, _managed_run_id: &str, ) -> anyhow::Result { - let extension = match std::env::var_os("BT_TRACE_PI_PLUGIN_SPEC") { + let extension = match crate::env::var_os("BRAINTRUST_TRACE_PI_PLUGIN_SPEC") { Some(extension) => extension, None => OsString::from(crate::setup::pi::plugin_spec()), }; diff --git a/bt-daemon/src/paths.rs b/bt-daemon/src/paths.rs index f1321655..c7799c85 100644 --- a/bt-daemon/src/paths.rs +++ b/bt-daemon/src/paths.rs @@ -5,18 +5,18 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; /// Env override for the socket path (also settable via `--socket`). -pub const SOCKET_ENV: &str = "BT_DAEMON_SOCKET"; +pub const SOCKET_ENV: &str = "BRAINTRUST_DAEMON_SOCKET"; /// Env override for the data/journal directory. -pub const DATA_DIR_ENV: &str = "BT_DAEMON_DATA_DIR"; +pub const DATA_DIR_ENV: &str = "BRAINTRUST_DAEMON_DATA_DIR"; /// Env override for the current agent's non-credential tracing settings file. -pub const SETTINGS_ENV: &str = "BT_DAEMON_CONFIG"; +pub const SETTINGS_ENV: &str = "BRAINTRUST_DAEMON_CONFIG"; /// Env override for Antigravity's native configuration directory. Primarily /// useful for isolated validation and managed environments. -pub const ANTIGRAVITY_CONFIG_DIR_ENV: &str = "BT_ANTIGRAVITY_CONFIG_DIR"; +pub const ANTIGRAVITY_CONFIG_DIR_ENV: &str = "BRAINTRUST_ANTIGRAVITY_CONFIG_DIR"; /// Env override for Cursor's per-user local plugin directory. -pub const CURSOR_PLUGIN_DIR_ENV: &str = "BT_CURSOR_PLUGIN_DIR"; +pub const CURSOR_PLUGIN_DIR_ENV: &str = "BRAINTRUST_CURSOR_PLUGIN_DIR"; /// Env override for Cursor's per-user configuration directory. -pub const CURSOR_CONFIG_DIR_ENV: &str = "BT_CURSOR_CONFIG_DIR"; +pub const CURSOR_CONFIG_DIR_ENV: &str = "BRAINTRUST_CURSOR_CONFIG_DIR"; fn home() -> PathBuf { std::env::var_os("HOME") @@ -25,13 +25,13 @@ fn home() -> PathBuf { .unwrap_or_else(|| PathBuf::from(".")) } -/// Resolve the socket path: explicit `override` → `$BT_DAEMON_SOCKET` → +/// Resolve the socket path: explicit `override` → `$BRAINTRUST_DAEMON_SOCKET` → /// `$XDG_RUNTIME_DIR/braintrust/daemon.sock` → `~/.braintrust/run/daemon.sock`. pub fn socket_path(explicit: Option<&Path>) -> PathBuf { if let Some(p) = explicit { return p.to_path_buf(); } - if let Some(p) = std::env::var_os(SOCKET_ENV) { + if let Some(p) = crate::env::var_os(SOCKET_ENV) { return PathBuf::from(p); } #[cfg(windows)] @@ -65,13 +65,13 @@ fn windows_pipe_name(domain: &str, user: &str) -> String { format!(r"\\.\pipe\braintrust-bt-daemon-{suffix}") } -/// Resolve the data dir: explicit `override` → `$BT_DAEMON_DATA_DIR` → +/// Resolve the data dir: explicit `override` → `$BRAINTRUST_DAEMON_DATA_DIR` → /// `$XDG_STATE_HOME/braintrust/bt-daemon` → `~/.braintrust/state/bt-daemon`. pub fn data_dir(explicit: Option<&Path>) -> PathBuf { if let Some(p) = explicit { return p.to_path_buf(); } - if let Some(p) = std::env::var_os(DATA_DIR_ENV) { + if let Some(p) = crate::env::var_os(DATA_DIR_ENV) { return PathBuf::from(p); } #[cfg(windows)] @@ -91,14 +91,14 @@ pub fn data_dir(explicit: Option<&Path>) -> PathBuf { /// Resolve one coding agent's persistent tracing settings file. /// -/// An explicit override and `$BT_DAEMON_CONFIG` remain useful for isolated -/// tests and managed environments. Normal setup keeps every agent independent +/// An explicit override and `$BRAINTRUST_DAEMON_CONFIG` remain useful for +/// isolated tests and managed environments. Normal setup keeps every agent independent /// by writing into that agent's native configuration directory. pub fn agent_settings_path(source: &str, explicit: Option<&Path>) -> PathBuf { if let Some(path) = explicit { return path.to_path_buf(); } - if let Some(path) = std::env::var_os(SETTINGS_ENV) { + if let Some(path) = crate::env::var_os(SETTINGS_ENV) { return PathBuf::from(path); } match source { @@ -141,7 +141,7 @@ fn claude_config_dir_from(config_dir: Option<&OsStr>, home: &Path) -> PathBuf { /// Resolve Antigravity's native configuration directory. pub(crate) fn antigravity_config_dir() -> PathBuf { - if let Some(path) = std::env::var_os(ANTIGRAVITY_CONFIG_DIR_ENV) { + if let Some(path) = crate::env::var_os(ANTIGRAVITY_CONFIG_DIR_ENV) { if !path.is_empty() { return PathBuf::from(path); } @@ -151,7 +151,7 @@ pub(crate) fn antigravity_config_dir() -> PathBuf { /// Resolve Cursor's per-user local plugin directory for the tracing plugin. pub(crate) fn cursor_plugin_dir() -> PathBuf { - std::env::var_os(CURSOR_PLUGIN_DIR_ENV) + crate::env::var_os(CURSOR_PLUGIN_DIR_ENV) .filter(|path| !path.is_empty()) .map(PathBuf::from) .unwrap_or_else(|| home().join(".cursor").join("plugins").join("local")) @@ -160,7 +160,7 @@ pub(crate) fn cursor_plugin_dir() -> PathBuf { /// Resolve Cursor's per-user configuration directory. pub(crate) fn cursor_config_dir() -> PathBuf { - std::env::var_os(CURSOR_CONFIG_DIR_ENV) + crate::env::var_os(CURSOR_CONFIG_DIR_ENV) .or_else(|| std::env::var_os("CURSOR_CONFIG_DIR")) .filter(|path| !path.is_empty()) .map(PathBuf::from) diff --git a/bt-daemon/src/setup/cursor/mod.rs b/bt-daemon/src/setup/cursor/mod.rs index 709f1f41..5444cc5c 100644 --- a/bt-daemon/src/setup/cursor/mod.rs +++ b/bt-daemon/src/setup/cursor/mod.rs @@ -22,7 +22,7 @@ const MONOREPO_SOURCE: &str = "braintrustdata/braintrust-coding-agent-plugins"; const PLUGIN_MANIFEST: &str = plugin_source!("cursor/content/.cursor-plugin/plugin.json"); const HOOKS_MANIFEST: &str = plugin_source!("cursor/content/hooks/hooks.json"); const HOOK_LAUNCHER: &str = plugin_source!("cursor/content/hooks/trace.sh"); -const POWERSHELL_LAUNCHER: &str = r#"$bt = if ($env:BT_BIN) { $env:BT_BIN } else { 'bt' } +const POWERSHELL_LAUNCHER: &str = r#"$bt = if ($env:BRAINTRUST_BT_BIN) { $env:BRAINTRUST_BT_BIN } elseif ($env:BT_BIN) { $env:BT_BIN } else { 'bt' } $hookArgs = @('trace', 'hook', '--source', 'cursor', '--session-id-field', 'conversation_id', '--event-field', 'hook_event_name', '--transcript-path-field', 'transcript_path', '--flush-on-turn-end', '--capture-timeout-ms', '8000') try { & $bt @hookArgs *> $null } catch {} if ($args.Count -gt 0 -and $args[0] -eq 'beforeSubmitPrompt') { @@ -410,7 +410,7 @@ mod tests { assert!(!command.contains("trace.sh")); } let script = std::fs::read_to_string(plugin.join("hooks/trace.ps1")).unwrap(); - assert!(script.contains("$env:BT_BIN")); + assert!(script.contains("$env:BRAINTRUST_BT_BIN")); assert!(script.contains("'--source', 'cursor'")); assert!(script.contains("'--event-field', 'hook_event_name'")); assert!(script.contains("'{\"continue\":true}'") || script.contains("\"continue\":true")); diff --git a/bt-daemon/tests/command_output.rs b/bt-daemon/tests/command_output.rs index 679b190f..e988d7f6 100644 --- a/bt-daemon/tests/command_output.rs +++ b/bt-daemon/tests/command_output.rs @@ -52,7 +52,7 @@ fn standalone_hook_exit_status_distinguishes_capture_failures_from_disabled_trac let mut child = Command::new(env!("CARGO_BIN_EXE_bt-daemon")) .args(["hook", "--source", "codex", "--no-spawn", "--socket"]) .arg(&socket) - .env("BT_DAEMON_CONFIG", &config) + .env("BRAINTRUST_DAEMON_CONFIG", &config) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -99,3 +99,99 @@ fn standalone_hook_exit_status_distinguishes_capture_failures_from_disabled_trac "trace destination is not configured", ); } + +#[test] +fn standalone_hook_honors_deprecated_bt_env_names() { + let temp = tempfile::tempdir().unwrap(); + let old_config = temp.path().join("old.json"); + let new_config = temp.path().join("new.json"); + std::fs::write(&old_config, r#"{"trace_to_braintrust":false}"#).unwrap(); + #[cfg(unix)] + let socket = temp.path().join("missing.sock"); + #[cfg(windows)] + let socket = std::path::PathBuf::from(format!( + r"\\.\pipe\missing-bt-daemon-{}", + uuid::Uuid::new_v4() + )); + + let hook = |env: &[(&str, &std::path::Path)]| { + let mut command = Command::new(env!("CARGO_BIN_EXE_bt-daemon")); + command + .args(["hook", "--source", "codex", "--no-spawn", "--socket"]) + .arg(&socket) + .env("HOME", temp.path()) + .env("USERPROFILE", temp.path()) + .env_remove("BT_DAEMON_CONFIG") + .env_remove("BRAINTRUST_DAEMON_CONFIG") + .stdin(Stdio::null()); + for (key, value) in env { + command.env(key, value); + } + command.output().unwrap() + }; + + // The deprecated name alone still selects the settings file, silently. + let output = hook(&[("BT_DAEMON_CONFIG", &old_config)]); + let stderr = String::from_utf8_lossy(&output.stderr); + assert_eq!(output.status.code(), Some(0), "stderr: {stderr}"); + assert!(stderr.is_empty(), "stderr: {stderr}"); + + // The canonical name wins when both are set. + let output = hook(&[ + ("BT_DAEMON_CONFIG", &old_config), + ("BRAINTRUST_DAEMON_CONFIG", &new_config), + ]); + let stderr = String::from_utf8_lossy(&output.stderr); + assert_eq!(output.status.code(), Some(1), "stderr: {stderr}"); + assert!(stderr.contains("new.json"), "stderr: {stderr}"); +} + +/// Plugins released before the `BRAINTRUST_` rename read only `BT_DAEMON_SOCKET`, +/// so a managed run must hand its child the resolved socket under both names. +#[cfg(unix)] +#[test] +fn standalone_run_mirrors_an_explicit_socket_to_the_deprecated_name() { + use std::os::unix::fs::PermissionsExt; + + let temp = tempfile::tempdir().unwrap(); + let agent = temp.path().join("fake-pi.sh"); + let seen = temp.path().join("seen-env"); + std::fs::write( + &agent, + format!( + "#!/bin/sh\nprintf '%s\\n%s\\n' \"$BRAINTRUST_DAEMON_SOCKET\" \"$BT_DAEMON_SOCKET\" > '{}'\n", + seen.display() + ), + ) + .unwrap(); + std::fs::set_permissions(&agent, std::fs::Permissions::from_mode(0o755)).unwrap(); + let explicit = temp.path().join("explicit.sock"); + + for stale in [None, Some(temp.path().join("stale.sock"))] { + let mut command = Command::new(env!("CARGO_BIN_EXE_bt-daemon")); + command + .args(["run", "--project", "mirror-test", "pi"]) + .env("HOME", temp.path()) + .env("PI_BIN", &agent) + .env("BRAINTRUST_API_KEY", "test-key") + .env("BRAINTRUST_DAEMON_SOCKET", &explicit) + .env_remove("BT_DAEMON_SOCKET") + .stdin(Stdio::null()); + if let Some(stale) = &stale { + command.env("BT_DAEMON_SOCKET", stale); + } + let output = command.output().unwrap(); + let seen = std::fs::read_to_string(&seen).unwrap_or_else(|error| { + panic!( + "agent did not run ({error}); stderr: {}", + String::from_utf8_lossy(&output.stderr) + ) + }); + let explicit = explicit.display().to_string(); + assert_eq!( + seen.lines().collect::>(), + vec![explicit.as_str(), explicit.as_str()], + "stale deprecated socket: {stale:?}" + ); + } +} diff --git a/bt-daemon/tests/cursor_pipeline.rs b/bt-daemon/tests/cursor_pipeline.rs index 3f1bfc17..0d21ea57 100644 --- a/bt-daemon/tests/cursor_pipeline.rs +++ b/bt-daemon/tests/cursor_pipeline.rs @@ -130,11 +130,14 @@ async fn assert_stalled_daemon_preserves_policy_response( let mut child = tokio::process::Command::new("/bin/sh") .arg(plugin.join("content/hooks/trace.sh")) .arg(event) - .env("BT_BIN", plugin.join("test/bt-standalone-wrapper.sh")) + .env( + "BRAINTRUST_BT_BIN", + plugin.join("test/bt-standalone-wrapper.sh"), + ) .env("BT_DAEMON_BIN", env!("CARGO_BIN_EXE_bt-daemon")) - .env("BT_DAEMON_SOCKET", &socket) - .env("BT_DAEMON_CONFIG", tmp.path().join("settings.json")) - .env("BT_DAEMON_DATA_DIR", tmp.path().join("data")) + .env("BRAINTRUST_DAEMON_SOCKET", &socket) + .env("BRAINTRUST_DAEMON_CONFIG", tmp.path().join("settings.json")) + .env("BRAINTRUST_DAEMON_DATA_DIR", tmp.path().join("data")) .env("CURSOR_PLUGIN_ROOT", plugin.join("content")) .env_remove("_BT_TRACE_MANAGED_RUN") .env( diff --git a/bt-daemon/tests/fixtures/cursor/real-cli/README.md b/bt-daemon/tests/fixtures/cursor/real-cli/README.md index fde95d51..45cc9b70 100644 --- a/bt-daemon/tests/fixtures/cursor/real-cli/README.md +++ b/bt-daemon/tests/fixtures/cursor/real-cli/README.md @@ -21,7 +21,7 @@ Both scenarios used an isolated temporary workspace containing only `sample.txt` Auto model, two successful user turns. Both `stop` observations precede their `afterAgentResponse` observations, which repeat the identical turn usage. - `plugin-headless-resume-success`: the exact default development artifact, - loaded with `--plugin-dir` and an absolute `BT_BIN` recording shim. A headless + loaded with `--plugin-dir` and an absolute `BRAINTRUST_BT_BIN` recording shim. A headless Auto Read/Shell session and a separate `--resume` invocation both succeeded. This records only the plugin's forwarded payloads, without the shim arguments. The two stream-result files retain native terminal success and usage. diff --git a/bt-daemon/tests/pipeline.rs b/bt-daemon/tests/pipeline.rs index e1bb3317..808994ba 100644 --- a/bt-daemon/tests/pipeline.rs +++ b/bt-daemon/tests/pipeline.rs @@ -2488,7 +2488,7 @@ esac .unwrap(); std::fs::set_permissions(&agent, std::fs::Permissions::from_mode(0o755)).unwrap(); - let _socket = EnvVarGuard::set("BT_DAEMON_SOCKET", &socket); + let _socket = EnvVarGuard::set("BRAINTRUST_DAEMON_SOCKET", &socket); let _agent = EnvVarGuard::set("CODEX_BIN", &agent); let _daemon = EnvVarGuard::set("BT_DAEMON_TEST_BIN", env!("CARGO_BIN_EXE_bt-daemon")); let route = || SessionRoute { @@ -2694,7 +2694,7 @@ async fn invoke_grok_hook( "transcriptPath", "--no-spawn", ]) - .env("BT_DAEMON_SOCKET", socket) + .env("BRAINTRUST_DAEMON_SOCKET", socket) .env( "BT_TRACE_INVOCATION_SETTINGS", invocation_settings.to_string(), diff --git a/bt-daemon/tests/support/agent_process.rs b/bt-daemon/tests/support/agent_process.rs index 47e0a782..83f6e755 100644 --- a/bt-daemon/tests/support/agent_process.rs +++ b/bt-daemon/tests/support/agent_process.rs @@ -161,9 +161,9 @@ impl AgentTestWorld { let combined = std::env::join_paths(entries).expect("construct test PATH"); command .env("PATH", combined) - .env("BT_DAEMON_SOCKET", &self.socket) - .env("BT_DAEMON_DATA_DIR", &self.data_dir) - .env("BT_DAEMON_CONFIG", &self.config_path) + .env("BRAINTRUST_DAEMON_SOCKET", &self.socket) + .env("BRAINTRUST_DAEMON_DATA_DIR", &self.data_dir) + .env("BRAINTRUST_DAEMON_CONFIG", &self.config_path) .env("BRAINTRUST_FLUSH_ON_TURN_END", "true") .env("BRAINTRUST_ADDITIONAL_METADATA", r#"{"test_harness":true}"#) .stdin(Stdio::null()); diff --git a/bt-daemon/tests/support/agents/opencode.rs b/bt-daemon/tests/support/agents/opencode.rs index ca791de6..de35a636 100644 --- a/bt-daemon/tests/support/agents/opencode.rs +++ b/bt-daemon/tests/support/agents/opencode.rs @@ -142,7 +142,7 @@ impl OpenCodeAgent { server.process_group(0); configure_environment(&mut server, self); server.env( - "BT_TRACE_OPENCODE_PLUGIN_SPEC", + "BRAINTRUST_TRACE_OPENCODE_PLUGIN_SPEC", path_to_file_url(&self.plugin), ); world.configure(&mut server); diff --git a/bt-daemon/tests/support/agents/pi.rs b/bt-daemon/tests/support/agents/pi.rs index ea8a1736..45c7c954 100644 --- a/bt-daemon/tests/support/agents/pi.rs +++ b/bt-daemon/tests/support/agents/pi.rs @@ -143,7 +143,7 @@ impl PiAgent { .arg(&self.session_dir) .current_dir(world.workspace()) .env("PI_CODING_AGENT_DIR", &self.config_dir) - .env("BT_TRACE_PI_PLUGIN_SPEC", &self.extension) + .env("BRAINTRUST_TRACE_PI_PLUGIN_SPEC", &self.extension) .env("PI_OFFLINE", "true"); world.configure(&mut command); run.options.apply(&mut command); diff --git a/scripts/build-and-run-local-daemon.sh b/scripts/build-and-run-local-daemon.sh index 71ed6dab..db229d74 100755 --- a/scripts/build-and-run-local-daemon.sh +++ b/scripts/build-and-run-local-daemon.sh @@ -90,14 +90,14 @@ case "$(uname -s)" in DAEMON_BIN="$DAEMON_BIN.exe" NATIVE_DIR="$(cygpath -m "$RUN_DIR")" export MSYS2_ARG_CONV_EXCL='*' - export MSYS2_ENV_CONV_EXCL='BT_DAEMON_SOCKET;BT_DAEMON_DATA_DIR;BT_DAEMON_CONFIG' - export BT_DAEMON_SOCKET="\\\\.\\pipe\\braintrust-bt-local-${RUN_DIR##*/}-$$" + export MSYS2_ENV_CONV_EXCL='BRAINTRUST_DAEMON_SOCKET;BRAINTRUST_DAEMON_DATA_DIR;BRAINTRUST_DAEMON_CONFIG' + export BRAINTRUST_DAEMON_SOCKET="\\\\.\\pipe\\braintrust-bt-local-${RUN_DIR##*/}-$$" ;; - *) export BT_DAEMON_SOCKET="$RUN_DIR/daemon.sock" ;; + *) export BRAINTRUST_DAEMON_SOCKET="$RUN_DIR/daemon.sock" ;; esac [[ -x "$DAEMON_BIN" ]] || fail "built daemon not found at $DAEMON_BIN; use a native CARGO_BUILD_TARGET" -export BT_DAEMON_DATA_DIR="$NATIVE_DIR/state" -export BT_DAEMON_CONFIG="$NATIVE_DIR/braintrust.json" +export BRAINTRUST_DAEMON_DATA_DIR="$NATIVE_DIR/state" +export BRAINTRUST_DAEMON_CONFIG="$NATIVE_DIR/braintrust.json" # This runner uses environment auth and an explicit project, never saved routes. unset BRAINTRUST_PROFILE BRAINTRUST_DESTINATION @@ -121,7 +121,7 @@ trap cleanup EXIT trap 'exit 130' INT trap 'exit 143' TERM -DAEMON_ARGS=(serve --socket "$BT_DAEMON_SOCKET" --data-dir "$BT_DAEMON_DATA_DIR" --idle-timeout-secs 0) +DAEMON_ARGS=(serve --socket "$BRAINTRUST_DAEMON_SOCKET" --data-dir "$BRAINTRUST_DAEMON_DATA_DIR" --idle-timeout-secs 0) if [[ "$UPLOAD" == false ]]; then DAEMON_ARGS+=(--debug-sink) fi @@ -130,7 +130,7 @@ DAEMON_PID=$! READY=false for ((attempt = 0; attempt < 100; attempt++)); do kill -0 "$DAEMON_PID" 2>/dev/null || { cat "$RUN_DIR/daemon.log" >&2; fail 'daemon exited during startup'; } - STATUS="$("$DAEMON_BIN" status --socket "$BT_DAEMON_SOCKET" --json 2>/dev/null || true)" + STATUS="$("$DAEMON_BIN" status --socket "$BRAINTRUST_DAEMON_SOCKET" --json 2>/dev/null || true)" if [[ "$STATUS" == *'"running":true'* ]]; then READY=true break diff --git a/scripts/tests/windows-background-process.mjs b/scripts/tests/windows-background-process.mjs index 7cf8f659..7f9b5fa7 100755 --- a/scripts/tests/windows-background-process.mjs +++ b/scripts/tests/windows-background-process.mjs @@ -48,7 +48,7 @@ async function exercise(directory) { assert.equal(JSON.parse(control.stdout)[0].console_window, true, "negative control failed: GetConsoleWindow did not detect an unsuppressed console") - process.env.BT_EXECUTABLE = probe + process.env.BRAINTRUST_BT_BIN = probe process.env.BT_CONSOLE_PROBE_REPORT = join(directory, "cli success.json") delete process.env.BT_CONSOLE_PROBE_MODE const tools = new BtCliToolsClient({ diff --git a/src/plugins/antigravity/test/bt-standalone-wrapper.sh b/src/plugins/antigravity/test/bt-standalone-wrapper.sh index 931e0738..46908f62 100755 --- a/src/plugins/antigravity/test/bt-standalone-wrapper.sh +++ b/src/plugins/antigravity/test/bt-standalone-wrapper.sh @@ -4,10 +4,10 @@ set -eu : "${BT_DAEMON_BIN:?set BT_DAEMON_BIN}" -: "${BT_DAEMON_SOCKET:?set BT_DAEMON_SOCKET}" +: "${BRAINTRUST_DAEMON_SOCKET:?set BRAINTRUST_DAEMON_SOCKET}" [ "${1:-}" = "trace" ] [ "${2:-}" = "hook" ] shift 2 -exec "$BT_DAEMON_BIN" hook "$@" --socket "$BT_DAEMON_SOCKET" --no-spawn +exec "$BT_DAEMON_BIN" hook "$@" --socket "$BRAINTRUST_DAEMON_SOCKET" --no-spawn diff --git a/src/plugins/cursor/content/hooks/trace.sh b/src/plugins/cursor/content/hooks/trace.sh index 036aa399..32aac9be 100755 --- a/src/plugins/cursor/content/hooks/trace.sh +++ b/src/plugins/cursor/content/hooks/trace.sh @@ -1,10 +1,11 @@ #!/bin/sh # Cursor consumes stdout as a policy response. Never expose bt output there. # Keep the original native JSON on stdin; the daemon owns all interpretation. -BT_BIN="${BT_BIN:-bt}" -if command -v "$BT_BIN" >/dev/null 2>&1; then +# BT_BIN is the deprecated name of BRAINTRUST_BT_BIN. +bt_bin="${BRAINTRUST_BT_BIN:-${BT_BIN:-bt}}" +if command -v "$bt_bin" >/dev/null 2>&1; then # Leave time to emit the policy response before Cursor's 10-second timeout. - if ! "$BT_BIN" trace hook --source cursor \ + if ! "$bt_bin" trace hook --source cursor \ --session-id-field conversation_id --event-field hook_event_name \ --transcript-path-field transcript_path --flush-on-turn-end \ --capture-timeout-ms 8000 \ diff --git a/src/plugins/cursor/test/bt-standalone-wrapper.sh b/src/plugins/cursor/test/bt-standalone-wrapper.sh index 1e6b88a6..5c85532e 100755 --- a/src/plugins/cursor/test/bt-standalone-wrapper.sh +++ b/src/plugins/cursor/test/bt-standalone-wrapper.sh @@ -2,8 +2,8 @@ # Adapt the production hook contract to the development-only daemon binary. set -eu : "${BT_DAEMON_BIN:?set BT_DAEMON_BIN}" -: "${BT_DAEMON_SOCKET:?set BT_DAEMON_SOCKET}" +: "${BRAINTRUST_DAEMON_SOCKET:?set BRAINTRUST_DAEMON_SOCKET}" [ "${1-}" = "trace" ] [ "${2-}" = "hook" ] shift 2 -exec "$BT_DAEMON_BIN" hook "$@" --socket "$BT_DAEMON_SOCKET" --no-spawn +exec "$BT_DAEMON_BIN" hook "$@" --socket "$BRAINTRUST_DAEMON_SOCKET" --no-spawn diff --git a/src/plugins/grok/local-dev.sh b/src/plugins/grok/local-dev.sh index 42768188..3978f5d5 100755 --- a/src/plugins/grok/local-dev.sh +++ b/src/plugins/grok/local-dev.sh @@ -369,7 +369,7 @@ DAEMON_ARGS+=( --idle-timeout-secs 0 ) RUST_LOG="${GROK_DAEMON_RUST_LOG:-bt_daemon::translate::grok=debug}" \ -BT_DAEMON_CONFIG="$CONFIG" \ +BRAINTRUST_DAEMON_CONFIG="$CONFIG" \ "$DAEMON_BIN" "${DAEMON_ARGS[@]}" >"$DAEMON_LOG" 2>&1 & DAEMON_PID=$! printf '%s\n' "$DAEMON_PID" >"$DEV_DIR/daemon.pid" @@ -441,10 +441,10 @@ if [[ "$SKIP_PLUGIN_RELOAD" == false ]]; then fi set +e -BT_BIN="$BT_WRAPPER" \ +BRAINTRUST_BT_BIN="$BT_WRAPPER" \ BT_DAEMON_BIN="$DAEMON_BIN" \ -BT_DAEMON_SOCKET="$SOCKET" \ -BT_DAEMON_CONFIG="$CONFIG" \ +BRAINTRUST_DAEMON_SOCKET="$SOCKET" \ +BRAINTRUST_DAEMON_CONFIG="$CONFIG" \ GROK_HOME="$GROK_HOME_DIR" \ GROK_CLAUDE_HOOKS_ENABLED=false \ GROK_CURSOR_HOOKS_ENABLED=false \ diff --git a/src/plugins/opencode/content/src/tools/bt-cli.ts b/src/plugins/opencode/content/src/tools/bt-cli.ts index 3b1795f5..1a24a333 100644 --- a/src/plugins/opencode/content/src/tools/bt-cli.ts +++ b/src/plugins/opencode/content/src/tools/bt-cli.ts @@ -26,7 +26,8 @@ export interface ToolLogData { export type BtCliRunner = (args: string[]) => Promise; async function defaultRunner(args: string[]): Promise { - const executable = process.env.BT_EXECUTABLE || "bt"; + // BT_EXECUTABLE is the deprecated name of BRAINTRUST_BT_BIN. + const executable = process.env.BRAINTRUST_BT_BIN || process.env.BT_EXECUTABLE || "bt"; try { const { stdout } = await execFileBackground(executable, args, { encoding: "utf8", diff --git a/src/runtime/js-daemon-client/src/index.ts b/src/runtime/js-daemon-client/src/index.ts index 17af57f5..eed4c226 100644 --- a/src/runtime/js-daemon-client/src/index.ts +++ b/src/runtime/js-daemon-client/src/index.ts @@ -133,7 +133,9 @@ export function daemonSocketPath( env: NodeJS.ProcessEnv = process.env, platform: NodeJS.Platform = process.platform, ): string { - if (env.BT_DAEMON_SOCKET) return env.BT_DAEMON_SOCKET + // BT_DAEMON_SOCKET is the deprecated name, still set by older `bt trace run`. + const explicit = env.BRAINTRUST_DAEMON_SOCKET || env.BT_DAEMON_SOCKET + if (explicit) return explicit if (platform === "win32") { const identity = `${env.USERDOMAIN ?? ""}\\${env.USERNAME ?? ""}` const suffix = createHash("sha256").update(identity).digest("hex").slice(0, 16) diff --git a/src/runtime/js-daemon-client/tests/client.test.ts b/src/runtime/js-daemon-client/tests/client.test.ts index 1e552aa2..1583917b 100644 --- a/src/runtime/js-daemon-client/tests/client.test.ts +++ b/src/runtime/js-daemon-client/tests/client.test.ts @@ -29,7 +29,18 @@ describe("managed adapter claims", () => { describe("daemonSocketPath", () => { test("prefers the explicit environment override", () => { - assert.equal(daemonSocketPath({ BT_DAEMON_SOCKET: "/tmp/custom.sock" }), "/tmp/custom.sock") + assert.equal( + daemonSocketPath({ BRAINTRUST_DAEMON_SOCKET: "/tmp/custom.sock" }), + "/tmp/custom.sock", + ) + }) + + test("still honors the deprecated BT_DAEMON_SOCKET name", () => { + assert.equal(daemonSocketPath({ BT_DAEMON_SOCKET: "/tmp/old.sock" }), "/tmp/old.sock") + assert.equal( + daemonSocketPath({ BRAINTRUST_DAEMON_SOCKET: "/tmp/new.sock", BT_DAEMON_SOCKET: "/tmp/old.sock" }), + "/tmp/new.sock", + ) }) test("matches the Unix runtime-directory contract", () => {