use crate::constants::env::acp::AgentClientProtocolEnvKey;
use serde::{Deserialize, Serialize};
fn parse_env_bool(key: AgentClientProtocolEnvKey, default: bool) -> bool {
std::env::var(key.as_str())
.ok()
.and_then(|value| {
let normalized = value.trim().to_ascii_lowercase();
match normalized.as_str() {
"1" | "true" | "yes" | "on" => Some(true),
"0" | "false" | "no" | "off" => Some(false),
_ => None,
}
})
.unwrap_or(default)
}
fn default_enabled() -> bool {
parse_env_bool(AgentClientProtocolEnvKey::Enabled, false)
}
fn default_zed_enabled() -> bool {
parse_env_bool(AgentClientProtocolEnvKey::ZedEnabled, default_enabled())
}
fn default_zed_tools_read_file_enabled() -> bool {
parse_env_bool(AgentClientProtocolEnvKey::ZedToolsReadFileEnabled, true)
}
fn default_zed_tools_list_files_enabled() -> bool {
parse_env_bool(AgentClientProtocolEnvKey::ZedToolsListFilesEnabled, true)
}
fn parse_env_trust_mode(
key: AgentClientProtocolEnvKey,
default: AgentClientProtocolZedWorkspaceTrustMode,
) -> AgentClientProtocolZedWorkspaceTrustMode {
std::env::var(key.as_str())
.ok()
.and_then(|value| AgentClientProtocolZedWorkspaceTrustMode::from_env_value(&value))
.unwrap_or(default)
}
fn default_zed_workspace_trust_mode() -> AgentClientProtocolZedWorkspaceTrustMode {
parse_env_trust_mode(
AgentClientProtocolEnvKey::ZedWorkspaceTrust,
AgentClientProtocolZedWorkspaceTrustMode::FullAuto,
)
}
fn default_transport() -> AgentClientProtocolTransport {
AgentClientProtocolTransport::Stdio
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AgentClientProtocolConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
#[serde(default)]
pub zed: AgentClientProtocolZedConfig,
}
impl Default for AgentClientProtocolConfig {
fn default() -> Self {
Self {
enabled: default_enabled(),
zed: AgentClientProtocolZedConfig::default(),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AgentClientProtocolTransport {
Stdio,
}
impl Default for AgentClientProtocolTransport {
fn default() -> Self {
default_transport()
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AgentClientProtocolZedConfig {
#[serde(default = "default_zed_enabled")]
pub enabled: bool,
#[serde(default = "default_transport")]
pub transport: AgentClientProtocolTransport,
#[serde(default)]
pub tools: AgentClientProtocolZedToolsConfig,
#[serde(default = "default_zed_workspace_trust_mode")]
pub workspace_trust: AgentClientProtocolZedWorkspaceTrustMode,
#[serde(default)]
pub auth: AcpAuthConfig,
}
impl Default for AgentClientProtocolZedConfig {
fn default() -> Self {
Self {
enabled: default_zed_enabled(),
transport: default_transport(),
tools: AgentClientProtocolZedToolsConfig::default(),
workspace_trust: default_zed_workspace_trust_mode(),
auth: AcpAuthConfig::default(),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AgentClientProtocolZedToolsConfig {
#[serde(default = "default_zed_tools_read_file_enabled")]
pub read_file: bool,
#[serde(default = "default_zed_tools_list_files_enabled")]
pub list_files: bool,
}
impl Default for AgentClientProtocolZedToolsConfig {
fn default() -> Self {
Self {
read_file: default_zed_tools_read_file_enabled(),
list_files: default_zed_tools_list_files_enabled(),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AgentClientProtocolZedWorkspaceTrustMode {
FullAuto,
ToolsPolicy,
}
impl AgentClientProtocolZedWorkspaceTrustMode {
fn from_env_value(value: &str) -> Option<Self> {
let normalized = value.trim().to_ascii_lowercase();
match normalized.as_str() {
"full_auto" | "full-auto" | "full" => Some(Self::FullAuto),
"tools_policy" | "tools-policy" | "tools" => Some(Self::ToolsPolicy),
_ => None,
}
}
pub fn to_workspace_trust_level(self) -> WorkspaceTrustLevel {
match self {
Self::FullAuto => WorkspaceTrustLevel::FullAuto,
Self::ToolsPolicy => WorkspaceTrustLevel::ToolsPolicy,
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum WorkspaceTrustLevel {
#[default]
ToolsPolicy,
FullAuto,
}
impl std::fmt::Display for WorkspaceTrustLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WorkspaceTrustLevel::ToolsPolicy => write!(f, "tools policy"),
WorkspaceTrustLevel::FullAuto => write!(f, "full auto"),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AcpAuthConfig {
#[serde(default = "default_auth_method")]
pub default_method: String,
#[serde(default)]
pub env_var_name: Option<String>,
#[serde(default)]
pub auth_url: Option<String>,
}
fn default_auth_method() -> String {
"agent".to_string()
}
impl Default for AcpAuthConfig {
fn default() -> Self {
Self {
default_method: default_auth_method(),
env_var_name: None,
auth_url: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_use_stdio_transport() {
let cfg = AgentClientProtocolConfig::default();
assert!(matches!(
cfg.zed.transport,
AgentClientProtocolTransport::Stdio
));
assert!(cfg.zed.tools.read_file);
assert!(cfg.zed.tools.list_files);
assert!(matches!(
cfg.zed.workspace_trust,
AgentClientProtocolZedWorkspaceTrustMode::FullAuto
));
}
#[test]
fn test_acp_auth_config_defaults() {
let auth = AcpAuthConfig::default();
assert_eq!(auth.default_method, "agent");
assert!(auth.env_var_name.is_none());
assert!(auth.auth_url.is_none());
}
#[test]
fn test_acp_auth_config_in_zed_config() {
let cfg = AgentClientProtocolZedConfig::default();
assert_eq!(cfg.auth.default_method, "agent");
}
#[test]
fn test_acp_auth_config_deserialization() {
let toml_str = r#"
default_method = "env_var"
env_var_name = "OPENAI_API_KEY"
auth_url = "https://platform.openai.com/api-keys"
"#;
let auth: AcpAuthConfig = toml::from_str(toml_str).unwrap();
assert_eq!(auth.default_method, "env_var");
assert_eq!(auth.env_var_name, Some("OPENAI_API_KEY".to_string()));
assert_eq!(
auth.auth_url,
Some("https://platform.openai.com/api-keys".to_string())
);
}
}