1use collections::{HashMap, IndexMap};
2use gpui::SharedString;
3use schemars::{JsonSchema, json_schema};
4use serde::{Deserialize, Serialize};
5use settings_macros::{MergeFrom, with_fallible_options};
6use std::sync::Arc;
7use std::{borrow::Cow, path::PathBuf};
8
9use crate::ExtendingVec;
10
11use crate::{DockPosition, DockSide};
12
13#[with_fallible_options]
14#[derive(Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, Default)]
15pub struct AgentSettingsContent {
16 /// Whether the Agent is enabled.
17 ///
18 /// Default: true
19 pub enabled: Option<bool>,
20 /// Whether to show the agent panel button in the status bar.
21 ///
22 /// Default: true
23 pub button: Option<bool>,
24 /// Where to dock the agent panel.
25 ///
26 /// Default: right
27 pub dock: Option<DockPosition>,
28 /// Where to dock the utility pane (the thread view pane).
29 ///
30 /// Default: left
31 pub agents_panel_dock: Option<DockSide>,
32 /// Default width in pixels when the agent panel is docked to the left or right.
33 ///
34 /// Default: 640
35 #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
36 pub default_width: Option<f32>,
37 /// Default height in pixels when the agent panel is docked to the bottom.
38 ///
39 /// Default: 320
40 #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
41 pub default_height: Option<f32>,
42 /// The default model to use when creating new chats and for other features when a specific model is not specified.
43 pub default_model: Option<LanguageModelSelection>,
44 /// Favorite models to show at the top of the model selector.
45 #[serde(default)]
46 pub favorite_models: Vec<LanguageModelSelection>,
47 /// Model to use for the inline assistant. Defaults to default_model when not specified.
48 pub inline_assistant_model: Option<LanguageModelSelection>,
49 /// Model to use for the inline assistant when streaming tools are enabled.
50 ///
51 /// Default: true
52 pub inline_assistant_use_streaming_tools: Option<bool>,
53 /// Model to use for generating git commit messages.
54 ///
55 /// Default: true
56 pub commit_message_model: Option<LanguageModelSelection>,
57 /// Model to use for generating thread summaries. Defaults to default_model when not specified.
58 pub thread_summary_model: Option<LanguageModelSelection>,
59 /// Additional models with which to generate alternatives when performing inline assists.
60 pub inline_alternatives: Option<Vec<LanguageModelSelection>>,
61 /// The default profile to use in the Agent.
62 ///
63 /// Default: write
64 pub default_profile: Option<Arc<str>>,
65 /// Which view type to show by default in the agent panel.
66 ///
67 /// Default: "thread"
68 pub default_view: Option<DefaultAgentView>,
69 /// The available agent profiles.
70 pub profiles: Option<IndexMap<Arc<str>, AgentProfileContent>>,
71 /// Whenever a tool action would normally wait for your confirmation
72 /// that you allow it, always choose to allow it.
73 ///
74 /// This setting has no effect on external agents that support permission modes, such as Claude Code.
75 ///
76 /// Set `agent_servers.claude.default_mode` to `bypassPermissions`, to disable all permission requests when using Claude Code.
77 ///
78 /// Default: false
79 pub always_allow_tool_actions: Option<bool>,
80 /// Where to show a popup notification when the agent is waiting for user input.
81 ///
82 /// Default: "primary_screen"
83 pub notify_when_agent_waiting: Option<NotifyWhenAgentWaiting>,
84 /// Whether to play a sound when the agent has either completed its response, or needs user input.
85 ///
86 /// Default: false
87 pub play_sound_when_agent_done: Option<bool>,
88 /// Whether to display agent edits in single-file editors in addition to the review multibuffer pane.
89 ///
90 /// Default: true
91 pub single_file_review: Option<bool>,
92 /// Additional parameters for language model requests. When making a request
93 /// to a model, parameters will be taken from the last entry in this list
94 /// that matches the model's provider and name. In each entry, both provider
95 /// and model are optional, so that you can specify parameters for either
96 /// one.
97 ///
98 /// Default: []
99 #[serde(default)]
100 pub model_parameters: Vec<LanguageModelParameters>,
101 /// What completion mode to enable for new threads
102 ///
103 /// Default: normal
104 pub preferred_completion_mode: Option<CompletionMode>,
105 /// Whether to show thumb buttons for feedback in the agent panel.
106 ///
107 /// Default: true
108 pub enable_feedback: Option<bool>,
109 /// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
110 ///
111 /// Default: true
112 pub expand_edit_card: Option<bool>,
113 /// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
114 ///
115 /// Default: true
116 pub expand_terminal_card: Option<bool>,
117 /// Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.
118 ///
119 /// Default: false
120 pub use_modifier_to_send: Option<bool>,
121 /// Minimum number of lines of height the agent message editor should have.
122 ///
123 /// Default: 4
124 pub message_editor_min_lines: Option<usize>,
125 /// Whether to show turn statistics (elapsed time during generation, final turn duration).
126 ///
127 /// Default: false
128 pub show_turn_stats: Option<bool>,
129 /// Per-tool permission rules for granular control over which tool actions require confirmation.
130 ///
131 /// This setting only applies to the native Zed agent. External agent servers (Claude Code, Gemini CLI, etc.)
132 /// have their own permission systems and are not affected by these settings.
133 pub tool_permissions: Option<ToolPermissionsContent>,
134}
135
136impl AgentSettingsContent {
137 pub fn set_dock(&mut self, dock: DockPosition) {
138 self.dock = Some(dock);
139 }
140
141 pub fn set_model(&mut self, language_model: LanguageModelSelection) {
142 // let model = language_model.id().0.to_string();
143 // let provider = language_model.provider_id().0.to_string();
144 // self.default_model = Some(LanguageModelSelection {
145 // provider: provider.into(),
146 // model,
147 // });
148 self.default_model = Some(language_model)
149 }
150
151 pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
152 self.inline_assistant_model = Some(LanguageModelSelection {
153 provider: provider.into(),
154 model,
155 });
156 }
157 pub fn set_inline_assistant_use_streaming_tools(&mut self, use_tools: bool) {
158 self.inline_assistant_use_streaming_tools = Some(use_tools);
159 }
160
161 pub fn set_commit_message_model(&mut self, provider: String, model: String) {
162 self.commit_message_model = Some(LanguageModelSelection {
163 provider: provider.into(),
164 model,
165 });
166 }
167
168 pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
169 self.thread_summary_model = Some(LanguageModelSelection {
170 provider: provider.into(),
171 model,
172 });
173 }
174
175 pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
176 self.always_allow_tool_actions = Some(allow);
177 }
178
179 pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
180 self.play_sound_when_agent_done = Some(allow);
181 }
182
183 pub fn set_single_file_review(&mut self, allow: bool) {
184 self.single_file_review = Some(allow);
185 }
186
187 pub fn set_use_modifier_to_send(&mut self, always_use: bool) {
188 self.use_modifier_to_send = Some(always_use);
189 }
190
191 pub fn set_profile(&mut self, profile_id: Arc<str>) {
192 self.default_profile = Some(profile_id);
193 }
194
195 pub fn add_favorite_model(&mut self, model: LanguageModelSelection) {
196 if !self.favorite_models.contains(&model) {
197 self.favorite_models.push(model);
198 }
199 }
200
201 pub fn remove_favorite_model(&mut self, model: &LanguageModelSelection) {
202 self.favorite_models.retain(|m| m != model);
203 }
204}
205
206#[with_fallible_options]
207#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
208pub struct AgentProfileContent {
209 pub name: Arc<str>,
210 #[serde(default)]
211 pub tools: IndexMap<Arc<str>, bool>,
212 /// Whether all context servers are enabled by default.
213 pub enable_all_context_servers: Option<bool>,
214 #[serde(default)]
215 pub context_servers: IndexMap<Arc<str>, ContextServerPresetContent>,
216 /// The default language model selected when using this profile.
217 pub default_model: Option<LanguageModelSelection>,
218}
219
220#[with_fallible_options]
221#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
222pub struct ContextServerPresetContent {
223 pub tools: IndexMap<Arc<str>, bool>,
224}
225
226#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
227#[serde(rename_all = "snake_case")]
228pub enum DefaultAgentView {
229 #[default]
230 Thread,
231 TextThread,
232}
233
234#[derive(
235 Copy,
236 Clone,
237 Default,
238 Debug,
239 Serialize,
240 Deserialize,
241 JsonSchema,
242 MergeFrom,
243 PartialEq,
244 strum::VariantArray,
245 strum::VariantNames,
246)]
247#[serde(rename_all = "snake_case")]
248pub enum NotifyWhenAgentWaiting {
249 #[default]
250 PrimaryScreen,
251 AllScreens,
252 Never,
253}
254
255#[with_fallible_options]
256#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
257pub struct LanguageModelSelection {
258 pub provider: LanguageModelProviderSetting,
259 pub model: String,
260}
261
262#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Default)]
263#[serde(rename_all = "snake_case")]
264pub enum CompletionMode {
265 #[default]
266 Normal,
267 #[serde(alias = "max")]
268 Burn,
269}
270
271#[with_fallible_options]
272#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
273pub struct LanguageModelParameters {
274 pub provider: Option<LanguageModelProviderSetting>,
275 pub model: Option<SharedString>,
276 #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
277 pub temperature: Option<f32>,
278}
279
280#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, MergeFrom)]
281pub struct LanguageModelProviderSetting(pub String);
282
283impl JsonSchema for LanguageModelProviderSetting {
284 fn schema_name() -> Cow<'static, str> {
285 "LanguageModelProviderSetting".into()
286 }
287
288 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
289 // list the builtin providers as a subset so that we still auto complete them in the settings
290 json_schema!({
291 "anyOf": [
292 {
293 "type": "string",
294 "enum": [
295 "amazon-bedrock",
296 "anthropic",
297 "copilot_chat",
298 "deepseek",
299 "google",
300 "lmstudio",
301 "mistral",
302 "ollama",
303 "openai",
304 "openrouter",
305 "vercel",
306 "x_ai",
307 "zed.dev"
308 ]
309 },
310 {
311 "type": "string",
312 }
313 ]
314 })
315 }
316}
317
318impl From<String> for LanguageModelProviderSetting {
319 fn from(provider: String) -> Self {
320 Self(provider)
321 }
322}
323
324impl From<&str> for LanguageModelProviderSetting {
325 fn from(provider: &str) -> Self {
326 Self(provider.to_string())
327 }
328}
329
330#[with_fallible_options]
331#[derive(Default, PartialEq, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug)]
332pub struct AllAgentServersSettings {
333 pub gemini: Option<BuiltinAgentServerSettings>,
334 pub claude: Option<BuiltinAgentServerSettings>,
335 pub codex: Option<BuiltinAgentServerSettings>,
336
337 /// Custom agent servers configured by the user
338 #[serde(flatten)]
339 pub custom: HashMap<SharedString, CustomAgentServerSettings>,
340}
341
342#[with_fallible_options]
343#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
344pub struct BuiltinAgentServerSettings {
345 /// Absolute path to a binary to be used when launching this agent.
346 ///
347 /// This can be used to run a specific binary without automatic downloads or searching `$PATH`.
348 #[serde(rename = "command")]
349 pub path: Option<PathBuf>,
350 /// If a binary is specified in `command`, it will be passed these arguments.
351 pub args: Option<Vec<String>>,
352 /// If a binary is specified in `command`, it will be passed these environment variables.
353 pub env: Option<HashMap<String, String>>,
354 /// Whether to skip searching `$PATH` for an agent server binary when
355 /// launching this agent.
356 ///
357 /// This has no effect if a `command` is specified. Otherwise, when this is
358 /// `false`, Zed will search `$PATH` for an agent server binary and, if one
359 /// is found, use it for threads with this agent. If no agent binary is
360 /// found on `$PATH`, Zed will automatically install and use its own binary.
361 /// When this is `true`, Zed will not search `$PATH`, and will always use
362 /// its own binary.
363 ///
364 /// Default: true
365 pub ignore_system_version: Option<bool>,
366 /// The default mode to use for this agent.
367 ///
368 /// Note: Not only all agents support modes.
369 ///
370 /// Default: None
371 pub default_mode: Option<String>,
372 /// The default model to use for this agent.
373 ///
374 /// This should be the model ID as reported by the agent.
375 ///
376 /// Default: None
377 pub default_model: Option<String>,
378 /// The favorite models for this agent.
379 ///
380 /// These are the model IDs as reported by the agent.
381 ///
382 /// Default: []
383 #[serde(default)]
384 pub favorite_models: Vec<String>,
385 /// Default values for session config options.
386 ///
387 /// This is a map from config option ID to value ID.
388 ///
389 /// Default: {}
390 #[serde(default)]
391 pub default_config_options: HashMap<String, String>,
392 /// Favorited values for session config options.
393 ///
394 /// This is a map from config option ID to a list of favorited value IDs.
395 ///
396 /// Default: {}
397 #[serde(default)]
398 pub favorite_config_option_values: HashMap<String, Vec<String>>,
399}
400
401#[with_fallible_options]
402#[derive(Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
403#[serde(tag = "type", rename_all = "snake_case")]
404pub enum CustomAgentServerSettings {
405 Custom {
406 #[serde(rename = "command")]
407 path: PathBuf,
408 #[serde(default)]
409 args: Vec<String>,
410 env: Option<HashMap<String, String>>,
411 /// The default mode to use for this agent.
412 ///
413 /// Note: Not only all agents support modes.
414 ///
415 /// Default: None
416 default_mode: Option<String>,
417 /// The default model to use for this agent.
418 ///
419 /// This should be the model ID as reported by the agent.
420 ///
421 /// Default: None
422 default_model: Option<String>,
423 /// The favorite models for this agent.
424 ///
425 /// These are the model IDs as reported by the agent.
426 ///
427 /// Default: []
428 #[serde(default)]
429 favorite_models: Vec<String>,
430 /// Default values for session config options.
431 ///
432 /// This is a map from config option ID to value ID.
433 ///
434 /// Default: {}
435 #[serde(default)]
436 default_config_options: HashMap<String, String>,
437 /// Favorited values for session config options.
438 ///
439 /// This is a map from config option ID to a list of favorited value IDs.
440 ///
441 /// Default: {}
442 #[serde(default)]
443 favorite_config_option_values: HashMap<String, Vec<String>>,
444 },
445 Extension {
446 /// The default mode to use for this agent.
447 ///
448 /// Note: Not only all agents support modes.
449 ///
450 /// Default: None
451 default_mode: Option<String>,
452 /// The default model to use for this agent.
453 ///
454 /// This should be the model ID as reported by the agent.
455 ///
456 /// Default: None
457 default_model: Option<String>,
458 /// The favorite models for this agent.
459 ///
460 /// These are the model IDs as reported by the agent.
461 ///
462 /// Default: []
463 #[serde(default)]
464 favorite_models: Vec<String>,
465 /// Default values for session config options.
466 ///
467 /// This is a map from config option ID to value ID.
468 ///
469 /// Default: {}
470 #[serde(default)]
471 default_config_options: HashMap<String, String>,
472 /// Favorited values for session config options.
473 ///
474 /// This is a map from config option ID to a list of favorited value IDs.
475 ///
476 /// Default: {}
477 #[serde(default)]
478 favorite_config_option_values: HashMap<String, Vec<String>>,
479 },
480}
481
482#[with_fallible_options]
483#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
484pub struct ToolPermissionsContent {
485 /// Per-tool permission rules.
486 /// Keys: terminal, edit_file, delete_path, move_path, create_directory,
487 /// save_file, fetch, web_search
488 #[serde(default)]
489 pub tools: HashMap<Arc<str>, ToolRulesContent>,
490}
491
492#[with_fallible_options]
493#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
494pub struct ToolRulesContent {
495 /// Default mode when no regex rules match.
496 /// Default: confirm
497 pub default_mode: Option<ToolPermissionMode>,
498
499 /// Regexes for inputs to auto-approve.
500 /// For terminal: matches command. For file tools: matches path. For fetch: matches URL.
501 /// Default: []
502 pub always_allow: Option<ExtendingVec<ToolRegexRule>>,
503
504 /// Regexes for inputs to auto-reject.
505 /// **SECURITY**: These take precedence over ALL other rules, across ALL settings layers.
506 /// Default: []
507 pub always_deny: Option<ExtendingVec<ToolRegexRule>>,
508
509 /// Regexes for inputs that must always prompt.
510 /// Takes precedence over always_allow but not always_deny.
511 /// Default: []
512 pub always_confirm: Option<ExtendingVec<ToolRegexRule>>,
513}
514
515#[with_fallible_options]
516#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
517pub struct ToolRegexRule {
518 /// The regex pattern to match.
519 pub pattern: String,
520
521 /// Whether the regex is case-sensitive.
522 /// Default: false (case-insensitive)
523 pub case_sensitive: Option<bool>,
524}
525
526#[derive(
527 Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom,
528)]
529#[serde(rename_all = "snake_case")]
530pub enum ToolPermissionMode {
531 /// Auto-approve without prompting.
532 Allow,
533 /// Auto-reject with an error.
534 Deny,
535 /// Always prompt for confirmation (default behavior).
536 #[default]
537 Confirm,
538}