1use crate::constants::env::acp::AgentClientProtocolEnvKey;
2use serde::{Deserialize, Serialize};
3
4fn parse_env_bool(key: AgentClientProtocolEnvKey, default: bool) -> bool {
5 std::env::var(key.as_str())
6 .ok()
7 .and_then(|value| {
8 let normalized = value.trim().to_ascii_lowercase();
9 match normalized.as_str() {
10 "1" | "true" | "yes" | "on" => Some(true),
11 "0" | "false" | "no" | "off" => Some(false),
12 _ => None,
13 }
14 })
15 .unwrap_or(default)
16}
17
18fn default_enabled() -> bool {
19 parse_env_bool(AgentClientProtocolEnvKey::Enabled, false)
20}
21
22fn default_zed_enabled() -> bool {
23 parse_env_bool(AgentClientProtocolEnvKey::ZedEnabled, default_enabled())
24}
25
26fn default_zed_tools_read_file_enabled() -> bool {
27 parse_env_bool(AgentClientProtocolEnvKey::ZedToolsReadFileEnabled, true)
28}
29
30fn default_zed_tools_list_files_enabled() -> bool {
31 parse_env_bool(AgentClientProtocolEnvKey::ZedToolsListFilesEnabled, true)
32}
33
34fn parse_env_trust_mode(
35 key: AgentClientProtocolEnvKey,
36 default: AgentClientProtocolZedWorkspaceTrustMode,
37) -> AgentClientProtocolZedWorkspaceTrustMode {
38 std::env::var(key.as_str())
39 .ok()
40 .and_then(|value| AgentClientProtocolZedWorkspaceTrustMode::from_env_value(&value))
41 .unwrap_or(default)
42}
43
44fn default_zed_workspace_trust_mode() -> AgentClientProtocolZedWorkspaceTrustMode {
45 parse_env_trust_mode(
46 AgentClientProtocolEnvKey::ZedWorkspaceTrust,
47 AgentClientProtocolZedWorkspaceTrustMode::FullAuto,
48 )
49}
50
51fn default_transport() -> AgentClientProtocolTransport {
52 AgentClientProtocolTransport::Stdio
53}
54
55#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57#[derive(Debug, Clone, Deserialize, Serialize)]
58pub struct AgentClientProtocolConfig {
59 #[serde(default = "default_enabled")]
61 pub enabled: bool,
62
63 #[serde(default)]
65 pub zed: AgentClientProtocolZedConfig,
66}
67
68impl Default for AgentClientProtocolConfig {
69 fn default() -> Self {
70 Self {
71 enabled: default_enabled(),
72 zed: AgentClientProtocolZedConfig::default(),
73 }
74 }
75}
76
77#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
80#[serde(rename_all = "snake_case")]
81pub enum AgentClientProtocolTransport {
82 Stdio,
84}
85
86impl Default for AgentClientProtocolTransport {
87 fn default() -> Self {
88 default_transport()
89 }
90}
91
92#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
94#[derive(Debug, Clone, Deserialize, Serialize)]
95pub struct AgentClientProtocolZedConfig {
96 #[serde(default = "default_zed_enabled")]
98 pub enabled: bool,
99
100 #[serde(default = "default_transport")]
102 pub transport: AgentClientProtocolTransport,
103
104 #[serde(default)]
106 pub tools: AgentClientProtocolZedToolsConfig,
107
108 #[serde(default = "default_zed_workspace_trust_mode")]
110 pub workspace_trust: AgentClientProtocolZedWorkspaceTrustMode,
111
112 #[serde(default)]
114 pub auth: AcpAuthConfig,
115}
116
117impl Default for AgentClientProtocolZedConfig {
118 fn default() -> Self {
119 Self {
120 enabled: default_zed_enabled(),
121 transport: default_transport(),
122 tools: AgentClientProtocolZedToolsConfig::default(),
123 workspace_trust: default_zed_workspace_trust_mode(),
124 auth: AcpAuthConfig::default(),
125 }
126 }
127}
128
129#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
131#[derive(Debug, Clone, Deserialize, Serialize)]
132pub struct AgentClientProtocolZedToolsConfig {
133 #[serde(default = "default_zed_tools_read_file_enabled")]
135 pub read_file: bool,
136
137 #[serde(default = "default_zed_tools_list_files_enabled")]
139 pub list_files: bool,
140}
141
142impl Default for AgentClientProtocolZedToolsConfig {
143 fn default() -> Self {
144 Self {
145 read_file: default_zed_tools_read_file_enabled(),
146 list_files: default_zed_tools_list_files_enabled(),
147 }
148 }
149}
150
151#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
153#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
154#[serde(rename_all = "snake_case")]
155pub enum AgentClientProtocolZedWorkspaceTrustMode {
156 FullAuto,
158 ToolsPolicy,
160}
161
162impl AgentClientProtocolZedWorkspaceTrustMode {
163 fn from_env_value(value: &str) -> Option<Self> {
164 let normalized = value.trim().to_ascii_lowercase();
165 match normalized.as_str() {
166 "full_auto" | "full-auto" | "full" => Some(Self::FullAuto),
167 "tools_policy" | "tools-policy" | "tools" => Some(Self::ToolsPolicy),
168 _ => None,
169 }
170 }
171
172 pub fn to_workspace_trust_level(self) -> WorkspaceTrustLevel {
174 match self {
175 Self::FullAuto => WorkspaceTrustLevel::FullAuto,
176 Self::ToolsPolicy => WorkspaceTrustLevel::ToolsPolicy,
177 }
178 }
179}
180
181#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
183#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
184#[serde(rename_all = "snake_case")]
185#[derive(Default)]
186pub enum WorkspaceTrustLevel {
187 #[default]
188 ToolsPolicy,
189 FullAuto,
190}
191
192impl std::fmt::Display for WorkspaceTrustLevel {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 match self {
195 WorkspaceTrustLevel::ToolsPolicy => write!(f, "tools policy"),
196 WorkspaceTrustLevel::FullAuto => write!(f, "full auto"),
197 }
198 }
199}
200
201#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
203#[derive(Debug, Clone, Deserialize, Serialize)]
204pub struct AcpAuthConfig {
205 #[serde(default = "default_auth_method")]
208 pub default_method: String,
209
210 #[serde(default)]
213 pub env_var_name: Option<String>,
214
215 #[serde(default)]
217 pub auth_url: Option<String>,
218}
219
220fn default_auth_method() -> String {
221 "agent".to_string()
222}
223
224impl Default for AcpAuthConfig {
225 fn default() -> Self {
226 Self {
227 default_method: default_auth_method(),
228 env_var_name: None,
229 auth_url: None,
230 }
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn defaults_use_stdio_transport() {
240 let cfg = AgentClientProtocolConfig::default();
241 assert!(matches!(
242 cfg.zed.transport,
243 AgentClientProtocolTransport::Stdio
244 ));
245 assert!(cfg.zed.tools.read_file);
246 assert!(cfg.zed.tools.list_files);
247 assert!(matches!(
248 cfg.zed.workspace_trust,
249 AgentClientProtocolZedWorkspaceTrustMode::FullAuto
250 ));
251 }
252
253 #[test]
254 fn test_acp_auth_config_defaults() {
255 let auth = AcpAuthConfig::default();
256 assert_eq!(auth.default_method, "agent");
257 assert!(auth.env_var_name.is_none());
258 assert!(auth.auth_url.is_none());
259 }
260
261 #[test]
262 fn test_acp_auth_config_in_zed_config() {
263 let cfg = AgentClientProtocolZedConfig::default();
264 assert_eq!(cfg.auth.default_method, "agent");
265 }
266
267 #[test]
268 fn test_acp_auth_config_deserialization() {
269 let toml_str = r#"
270default_method = "env_var"
271env_var_name = "OPENAI_API_KEY"
272auth_url = "https://platform.openai.com/api-keys"
273"#;
274 let auth: AcpAuthConfig = toml::from_str(toml_str).unwrap();
275 assert_eq!(auth.default_method, "env_var");
276 assert_eq!(auth.env_var_name, Some("OPENAI_API_KEY".to_string()));
277 assert_eq!(auth.auth_url, Some("https://platform.openai.com/api-keys".to_string()));
278 }
279}