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    /// 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}