pub const BITROUTER_API_KEY_ENV: &str = "BITROUTER_API_KEY";
pub const PLACEHOLDER_API_KEY: &str = "bitrouter-local";
#[derive(Debug, Clone, Copy)]
pub struct Harness {
pub id: &'static str,
pub description: &'static str,
pub project_url: &'static str,
pub acp_command: &'static str,
pub acp_args: &'static [&'static str],
pub package_marker: &'static str,
pub interactive_binary: Option<&'static str>,
pub routing: Routing,
}
#[derive(Debug, Clone, Copy)]
pub enum Routing {
Env {
base_url_env: &'static str,
auth_env: &'static str,
bearer_auth: bool,
model_env: Option<&'static str>,
extra: &'static [(&'static str, &'static str)],
},
CodexArgs,
Unroutable {
reason: &'static str,
},
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct RoutingOverlay {
pub env: Vec<(String, String)>,
pub args: Vec<String>,
}
pub const CATALOG: &[Harness] = &[
Harness {
id: "claude-acp",
description: "Anthropic Claude via Zed's `claude-code-acp`",
project_url: "https://github.com/zed-industries/claude-code-acp",
acp_command: "npx",
acp_args: &["-y", "@zed-industries/claude-code-acp@latest"],
package_marker: "claude-code-acp",
interactive_binary: Some("claude"),
routing: Routing::Env {
base_url_env: "ANTHROPIC_BASE_URL",
auth_env: "ANTHROPIC_AUTH_TOKEN",
bearer_auth: true,
model_env: Some("ANTHROPIC_MODEL"),
extra: &[],
},
},
Harness {
id: "codex-acp",
description: "OpenAI Codex via Zed's `codex-acp`",
project_url: "https://github.com/zed-industries/codex-acp",
acp_command: "npx",
acp_args: &["-y", "@zed-industries/codex-acp@latest"],
package_marker: "codex-acp",
interactive_binary: Some("codex"),
routing: Routing::CodexArgs,
},
Harness {
id: "gemini-cli",
description: "Google's Gemini CLI with `--experimental-acp` (best-effort routing)",
project_url: "https://github.com/google-gemini/gemini-cli",
acp_command: "npx",
acp_args: &[
"-y",
"--",
"@google/gemini-cli@latest",
"--experimental-acp",
],
package_marker: "gemini-cli",
interactive_binary: None,
routing: Routing::Env {
base_url_env: "GOOGLE_GEMINI_BASE_URL",
auth_env: "GEMINI_API_KEY",
bearer_auth: false,
model_env: Some("GEMINI_MODEL"),
extra: &[],
},
},
Harness {
id: "pi-acp",
description: "pi coding agent via `pi-acp` (needs `pi` on PATH)",
project_url: "https://github.com/svkozak/pi-acp",
acp_command: "npx",
acp_args: &["-y", "pi-acp@latest"],
package_marker: "pi-acp",
interactive_binary: None,
routing: Routing::Unroutable {
reason: "pi has no base-URL env var; route by synthesizing a \
PI_CODING_AGENT_DIR with a models.json provider \
(SPAWN_SPEC §6.4, planned v1.1)",
},
},
];
pub fn by_id(id: &str) -> Option<&'static Harness> {
CATALOG.iter().find(|h| h.id == id)
}
pub fn by_interactive_binary(binary: &str) -> Option<&'static Harness> {
CATALOG
.iter()
.find(|h| h.interactive_binary == Some(binary))
}
pub fn match_invocation(command: &str, args: &[String]) -> Option<&'static Harness> {
CATALOG.iter().find(|h| {
command.contains(h.package_marker) || args.iter().any(|a| a.contains(h.package_marker))
})
}
impl Harness {
pub fn routing_overlay(
&self,
base_url: &str,
auth: &str,
model: Option<&str>,
) -> RoutingOverlay {
match &self.routing {
Routing::Env {
base_url_env,
auth_env,
model_env,
extra,
..
} => {
let mut env = vec![
((*base_url_env).to_string(), base_url.to_string()),
((*auth_env).to_string(), auth.to_string()),
];
for (k, v) in *extra {
env.push(((*k).to_string(), (*v).to_string()));
}
if let (Some(var), Some(m)) = (model_env, model) {
env.push(((*var).to_string(), m.to_string()));
}
RoutingOverlay {
env,
args: Vec::new(),
}
}
Routing::CodexArgs => codex_overlay(base_url, auth, model),
Routing::Unroutable { .. } => RoutingOverlay::default(),
}
}
pub fn is_routable(&self) -> bool {
!matches!(self.routing, Routing::Unroutable { .. })
}
pub fn auth_is_bearer(&self) -> bool {
match self.routing {
Routing::Env { bearer_auth, .. } => bearer_auth,
_ => true,
}
}
pub fn supports_model_pin(&self) -> bool {
match self.routing {
Routing::Env { model_env, .. } => model_env.is_some(),
Routing::CodexArgs => true,
Routing::Unroutable { .. } => false,
}
}
}
pub fn resolve_gateway_auth(bitrouter_key: Option<String>, require_key: bool) -> Option<String> {
match bitrouter_key {
Some(key) => Some(key),
None if require_key => None,
None => Some(PLACEHOLDER_API_KEY.to_string()),
}
}
fn codex_overlay(base_url: &str, auth: &str, model: Option<&str>) -> RoutingOverlay {
let mut env = Vec::new();
let mut args = vec![
"-c".to_string(),
codex_config_string("model_provider", "bitrouter"),
"-c".to_string(),
codex_config_string("model_providers.bitrouter.name", "BitRouter"),
"-c".to_string(),
codex_config_string(
"model_providers.bitrouter.base_url",
&codex_api_base_url(base_url),
),
"-c".to_string(),
codex_config_string("model_providers.bitrouter.wire_api", "responses"),
];
if auth == PLACEHOLDER_API_KEY {
args.push("-c".to_string());
args.push(codex_config_string(
"model_providers.bitrouter.experimental_bearer_token",
PLACEHOLDER_API_KEY,
));
} else {
env.push((BITROUTER_API_KEY_ENV.to_string(), auth.to_string()));
args.push("-c".to_string());
args.push(codex_config_string(
"model_providers.bitrouter.env_key",
BITROUTER_API_KEY_ENV,
));
}
if let Some(m) = model {
args.push("-c".to_string());
args.push(codex_config_string("model", m));
}
RoutingOverlay { env, args }
}
fn codex_api_base_url(base_url: &str) -> String {
let trimmed = base_url.trim_end_matches('/');
if trimmed.ends_with("/v1") {
trimmed.to_string()
} else {
format!("{trimmed}/v1")
}
}
fn codex_config_string(key: &str, value: &str) -> String {
format!("{key}={}", toml_string(value))
}
fn toml_string(value: &str) -> String {
let mut out = String::with_capacity(value.len() + 2);
out.push('"');
for ch in value.chars() {
match ch {
'\\' => out.push_str("\\\\"),
'"' => out.push_str("\\\""),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
other => out.push(other),
}
}
out.push('"');
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn catalog_ids_are_unique() {
let mut ids: Vec<_> = CATALOG.iter().map(|h| h.id).collect();
ids.sort_unstable();
let before = ids.len();
ids.dedup();
assert_eq!(before, ids.len(), "catalog ids must be unique");
}
#[test]
fn by_interactive_binary_finds_claude_and_codex() {
assert_eq!(by_interactive_binary("claude").unwrap().id, "claude-acp");
assert_eq!(by_interactive_binary("codex").unwrap().id, "codex-acp");
assert!(by_interactive_binary("gemini").is_none());
}
#[test]
fn match_invocation_maps_renamed_key_by_package_marker() {
let h = match_invocation(
"npx",
&[
"-y".to_string(),
"@zed-industries/claude-code-acp@latest".to_string(),
],
)
.expect("matches claude-acp");
assert_eq!(h.id, "claude-acp");
}
#[test]
fn match_invocation_none_for_unknown_command() {
assert!(match_invocation("./my-custom-agent", &[]).is_none());
}
#[test]
fn match_invocation_matches_globally_installed_binary_command() {
assert_eq!(
match_invocation("claude-code-acp", &[]).unwrap().id,
"claude-acp"
);
assert_eq!(
match_invocation("gemini-cli", &[]).unwrap().id,
"gemini-cli"
);
assert_eq!(match_invocation("codex-acp", &[]).unwrap().id, "codex-acp");
}
#[test]
fn catalog_markers_are_mutually_non_substrings() {
for a in CATALOG {
for b in CATALOG {
if a.id != b.id {
assert!(
!a.package_marker.contains(b.package_marker),
"marker '{}' contains '{}'",
a.package_marker,
b.package_marker
);
}
}
}
}
#[test]
fn auth_is_bearer_flags_gemini_as_non_bearer() {
assert!(by_id("claude-acp").unwrap().auth_is_bearer());
assert!(by_id("codex-acp").unwrap().auth_is_bearer());
assert!(!by_id("gemini-cli").unwrap().auth_is_bearer());
}
#[test]
fn supports_model_pin_reflects_catalog() {
assert!(by_id("claude-acp").unwrap().supports_model_pin());
assert!(by_id("codex-acp").unwrap().supports_model_pin());
assert!(by_id("gemini-cli").unwrap().supports_model_pin());
assert!(!by_id("pi-acp").unwrap().supports_model_pin());
}
#[test]
fn claude_overlay_sets_base_url_and_bearer_token() {
let h = by_id("claude-acp").unwrap();
let o = h.routing_overlay("http://127.0.0.1:4356", "brk_real", None);
assert!(o.env.contains(&(
"ANTHROPIC_BASE_URL".to_string(),
"http://127.0.0.1:4356".to_string()
)));
assert!(
o.env
.contains(&("ANTHROPIC_AUTH_TOKEN".to_string(), "brk_real".to_string()))
);
assert!(o.env.iter().all(|(k, _)| k != "ANTHROPIC_API_KEY"));
assert!(o.args.is_empty());
}
#[test]
fn claude_overlay_pins_model_when_given() {
let h = by_id("claude-acp").unwrap();
let o = h.routing_overlay("http://x:1", "t", Some("claude-sonnet-5"));
assert!(
o.env
.contains(&("ANTHROPIC_MODEL".to_string(), "claude-sonnet-5".to_string()))
);
let o2 = h.routing_overlay("http://x:1", "t", None);
assert!(o2.env.iter().all(|(k, _)| k != "ANTHROPIC_MODEL"));
}
#[test]
fn codex_overlay_routes_responses_to_v1_and_uses_env_key_for_real_key() {
let h = by_id("codex-acp").unwrap();
let o = h.routing_overlay("http://127.0.0.1:4356", "brk_real", None);
assert!(o.args.contains(&"model_provider=\"bitrouter\"".to_string()));
assert!(o.args.contains(
&"model_providers.bitrouter.base_url=\"http://127.0.0.1:4356/v1\"".to_string()
));
assert!(
o.args
.contains(&"model_providers.bitrouter.wire_api=\"responses\"".to_string())
);
assert!(
o.args
.contains(&"model_providers.bitrouter.env_key=\"BITROUTER_API_KEY\"".to_string())
);
assert!(
o.env
.contains(&("BITROUTER_API_KEY".to_string(), "brk_real".to_string()))
);
assert!(
o.args
.iter()
.all(|a| !a.contains("experimental_bearer_token"))
);
}
#[test]
fn codex_overlay_uses_inline_token_for_placeholder() {
let h = by_id("codex-acp").unwrap();
let o = h.routing_overlay("http://127.0.0.1:4356", PLACEHOLDER_API_KEY, None);
assert!(o.args.contains(
&"model_providers.bitrouter.experimental_bearer_token=\"bitrouter-local\"".to_string()
));
assert!(o.env.iter().all(|(k, _)| k != "BITROUTER_API_KEY"));
}
#[test]
fn codex_overlay_pins_model() {
let h = by_id("codex-acp").unwrap();
let o = h.routing_overlay("http://x:1", PLACEHOLDER_API_KEY, Some("gpt-5.2"));
assert!(o.args.contains(&"model=\"gpt-5.2\"".to_string()));
}
#[test]
fn unroutable_harness_yields_empty_overlay() {
let h = by_id("pi-acp").unwrap();
assert!(!h.is_routable());
assert_eq!(
h.routing_overlay("http://x:1", "t", None),
RoutingOverlay::default()
);
}
#[test]
fn resolve_gateway_auth_precedence() {
assert_eq!(
resolve_gateway_auth(Some("brk_x".into()), false).as_deref(),
Some("brk_x")
);
assert_eq!(
resolve_gateway_auth(None, false).as_deref(),
Some(PLACEHOLDER_API_KEY)
);
assert_eq!(resolve_gateway_auth(None, true), None);
assert_eq!(
resolve_gateway_auth(Some("brk_x".into()), true).as_deref(),
Some("brk_x")
);
}
}