agent.rs

  1use collections::{HashMap, IndexMap};
  2use gpui::SharedString;
  3use schemars::{JsonSchema, json_schema};
  4use serde::{Deserialize, Serialize};
  5use serde_with::skip_serializing_none;
  6use settings_macros::MergeFrom;
  7use std::{borrow::Cow, path::PathBuf, sync::Arc};
  8
  9use crate::DockPosition;
 10
 11#[skip_serializing_none]
 12#[derive(Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, Default)]
 13pub struct AgentSettingsContent {
 14    /// Whether the Agent is enabled.
 15    ///
 16    /// Default: true
 17    pub enabled: Option<bool>,
 18    /// Whether to show the agent panel button in the status bar.
 19    ///
 20    /// Default: true
 21    pub button: Option<bool>,
 22    /// Where to dock the agent panel.
 23    ///
 24    /// Default: right
 25    pub dock: Option<DockPosition>,
 26    /// Default width in pixels when the agent panel is docked to the left or right.
 27    ///
 28    /// Default: 640
 29    #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
 30    pub default_width: Option<f32>,
 31    /// Default height in pixels when the agent panel is docked to the bottom.
 32    ///
 33    /// Default: 320
 34    #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
 35    pub default_height: Option<f32>,
 36    /// The default model to use when creating new chats and for other features when a specific model is not specified.
 37    pub default_model: Option<LanguageModelSelection>,
 38    /// Model to use for the inline assistant. Defaults to default_model when not specified.
 39    pub inline_assistant_model: Option<LanguageModelSelection>,
 40    /// Model to use for generating git commit messages. Defaults to default_model when not specified.
 41    pub commit_message_model: Option<LanguageModelSelection>,
 42    /// Model to use for generating thread summaries. Defaults to default_model when not specified.
 43    pub thread_summary_model: Option<LanguageModelSelection>,
 44    /// Additional models with which to generate alternatives when performing inline assists.
 45    pub inline_alternatives: Option<Vec<LanguageModelSelection>>,
 46    /// The default profile to use in the Agent.
 47    ///
 48    /// Default: write
 49    pub default_profile: Option<Arc<str>>,
 50    /// Which view type to show by default in the agent panel.
 51    ///
 52    /// Default: "thread"
 53    pub default_view: Option<DefaultAgentView>,
 54    /// The available agent profiles.
 55    pub profiles: Option<IndexMap<Arc<str>, AgentProfileContent>>,
 56    /// Whenever a tool action would normally wait for your confirmation
 57    /// that you allow it, always choose to allow it.
 58    ///
 59    /// This setting has no effect on external agents that support permission modes, such as Claude Code.
 60    ///
 61    /// Set `agent_servers.claude.default_mode` to `bypassPermissions`, to disable all permission requests when using Claude Code.
 62    ///
 63    /// Default: false
 64    pub always_allow_tool_actions: Option<bool>,
 65    /// Where to show a popup notification when the agent is waiting for user input.
 66    ///
 67    /// Default: "primary_screen"
 68    pub notify_when_agent_waiting: Option<NotifyWhenAgentWaiting>,
 69    /// Whether to play a sound when the agent has either completed its response, or needs user input.
 70    ///
 71    /// Default: false
 72    pub play_sound_when_agent_done: Option<bool>,
 73    /// Whether to display agent edits in single-file editors in addition to the review multibuffer pane.
 74    ///
 75    /// Default: true
 76    pub single_file_review: Option<bool>,
 77    /// Additional parameters for language model requests. When making a request
 78    /// to a model, parameters will be taken from the last entry in this list
 79    /// that matches the model's provider and name. In each entry, both provider
 80    /// and model are optional, so that you can specify parameters for either
 81    /// one.
 82    ///
 83    /// Default: []
 84    #[serde(default)]
 85    pub model_parameters: Vec<LanguageModelParameters>,
 86    /// What completion mode to enable for new threads
 87    ///
 88    /// Default: normal
 89    pub preferred_completion_mode: Option<CompletionMode>,
 90    /// Whether to show thumb buttons for feedback in the agent panel.
 91    ///
 92    /// Default: true
 93    pub enable_feedback: Option<bool>,
 94    /// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
 95    ///
 96    /// Default: true
 97    pub expand_edit_card: Option<bool>,
 98    /// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
 99    ///
100    /// Default: true
101    pub expand_terminal_card: Option<bool>,
102    /// Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.
103    ///
104    /// Default: false
105    pub use_modifier_to_send: Option<bool>,
106    /// Minimum number of lines of height the agent message editor should have.
107    ///
108    /// Default: 4
109    pub message_editor_min_lines: Option<usize>,
110}
111
112impl AgentSettingsContent {
113    pub fn set_dock(&mut self, dock: DockPosition) {
114        self.dock = Some(dock);
115    }
116
117    pub fn set_model(&mut self, language_model: LanguageModelSelection) {
118        // let model = language_model.id().0.to_string();
119        // let provider = language_model.provider_id().0.to_string();
120        // self.default_model = Some(LanguageModelSelection {
121        //     provider: provider.into(),
122        //     model,
123        // });
124        self.default_model = Some(language_model)
125    }
126
127    pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
128        self.inline_assistant_model = Some(LanguageModelSelection {
129            provider: provider.into(),
130            model,
131        });
132    }
133
134    pub fn set_commit_message_model(&mut self, provider: String, model: String) {
135        self.commit_message_model = Some(LanguageModelSelection {
136            provider: provider.into(),
137            model,
138        });
139    }
140
141    pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
142        self.thread_summary_model = Some(LanguageModelSelection {
143            provider: provider.into(),
144            model,
145        });
146    }
147
148    pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
149        self.always_allow_tool_actions = Some(allow);
150    }
151
152    pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
153        self.play_sound_when_agent_done = Some(allow);
154    }
155
156    pub fn set_single_file_review(&mut self, allow: bool) {
157        self.single_file_review = Some(allow);
158    }
159
160    pub fn set_use_modifier_to_send(&mut self, always_use: bool) {
161        self.use_modifier_to_send = Some(always_use);
162    }
163
164    pub fn set_profile(&mut self, profile_id: Arc<str>) {
165        self.default_profile = Some(profile_id);
166    }
167}
168
169#[skip_serializing_none]
170#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
171pub struct AgentProfileContent {
172    pub name: Arc<str>,
173    #[serde(default)]
174    pub tools: IndexMap<Arc<str>, bool>,
175    /// Whether all context servers are enabled by default.
176    pub enable_all_context_servers: Option<bool>,
177    #[serde(default)]
178    pub context_servers: IndexMap<Arc<str>, ContextServerPresetContent>,
179}
180
181#[skip_serializing_none]
182#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
183pub struct ContextServerPresetContent {
184    pub tools: IndexMap<Arc<str>, bool>,
185}
186
187#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
188#[serde(rename_all = "snake_case")]
189pub enum DefaultAgentView {
190    #[default]
191    Thread,
192    TextThread,
193}
194
195#[derive(
196    Copy,
197    Clone,
198    Default,
199    Debug,
200    Serialize,
201    Deserialize,
202    JsonSchema,
203    MergeFrom,
204    PartialEq,
205    strum::VariantArray,
206    strum::VariantNames,
207)]
208#[serde(rename_all = "snake_case")]
209pub enum NotifyWhenAgentWaiting {
210    #[default]
211    PrimaryScreen,
212    AllScreens,
213    Never,
214}
215
216#[skip_serializing_none]
217#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
218pub struct LanguageModelSelection {
219    pub provider: LanguageModelProviderSetting,
220    pub model: String,
221}
222
223#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Default)]
224#[serde(rename_all = "snake_case")]
225pub enum CompletionMode {
226    #[default]
227    Normal,
228    #[serde(alias = "max")]
229    Burn,
230}
231
232#[skip_serializing_none]
233#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
234pub struct LanguageModelParameters {
235    pub provider: Option<LanguageModelProviderSetting>,
236    pub model: Option<SharedString>,
237    #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
238    pub temperature: Option<f32>,
239}
240
241#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, MergeFrom)]
242pub struct LanguageModelProviderSetting(pub String);
243
244impl JsonSchema for LanguageModelProviderSetting {
245    fn schema_name() -> Cow<'static, str> {
246        "LanguageModelProviderSetting".into()
247    }
248
249    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
250        // list the builtin providers as a subset so that we still auto complete them in the settings
251        json_schema!({
252            "anyOf": [
253                {
254                    "type": "string",
255                    "enum": [
256                        "amazon-bedrock",
257                        "anthropic",
258                        "copilot_chat",
259                        "deepseek",
260                        "google",
261                        "lmstudio",
262                        "mistral",
263                        "ollama",
264                        "openai",
265                        "openrouter",
266                        "vercel",
267                        "x_ai",
268                        "zed.dev"
269                    ]
270                },
271                {
272                    "type": "string",
273                }
274            ]
275        })
276    }
277}
278
279impl From<String> for LanguageModelProviderSetting {
280    fn from(provider: String) -> Self {
281        Self(provider)
282    }
283}
284
285impl From<&str> for LanguageModelProviderSetting {
286    fn from(provider: &str) -> Self {
287        Self(provider.to_string())
288    }
289}
290
291#[skip_serializing_none]
292#[derive(Default, PartialEq, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug)]
293pub struct AllAgentServersSettings {
294    pub gemini: Option<BuiltinAgentServerSettings>,
295    pub claude: Option<BuiltinAgentServerSettings>,
296    pub codex: Option<BuiltinAgentServerSettings>,
297
298    /// Custom agent servers configured by the user
299    #[serde(flatten)]
300    pub custom: HashMap<SharedString, CustomAgentServerSettings>,
301}
302
303#[skip_serializing_none]
304#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
305pub struct BuiltinAgentServerSettings {
306    /// Absolute path to a binary to be used when launching this agent.
307    ///
308    /// This can be used to run a specific binary without automatic downloads or searching `$PATH`.
309    #[serde(rename = "command")]
310    pub path: Option<PathBuf>,
311    /// If a binary is specified in `command`, it will be passed these arguments.
312    pub args: Option<Vec<String>>,
313    /// If a binary is specified in `command`, it will be passed these environment variables.
314    pub env: Option<HashMap<String, String>>,
315    /// Whether to skip searching `$PATH` for an agent server binary when
316    /// launching this agent.
317    ///
318    /// This has no effect if a `command` is specified. Otherwise, when this is
319    /// `false`, Zed will search `$PATH` for an agent server binary and, if one
320    /// is found, use it for threads with this agent. If no agent binary is
321    /// found on `$PATH`, Zed will automatically install and use its own binary.
322    /// When this is `true`, Zed will not search `$PATH`, and will always use
323    /// its own binary.
324    ///
325    /// Default: true
326    pub ignore_system_version: Option<bool>,
327    /// The default mode to use for this agent.
328    ///
329    /// Note: Not only all agents support modes.
330    ///
331    /// Default: None
332    pub default_mode: Option<String>,
333}
334
335#[skip_serializing_none]
336#[derive(Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
337pub struct CustomAgentServerSettings {
338    #[serde(rename = "command")]
339    pub path: PathBuf,
340    #[serde(default)]
341    pub args: Vec<String>,
342    pub env: Option<HashMap<String, String>>,
343    /// The default mode to use for this agent.
344    ///
345    /// Note: Not only all agents support modes.
346    ///
347    /// Default: None
348    pub default_mode: Option<String>,
349}