use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::{
AcpRuntimeBackend, ClaudeCodeRuntimeBackend, CodexRuntimeBackend, HarnessId,
OpenCodeRuntimeBackend, PiRuntimeBackend, RuntimeBackend, RuntimeCapabilities, RuntimeLaunch,
};
pub const SUPPORT_REGISTRY_SCHEMA: &str = "supercode.support-registry.v1";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ImplementationKind {
BuiltIn,
GenericProtocol,
Absent,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NativeSupport {
pub discover: ImplementationKind,
pub load: ImplementationKind,
pub follow: ImplementationKind,
pub import: ImplementationKind,
pub export: ImplementationKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuntimeSupport {
pub implementation: ImplementationKind,
pub protocol: String,
pub default_launch: Option<RuntimeLaunch>,
pub capabilities: RuntimeCapabilities,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HarnessSupportDescriptor {
pub id: HarnessId,
pub display_name: String,
pub native: NativeSupport,
pub runtime: RuntimeSupport,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SupportRegistryReport {
pub schema: String,
pub harnesses: Vec<HarnessSupportDescriptor>,
}
fn built_in_native() -> NativeSupport {
NativeSupport {
discover: ImplementationKind::BuiltIn,
load: ImplementationKind::BuiltIn,
follow: ImplementationKind::BuiltIn,
import: ImplementationKind::BuiltIn,
export: ImplementationKind::BuiltIn,
}
}
fn built_in_runtime(
backend: &dyn RuntimeBackend,
protocol: &str,
launch: RuntimeLaunch,
) -> RuntimeSupport {
RuntimeSupport {
implementation: ImplementationKind::BuiltIn,
protocol: protocol.into(),
default_launch: Some(launch),
capabilities: backend.capabilities(),
}
}
pub fn harness_support_registry() -> SupportRegistryReport {
let claude = ClaudeCodeRuntimeBackend::new();
let codex = CodexRuntimeBackend::new();
let opencode = OpenCodeRuntimeBackend::new();
let pi = PiRuntimeBackend::new();
let grok_launch = RuntimeLaunch {
program: "grok".into(),
arguments: vec![
"--sandbox".into(),
"workspace".into(),
"agent".into(),
"--no-leader".into(),
"stdio".into(),
],
env: BTreeMap::from([("GROK_AGENT_DASHBOARD".into(), "0".into())]),
};
let grok = AcpRuntimeBackend::new(HarnessId::from(HarnessId::GROK), grok_launch.clone())
.with_resume_support(true);
let gemini_launch = RuntimeLaunch {
program: "gemini".into(),
arguments: vec!["--acp".into()],
env: BTreeMap::new(),
};
let gemini = AcpRuntimeBackend::new(HarnessId::from(HarnessId::GEMINI), gemini_launch.clone())
.with_resume_support(true);
let goose_launch = RuntimeLaunch {
program: "goose".into(),
arguments: vec!["acp".into()],
env: BTreeMap::new(),
};
let goose = AcpRuntimeBackend::new(HarnessId::from(HarnessId::GOOSE), goose_launch.clone())
.with_resume_support(true);
let supercode_launch = RuntimeLaunch {
program: "supercode".into(),
arguments: vec!["acp".into()],
env: BTreeMap::new(),
};
let supercode = AcpRuntimeBackend::new(
HarnessId::from(HarnessId::SUPERCODE),
supercode_launch.clone(),
)
.with_resume_support(true);
SupportRegistryReport {
schema: SUPPORT_REGISTRY_SCHEMA.into(),
harnesses: vec![
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::CLAUDE_CODE),
display_name: "Claude Code".into(),
native: built_in_native(),
runtime: built_in_runtime(
&claude,
"claude-stream-json",
RuntimeLaunch {
program: "claude".into(),
arguments: Vec::new(),
env: BTreeMap::new(),
},
),
},
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::CODEX),
display_name: "Codex".into(),
native: built_in_native(),
runtime: built_in_runtime(
&codex,
"codex-app-server-jsonl",
RuntimeLaunch {
program: "codex".into(),
arguments: vec!["app-server".into()],
env: BTreeMap::new(),
},
),
},
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::OPENCODE),
display_name: "OpenCode".into(),
native: built_in_native(),
runtime: built_in_runtime(
&opencode,
"opencode-http-sse",
RuntimeLaunch {
program: "opencode".into(),
arguments: vec!["serve".into()],
env: BTreeMap::new(),
},
),
},
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::PI),
display_name: "Pi".into(),
native: built_in_native(),
runtime: built_in_runtime(
&pi,
"pi-rpc-jsonl",
RuntimeLaunch {
program: "pi".into(),
arguments: vec!["--mode".into(), "rpc".into()],
env: BTreeMap::new(),
},
),
},
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::GROK),
display_name: "Grok".into(),
native: built_in_native(),
runtime: RuntimeSupport {
implementation: ImplementationKind::GenericProtocol,
protocol: "acp-v1-jsonrpc".into(),
default_launch: Some(grok_launch),
capabilities: grok.capabilities(),
},
},
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::GEMINI),
display_name: "Gemini CLI".into(),
native: built_in_native(),
runtime: RuntimeSupport {
implementation: ImplementationKind::GenericProtocol,
protocol: "acp-v1-jsonrpc".into(),
default_launch: Some(gemini_launch),
capabilities: gemini.capabilities(),
},
},
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::GOOSE),
display_name: "Goose".into(),
native: built_in_native(),
runtime: RuntimeSupport {
implementation: ImplementationKind::GenericProtocol,
protocol: "acp-v1-jsonrpc".into(),
default_launch: Some(goose_launch),
capabilities: goose.capabilities(),
},
},
HarnessSupportDescriptor {
id: HarnessId::from(HarnessId::SUPERCODE),
display_name: "Supercode".into(),
native: built_in_native(),
runtime: RuntimeSupport {
implementation: ImplementationKind::GenericProtocol,
protocol: "acp-v1-jsonrpc".into(),
default_launch: Some(supercode_launch),
capabilities: supercode.capabilities(),
},
},
],
}
}
pub fn harness_support(id: &str) -> Option<HarnessSupportDescriptor> {
harness_support_registry()
.harnesses
.into_iter()
.find(|harness| harness.id.as_str() == id)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_is_unique_and_reports_all_native_support() {
let report = harness_support_registry();
assert_eq!(report.schema, SUPPORT_REGISTRY_SCHEMA);
assert_eq!(report.harnesses.len(), 8);
let ids = report
.harnesses
.iter()
.map(|harness| harness.id.as_str())
.collect::<std::collections::BTreeSet<_>>();
assert_eq!(ids.len(), report.harnesses.len());
let grok = report
.harnesses
.iter()
.find(|harness| harness.id.as_str() == HarnessId::GROK)
.unwrap();
assert_eq!(grok.native.discover, ImplementationKind::BuiltIn);
assert_eq!(grok.native.load, ImplementationKind::BuiltIn);
assert_eq!(grok.native.follow, ImplementationKind::BuiltIn);
assert_eq!(grok.native.import, ImplementationKind::BuiltIn);
for id in [HarnessId::GEMINI, HarnessId::SUPERCODE] {
let harness = report
.harnesses
.iter()
.find(|harness| harness.id.as_str() == id)
.unwrap();
assert_eq!(harness.native.discover, ImplementationKind::BuiltIn);
assert_eq!(harness.native.load, ImplementationKind::BuiltIn);
assert_eq!(harness.native.follow, ImplementationKind::BuiltIn);
}
assert_eq!(grok.native.export, ImplementationKind::BuiltIn);
assert_eq!(
grok.runtime.implementation,
ImplementationKind::GenericProtocol
);
assert_eq!(
grok.runtime.default_launch.as_ref().unwrap().arguments,
["--sandbox", "workspace", "agent", "--no-leader", "stdio"]
);
assert!(!grok
.runtime
.default_launch
.as_ref()
.unwrap()
.arguments
.iter()
.any(|argument| argument == "--always-approve"));
assert!(grok.runtime.capabilities.resume_session);
}
}