diff --git a/crates/colibri-client/src/lib.rs b/crates/colibri-client/src/lib.rs index b82fe9a..f85179f 100644 --- a/crates/colibri-client/src/lib.rs +++ b/crates/colibri-client/src/lib.rs @@ -108,6 +108,7 @@ impl DaemonClient { model: model.into(), session_id, system_prompt, + local_args: None, }) .await } diff --git a/crates/colibri-daemon/src/lib.rs b/crates/colibri-daemon/src/lib.rs index ca32a53..f185b80 100644 --- a/crates/colibri-daemon/src/lib.rs +++ b/crates/colibri-daemon/src/lib.rs @@ -43,6 +43,10 @@ pub enum HerdrCommand { model: String, session_id: Option, system_prompt: Option, + /// Extra args passed to the agent binary (used by Local provider + /// to pass argv without a wrapper script). + #[serde(default)] + local_args: Option>, }, #[serde(rename = "kill-agent")] KillAgent { agent_id: String }, diff --git a/crates/colibri-daemon/src/socket.rs b/crates/colibri-daemon/src/socket.rs index 5b93d07..57a327a 100644 --- a/crates/colibri-daemon/src/socket.rs +++ b/crates/colibri-daemon/src/socket.rs @@ -151,7 +151,18 @@ async fn dispatch(cmd: HerdrCommand, state: &SharedState) -> HerdrResponse { model, session_id, system_prompt, - } => cmd_spawn_agent(state, provider, model, session_id, system_prompt).await, + local_args, + } => { + cmd_spawn_agent( + state, + provider, + model, + session_id, + system_prompt, + local_args, + ) + .await + } HerdrCommand::KillAgent { agent_id } => cmd_kill_agent(state, agent_id).await, HerdrCommand::GetSession { session_id } => cmd_get_session(state, session_id).await, HerdrCommand::CompactSession { session_id } => cmd_compact_session(state, session_id).await, @@ -287,6 +298,7 @@ async fn cmd_spawn_agent( model: String, session_id: Option, system_prompt: Option, + local_args: Option>, ) -> HerdrResponse { let provider = match provider_str.to_lowercase().as_str() { "deepseek" => Provider::DeepSeek, @@ -297,9 +309,11 @@ async fn cmd_spawn_agent( }; let (binary, args) = if provider == Provider::Local { - // Local provider is the no-network smoke/test path: the socket `model` - // field is treated as the executable path and no Pi CLI flags are added. - (model.clone(), Vec::new()) + // Local provider: model is the executable path. + // local_args (if provided) are passed as argv — enables real Pi spawn + // without a wrapper script (e.g. pi --mode json --no-tools -p 'task'). + let args = local_args.unwrap_or_default(); + (model.clone(), args) } else { ( std::env::var("COLIBRI_AGENT_BINARY").unwrap_or_else(|_| "hermes-agent".to_string()),