Add --docker flag to attractor run and serve commands

When --docker is passed, agent tools execute inside a Docker container
via DockerExecutionEnvironment instead of running on the host. The host
working directory is bind-mounted into the container at /workspace.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-02-23 13:42:10 -05:00
parent ffa92cd23d
commit 379e90c11d
4 changed files with 29 additions and 3 deletions

View file

@ -4,8 +4,9 @@ use std::sync::Arc;
use async_trait::async_trait;
use agent::{
AnthropicProfile, EventData, EventKind, GeminiProfile, LocalExecutionEnvironment,
OpenAiProfile, ProviderProfile, Session, SessionConfig, Turn,
AnthropicProfile, DockerConfig, DockerExecutionEnvironment, EventData, EventKind,
ExecutionEnvironment, GeminiProfile, LocalExecutionEnvironment, OpenAiProfile, ProviderProfile,
Session, SessionConfig, Turn,
};
use llm::client::Client;
use terminal::Styles;
@ -21,6 +22,7 @@ pub struct AgentBackend {
provider: Option<String>,
verbose: u8,
styles: &'static Styles,
docker: bool,
}
impl AgentBackend {
@ -30,12 +32,14 @@ impl AgentBackend {
provider: Option<String>,
verbose: u8,
styles: &'static Styles,
docker: bool,
) -> Self {
Self {
model,
provider,
verbose,
styles,
docker,
}
}
@ -64,7 +68,19 @@ impl CodergenBackend for AgentBackend {
let profile = self.build_profile();
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let exec_env = Arc::new(LocalExecutionEnvironment::new(cwd));
let exec_env: Arc<dyn ExecutionEnvironment> = if self.docker {
let config = DockerConfig {
host_working_directory: cwd.to_string_lossy().to_string(),
..DockerConfig::default()
};
Arc::new(
DockerExecutionEnvironment::new(config)
.map_err(|e| AttractorError::Handler(format!("Failed to create Docker environment: {e}")))?,
)
} else {
Arc::new(LocalExecutionEnvironment::new(cwd))
};
let config = SessionConfig {
reasoning_effort: Some(node.reasoning_effort().to_string()),

View file

@ -63,6 +63,10 @@ pub struct RunArgs {
/// Verbosity level (-v summary, -vv full details)
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
/// Run agent tools inside a Docker container
#[arg(long)]
pub docker: bool,
}
#[derive(Args)]
@ -93,6 +97,10 @@ pub struct ServeArgs {
/// Execute with simulated LLM backend
#[arg(long)]
pub dry_run: bool,
/// Run agent tools inside a Docker container
#[arg(long)]
pub docker: bool,
}
/// Read a .dot file from disk.

View file

@ -133,6 +133,7 @@ pub async fn run_command(args: RunArgs, styles: &'static Styles) -> anyhow::Resu
provider.clone(),
args.verbose,
styles,
args.docker,
)))
}
});

View file

@ -60,6 +60,7 @@ pub async fn serve_command(args: ServeArgs, styles: &'static Styles) -> anyhow::
provider.clone(),
0,
styles,
args.docker,
)))
}
})