agent.rs

  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    /// Per-tool permission rules for granular control over which tool actions require confirmation.
126    ///
127    /// This setting only applies to the native Zed agent. External agent servers (Claude Code, Gemini CLI, etc.)
128    /// have their own permission systems and are not affected by these settings.
129    pub tool_permissions: Option<ToolPermissionsContent>,
130}
131
132impl AgentSettingsContent {
133    pub fn set_dock(&mut self, dock: DockPosition) {
134        self.dock = Some(dock);
135    }
136
137    pub fn set_model(&mut self, language_model: LanguageModelSelection) {
138        // let model = language_model.id().0.to_string();
139        // let provider = language_model.provider_id().0.to_string();
140        // self.default_model = Some(LanguageModelSelection {
141        //     provider: provider.into(),
142        //     model,
143        // });
144        self.default_model = Some(language_model)
145    }
146
147    pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
148        self.inline_assistant_model = Some(LanguageModelSelection {
149            provider: provider.into(),
150            model,
151        });
152    }
153    pub fn set_inline_assistant_use_streaming_tools(&mut self, use_tools: bool) {
154        self.inline_assistant_use_streaming_tools = Some(use_tools);
155    }
156
157    pub fn set_commit_message_model(&mut self, provider: String, model: String) {
158        self.commit_message_model = Some(LanguageModelSelection {
159            provider: provider.into(),
160            model,
161        });
162    }
163
164    pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
165        self.thread_summary_model = Some(LanguageModelSelection {
166            provider: provider.into(),
167            model,
168        });
169    }
170
171    pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
172        self.always_allow_tool_actions = Some(allow);
173    }
174
175    pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
176        self.play_sound_when_agent_done = Some(allow);
177    }
178
179    pub fn set_single_file_review(&mut self, allow: bool) {
180        self.single_file_review = Some(allow);
181    }
182
183    pub fn set_use_modifier_to_send(&mut self, always_use: bool) {
184        self.use_modifier_to_send = Some(always_use);
185    }
186
187    pub fn set_profile(&mut self, profile_id: Arc<str>) {
188        self.default_profile = Some(profile_id);
189    }
190
191    pub fn add_favorite_model(&mut self, model: LanguageModelSelection) {
192        if !self.favorite_models.contains(&model) {
193            self.favorite_models.push(model);
194        }
195    }
196
197    pub fn remove_favorite_model(&mut self, model: &LanguageModelSelection) {
198        self.favorite_models.retain(|m| m != model);
199    }
200}
201
202#[with_fallible_options]
203#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
204pub struct AgentProfileContent {
205    pub name: Arc<str>,
206    #[serde(default)]
207    pub tools: IndexMap<Arc<str>, bool>,
208    /// Whether all context servers are enabled by default.
209    pub enable_all_context_servers: Option<bool>,
210    #[serde(default)]
211    pub context_servers: IndexMap<Arc<str>, ContextServerPresetContent>,
212    /// The default language model selected when using this profile.
213    pub default_model: Option<LanguageModelSelection>,
214}
215
216#[with_fallible_options]
217#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
218pub struct ContextServerPresetContent {
219    pub tools: IndexMap<Arc<str>, bool>,
220}
221
222#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
223#[serde(rename_all = "snake_case")]
224pub enum DefaultAgentView {
225    #[default]
226    Thread,
227    TextThread,
228}
229
230#[derive(
231    Copy,
232    Clone,
233    Default,
234    Debug,
235    Serialize,
236    Deserialize,
237    JsonSchema,
238    MergeFrom,
239    PartialEq,
240    strum::VariantArray,
241    strum::VariantNames,
242)]
243#[serde(rename_all = "snake_case")]
244pub enum NotifyWhenAgentWaiting {
245    #[default]
246    PrimaryScreen,
247    AllScreens,
248    Never,
249}
250
251#[with_fallible_options]
252#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
253pub struct LanguageModelSelection {
254    pub provider: LanguageModelProviderSetting,
255    pub model: String,
256}
257
258#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Default)]
259#[serde(rename_all = "snake_case")]
260pub enum CompletionMode {
261    #[default]
262    Normal,
263    #[serde(alias = "max")]
264    Burn,
265}
266
267#[with_fallible_options]
268#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
269pub struct LanguageModelParameters {
270    pub provider: Option<LanguageModelProviderSetting>,
271    pub model: Option<SharedString>,
272    #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
273    pub temperature: Option<f32>,
274}
275
276#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, MergeFrom)]
277pub struct LanguageModelProviderSetting(pub String);
278
279impl JsonSchema for LanguageModelProviderSetting {
280    fn schema_name() -> Cow<'static, str> {
281        "LanguageModelProviderSetting".into()
282    }
283
284    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
285        // list the builtin providers as a subset so that we still auto complete them in the settings
286        json_schema!({
287            "anyOf": [
288                {
289                    "type": "string",
290                    "enum": [
291                        "amazon-bedrock",
292                        "anthropic",
293                        "copilot_chat",
294                        "deepseek",
295                        "google",
296                        "lmstudio",
297                        "mistral",
298                        "ollama",
299                        "openai",
300                        "openrouter",
301                        "vercel",
302                        "x_ai",
303                        "zed.dev"
304                    ]
305                },
306                {
307                    "type": "string",
308                }
309            ]
310        })
311    }
312}
313
314impl From<String> for LanguageModelProviderSetting {
315    fn from(provider: String) -> Self {
316        Self(provider)
317    }
318}
319
320impl From<&str> for LanguageModelProviderSetting {
321    fn from(provider: &str) -> Self {
322        Self(provider.to_string())
323    }
324}
325
326#[with_fallible_options]
327#[derive(Default, PartialEq, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug)]
328pub struct AllAgentServersSettings {
329    pub gemini: Option<BuiltinAgentServerSettings>,
330    pub claude: Option<BuiltinAgentServerSettings>,
331    pub codex: Option<BuiltinAgentServerSettings>,
332
333    /// Custom agent servers configured by the user
334    #[serde(flatten)]
335    pub custom: HashMap<SharedString, CustomAgentServerSettings>,
336}
337
338#[with_fallible_options]
339#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
340pub struct BuiltinAgentServerSettings {
341    /// Absolute path to a binary to be used when launching this agent.
342    ///
343    /// This can be used to run a specific binary without automatic downloads or searching `$PATH`.
344    #[serde(rename = "command")]
345    pub path: Option<PathBuf>,
346    /// If a binary is specified in `command`, it will be passed these arguments.
347    pub args: Option<Vec<String>>,
348    /// If a binary is specified in `command`, it will be passed these environment variables.
349    pub env: Option<HashMap<String, String>>,
350    /// Whether to skip searching `$PATH` for an agent server binary when
351    /// launching this agent.
352    ///
353    /// This has no effect if a `command` is specified. Otherwise, when this is
354    /// `false`, Zed will search `$PATH` for an agent server binary and, if one
355    /// is found, use it for threads with this agent. If no agent binary is
356    /// found on `$PATH`, Zed will automatically install and use its own binary.
357    /// When this is `true`, Zed will not search `$PATH`, and will always use
358    /// its own binary.
359    ///
360    /// Default: true
361    pub ignore_system_version: Option<bool>,
362    /// The default mode to use for this agent.
363    ///
364    /// Note: Not only all agents support modes.
365    ///
366    /// Default: None
367    pub default_mode: Option<String>,
368    /// The default model to use for this agent.
369    ///
370    /// This should be the model ID as reported by the agent.
371    ///
372    /// Default: None
373    pub default_model: Option<String>,
374    /// The favorite models for this agent.
375    ///
376    /// These are the model IDs as reported by the agent.
377    ///
378    /// Default: []
379    #[serde(default)]
380    pub favorite_models: Vec<String>,
381    /// Default values for session config options.
382    ///
383    /// This is a map from config option ID to value ID.
384    ///
385    /// Default: {}
386    #[serde(default)]
387    pub default_config_options: HashMap<String, String>,
388    /// Favorited values for session config options.
389    ///
390    /// This is a map from config option ID to a list of favorited value IDs.
391    ///
392    /// Default: {}
393    #[serde(default)]
394    pub favorite_config_option_values: HashMap<String, Vec<String>>,
395}
396
397#[with_fallible_options]
398#[derive(Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
399#[serde(tag = "type", rename_all = "snake_case")]
400pub enum CustomAgentServerSettings {
401    Custom {
402        #[serde(rename = "command")]
403        path: PathBuf,
404        #[serde(default)]
405        args: Vec<String>,
406        env: Option<HashMap<String, String>>,
407        /// The default mode to use for this agent.
408        ///
409        /// Note: Not only all agents support modes.
410        ///
411        /// Default: None
412        default_mode: Option<String>,
413        /// The default model to use for this agent.
414        ///
415        /// This should be the model ID as reported by the agent.
416        ///
417        /// Default: None
418        default_model: Option<String>,
419        /// The favorite models for this agent.
420        ///
421        /// These are the model IDs as reported by the agent.
422        ///
423        /// Default: []
424        #[serde(default)]
425        favorite_models: Vec<String>,
426        /// Default values for session config options.
427        ///
428        /// This is a map from config option ID to value ID.
429        ///
430        /// Default: {}
431        #[serde(default)]
432        default_config_options: HashMap<String, String>,
433        /// Favorited values for session config options.
434        ///
435        /// This is a map from config option ID to a list of favorited value IDs.
436        ///
437        /// Default: {}
438        #[serde(default)]
439        favorite_config_option_values: HashMap<String, Vec<String>>,
440    },
441    Extension {
442        /// The default mode to use for this agent.
443        ///
444        /// Note: Not only all agents support modes.
445        ///
446        /// Default: None
447        default_mode: Option<String>,
448        /// The default model to use for this agent.
449        ///
450        /// This should be the model ID as reported by the agent.
451        ///
452        /// Default: None
453        default_model: Option<String>,
454        /// The favorite models for this agent.
455        ///
456        /// These are the model IDs as reported by the agent.
457        ///
458        /// Default: []
459        #[serde(default)]
460        favorite_models: Vec<String>,
461        /// Default values for session config options.
462        ///
463        /// This is a map from config option ID to value ID.
464        ///
465        /// Default: {}
466        #[serde(default)]
467        default_config_options: HashMap<String, String>,
468        /// Favorited values for session config options.
469        ///
470        /// This is a map from config option ID to a list of favorited value IDs.
471        ///
472        /// Default: {}
473        #[serde(default)]
474        favorite_config_option_values: HashMap<String, Vec<String>>,
475    },
476}
477
478#[with_fallible_options]
479#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
480pub struct ToolPermissionsContent {
481    /// Per-tool permission rules.
482    /// Keys: terminal, edit_file, delete_path, move_path, create_directory,
483    ///       save_file, fetch, web_search
484    #[serde(default)]
485    pub tools: HashMap<Arc<str>, ToolRulesContent>,
486}
487
488#[with_fallible_options]
489#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
490pub struct ToolRulesContent {
491    /// Default mode when no regex rules match.
492    /// Default: confirm
493    pub default_mode: Option<ToolPermissionMode>,
494
495    /// Regexes for inputs to auto-approve.
496    /// For terminal: matches command. For file tools: matches path. For fetch: matches URL.
497    /// Default: []
498    pub always_allow: Option<ExtendingVec<ToolRegexRule>>,
499
500    /// Regexes for inputs to auto-reject.
501    /// **SECURITY**: These take precedence over ALL other rules, across ALL settings layers.
502    /// Default: []
503    pub always_deny: Option<ExtendingVec<ToolRegexRule>>,
504
505    /// Regexes for inputs that must always prompt.
506    /// Takes precedence over always_allow but not always_deny.
507    /// Default: []
508    pub always_confirm: Option<ExtendingVec<ToolRegexRule>>,
509}
510
511#[with_fallible_options]
512#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
513pub struct ToolRegexRule {
514    /// The regex pattern to match.
515    pub pattern: String,
516
517    /// Whether the regex is case-sensitive.
518    /// Default: false (case-insensitive)
519    pub case_sensitive: Option<bool>,
520}
521
522#[derive(
523    Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom,
524)]
525#[serde(rename_all = "snake_case")]
526pub enum ToolPermissionMode {
527    /// Auto-approve without prompting.
528    Allow,
529    /// Auto-reject with an error.
530    Deny,
531    /// Always prompt for confirmation (default behavior).
532    #[default]
533    Confirm,
534}