From 63a3a7ff7c78fc491bb938281adf043670c41d70 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Thu, 30 Jul 2026 01:45:21 +0800 Subject: [PATCH 01/19] Embed the coding-agent tracing daemon Signed-off-by: Stephen Belanger --- Cargo.lock | 58 +++++++++++++++++++++- Cargo.toml | 1 + src/daemon.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 7 +++ tests/cli.rs | 20 ++++++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/daemon.rs diff --git a/Cargo.lock b/Cargo.lock index cefa77c6..8eb7bf60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -464,6 +464,34 @@ dependencies = [ "uuid", ] +[[package]] +name = "braintrust-sdk-rust" +version = "0.1.0-alpha.2" +source = "git+https://github.com/braintrustdata/braintrust-sdk-rust?rev=d33e806bf6ab9548d37355f6a5098a971ef150aa#d33e806bf6ab9548d37355f6a5098a971ef150aa" +dependencies = [ + "anyhow", + "arc-swap", + "async-trait", + "backoff", + "base64 0.22.1", + "bon", + "bytes", + "chrono", + "crossbeam", + "futures", + "indexmap 2.13.0", + "regex", + "reqwest", + "serde", + "serde_json 1.0.149", + "serde_repr", + "thiserror 1.0.69", + "tokio", + "tracing", + "url", + "uuid", +] + [[package]] name = "brotli" version = "8.0.2" @@ -505,7 +533,8 @@ dependencies = [ "assert_cmd", "backoff", "base64 0.22.1", - "braintrust-sdk-rust", + "braintrust-sdk-rust 0.1.0-alpha.2 (git+https://github.com/braintrustdata/braintrust-sdk-rust?rev=43ba73edbf5220b57090e049feb094b60a92fcd4)", + "bt-daemon", "chrono", "clap", "comfy-table", @@ -542,6 +571,26 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "bt-daemon" +version = "0.1.0" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=03928b90a02b04475d14d90c9640e4992a27df3d#03928b90a02b04475d14d90c9640e4992a27df3d" +dependencies = [ + "anyhow", + "async-trait", + "braintrust-sdk-rust 0.1.0-alpha.2 (git+https://github.com/braintrustdata/braintrust-sdk-rust?rev=d33e806bf6ab9548d37355f6a5098a971ef150aa)", + "chrono", + "clap", + "regex", + "serde", + "serde_json 1.0.149", + "sha2", + "thiserror 2.0.18", + "tokio", + "tracing", + "uuid", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -2894,6 +2943,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "sha2" version = "0.10.9" @@ -3512,6 +3567,7 @@ dependencies = [ "getrandom 0.4.1", "js-sys", "serde_core", + "sha1_smol", "wasm-bindgen", ] diff --git a/Cargo.toml b/Cargo.toml index a7da2549..d5390888 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "03928b90a02b04475d14d90c9640e4992a27df3d" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" diff --git a/src/daemon.rs b/src/daemon.rs new file mode 100644 index 00000000..e9f5afe4 --- /dev/null +++ b/src/daemon.rs @@ -0,0 +1,130 @@ +//! `bt daemon` — embeds the `bt-daemon` tracing daemon as subcommands. +//! +//! The daemon library is credential-passive: it receives a resolved +//! `BackendAuth` with each session's config. Here `bt` fills that from its own +//! `resolve_auth` (profiles / OAuth refresh / keychain), so a `bt daemon hook` +//! invocation traces to whatever profile the user is on. See +//! `../plugin-monorepo/bt-daemon/DESIGN.md` ("Dual consumption", auth handoff). + +use std::ffi::OsString; +use std::sync::Arc; + +use clap::{Args, Subcommand}; + +use bt_daemon::wire::{BackendAuth, FlushMode, SessionConfig}; +use bt_daemon::{ + braintrust_serve_options, paths, run_hook, run_replay, run_serve, run_status, + BraintrustSinkConfig, DebugSinkFactory, HookArgs, HostInfo, Registry, ReplayArgs, ServeArgs, + ServeOptions, StatusArgs, +}; + +use crate::args::BaseArgs; + +#[derive(Debug, Clone, Args)] +pub struct DaemonArgs { + #[command(subcommand)] + command: DaemonCommand, +} + +#[derive(Debug, Clone, Subcommand)] +enum DaemonCommand { + /// Run the tracing daemon (foreground). + Serve(ServeArgs), + /// Forward one coding-agent hook event (read from stdin) to the daemon. + Hook(HookArgs), + /// Print daemon/session status. + Status(StatusArgs), + /// Replay a journal file through the translators + sink. + Replay(ReplayArgs), +} + +/// How the shim (re)launches the daemon: `bt daemon serve` from this same +/// binary. +fn host_info() -> HostInfo { + let exe = std::env::current_exe() + .map(OsString::from) + .unwrap_or_else(|_| OsString::from("bt")); + HostInfo { + serve_argv: vec![exe, OsString::from("daemon"), OsString::from("serve")], + version: crate::CLI_VERSION.to_string(), + } +} + +/// Production serve options: real agent translators + the Braintrust sink. +/// Per-session backend URLs arrive with each event's config (bt resolves them +/// per profile), so no daemon-level defaults are set here. +fn serve_options() -> ServeOptions { + let cfg = BraintrustSinkConfig { + api_url: None, + app_url: None, + version: crate::CLI_VERSION.to_string(), + }; + braintrust_serve_options( + crate::CLI_VERSION, + cfg, + Arc::new(Registry::default_agents()), + ) +} + +/// Resolve `bt`'s auth into the daemon's per-session config. +async fn session_config(base: &BaseArgs) -> anyhow::Result { + let auth = crate::auth::resolve_auth(base) + .await + .map_err(|e| anyhow::anyhow!("resolve auth: {e}"))?; + Ok(SessionConfig { + auth: BackendAuth { + token: auth.api_key.unwrap_or_default(), + api_url: auth.api_url, + app_url: auth.app_url, + org_name: auth.org_name, + org_id: None, + }, + project: base.project.clone(), + parent_span_id: None, + root_span_id: None, + flush_mode: FlushMode::FireAndForget, + additional_metadata: None, + }) +} + +pub async fn run(base: BaseArgs, args: DaemonArgs) -> anyhow::Result<()> { + match args.command { + DaemonCommand::Serve(serve_args) => run_serve(serve_args, serve_options()).await, + DaemonCommand::Hook(hook_args) => { + // A hook must NEVER fail the agent's turn. Resolve auth and forward; + // log and swallow any error, exit 0. + match session_config(&base).await { + Ok(config) => { + if let Err(e) = run_hook(hook_args, config, host_info()).await { + eprintln!("bt daemon hook (non-fatal): {e}"); + } + } + Err(e) => eprintln!("bt daemon hook (non-fatal): {e}"), + } + Ok(()) + } + DaemonCommand::Status(status_args) => match run_status(status_args).await? { + Some(status) => { + println!("{}", serde_json::to_string_pretty(&status)?); + Ok(()) + } + None => { + println!("bt-daemon is not running"); + Ok(()) + } + }, + DaemonCommand::Replay(replay_args) => { + // Replay through the real translators into the debug sink (no + // network): useful for inspecting what a journal produces. + let data_dir = paths::data_dir(None); + let opts = ServeOptions { + version: crate::CLI_VERSION.to_string(), + translators: Arc::new(Registry::default_agents()), + sink_factory: Arc::new(DebugSinkFactory { + dir: data_dir.join("spans"), + }), + }; + run_replay(replay_args, opts).await + } + } +} diff --git a/src/main.rs b/src/main.rs index 5f12e60f..1a8bdb6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod args; mod auth; #[allow(dead_code)] mod config; +mod daemon; mod datasets; mod env; #[cfg(unix)] @@ -79,6 +80,7 @@ Data & evaluation Additional docs Manage workflow docs for coding agents + daemon Run the coding-agent tracing daemon setup Configure Braintrust setup flows status Show current org and project context update Update bt in-place @@ -165,6 +167,8 @@ enum Commands { Switch(CLIArgs), /// Show current org and project context Status(CLIArgs), + /// Run the coding-agent tracing daemon (serve/hook/status/replay) + Daemon(CLIArgs), // /// View and modify config // Config(CLIArgs), } @@ -194,6 +198,7 @@ impl Commands { Commands::Util(cmd) => &cmd.base, Commands::Switch(cmd) => &cmd.base, Commands::Status(cmd) => &cmd.base, + Commands::Daemon(cmd) => &cmd.base, } } @@ -221,6 +226,7 @@ impl Commands { Commands::Util(cmd) => &mut cmd.base, Commands::Switch(cmd) => &mut cmd.base, Commands::Status(cmd) => &mut cmd.base, + Commands::Daemon(cmd) => &mut cmd.base, } } @@ -337,6 +343,7 @@ fn try_main() -> Result<()> { Commands::SelfCommand(cmd) => self_update::run(cmd.base, cmd.args).await?, Commands::Switch(cmd) => switch::run(cmd.base, cmd.args).await?, Commands::Status(cmd) => status::run(cmd.base, cmd.args).await?, + Commands::Daemon(cmd) => daemon::run(cmd.base, cmd.args).await?, } Ok(()) }); diff --git a/tests/cli.rs b/tests/cli.rs index acb09bfd..27b9de8e 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -109,6 +109,26 @@ fn top_level_help_shows_update_not_self() { .stdout(predicate::str::contains("self Self-management commands").not()); } +#[test] +fn daemon_help_exposes_embedded_tracing_commands() { + bt_command() + .args(["daemon", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("serve")) + .stdout(predicate::str::contains("hook")) + .stdout(predicate::str::contains("status")) + .stdout(predicate::str::contains("replay")); + + bt_command() + .args(["daemon", "hook", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--source")) + .stdout(predicate::str::contains("--flush-on-turn-end")) + .stdout(predicate::str::contains("--experiment-id")); +} + #[test] fn topics_report_help_accepts_global_org_short_conflict_free() { bt_command() From 6780c1de259f343aa81bab061fee93cfab62b934 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Thu, 30 Jul 2026 21:52:17 +0800 Subject: [PATCH 02/19] Rename daemon commands to agents Signed-off-by: Stephen Belanger --- src/{daemon.rs => agents.rs} | 30 +++++++++++++++--------------- src/main.rs | 14 +++++++------- tests/cli.rs | 19 +++++++++++++++---- 3 files changed, 37 insertions(+), 26 deletions(-) rename src/{daemon.rs => agents.rs} (83%) diff --git a/src/daemon.rs b/src/agents.rs similarity index 83% rename from src/daemon.rs rename to src/agents.rs index e9f5afe4..d5774789 100644 --- a/src/daemon.rs +++ b/src/agents.rs @@ -1,8 +1,8 @@ -//! `bt daemon` — embeds the `bt-daemon` tracing daemon as subcommands. +//! `bt agents` — manages coding-agent tracing integrations. //! //! The daemon library is credential-passive: it receives a resolved //! `BackendAuth` with each session's config. Here `bt` fills that from its own -//! `resolve_auth` (profiles / OAuth refresh / keychain), so a `bt daemon hook` +//! `resolve_auth` (profiles / OAuth refresh / keychain), so a `bt agents hook` //! invocation traces to whatever profile the user is on. See //! `../plugin-monorepo/bt-daemon/DESIGN.md` ("Dual consumption", auth handoff). @@ -21,15 +21,15 @@ use bt_daemon::{ use crate::args::BaseArgs; #[derive(Debug, Clone, Args)] -pub struct DaemonArgs { +pub struct AgentsArgs { #[command(subcommand)] - command: DaemonCommand, + command: AgentsCommand, } #[derive(Debug, Clone, Subcommand)] -enum DaemonCommand { +enum AgentsCommand { /// Run the tracing daemon (foreground). - Serve(ServeArgs), + Daemon(ServeArgs), /// Forward one coding-agent hook event (read from stdin) to the daemon. Hook(HookArgs), /// Print daemon/session status. @@ -38,14 +38,14 @@ enum DaemonCommand { Replay(ReplayArgs), } -/// How the shim (re)launches the daemon: `bt daemon serve` from this same +/// How the shim (re)launches the daemon: `bt agents daemon` from this same /// binary. fn host_info() -> HostInfo { let exe = std::env::current_exe() .map(OsString::from) .unwrap_or_else(|_| OsString::from("bt")); HostInfo { - serve_argv: vec![exe, OsString::from("daemon"), OsString::from("serve")], + serve_argv: vec![exe, OsString::from("agents"), OsString::from("daemon")], version: crate::CLI_VERSION.to_string(), } } @@ -87,23 +87,23 @@ async fn session_config(base: &BaseArgs) -> anyhow::Result { }) } -pub async fn run(base: BaseArgs, args: DaemonArgs) -> anyhow::Result<()> { +pub async fn run(base: BaseArgs, args: AgentsArgs) -> anyhow::Result<()> { match args.command { - DaemonCommand::Serve(serve_args) => run_serve(serve_args, serve_options()).await, - DaemonCommand::Hook(hook_args) => { + AgentsCommand::Daemon(serve_args) => run_serve(serve_args, serve_options()).await, + AgentsCommand::Hook(hook_args) => { // A hook must NEVER fail the agent's turn. Resolve auth and forward; // log and swallow any error, exit 0. match session_config(&base).await { Ok(config) => { if let Err(e) = run_hook(hook_args, config, host_info()).await { - eprintln!("bt daemon hook (non-fatal): {e}"); + eprintln!("bt agents hook (non-fatal): {e}"); } } - Err(e) => eprintln!("bt daemon hook (non-fatal): {e}"), + Err(e) => eprintln!("bt agents hook (non-fatal): {e}"), } Ok(()) } - DaemonCommand::Status(status_args) => match run_status(status_args).await? { + AgentsCommand::Status(status_args) => match run_status(status_args).await? { Some(status) => { println!("{}", serde_json::to_string_pretty(&status)?); Ok(()) @@ -113,7 +113,7 @@ pub async fn run(base: BaseArgs, args: DaemonArgs) -> anyhow::Result<()> { Ok(()) } }, - DaemonCommand::Replay(replay_args) => { + AgentsCommand::Replay(replay_args) => { // Replay through the real translators into the debug sink (no // network): useful for inspecting what a journal produces. let data_dir = paths::data_dir(None); diff --git a/src/main.rs b/src/main.rs index 1a8bdb6c..11ee2026 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,11 @@ use anyhow::{Context, Result}; use clap::{parser::ValueSource, ArgMatches, CommandFactory, FromArgMatches, Parser, Subcommand}; use std::ffi::{OsStr, OsString}; +mod agents; mod args; mod auth; #[allow(dead_code)] mod config; -mod daemon; mod datasets; mod env; #[cfg(unix)] @@ -80,7 +80,7 @@ Data & evaluation Additional docs Manage workflow docs for coding agents - daemon Run the coding-agent tracing daemon + agents Manage coding-agent integrations setup Configure Braintrust setup flows status Show current org and project context update Update bt in-place @@ -167,8 +167,8 @@ enum Commands { Switch(CLIArgs), /// Show current org and project context Status(CLIArgs), - /// Run the coding-agent tracing daemon (serve/hook/status/replay) - Daemon(CLIArgs), + /// Manage coding-agent integrations (daemon/hook/status/replay) + Agents(CLIArgs), // /// View and modify config // Config(CLIArgs), } @@ -198,7 +198,7 @@ impl Commands { Commands::Util(cmd) => &cmd.base, Commands::Switch(cmd) => &cmd.base, Commands::Status(cmd) => &cmd.base, - Commands::Daemon(cmd) => &cmd.base, + Commands::Agents(cmd) => &cmd.base, } } @@ -226,7 +226,7 @@ impl Commands { Commands::Util(cmd) => &mut cmd.base, Commands::Switch(cmd) => &mut cmd.base, Commands::Status(cmd) => &mut cmd.base, - Commands::Daemon(cmd) => &mut cmd.base, + Commands::Agents(cmd) => &mut cmd.base, } } @@ -343,7 +343,7 @@ fn try_main() -> Result<()> { Commands::SelfCommand(cmd) => self_update::run(cmd.base, cmd.args).await?, Commands::Switch(cmd) => switch::run(cmd.base, cmd.args).await?, Commands::Status(cmd) => status::run(cmd.base, cmd.args).await?, - Commands::Daemon(cmd) => daemon::run(cmd.base, cmd.args).await?, + Commands::Agents(cmd) => agents::run(cmd.base, cmd.args).await?, } Ok(()) }); diff --git a/tests/cli.rs b/tests/cli.rs index 27b9de8e..079b4322 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -110,18 +110,29 @@ fn top_level_help_shows_update_not_self() { } #[test] -fn daemon_help_exposes_embedded_tracing_commands() { +fn agents_help_exposes_embedded_tracing_commands() { + bt_command().args(["daemon", "--help"]).assert().failure(); + bt_command() - .args(["daemon", "--help"]) + .args(["agents", "--help"]) .assert() .success() - .stdout(predicate::str::contains("serve")) + .stdout(predicate::str::contains("daemon")) + .stdout(predicate::str::contains("serve").not()) .stdout(predicate::str::contains("hook")) .stdout(predicate::str::contains("status")) .stdout(predicate::str::contains("replay")); bt_command() - .args(["daemon", "hook", "--help"]) + .args(["agents", "daemon", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("Run the tracing daemon")) + .stdout(predicate::str::contains("--socket")) + .stdout(predicate::str::contains("--idle-timeout-secs")); + + bt_command() + .args(["agents", "hook", "--help"]) .assert() .success() .stdout(predicate::str::contains("--source")) From 41e1a092fe4cc30acbe114bffd68cf92df3286f5 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Thu, 30 Jul 2026 22:47:20 +0800 Subject: [PATCH 03/19] Add coding agent plugin setup commands Add bt agents setup codex and bt agents setup claude. The commands use each agent's plugin manager to install the currently published Braintrust tracing plugin without configuring the unreleased embedded daemon. Signed-off-by: Stephen Belanger --- src/agents.rs | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/cli.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) diff --git a/src/agents.rs b/src/agents.rs index d5774789..469255a1 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -7,9 +7,12 @@ //! `../plugin-monorepo/bt-daemon/DESIGN.md` ("Dual consumption", auth handoff). use std::ffi::OsString; +use std::process::Command; use std::sync::Arc; +use anyhow::{bail, Context}; use clap::{Args, Subcommand}; +use serde_json::Value; use bt_daemon::wire::{BackendAuth, FlushMode, SessionConfig}; use bt_daemon::{ @@ -28,6 +31,8 @@ pub struct AgentsArgs { #[derive(Debug, Clone, Subcommand)] enum AgentsCommand { + /// Install the published Braintrust tracing plugin for a coding agent. + Setup(SetupArgs), /// Run the tracing daemon (foreground). Daemon(ServeArgs), /// Forward one coding-agent hook event (read from stdin) to the daemon. @@ -38,6 +43,27 @@ enum AgentsCommand { Replay(ReplayArgs), } +#[derive(Debug, Clone, Args)] +struct SetupArgs { + #[command(subcommand)] + agent: SetupAgent, +} + +#[derive(Debug, Clone, Copy, Subcommand)] +enum SetupAgent { + /// Install the published Codex tracing plugin. + Codex, + /// Install the published Claude Code tracing plugin. + Claude, +} + +const CODEX_MARKETPLACE: &str = "braintrust-codex-plugins"; +const CODEX_MARKETPLACE_SOURCE: &str = "braintrustdata/braintrust-codex-plugin"; +const CODEX_PLUGIN: &str = "trace-codex@braintrust-codex-plugins"; +const CLAUDE_MARKETPLACE: &str = "braintrust-claude-plugin"; +const CLAUDE_MARKETPLACE_SOURCE: &str = "braintrustdata/braintrust-claude-plugin"; +const CLAUDE_PLUGIN: &str = "trace-claude-code@braintrust-claude-plugin"; + /// How the shim (re)launches the daemon: `bt agents daemon` from this same /// binary. fn host_info() -> HostInfo { @@ -66,6 +92,117 @@ fn serve_options() -> ServeOptions { ) } +fn command_json(program: &str, args: &[&str]) -> anyhow::Result { + let output = Command::new(program).args(args).output().with_context(|| { + format!("failed to run `{program}`; install {program} and ensure it is on PATH") + })?; + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + bail!("`{program} {}` failed: {}", args.join(" "), stderr.trim()); + } + serde_json::from_slice(&output.stdout) + .with_context(|| format!("`{program} {}` returned invalid JSON", args.join(" "))) +} + +fn run_command(program: &str, args: &[&str]) -> anyhow::Result<()> { + let status = Command::new(program).args(args).status().with_context(|| { + format!("failed to run `{program}`; install {program} and ensure it is on PATH") + })?; + if !status.success() { + bail!("`{program} {}` failed with {status}", args.join(" ")); + } + Ok(()) +} + +fn codex_marketplace_installed(value: &Value) -> bool { + value + .get("marketplaces") + .and_then(Value::as_array) + .is_some_and(|items| { + items + .iter() + .any(|item| item.get("name").and_then(Value::as_str) == Some(CODEX_MARKETPLACE)) + }) +} + +fn codex_plugin_installed(value: &Value) -> bool { + value + .get("installed") + .and_then(Value::as_array) + .is_some_and(|items| { + items + .iter() + .any(|item| item.get("pluginId").and_then(Value::as_str) == Some(CODEX_PLUGIN)) + }) +} + +fn claude_marketplace_installed(value: &Value) -> bool { + value.as_array().is_some_and(|items| { + items + .iter() + .any(|item| item.get("name").and_then(Value::as_str) == Some(CLAUDE_MARKETPLACE)) + }) +} + +fn claude_plugin(value: &Value) -> Option<&Value> { + value + .as_array()? + .iter() + .find(|item| item.get("id").and_then(Value::as_str) == Some(CLAUDE_PLUGIN)) +} + +fn setup_codex() -> anyhow::Result<()> { + let marketplaces = command_json("codex", &["plugin", "marketplace", "list", "--json"])?; + if !codex_marketplace_installed(&marketplaces) { + run_command( + "codex", + &["plugin", "marketplace", "add", CODEX_MARKETPLACE_SOURCE], + )?; + } + + let plugins = command_json("codex", &["plugin", "list", "--json"])?; + if !codex_plugin_installed(&plugins) { + run_command("codex", &["plugin", "add", CODEX_PLUGIN])?; + } + Ok(()) +} + +fn setup_claude() -> anyhow::Result<()> { + let marketplaces = command_json("claude", &["plugin", "marketplace", "list", "--json"])?; + if !claude_marketplace_installed(&marketplaces) { + run_command( + "claude", + &["plugin", "marketplace", "add", CLAUDE_MARKETPLACE_SOURCE], + )?; + } + + let plugins = command_json("claude", &["plugin", "list", "--json"])?; + match claude_plugin(&plugins) { + None => run_command("claude", &["plugin", "install", CLAUDE_PLUGIN])?, + Some(plugin) if plugin.get("enabled").and_then(Value::as_bool) == Some(false) => { + run_command("claude", &["plugin", "enable", CLAUDE_PLUGIN])?; + } + Some(_) => {} + } + Ok(()) +} + +fn run_setup(args: SetupArgs) -> anyhow::Result<()> { + match args.agent { + SetupAgent::Codex => setup_codex()?, + SetupAgent::Claude => setup_claude()?, + } + println!( + "The Braintrust tracing plugin is installed for {}.", + match args.agent { + SetupAgent::Codex => "Codex", + SetupAgent::Claude => "Claude Code", + } + ); + println!("Restart the coding agent to load the tracing plugin."); + Ok(()) +} + /// Resolve `bt`'s auth into the daemon's per-session config. async fn session_config(base: &BaseArgs) -> anyhow::Result { let auth = crate::auth::resolve_auth(base) @@ -89,6 +226,7 @@ async fn session_config(base: &BaseArgs) -> anyhow::Result { pub async fn run(base: BaseArgs, args: AgentsArgs) -> anyhow::Result<()> { match args.command { + AgentsCommand::Setup(setup_args) => run_setup(setup_args), AgentsCommand::Daemon(serve_args) => run_serve(serve_args, serve_options()).await, AgentsCommand::Hook(hook_args) => { // A hook must NEVER fail the agent's turn. Resolve auth and forward; diff --git a/tests/cli.rs b/tests/cli.rs index 079b4322..444fac0a 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -29,6 +29,28 @@ fn write_executable(path: &Path) { } } +#[cfg(unix)] +fn write_agent_cli(path: &Path, marketplace_json: &str, plugin_json: &str) { + let script = format!( + r#"#!/bin/sh +printf '%s\n' "$*" >> "$AGENT_SETUP_LOG" +case "$*" in + "plugin marketplace list --json") + printf '%s\n' '{marketplace_json}' + ;; + "plugin list --json") + printf '%s\n' '{plugin_json}' + ;; +esac +"# + ); + fs::write(path, script).expect("write fake agent CLI"); + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(path).expect("metadata").permissions(); + perms.set_mode(0o755); + fs::set_permissions(path, perms).expect("chmod"); +} + fn make_git_repo() -> tempfile::TempDir { let dir = tempfile::tempdir().expect("tempdir"); fs::write(dir.path().join(".git"), "gitdir: /tmp/fake").expect("write .git"); @@ -117,6 +139,7 @@ fn agents_help_exposes_embedded_tracing_commands() { .args(["agents", "--help"]) .assert() .success() + .stdout(predicate::str::contains("setup")) .stdout(predicate::str::contains("daemon")) .stdout(predicate::str::contains("serve").not()) .stdout(predicate::str::contains("hook")) @@ -138,6 +161,106 @@ fn agents_help_exposes_embedded_tracing_commands() { .stdout(predicate::str::contains("--source")) .stdout(predicate::str::contains("--flush-on-turn-end")) .stdout(predicate::str::contains("--experiment-id")); + + bt_command() + .args(["agents", "setup", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("codex")) + .stdout(predicate::str::contains("claude")); +} + +#[cfg(unix)] +#[test] +fn agents_setup_codex_installs_the_published_plugin_only() { + let home = tempfile::tempdir().expect("home tempdir"); + let bin_dir = tempfile::tempdir().expect("bin tempdir"); + let state_dir = tempfile::tempdir().expect("state tempdir"); + let log = state_dir.path().join("codex.log"); + let config = state_dir.path().join("config.json"); + write_agent_cli( + &bin_dir.path().join("codex"), + r#"{"marketplaces":[]}"#, + r#"{"installed":[]}"#, + ); + + bt_command() + .env("HOME", home.path()) + .env("PATH", bin_dir.path()) + .env("AGENT_SETUP_LOG", &log) + .env("BT_DAEMON_CONFIG", &config) + .args(["agents", "setup", "codex"]) + .assert() + .success() + .stdout(predicate::str::contains( + "The Braintrust tracing plugin is installed for Codex", + )); + + let calls = fs::read_to_string(log).expect("read fake CLI calls"); + assert!(calls.contains("plugin marketplace add braintrustdata/braintrust-codex-plugin")); + assert!(calls.contains("plugin add trace-codex@braintrust-codex-plugins")); + assert!( + !config.exists(), + "setup must not configure the unreleased daemon" + ); +} + +#[cfg(unix)] +#[test] +fn agents_setup_claude_installs_the_published_plugin_only() { + let home = tempfile::tempdir().expect("home tempdir"); + let bin_dir = tempfile::tempdir().expect("bin tempdir"); + let state_dir = tempfile::tempdir().expect("state tempdir"); + let log = state_dir.path().join("claude.log"); + let config = state_dir.path().join("config.json"); + write_agent_cli(&bin_dir.path().join("claude"), "[]", "[]"); + + bt_command() + .env("HOME", home.path()) + .env("PATH", bin_dir.path()) + .env("AGENT_SETUP_LOG", &log) + .env("BT_DAEMON_CONFIG", &config) + .args(["agents", "setup", "claude"]) + .assert() + .success() + .stdout(predicate::str::contains( + "The Braintrust tracing plugin is installed for Claude Code", + )); + + let calls = fs::read_to_string(log).expect("read fake CLI calls"); + assert!(calls.contains("plugin marketplace add braintrustdata/braintrust-claude-plugin")); + assert!(calls.contains("plugin install trace-claude-code@braintrust-claude-plugin")); + assert!( + !config.exists(), + "setup must not configure the unreleased daemon" + ); +} + +#[cfg(unix)] +#[test] +fn agents_setup_claude_enables_an_existing_disabled_plugin() { + let home = tempfile::tempdir().expect("home tempdir"); + let bin_dir = tempfile::tempdir().expect("bin tempdir"); + let state_dir = tempfile::tempdir().expect("state tempdir"); + let log = state_dir.path().join("claude.log"); + write_agent_cli( + &bin_dir.path().join("claude"), + r#"[{"name":"braintrust-claude-plugin"}]"#, + r#"[{"id":"trace-claude-code@braintrust-claude-plugin","enabled":false}]"#, + ); + + bt_command() + .env("HOME", home.path()) + .env("PATH", bin_dir.path()) + .env("AGENT_SETUP_LOG", &log) + .args(["agents", "setup", "claude"]) + .assert() + .success(); + + let calls = fs::read_to_string(log).expect("read fake CLI calls"); + assert!(calls.contains("plugin enable trace-claude-code@braintrust-claude-plugin")); + assert!(!calls.contains("plugin marketplace add")); + assert!(!calls.contains("plugin install")); } #[test] From 3026fe55e6f9998dbd6e3f5e7a4bec9392397019 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Thu, 30 Jul 2026 23:00:14 +0800 Subject: [PATCH 04/19] Preserve agent settings during plugin setup Create the shared tracing settings file when missing and allow --project to select the trace project. Existing settings, including legacy authentication fields, are preserved unchanged so current published plugins continue to work. Signed-off-by: Stephen Belanger --- src/agents.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++----- tests/cli.rs | 40 ++++++++++++++++++++++++---------- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/agents.rs b/src/agents.rs index 469255a1..aedebf00 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -7,12 +7,13 @@ //! `../plugin-monorepo/bt-daemon/DESIGN.md` ("Dual consumption", auth handoff). use std::ffi::OsString; +use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Arc; use anyhow::{bail, Context}; use clap::{Args, Subcommand}; -use serde_json::Value; +use serde_json::{Map, Value}; use bt_daemon::wire::{BackendAuth, FlushMode, SessionConfig}; use bt_daemon::{ @@ -187,17 +188,65 @@ fn setup_claude() -> anyhow::Result<()> { Ok(()) } -fn run_setup(args: SetupArgs) -> anyhow::Result<()> { +fn load_settings(path: &Path) -> anyhow::Result> { + match std::fs::read(path) { + Ok(raw) => { + let value: Value = serde_json::from_slice(&raw) + .with_context(|| format!("invalid shared agent settings: {}", path.display()))?; + value.as_object().cloned().ok_or_else(|| { + anyhow::anyhow!( + "shared agent settings must be a JSON object: {}", + path.display() + ) + }) + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(Map::new()), + Err(error) => Err(error) + .with_context(|| format!("failed to read shared agent settings: {}", path.display())), + } +} + +fn enable_tracing(project: Option<&str>) -> anyhow::Result { + let path = paths::settings_path(None); + let mut settings = load_settings(&path)?; + settings.insert("traceToBraintrust".into(), Value::Bool(true)); + + let existing_project = settings + .get("project") + .and_then(Value::as_str) + .filter(|project| !project.is_empty()); + let project = project + .filter(|project| !project.is_empty()) + .or(existing_project) + .unwrap_or("coding-agents"); + settings.insert("project".into(), Value::String(project.to_string())); + + let mut encoded = serde_json::to_string_pretty(&Value::Object(settings))?; + encoded.push('\n'); + crate::utils::write_text_atomic(&path, &encoded)?; + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600)) + .with_context(|| format!("failed to protect shared settings: {}", path.display()))?; + } + Ok(path) +} + +fn run_setup(base: &BaseArgs, args: SetupArgs) -> anyhow::Result<()> { match args.agent { SetupAgent::Codex => setup_codex()?, SetupAgent::Claude => setup_claude()?, } + let settings_path = enable_tracing(base.project.as_deref())?; println!( - "The Braintrust tracing plugin is installed for {}.", + "The Braintrust tracing plugin is installed for {} and configured in {}.", match args.agent { SetupAgent::Codex => "Codex", SetupAgent::Claude => "Claude Code", - } + }, + settings_path.display() ); println!("Restart the coding agent to load the tracing plugin."); Ok(()) @@ -226,7 +275,7 @@ async fn session_config(base: &BaseArgs) -> anyhow::Result { pub async fn run(base: BaseArgs, args: AgentsArgs) -> anyhow::Result<()> { match args.command { - AgentsCommand::Setup(setup_args) => run_setup(setup_args), + AgentsCommand::Setup(setup_args) => run_setup(&base, setup_args), AgentsCommand::Daemon(serve_args) => run_serve(serve_args, serve_options()).await, AgentsCommand::Hook(hook_args) => { // A hook must NEVER fail the agent's turn. Resolve auth and forward; diff --git a/tests/cli.rs b/tests/cli.rs index 444fac0a..48163e7c 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -172,7 +172,7 @@ fn agents_help_exposes_embedded_tracing_commands() { #[cfg(unix)] #[test] -fn agents_setup_codex_installs_the_published_plugin_only() { +fn agents_setup_codex_installs_plugin_and_preserves_existing_settings() { let home = tempfile::tempdir().expect("home tempdir"); let bin_dir = tempfile::tempdir().expect("bin tempdir"); let state_dir = tempfile::tempdir().expect("state tempdir"); @@ -183,13 +183,24 @@ fn agents_setup_codex_installs_the_published_plugin_only() { r#"{"marketplaces":[]}"#, r#"{"installed":[]}"#, ); + fs::write( + &config, + r#"{ + "flushOnTurnEnd": true, + "additionalMetadata": {"team": "sdk"}, + "apiKey": "legacy-secret", + "apiUrl": "https://legacy.example", + "auth": {"type": "legacy"} + }"#, + ) + .expect("seed config"); bt_command() .env("HOME", home.path()) .env("PATH", bin_dir.path()) .env("AGENT_SETUP_LOG", &log) .env("BT_DAEMON_CONFIG", &config) - .args(["agents", "setup", "codex"]) + .args(["agents", "setup", "codex", "--project", "agent-traces"]) .assert() .success() .stdout(predicate::str::contains( @@ -199,15 +210,21 @@ fn agents_setup_codex_installs_the_published_plugin_only() { let calls = fs::read_to_string(log).expect("read fake CLI calls"); assert!(calls.contains("plugin marketplace add braintrustdata/braintrust-codex-plugin")); assert!(calls.contains("plugin add trace-codex@braintrust-codex-plugins")); - assert!( - !config.exists(), - "setup must not configure the unreleased daemon" - ); + + let settings: serde_json::Value = + serde_json::from_slice(&fs::read(config).expect("read config")).expect("parse config"); + assert_eq!(settings["traceToBraintrust"], true); + assert_eq!(settings["project"], "agent-traces"); + assert_eq!(settings["flushOnTurnEnd"], true); + assert_eq!(settings["additionalMetadata"]["team"], "sdk"); + assert_eq!(settings["apiKey"], "legacy-secret"); + assert_eq!(settings["apiUrl"], "https://legacy.example"); + assert_eq!(settings["auth"]["type"], "legacy"); } #[cfg(unix)] #[test] -fn agents_setup_claude_installs_the_published_plugin_only() { +fn agents_setup_claude_installs_plugin_and_creates_default_settings() { let home = tempfile::tempdir().expect("home tempdir"); let bin_dir = tempfile::tempdir().expect("bin tempdir"); let state_dir = tempfile::tempdir().expect("state tempdir"); @@ -230,10 +247,11 @@ fn agents_setup_claude_installs_the_published_plugin_only() { let calls = fs::read_to_string(log).expect("read fake CLI calls"); assert!(calls.contains("plugin marketplace add braintrustdata/braintrust-claude-plugin")); assert!(calls.contains("plugin install trace-claude-code@braintrust-claude-plugin")); - assert!( - !config.exists(), - "setup must not configure the unreleased daemon" - ); + + let settings: serde_json::Value = + serde_json::from_slice(&fs::read(config).expect("read config")).expect("parse config"); + assert_eq!(settings["traceToBraintrust"], true); + assert_eq!(settings["project"], "coding-agents"); } #[cfg(unix)] From 3298cdf52986b9b1f4094962c691bc979dd4ea3d Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Thu, 30 Jul 2026 23:11:05 +0800 Subject: [PATCH 05/19] Repin daemon after plugin rollout split Point bt at the daemon PR commit that restores src to main, keeping the existing published plugin runtimes independent from the daemon-capable CLI release. Signed-off-by: Stephen Belanger --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8eb7bf60..2719ae23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,7 +574,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=03928b90a02b04475d14d90c9640e4992a27df3d#03928b90a02b04475d14d90c9640e4992a27df3d" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=3e019e47290318b1332c81311e64647b964c21b8#3e019e47290318b1332c81311e64647b964c21b8" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index d5390888..a72809d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "03928b90a02b04475d14d90c9640e4992a27df3d" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "3e019e47290318b1332c81311e64647b964c21b8" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" From 20cbcc3b6eef0583b2f6b80dbcc70584cdbd9a42 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 20:46:41 +0800 Subject: [PATCH 06/19] Log daemon traffic and hide internal commands Signed-off-by: Stephen Belanger --- Cargo.lock | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 ++- src/agents.rs | 19 +++++++++++++- src/main.rs | 2 +- tests/cli.rs | 6 ++--- 5 files changed, 95 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2719ae23..d06de19c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,6 +565,7 @@ dependencies = [ "tempfile", "tokio", "toml", + "tracing-subscriber", "unicode-width 0.1.14", "urlencoding", "uuid", @@ -574,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=3e019e47290318b1332c81311e64647b964c21b8#3e019e47290318b1332c81311e64647b964c21b8" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=3ab5195ec8d87edcc5f175d041c7eaec98557033#3ab5195ec8d87edcc5f175d041c7eaec98557033" dependencies = [ "anyhow", "async-trait", @@ -1838,6 +1839,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "leb128fmt" version = "0.1.0" @@ -1961,6 +1968,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.8.0" @@ -2017,6 +2033,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-conv" version = "0.2.0" @@ -2960,6 +2985,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-words" version = "1.1.1" @@ -3432,6 +3466,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -3571,6 +3635,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index a72809d6..6bfc7917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "3e019e47290318b1332c81311e64647b964c21b8" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "3ab5195ec8d87edcc5f175d041c7eaec98557033" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" @@ -33,6 +33,7 @@ toml = "0.8" sha2 = "0.10.8" strip-ansi-escapes = "0.2.0" tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "process", "net", "signal", "sync"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } unicode-width = "0.1.13" dialoguer = { version = "0.11", features = ["fuzzy-select"] } fuzzy-matcher = "0.3" diff --git a/src/agents.rs b/src/agents.rs index aedebf00..fe3d4636 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -35,8 +35,10 @@ enum AgentsCommand { /// Install the published Braintrust tracing plugin for a coding agent. Setup(SetupArgs), /// Run the tracing daemon (foreground). + #[command(hide = true)] Daemon(ServeArgs), /// Forward one coding-agent hook event (read from stdin) to the daemon. + #[command(hide = true)] Hook(HookArgs), /// Print daemon/session status. Status(StatusArgs), @@ -93,6 +95,18 @@ fn serve_options() -> ServeOptions { ) } +fn init_daemon_logging(verbose: bool) { + let fallback = if verbose { "debug" } else { "info" }; + let filter = tracing_subscriber::EnvFilter::new(fallback); + if let Err(error) = tracing_subscriber::fmt() + .with_env_filter(filter) + .with_writer(std::io::stderr) + .try_init() + { + eprintln!("bt agents daemon logging unavailable: {error}"); + } +} + fn command_json(program: &str, args: &[&str]) -> anyhow::Result { let output = Command::new(program).args(args).output().with_context(|| { format!("failed to run `{program}`; install {program} and ensure it is on PATH") @@ -276,7 +290,10 @@ async fn session_config(base: &BaseArgs) -> anyhow::Result { pub async fn run(base: BaseArgs, args: AgentsArgs) -> anyhow::Result<()> { match args.command { AgentsCommand::Setup(setup_args) => run_setup(&base, setup_args), - AgentsCommand::Daemon(serve_args) => run_serve(serve_args, serve_options()).await, + AgentsCommand::Daemon(serve_args) => { + init_daemon_logging(base.verbose); + run_serve(serve_args, serve_options()).await + } AgentsCommand::Hook(hook_args) => { // A hook must NEVER fail the agent's turn. Resolve auth and forward; // log and swallow any error, exit 0. diff --git a/src/main.rs b/src/main.rs index 11ee2026..1e1c5227 100644 --- a/src/main.rs +++ b/src/main.rs @@ -167,7 +167,7 @@ enum Commands { Switch(CLIArgs), /// Show current org and project context Status(CLIArgs), - /// Manage coding-agent integrations (daemon/hook/status/replay) + /// Manage coding-agent integrations Agents(CLIArgs), // /// View and modify config // Config(CLIArgs), diff --git a/tests/cli.rs b/tests/cli.rs index 48163e7c..66b22ca3 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -132,7 +132,7 @@ fn top_level_help_shows_update_not_self() { } #[test] -fn agents_help_exposes_embedded_tracing_commands() { +fn agents_help_hides_internal_commands_but_keeps_them_callable() { bt_command().args(["daemon", "--help"]).assert().failure(); bt_command() @@ -140,9 +140,9 @@ fn agents_help_exposes_embedded_tracing_commands() { .assert() .success() .stdout(predicate::str::contains("setup")) - .stdout(predicate::str::contains("daemon")) + .stdout(predicate::str::contains("\n daemon").not()) .stdout(predicate::str::contains("serve").not()) - .stdout(predicate::str::contains("hook")) + .stdout(predicate::str::contains("\n hook").not()) .stdout(predicate::str::contains("status")) .stdout(predicate::str::contains("replay")); From c23fd3b3521c17a774573f56e3dd40994ac1d66c Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 20:48:29 +0800 Subject: [PATCH 07/19] Hide internal agent maintenance commands Signed-off-by: Stephen Belanger --- src/agents.rs | 2 ++ tests/cli.rs | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/agents.rs b/src/agents.rs index fe3d4636..6a1bb131 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -41,8 +41,10 @@ enum AgentsCommand { #[command(hide = true)] Hook(HookArgs), /// Print daemon/session status. + #[command(hide = true)] Status(StatusArgs), /// Replay a journal file through the translators + sink. + #[command(hide = true)] Replay(ReplayArgs), } diff --git a/tests/cli.rs b/tests/cli.rs index 66b22ca3..e48a3f8c 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -143,8 +143,8 @@ fn agents_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("\n daemon").not()) .stdout(predicate::str::contains("serve").not()) .stdout(predicate::str::contains("\n hook").not()) - .stdout(predicate::str::contains("status")) - .stdout(predicate::str::contains("replay")); + .stdout(predicate::str::contains("\n status").not()) + .stdout(predicate::str::contains("\n replay").not()); bt_command() .args(["agents", "daemon", "--help"]) @@ -162,6 +162,18 @@ fn agents_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("--flush-on-turn-end")) .stdout(predicate::str::contains("--experiment-id")); + bt_command() + .args(["agents", "status", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--socket")); + + bt_command() + .args(["agents", "replay", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("")); + bt_command() .args(["agents", "setup", "--help"]) .assert() From ebe229a5da258f6395683939099dd98b664bd816 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 20:58:51 +0800 Subject: [PATCH 08/19] Rename agents commands to trace Signed-off-by: Stephen Belanger --- src/agents.rs | 32 ++++++++++++++++---------------- src/main.rs | 12 ++++++------ tests/cli.rs | 27 ++++++++++++++------------- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/agents.rs b/src/agents.rs index 6a1bb131..e839f9a9 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -1,8 +1,8 @@ -//! `bt agents` — manages coding-agent tracing integrations. +//! `bt trace` — manages coding-agent tracing integrations. //! //! The daemon library is credential-passive: it receives a resolved //! `BackendAuth` with each session's config. Here `bt` fills that from its own -//! `resolve_auth` (profiles / OAuth refresh / keychain), so a `bt agents hook` +//! `resolve_auth` (profiles / OAuth refresh / keychain), so a `bt trace hook` //! invocation traces to whatever profile the user is on. See //! `../plugin-monorepo/bt-daemon/DESIGN.md` ("Dual consumption", auth handoff). @@ -25,13 +25,13 @@ use bt_daemon::{ use crate::args::BaseArgs; #[derive(Debug, Clone, Args)] -pub struct AgentsArgs { +pub struct TraceArgs { #[command(subcommand)] - command: AgentsCommand, + command: TraceCommand, } #[derive(Debug, Clone, Subcommand)] -enum AgentsCommand { +enum TraceCommand { /// Install the published Braintrust tracing plugin for a coding agent. Setup(SetupArgs), /// Run the tracing daemon (foreground). @@ -69,14 +69,14 @@ const CLAUDE_MARKETPLACE: &str = "braintrust-claude-plugin"; const CLAUDE_MARKETPLACE_SOURCE: &str = "braintrustdata/braintrust-claude-plugin"; const CLAUDE_PLUGIN: &str = "trace-claude-code@braintrust-claude-plugin"; -/// How the shim (re)launches the daemon: `bt agents daemon` from this same +/// How the shim (re)launches the daemon: `bt trace daemon` from this same /// binary. fn host_info() -> HostInfo { let exe = std::env::current_exe() .map(OsString::from) .unwrap_or_else(|_| OsString::from("bt")); HostInfo { - serve_argv: vec![exe, OsString::from("agents"), OsString::from("daemon")], + serve_argv: vec![exe, OsString::from("trace"), OsString::from("daemon")], version: crate::CLI_VERSION.to_string(), } } @@ -105,7 +105,7 @@ fn init_daemon_logging(verbose: bool) { .with_writer(std::io::stderr) .try_init() { - eprintln!("bt agents daemon logging unavailable: {error}"); + eprintln!("bt trace daemon logging unavailable: {error}"); } } @@ -289,27 +289,27 @@ async fn session_config(base: &BaseArgs) -> anyhow::Result { }) } -pub async fn run(base: BaseArgs, args: AgentsArgs) -> anyhow::Result<()> { +pub async fn run(base: BaseArgs, args: TraceArgs) -> anyhow::Result<()> { match args.command { - AgentsCommand::Setup(setup_args) => run_setup(&base, setup_args), - AgentsCommand::Daemon(serve_args) => { + TraceCommand::Setup(setup_args) => run_setup(&base, setup_args), + TraceCommand::Daemon(serve_args) => { init_daemon_logging(base.verbose); run_serve(serve_args, serve_options()).await } - AgentsCommand::Hook(hook_args) => { + TraceCommand::Hook(hook_args) => { // A hook must NEVER fail the agent's turn. Resolve auth and forward; // log and swallow any error, exit 0. match session_config(&base).await { Ok(config) => { if let Err(e) = run_hook(hook_args, config, host_info()).await { - eprintln!("bt agents hook (non-fatal): {e}"); + eprintln!("bt trace hook (non-fatal): {e}"); } } - Err(e) => eprintln!("bt agents hook (non-fatal): {e}"), + Err(e) => eprintln!("bt trace hook (non-fatal): {e}"), } Ok(()) } - AgentsCommand::Status(status_args) => match run_status(status_args).await? { + TraceCommand::Status(status_args) => match run_status(status_args).await? { Some(status) => { println!("{}", serde_json::to_string_pretty(&status)?); Ok(()) @@ -319,7 +319,7 @@ pub async fn run(base: BaseArgs, args: AgentsArgs) -> anyhow::Result<()> { Ok(()) } }, - AgentsCommand::Replay(replay_args) => { + TraceCommand::Replay(replay_args) => { // Replay through the real translators into the debug sink (no // network): useful for inspecting what a journal produces. let data_dir = paths::data_dir(None); diff --git a/src/main.rs b/src/main.rs index 1e1c5227..163d33f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,7 +80,7 @@ Data & evaluation Additional docs Manage workflow docs for coding agents - agents Manage coding-agent integrations + trace Manage coding-agent tracing setup Configure Braintrust setup flows status Show current org and project context update Update bt in-place @@ -167,8 +167,8 @@ enum Commands { Switch(CLIArgs), /// Show current org and project context Status(CLIArgs), - /// Manage coding-agent integrations - Agents(CLIArgs), + /// Manage coding-agent tracing + Trace(CLIArgs), // /// View and modify config // Config(CLIArgs), } @@ -198,7 +198,7 @@ impl Commands { Commands::Util(cmd) => &cmd.base, Commands::Switch(cmd) => &cmd.base, Commands::Status(cmd) => &cmd.base, - Commands::Agents(cmd) => &cmd.base, + Commands::Trace(cmd) => &cmd.base, } } @@ -226,7 +226,7 @@ impl Commands { Commands::Util(cmd) => &mut cmd.base, Commands::Switch(cmd) => &mut cmd.base, Commands::Status(cmd) => &mut cmd.base, - Commands::Agents(cmd) => &mut cmd.base, + Commands::Trace(cmd) => &mut cmd.base, } } @@ -343,7 +343,7 @@ fn try_main() -> Result<()> { Commands::SelfCommand(cmd) => self_update::run(cmd.base, cmd.args).await?, Commands::Switch(cmd) => switch::run(cmd.base, cmd.args).await?, Commands::Status(cmd) => status::run(cmd.base, cmd.args).await?, - Commands::Agents(cmd) => agents::run(cmd.base, cmd.args).await?, + Commands::Trace(cmd) => agents::run(cmd.base, cmd.args).await?, } Ok(()) }); diff --git a/tests/cli.rs b/tests/cli.rs index e48a3f8c..aeb64727 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -132,11 +132,12 @@ fn top_level_help_shows_update_not_self() { } #[test] -fn agents_help_hides_internal_commands_but_keeps_them_callable() { +fn trace_help_hides_internal_commands_but_keeps_them_callable() { bt_command().args(["daemon", "--help"]).assert().failure(); + bt_command().args(["agents", "--help"]).assert().failure(); bt_command() - .args(["agents", "--help"]) + .args(["trace", "--help"]) .assert() .success() .stdout(predicate::str::contains("setup")) @@ -147,7 +148,7 @@ fn agents_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("\n replay").not()); bt_command() - .args(["agents", "daemon", "--help"]) + .args(["trace", "daemon", "--help"]) .assert() .success() .stdout(predicate::str::contains("Run the tracing daemon")) @@ -155,7 +156,7 @@ fn agents_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("--idle-timeout-secs")); bt_command() - .args(["agents", "hook", "--help"]) + .args(["trace", "hook", "--help"]) .assert() .success() .stdout(predicate::str::contains("--source")) @@ -163,19 +164,19 @@ fn agents_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("--experiment-id")); bt_command() - .args(["agents", "status", "--help"]) + .args(["trace", "status", "--help"]) .assert() .success() .stdout(predicate::str::contains("--socket")); bt_command() - .args(["agents", "replay", "--help"]) + .args(["trace", "replay", "--help"]) .assert() .success() .stdout(predicate::str::contains("")); bt_command() - .args(["agents", "setup", "--help"]) + .args(["trace", "setup", "--help"]) .assert() .success() .stdout(predicate::str::contains("codex")) @@ -184,7 +185,7 @@ fn agents_help_hides_internal_commands_but_keeps_them_callable() { #[cfg(unix)] #[test] -fn agents_setup_codex_installs_plugin_and_preserves_existing_settings() { +fn trace_setup_codex_installs_plugin_and_preserves_existing_settings() { let home = tempfile::tempdir().expect("home tempdir"); let bin_dir = tempfile::tempdir().expect("bin tempdir"); let state_dir = tempfile::tempdir().expect("state tempdir"); @@ -212,7 +213,7 @@ fn agents_setup_codex_installs_plugin_and_preserves_existing_settings() { .env("PATH", bin_dir.path()) .env("AGENT_SETUP_LOG", &log) .env("BT_DAEMON_CONFIG", &config) - .args(["agents", "setup", "codex", "--project", "agent-traces"]) + .args(["trace", "setup", "codex", "--project", "agent-traces"]) .assert() .success() .stdout(predicate::str::contains( @@ -236,7 +237,7 @@ fn agents_setup_codex_installs_plugin_and_preserves_existing_settings() { #[cfg(unix)] #[test] -fn agents_setup_claude_installs_plugin_and_creates_default_settings() { +fn trace_setup_claude_installs_plugin_and_creates_default_settings() { let home = tempfile::tempdir().expect("home tempdir"); let bin_dir = tempfile::tempdir().expect("bin tempdir"); let state_dir = tempfile::tempdir().expect("state tempdir"); @@ -249,7 +250,7 @@ fn agents_setup_claude_installs_plugin_and_creates_default_settings() { .env("PATH", bin_dir.path()) .env("AGENT_SETUP_LOG", &log) .env("BT_DAEMON_CONFIG", &config) - .args(["agents", "setup", "claude"]) + .args(["trace", "setup", "claude"]) .assert() .success() .stdout(predicate::str::contains( @@ -268,7 +269,7 @@ fn agents_setup_claude_installs_plugin_and_creates_default_settings() { #[cfg(unix)] #[test] -fn agents_setup_claude_enables_an_existing_disabled_plugin() { +fn trace_setup_claude_enables_an_existing_disabled_plugin() { let home = tempfile::tempdir().expect("home tempdir"); let bin_dir = tempfile::tempdir().expect("bin tempdir"); let state_dir = tempfile::tempdir().expect("state tempdir"); @@ -283,7 +284,7 @@ fn agents_setup_claude_enables_an_existing_disabled_plugin() { .env("HOME", home.path()) .env("PATH", bin_dir.path()) .env("AGENT_SETUP_LOG", &log) - .args(["agents", "setup", "claude"]) + .args(["trace", "setup", "claude"]) .assert() .success(); From bb0a957c1e08aed1d4edf9ad139576752299c85f Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 21:24:23 +0800 Subject: [PATCH 09/19] Add hidden trace stop command Signed-off-by: Stephen Belanger --- src/agents.rs | 26 ++++++++++++++++- tests/cli.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/agents.rs b/src/agents.rs index e839f9a9..86d7d4f0 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -17,7 +17,7 @@ use serde_json::{Map, Value}; use bt_daemon::wire::{BackendAuth, FlushMode, SessionConfig}; use bt_daemon::{ - braintrust_serve_options, paths, run_hook, run_replay, run_serve, run_status, + braintrust_serve_options, paths, run_hook, run_replay, run_serve, run_status, shutdown_daemon, BraintrustSinkConfig, DebugSinkFactory, HookArgs, HostInfo, Registry, ReplayArgs, ServeArgs, ServeOptions, StatusArgs, }; @@ -43,11 +43,21 @@ enum TraceCommand { /// Print daemon/session status. #[command(hide = true)] Status(StatusArgs), + /// Gracefully stop the tracing daemon. + #[command(hide = true)] + Stop(StopArgs), /// Replay a journal file through the translators + sink. #[command(hide = true)] Replay(ReplayArgs), } +#[derive(Debug, Clone, Args)] +struct StopArgs { + /// Socket path override (default: see the daemon protocol documentation). + #[arg(long)] + socket: Option, +} + #[derive(Debug, Clone, Args)] struct SetupArgs { #[command(subcommand)] @@ -319,6 +329,20 @@ pub async fn run(base: BaseArgs, args: TraceArgs) -> anyhow::Result<()> { Ok(()) } }, + TraceCommand::Stop(stop_args) => { + let socket = paths::socket_path(stop_args.socket.as_deref()); + let status_args = StatusArgs { + socket: Some(socket.clone()), + session_id: None, + }; + if run_status(status_args).await?.is_none() { + println!("No tracing daemon is running."); + return Ok(()); + } + shutdown_daemon(&socket).await?; + println!("Tracing daemon stopped."); + Ok(()) + } TraceCommand::Replay(replay_args) => { // Replay through the real translators into the debug sink (no // network): useful for inspecting what a journal produces. diff --git a/tests/cli.rs b/tests/cli.rs index aeb64727..70bf64a4 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -145,6 +145,7 @@ fn trace_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("serve").not()) .stdout(predicate::str::contains("\n hook").not()) .stdout(predicate::str::contains("\n status").not()) + .stdout(predicate::str::contains("\n stop").not()) .stdout(predicate::str::contains("\n replay").not()); bt_command() @@ -169,6 +170,12 @@ fn trace_help_hides_internal_commands_but_keeps_them_callable() { .success() .stdout(predicate::str::contains("--socket")); + bt_command() + .args(["trace", "stop", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--socket")); + bt_command() .args(["trace", "replay", "--help"]) .assert() @@ -183,6 +190,77 @@ fn trace_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("claude")); } +#[cfg(unix)] +#[test] +fn trace_stop_gracefully_stops_an_isolated_daemon() { + use std::process::Stdio; + use std::thread; + use std::time::Duration; + + let state = tempfile::tempdir().expect("state tempdir"); + let socket = state.path().join("daemon.sock"); + let bin = env!("CARGO_BIN_EXE_bt"); + let mut daemon = std::process::Command::new(bin) + .args([ + "trace", + "daemon", + "--socket", + socket.to_str().expect("UTF-8 socket path"), + "--data-dir", + state.path().to_str().expect("UTF-8 state path"), + "--idle-timeout-secs", + "0", + ]) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("spawn tracing daemon"); + + for _ in 0..100 { + if socket.exists() { + break; + } + thread::sleep(Duration::from_millis(25)); + } + if !socket.exists() { + let _ = daemon.kill(); + panic!("tracing daemon did not create its socket"); + } + + bt_command() + .args([ + "trace", + "stop", + "--socket", + socket.to_str().expect("UTF-8 socket path"), + ]) + .assert() + .success() + .stdout(predicate::str::contains("Tracing daemon stopped.")); + + for _ in 0..100 { + if let Some(status) = daemon.try_wait().expect("poll tracing daemon") { + assert!(status.success(), "tracing daemon exited unsuccessfully"); + + bt_command() + .args([ + "trace", + "stop", + "--socket", + socket.to_str().expect("UTF-8 socket path"), + ]) + .assert() + .success() + .stdout(predicate::str::contains("No tracing daemon is running.")); + return; + } + thread::sleep(Duration::from_millis(25)); + } + + let _ = daemon.kill(); + panic!("tracing daemon did not stop"); +} + #[cfg(unix)] #[test] fn trace_setup_codex_installs_plugin_and_preserves_existing_settings() { From 765fe1ad5cca7711ab5d24b6e3964b91292735c3 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 22:01:45 +0800 Subject: [PATCH 10/19] Import agent transcripts with trace replay Signed-off-by: Stephen Belanger --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 2 +- src/agents.rs | 19 +++++-------------- tests/cli.rs | 5 ++++- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d06de19c..a3c5314a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,7 +427,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.23.0", + "darling 0.21.3", "ident_case", "prettyplease", "proc-macro2", @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=3ab5195ec8d87edcc5f175d041c7eaec98557033#3ab5195ec8d87edcc5f175d041c7eaec98557033" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=2e597db4315f95d3fb07cb3dbad2a85d5265cae4#2e597db4315f95d3fb07cb3dbad2a85d5265cae4" dependencies = [ "anyhow", "async-trait", @@ -1151,7 +1151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1531,7 +1531,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2039,7 +2039,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2367,7 +2367,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.2", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -2404,9 +2404,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.2", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -2674,7 +2674,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3171,7 +3171,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3855,7 +3855,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6bfc7917..e961f4cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "3ab5195ec8d87edcc5f175d041c7eaec98557033" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "2e597db4315f95d3fb07cb3dbad2a85d5265cae4" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" diff --git a/src/agents.rs b/src/agents.rs index 86d7d4f0..3d29acd1 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -18,8 +18,8 @@ use serde_json::{Map, Value}; use bt_daemon::wire::{BackendAuth, FlushMode, SessionConfig}; use bt_daemon::{ braintrust_serve_options, paths, run_hook, run_replay, run_serve, run_status, shutdown_daemon, - BraintrustSinkConfig, DebugSinkFactory, HookArgs, HostInfo, Registry, ReplayArgs, ServeArgs, - ServeOptions, StatusArgs, + BraintrustSinkConfig, HookArgs, HostInfo, Registry, ReplayArgs, ServeArgs, ServeOptions, + StatusArgs, }; use crate::args::BaseArgs; @@ -46,7 +46,7 @@ enum TraceCommand { /// Gracefully stop the tracing daemon. #[command(hide = true)] Stop(StopArgs), - /// Replay a journal file through the translators + sink. + /// Import a native Codex or Claude Code transcript. #[command(hide = true)] Replay(ReplayArgs), } @@ -344,17 +344,8 @@ pub async fn run(base: BaseArgs, args: TraceArgs) -> anyhow::Result<()> { Ok(()) } TraceCommand::Replay(replay_args) => { - // Replay through the real translators into the debug sink (no - // network): useful for inspecting what a journal produces. - let data_dir = paths::data_dir(None); - let opts = ServeOptions { - version: crate::CLI_VERSION.to_string(), - translators: Arc::new(Registry::default_agents()), - sink_factory: Arc::new(DebugSinkFactory { - dir: data_dir.join("spans"), - }), - }; - run_replay(replay_args, opts).await + let config = session_config(&base).await?; + run_replay(replay_args, serve_options(), Some(config)).await } } } diff --git a/tests/cli.rs b/tests/cli.rs index 70bf64a4..4fc00acc 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -180,7 +180,10 @@ fn trace_help_hides_internal_commands_but_keeps_them_callable() { .args(["trace", "replay", "--help"]) .assert() .success() - .stdout(predicate::str::contains("")); + .stdout(predicate::str::contains("")) + .stdout(predicate::str::contains("--source ")) + .stdout(predicate::str::contains("codex")) + .stdout(predicate::str::contains("claude")); bt_command() .args(["trace", "setup", "--help"]) From 7f383a638b73650726408a0e08b5db672347487f Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 22:07:49 +0800 Subject: [PATCH 11/19] Update tracing daemon revision Signed-off-by: Stephen Belanger --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3c5314a..97de0e4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,7 +427,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=2e597db4315f95d3fb07cb3dbad2a85d5265cae4#2e597db4315f95d3fb07cb3dbad2a85d5265cae4" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=93afb7c2f888b1873c66a99a91e5591533feb9d5#93afb7c2f888b1873c66a99a91e5591533feb9d5" dependencies = [ "anyhow", "async-trait", @@ -1151,7 +1151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1531,7 +1531,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.2", "tokio", "tower-service", "tracing", @@ -2039,7 +2039,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2367,7 +2367,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.2", "thiserror 2.0.18", "tokio", "tracing", @@ -2404,9 +2404,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2674,7 +2674,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3171,7 +3171,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e961f4cc..d40528d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "2e597db4315f95d3fb07cb3dbad2a85d5265cae4" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "93afb7c2f888b1873c66a99a91e5591533feb9d5" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" From 19401c2f9436af111e0895a3dd5ab282e230b9f3 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 22:32:51 +0800 Subject: [PATCH 12/19] Import coding-agent sessions by id Signed-off-by: Stephen Belanger --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- src/agents.rs | 12 ++++++------ tests/cli.rs | 12 +++++++++--- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97de0e4f..995c9258 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,7 +427,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.23.0", + "darling 0.21.3", "ident_case", "prettyplease", "proc-macro2", @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=93afb7c2f888b1873c66a99a91e5591533feb9d5#93afb7c2f888b1873c66a99a91e5591533feb9d5" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=ee71ceb110c348bc7d94e65da4a2280069a6a971#ee71ceb110c348bc7d94e65da4a2280069a6a971" dependencies = [ "anyhow", "async-trait", @@ -1151,7 +1151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1531,7 +1531,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2039,7 +2039,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2367,7 +2367,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.2", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -2404,9 +2404,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.2", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -2674,7 +2674,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3171,7 +3171,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d40528d4..278a63d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "93afb7c2f888b1873c66a99a91e5591533feb9d5" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "ee71ceb110c348bc7d94e65da4a2280069a6a971" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" diff --git a/src/agents.rs b/src/agents.rs index 3d29acd1..14a6c0a0 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -17,8 +17,8 @@ use serde_json::{Map, Value}; use bt_daemon::wire::{BackendAuth, FlushMode, SessionConfig}; use bt_daemon::{ - braintrust_serve_options, paths, run_hook, run_replay, run_serve, run_status, shutdown_daemon, - BraintrustSinkConfig, HookArgs, HostInfo, Registry, ReplayArgs, ServeArgs, ServeOptions, + braintrust_serve_options, paths, run_hook, run_import, run_serve, run_status, shutdown_daemon, + BraintrustSinkConfig, HookArgs, HostInfo, ImportArgs, Registry, ServeArgs, ServeOptions, StatusArgs, }; @@ -46,9 +46,9 @@ enum TraceCommand { /// Gracefully stop the tracing daemon. #[command(hide = true)] Stop(StopArgs), - /// Import a native Codex or Claude Code transcript. + /// Import a past Codex or Claude Code session by its resume id. #[command(hide = true)] - Replay(ReplayArgs), + Import(ImportArgs), } #[derive(Debug, Clone, Args)] @@ -343,9 +343,9 @@ pub async fn run(base: BaseArgs, args: TraceArgs) -> anyhow::Result<()> { println!("Tracing daemon stopped."); Ok(()) } - TraceCommand::Replay(replay_args) => { + TraceCommand::Import(import_args) => { let config = session_config(&base).await?; - run_replay(replay_args, serve_options(), Some(config)).await + run_import(import_args, serve_options(), Some(config)).await } } } diff --git a/tests/cli.rs b/tests/cli.rs index 4fc00acc..635831df 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -146,6 +146,7 @@ fn trace_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("\n hook").not()) .stdout(predicate::str::contains("\n status").not()) .stdout(predicate::str::contains("\n stop").not()) + .stdout(predicate::str::contains("\n import").not()) .stdout(predicate::str::contains("\n replay").not()); bt_command() @@ -177,14 +178,19 @@ fn trace_help_hides_internal_commands_but_keeps_them_callable() { .stdout(predicate::str::contains("--socket")); bt_command() - .args(["trace", "replay", "--help"]) + .args(["trace", "import", "--help"]) .assert() .success() - .stdout(predicate::str::contains("")) - .stdout(predicate::str::contains("--source ")) + .stdout(predicate::str::contains("")) + .stdout(predicate::str::contains("")) .stdout(predicate::str::contains("codex")) .stdout(predicate::str::contains("claude")); + bt_command() + .args(["trace", "replay", "--help"]) + .assert() + .failure(); + bt_command() .args(["trace", "setup", "--help"]) .assert() From fcd416b1deb0e7a81bf5f33659d507d0c9eba943 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 22:52:14 +0800 Subject: [PATCH 13/19] Preserve turns in agent imports Signed-off-by: Stephen Belanger --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 995c9258..fb3aa441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=ee71ceb110c348bc7d94e65da4a2280069a6a971#ee71ceb110c348bc7d94e65da4a2280069a6a971" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=4ab9cdd81bb56a33975c1c34e0183d5ad4878435#4ab9cdd81bb56a33975c1c34e0183d5ad4878435" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 278a63d1..f012bab4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "ee71ceb110c348bc7d94e65da4a2280069a6a971" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "4ab9cdd81bb56a33975c1c34e0183d5ad4878435" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" From 12257e647795e66eac5114c720b1e6611c320b8d Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 22:57:19 +0800 Subject: [PATCH 14/19] Pin Codex compaction import fix Signed-off-by: Stephen Belanger --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb3aa441..cc3ac16b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=4ab9cdd81bb56a33975c1c34e0183d5ad4878435#4ab9cdd81bb56a33975c1c34e0183d5ad4878435" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=56db37932c125c90add0e23be57641e6f06d80c4#56db37932c125c90add0e23be57641e6f06d80c4" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index f012bab4..41f4746b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "4ab9cdd81bb56a33975c1c34e0183d5ad4878435" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "56db37932c125c90add0e23be57641e6f06d80c4" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" From 93420c1b180de50fea902bb790912f24f210996f Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 23:18:37 +0800 Subject: [PATCH 15/19] Pin batched transcript imports Signed-off-by: Stephen Belanger --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc3ac16b..3c2aa449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=56db37932c125c90add0e23be57641e6f06d80c4#56db37932c125c90add0e23be57641e6f06d80c4" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=c94a9170e3aa0d0199e391eaccc09fe4e32e2656#c94a9170e3aa0d0199e391eaccc09fe4e32e2656" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 41f4746b..1ef59b63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "56db37932c125c90add0e23be57641e6f06d80c4" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "c94a9170e3aa0d0199e391eaccc09fe4e32e2656" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" From 64fabef6c4b14d0ffce077dc8ddbccb5bb59e9c8 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Fri, 31 Jul 2026 23:30:53 +0800 Subject: [PATCH 16/19] Pin streaming transcript imports Signed-off-by: Stephen Belanger --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c2aa449..80fe0900 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=c94a9170e3aa0d0199e391eaccc09fe4e32e2656#c94a9170e3aa0d0199e391eaccc09fe4e32e2656" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=d124bae4712daa98aa6140e20b65c3b6d9c7af61#d124bae4712daa98aa6140e20b65c3b6d9c7af61" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 1ef59b63..f9d5a23a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "c94a9170e3aa0d0199e391eaccc09fe4e32e2656" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "d124bae4712daa98aa6140e20b65c3b6d9c7af61" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" From 22b535aaf6c61b6d2f1438b7b59e50d1477cc4e8 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Sat, 1 Aug 2026 00:03:51 +0800 Subject: [PATCH 17/19] Expose trace import command Signed-off-by: Stephen Belanger --- src/agents.rs | 1 - tests/cli.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/agents.rs b/src/agents.rs index 14a6c0a0..9482f6ca 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -47,7 +47,6 @@ enum TraceCommand { #[command(hide = true)] Stop(StopArgs), /// Import a past Codex or Claude Code session by its resume id. - #[command(hide = true)] Import(ImportArgs), } diff --git a/tests/cli.rs b/tests/cli.rs index 635831df..aa13a8d0 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -132,7 +132,7 @@ fn top_level_help_shows_update_not_self() { } #[test] -fn trace_help_hides_internal_commands_but_keeps_them_callable() { +fn trace_help_exposes_user_commands_and_hides_internal_commands() { bt_command().args(["daemon", "--help"]).assert().failure(); bt_command().args(["agents", "--help"]).assert().failure(); @@ -141,12 +141,12 @@ fn trace_help_hides_internal_commands_but_keeps_them_callable() { .assert() .success() .stdout(predicate::str::contains("setup")) + .stdout(predicate::str::contains("\n import")) .stdout(predicate::str::contains("\n daemon").not()) .stdout(predicate::str::contains("serve").not()) .stdout(predicate::str::contains("\n hook").not()) .stdout(predicate::str::contains("\n status").not()) .stdout(predicate::str::contains("\n stop").not()) - .stdout(predicate::str::contains("\n import").not()) .stdout(predicate::str::contains("\n replay").not()); bt_command() From cd29226518e2860ae3aa070057626b5109057cf9 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Sat, 1 Aug 2026 00:21:37 +0800 Subject: [PATCH 18/19] Update daemon import dependency Signed-off-by: Stephen Belanger --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80fe0900..c41a76b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=d124bae4712daa98aa6140e20b65c3b6d9c7af61#d124bae4712daa98aa6140e20b65c3b6d9c7af61" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=ce74e5d164fc7a1d77e4512b04de7bf043df35f9#ce74e5d164fc7a1d77e4512b04de7bf043df35f9" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index f9d5a23a..13731b82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "d124bae4712daa98aa6140e20b65c3b6d9c7af61" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "ce74e5d164fc7a1d77e4512b04de7bf043df35f9" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31" From def3be2904cc1e8a392ebc0ac3c725dbcd982fb8 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Sat, 1 Aug 2026 01:03:31 +0800 Subject: [PATCH 19/19] Pin merged daemon commit Signed-off-by: Stephen Belanger --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c41a76b5..8cb11884 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "bt-daemon" version = "0.1.0" -source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=ce74e5d164fc7a1d77e4512b04de7bf043df35f9#ce74e5d164fc7a1d77e4512b04de7bf043df35f9" +source = "git+https://github.com/braintrustdata/braintrust-coding-agent-plugins?rev=bebff4e0349fd9d2ee4a8ce1eeeafafee91ac74b#bebff4e0349fd9d2ee4a8ce1eeeafafee91ac74b" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 13731b82..488195bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ actix-web = "4.11.0" anyhow = "1.0.89" backoff = { version = "0.4.0", features = ["tokio"] } braintrust-sdk-rust = { git = "https://github.com/braintrustdata/braintrust-sdk-rust", rev = "43ba73edbf5220b57090e049feb094b60a92fcd4" } -bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "ce74e5d164fc7a1d77e4512b04de7bf043df35f9" } +bt-daemon = { git = "https://github.com/braintrustdata/braintrust-coding-agent-plugins", rev = "bebff4e0349fd9d2ee4a8ce1eeeafafee91ac74b" } clap = { version = "4.5.20", features = ["derive", "env"] } crossterm = "0.28.1" futures-util = "0.3.31"