Skip to main content

vtcode_config/
acp.rs

1use crate::constants::env::acp::AgentClientProtocolEnvKey;
2use serde::{Deserialize, Serialize};
3
4fn parse_env_bool(key: AgentClientProtocolEnvKey, default: bool) -> bool {
5    crate::env_helpers::parse_env_bool(key.as_str(), default)
6}
7
8fn default_enabled() -> bool {
9    parse_env_bool(AgentClientProtocolEnvKey::Enabled, false)
10}
11
12fn default_zed_enabled() -> bool {
13    parse_env_bool(AgentClientProtocolEnvKey::ZedEnabled, default_enabled())
14}
15
16fn default_zed_tools_read_file_enabled() -> bool {
17    parse_env_bool(AgentClientProtocolEnvKey::ZedToolsReadFileEnabled, true)
18}
19
20fn default_zed_tools_list_files_enabled() -> bool {
21    parse_env_bool(AgentClientProtocolEnvKey::ZedToolsListFilesEnabled, true)
22}
23
24fn parse_env_trust_mode(
25    key: AgentClientProtocolEnvKey,
26    default: AgentClientProtocolZedWorkspaceTrustMode,
27) -> AgentClientProtocolZedWorkspaceTrustMode {
28    std::env::var(key.as_str())
29        .ok()
30        .and_then(|value| AgentClientProtocolZedWorkspaceTrustMode::from_env_value(&value))
31        .unwrap_or(default)
32}
33
34fn default_zed_workspace_trust_mode() -> AgentClientProtocolZedWorkspaceTrustMode {
35    parse_env_trust_mode(
36        AgentClientProtocolEnvKey::ZedWorkspaceTrust,
37        AgentClientProtocolZedWorkspaceTrustMode::FullAuto,
38    )
39}
40
41fn default_transport() -> AgentClientProtocolTransport {
42    AgentClientProtocolTransport::Stdio
43}
44
45/// Agent Client Protocol configuration root
46#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
47#[derive(Debug, Clone, Deserialize, Serialize)]
48pub struct AgentClientProtocolConfig {
49    /// Globally enable the ACP bridge
50    #[serde(default = "default_enabled")]
51    pub enabled: bool,
52
53    /// Agent Client Protocol settings
54    #[serde(default)]
55    pub zed: AgentClientProtocolZedConfig,
56}
57
58impl Default for AgentClientProtocolConfig {
59    fn default() -> Self {
60        Self {
61            enabled: default_enabled(),
62            zed: AgentClientProtocolZedConfig::default(),
63        }
64    }
65}
66
67/// Transport options supported by the ACP bridge
68#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
70#[serde(rename_all = "snake_case")]
71pub enum AgentClientProtocolTransport {
72    /// Communicate over stdio (spawned process model)
73    Stdio,
74}
75
76impl Default for AgentClientProtocolTransport {
77    fn default() -> Self {
78        default_transport()
79    }
80}
81
82/// Zed-specific configuration
83#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
84#[derive(Debug, Clone, Deserialize, Serialize)]
85pub struct AgentClientProtocolZedConfig {
86    /// Enable Zed integration
87    #[serde(default = "default_zed_enabled")]
88    pub enabled: bool,
89
90    /// Transport used to communicate with the Zed client
91    #[serde(default = "default_transport")]
92    pub transport: AgentClientProtocolTransport,
93
94    /// Tool toggles exposed through the Zed bridge
95    #[serde(default)]
96    pub tools: AgentClientProtocolZedToolsConfig,
97
98    /// Desired workspace trust level when running under ACP
99    #[serde(default = "default_zed_workspace_trust_mode")]
100    pub workspace_trust: AgentClientProtocolZedWorkspaceTrustMode,
101
102    /// Authentication method configuration for ACP
103    #[serde(default)]
104    pub auth: AcpAuthConfig,
105}
106
107impl Default for AgentClientProtocolZedConfig {
108    fn default() -> Self {
109        Self {
110            enabled: default_zed_enabled(),
111            transport: default_transport(),
112            tools: AgentClientProtocolZedToolsConfig::default(),
113            workspace_trust: default_zed_workspace_trust_mode(),
114            auth: AcpAuthConfig::default(),
115        }
116    }
117}
118
119/// Zed bridge tool configuration flags
120#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
121#[derive(Debug, Clone, Deserialize, Serialize)]
122pub struct AgentClientProtocolZedToolsConfig {
123    /// Toggle the read_file function bridge
124    #[serde(default = "default_zed_tools_read_file_enabled")]
125    pub read_file: bool,
126
127    /// Toggle the list_files function bridge
128    #[serde(default = "default_zed_tools_list_files_enabled")]
129    pub list_files: bool,
130}
131
132impl Default for AgentClientProtocolZedToolsConfig {
133    fn default() -> Self {
134        Self {
135            read_file: default_zed_tools_read_file_enabled(),
136            list_files: default_zed_tools_list_files_enabled(),
137        }
138    }
139}
140
141/// Workspace trust configuration for the Zed ACP bridge
142#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
143#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
144#[serde(rename_all = "snake_case")]
145pub enum AgentClientProtocolZedWorkspaceTrustMode {
146    /// Maintain full automation trust
147    FullAuto,
148    /// Restrict to tools policy safeguards
149    ToolsPolicy,
150}
151
152impl AgentClientProtocolZedWorkspaceTrustMode {
153    fn from_env_value(value: &str) -> Option<Self> {
154        let normalized = value.trim().to_ascii_lowercase();
155        match normalized.as_str() {
156            "full_auto" | "full-auto" | "full" => Some(Self::FullAuto),
157            "tools_policy" | "tools-policy" | "tools" => Some(Self::ToolsPolicy),
158            _ => None,
159        }
160    }
161
162    /// Resolve the workspace trust level represented by this configuration.
163    pub fn to_workspace_trust_level(self) -> WorkspaceTrustLevel {
164        match self {
165            Self::FullAuto => WorkspaceTrustLevel::FullAuto,
166            Self::ToolsPolicy => WorkspaceTrustLevel::ToolsPolicy,
167        }
168    }
169}
170
171/// Workspace trust levels exposed through the Agent Client Protocol configuration.
172#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
173#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
174#[serde(rename_all = "snake_case")]
175#[derive(Default)]
176pub enum WorkspaceTrustLevel {
177    #[default]
178    ToolsPolicy,
179    FullAuto,
180}
181
182impl std::fmt::Display for WorkspaceTrustLevel {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        match self {
185            WorkspaceTrustLevel::ToolsPolicy => write!(f, "tools policy"),
186            WorkspaceTrustLevel::FullAuto => write!(f, "full auto"),
187        }
188    }
189}
190
191/// ACP authentication configuration
192#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
193#[derive(Debug, Clone, Deserialize, Serialize)]
194pub struct AcpAuthConfig {
195    /// Default authentication method for ACP agents
196    /// Options: "agent" (default - agent handles auth), "env_var", "terminal"
197    #[serde(default = "default_auth_method")]
198    pub default_method: String,
199
200    /// Environment variable name for auth (used when default_method is "env_var")
201    /// Examples: "OPENAI_API_KEY", "ANTHROPIC_API_KEY"
202    #[serde(default)]
203    pub env_var_name: Option<String>,
204
205    /// URL where users can get their API key (optional, for UI display)
206    #[serde(default)]
207    pub auth_url: Option<String>,
208}
209
210fn default_auth_method() -> String {
211    "agent".to_string()
212}
213
214impl Default for AcpAuthConfig {
215    fn default() -> Self {
216        Self {
217            default_method: default_auth_method(),
218            env_var_name: None,
219            auth_url: None,
220        }
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    #[test]
229    fn defaults_use_stdio_transport() {
230        let cfg = AgentClientProtocolConfig::default();
231        assert!(matches!(
232            cfg.zed.transport,
233            AgentClientProtocolTransport::Stdio
234        ));
235        assert!(cfg.zed.tools.read_file);
236        assert!(cfg.zed.tools.list_files);
237        assert!(matches!(
238            cfg.zed.workspace_trust,
239            AgentClientProtocolZedWorkspaceTrustMode::FullAuto
240        ));
241    }
242
243    #[test]
244    fn test_acp_auth_config_defaults() {
245        let auth = AcpAuthConfig::default();
246        assert_eq!(auth.default_method, "agent");
247        assert!(auth.env_var_name.is_none());
248        assert!(auth.auth_url.is_none());
249    }
250
251    #[test]
252    fn test_acp_auth_config_in_zed_config() {
253        let cfg = AgentClientProtocolZedConfig::default();
254        assert_eq!(cfg.auth.default_method, "agent");
255    }
256
257    #[test]
258    fn test_acp_auth_config_deserialization() {
259        let toml_str = r#"
260default_method = "env_var"
261env_var_name = "OPENAI_API_KEY"
262auth_url = "https://platform.openai.com/api-keys"
263"#;
264        let auth: AcpAuthConfig = toml::from_str(toml_str).unwrap();
265        assert_eq!(auth.default_method, "env_var");
266        assert_eq!(auth.env_var_name, Some("OPENAI_API_KEY".to_string()));
267        assert_eq!(
268            auth.auth_url,
269            Some("https://platform.openai.com/api-keys".to_string())
270        );
271    }
272}