diff --git a/Cargo.lock b/Cargo.lock index d43ecb4..6872102 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "animus-provider-acp" -version = "0.1.0" +version = "0.1.1" dependencies = [ "agent-client-protocol", "animus-plugin-protocol", diff --git a/Cargo.toml b/Cargo.toml index 0ab9154..5aeb5f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "animus-provider-acp" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "MIT" description = "Generic ACP (Agent Client Protocol) client provider plugin for Animus" diff --git a/src/backend.rs b/src/backend.rs index 3866c5c..025a364 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -76,7 +76,7 @@ impl SessionBackend for AcpSessionBackend { fn info(&self) -> SessionBackendInfo { SessionBackendInfo { kind: SessionBackendKind::Subprocess, - provider_tool: "acp".to_string(), + provider_tool: self.config.provider_tool.clone(), stability: SessionStability::Experimental, display_name: "ACP client".to_string(), } diff --git a/src/config.rs b/src/config.rs index 35ff30d..7f950df 100644 --- a/src/config.rs +++ b/src/config.rs @@ -26,8 +26,17 @@ impl AcpCommand { } /// Process-wide config sourced from the environment. +/// The default `provider_tool` this plugin reports when none is configured. +pub const DEFAULT_PROVIDER_TOOL: &str = "acp"; + #[derive(Debug, Clone)] pub struct AcpConfig { + /// The `provider_tool` this plugin advertises to the kernel. Defaults to + /// `"acp"` (the generic ACP client). A per-harness wrapper binary (e.g. + /// `animus-provider-gemini`) sets this to its harness id (`"gemini"`) so the + /// kernel routes that harness's models to it while the ACP transport stays + /// an internal detail. Sourced from `ACP_PROVIDER_TOOL` in `from_env`. + pub provider_tool: String, /// Fallback model id used when an `agent/run` request omits one. pub default_model: String, /// Optional global harness binary override (`ACP_AGENT_BIN`). When set it @@ -41,10 +50,15 @@ pub struct AcpConfig { impl AcpConfig { /// Load config from the process environment. /// + /// - `ACP_PROVIDER_TOOL` — advertised provider_tool (default `"acp"`). /// - `ACP_DEFAULT_MODEL` — fallback model (default `"gemini-2.5-flash"`). /// - `ACP_AGENT_BIN` — global harness binary override. /// - `ACP_AGENT_ARGS` — global harness args override (space-separated). pub fn from_env() -> Result { + let provider_tool = std::env::var("ACP_PROVIDER_TOOL") + .ok() + .filter(|s| !s.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_PROVIDER_TOOL.to_string()); let default_model = std::env::var("ACP_DEFAULT_MODEL") .ok() .filter(|s| !s.is_empty()) @@ -58,15 +72,35 @@ impl AcpConfig { .collect::>() }); Ok(Self { + provider_tool, default_model, agent_bin_override, agent_args_override, }) } + /// Construct config for a per-harness wrapper: a fixed `provider_tool` + /// driving a fixed harness command over ACP. This is how + /// `animus-provider-gemini` (and other per-harness binaries) reuse the ACP + /// client without exposing a generic `acp` tool. + pub fn for_harness( + provider_tool: impl Into, + bin: impl Into, + args: impl IntoIterator>, + default_model: impl Into, + ) -> Self { + Self { + provider_tool: provider_tool.into(), + default_model: default_model.into(), + agent_bin_override: Some(bin.into()), + agent_args_override: Some(args.into_iter().map(Into::into).collect()), + } + } + /// Test helper — construct config with explicit values. pub fn for_testing(default_model: impl Into) -> Self { Self { + provider_tool: DEFAULT_PROVIDER_TOOL.to_string(), default_model: default_model.into(), agent_bin_override: None, agent_args_override: None, @@ -77,6 +111,7 @@ impl AcpConfig { impl Default for AcpConfig { fn default() -> Self { Self { + provider_tool: DEFAULT_PROVIDER_TOOL.to_string(), default_model: "gemini-2.5-flash".to_string(), agent_bin_override: None, agent_args_override: None, @@ -231,9 +266,20 @@ mod tests { assert_eq!(cmd.args, vec!["acp".to_string()]); } + #[test] + fn for_harness_pins_provider_tool_and_command() { + let cfg = AcpConfig::for_harness("gemini", "gemini", ["--acp"], "gemini-2.5-flash"); + assert_eq!(cfg.provider_tool, "gemini"); + // The pinned harness command wins regardless of the requested model. + let cmd = resolve_command(&cfg, "some-unknown-model", &json!({})).unwrap(); + assert_eq!(cmd.bin, "gemini"); + assert_eq!(cmd.args, vec!["--acp".to_string()]); + } + #[test] fn env_override_wins_over_table() { let cfg = AcpConfig { + provider_tool: DEFAULT_PROVIDER_TOOL.to_string(), default_model: "gemini-2.5-flash".to_string(), agent_bin_override: Some("gemini".to_string()), agent_args_override: Some(vec!["--acp".to_string(), "--debug".to_string()]),