Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down
46 changes: 46 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Self> {
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())
Expand All @@ -58,15 +72,35 @@ impl AcpConfig {
.collect::<Vec<_>>()
});
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<String>,
bin: impl Into<String>,
args: impl IntoIterator<Item = impl Into<String>>,
default_model: impl Into<String>,
) -> 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<String>) -> Self {
Self {
provider_tool: DEFAULT_PROVIDER_TOOL.to_string(),
default_model: default_model.into(),
agent_bin_override: None,
agent_args_override: None,
Expand All @@ -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,
Expand Down Expand Up @@ -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()]),
Expand Down
Loading