1191 lines · 42.5 KB · Rust
RawDownload
use collections::{HashMap, IndexMap};use schemars::{JsonSchema, json_schema};use serde::{Deserialize, Serialize};use settings_macros::{MergeFrom, with_fallible_options};use std::sync::Arc;use std::{borrow::Cow, path::PathBuf}; use crate::ExtendingVec; use crate::DockPosition; /// Where to position the threads sidebar.#[derive(    Clone,    Copy,    Debug,    Default,    PartialEq,    Eq,    Serialize,    Deserialize,    JsonSchema,    MergeFrom,    strum::VariantArray,    strum::VariantNames,)]#[serde(rename_all = "snake_case")]pub enum SidebarDockPosition {    /// Always show the sidebar on the left side.    #[default]    Left,    /// Always show the sidebar on the right side.    Right,} #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]pub enum SidebarSide {    #[default]    Left,    Right,} /// How thinking blocks should be displayed by default in the agent panel.#[derive(    Clone,    Copy,    Debug,    Default,    PartialEq,    Eq,    Serialize,    Deserialize,    JsonSchema,    MergeFrom,    strum::VariantArray,    strum::VariantNames,)]#[serde(rename_all = "snake_case")]pub enum ThinkingBlockDisplay {    /// Thinking blocks fully expand during streaming, then auto-collapse    /// when the model finishes thinking. Users can re-expand after collapse.    #[default]    Auto,    /// Thinking blocks auto-expand with a height constraint during streaming,    /// then remain in their constrained state when complete. Users can click    /// to fully expand or collapse.    Preview,    /// Thinking blocks are always fully expanded by default (no height constraint).    AlwaysExpanded,    /// Thinking blocks are always collapsed by default.    AlwaysCollapsed,} /// Threshold at which agent auto-compaction runs. See/// [`AutoCompactSettingsContent::threshold`] for the accepted formats.////// The canonical textual form is stored verbatim so it can round-trip through/// the settings UI; it is serialized back as a JSON string for percentages and/// as a JSON integer for token counts.#[derive(Clone, Debug, PartialEq, Eq, MergeFrom)]pub struct AutoCompactThreshold(pub String); impl From<String> for AutoCompactThreshold {    fn from(value: String) -> Self {        Self(value)    }} impl From<AutoCompactThreshold> for String {    fn from(value: AutoCompactThreshold) -> Self {        value.0    }} impl AsRef<str> for AutoCompactThreshold {    fn as_ref(&self) -> &str {        &self.0    }} impl Serialize for AutoCompactThreshold {    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {        if self.0.ends_with('%') {            serializer.serialize_str(&self.0)        } else if let Ok(tokens) = self.0.parse::<i64>() {            serializer.serialize_i64(tokens)        } else {            serializer.serialize_str(&self.0)        }    }} impl<'de> Deserialize<'de> for AutoCompactThreshold {    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {        struct ThresholdVisitor;         impl serde::de::Visitor<'_> for ThresholdVisitor {            type Value = AutoCompactThreshold;             fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {                formatter                    .write_str("a percentage string like \"90%\" or an integer number of tokens")            }             fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {                Ok(AutoCompactThreshold(value.to_owned()))            }             fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Self::Value, E> {                Ok(AutoCompactThreshold(value.to_string()))            }             fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Self::Value, E> {                Ok(AutoCompactThreshold(value.to_string()))            }             fn visit_f64<E: serde::de::Error>(self, value: f64) -> Result<Self::Value, E> {                Ok(AutoCompactThreshold(value.to_string()))            }        }         deserializer.deserialize_any(ThresholdVisitor)    }} impl JsonSchema for AutoCompactThreshold {    fn schema_name() -> Cow<'static, str> {        "AutoCompactThreshold".into()    }     fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {        json_schema!({            "oneOf": [                {                    "type": "string",                    "pattern": "^\\d+(\\.\\d+)?%$"                },                {                    "type": "integer"                }            ]        })    }} #[with_fallible_options]#[derive(Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, Default)]pub struct AutoCompactSettingsContent {    /// Whether to automatically compact the agent's context when it grows too    /// large, summarizing earlier messages to free up room in the model's    /// context window.    ///    /// Default: true    pub enabled: Option<bool>,    /// The threshold at which auto-compaction runs. This is one of:    ///    /// - A percentage string ending in `%`, e.g. `"90%"`, measured against the    ///   model's context window. `"90%"` compacts once the context is 90% full.    /// - A positive integer: compaction runs once that many tokens have been    ///   used. For example, `100000` compacts after 100,000 tokens are used.    /// - A negative integer: compaction runs once that many tokens remain in    ///   the context window. For example, `-20000` compacts once there are    ///   fewer than 20,000 tokens of headroom left in the context window.    ///    /// `0` is not a valid threshold.    ///    /// Default: "90%"    pub threshold: Option<AutoCompactThreshold>,} #[with_fallible_options]#[derive(Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, Default)]pub struct AgentSettingsContent {    /// Whether the Agent is enabled.    ///    /// Default: true    pub enabled: Option<bool>,    /// Whether to show the agent panel button in the status bar.    ///    /// Default: true    pub button: Option<bool>,    /// Where to dock the agent panel.    ///    /// Default: left (Agentic layout), right (Classic layout)    pub dock: Option<DockPosition>,    /// Whether the agent panel should use flexible (proportional) sizing.    ///    /// Default: true    pub flexible: Option<bool>,    /// Where to position the threads sidebar.    ///    /// Default: left    pub sidebar_side: Option<SidebarDockPosition>,    /// Default width in pixels when the agent panel is docked to the left or right.    ///    /// Default: 640    #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]    pub default_width: Option<f32>,    /// Default height in pixels when the agent panel is docked to the bottom.    ///    /// Default: 320    #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]    pub default_height: Option<f32>,    /// Whether to limit the content width in the agent panel. When enabled,    /// content will be constrained to `max_content_width` and centered when    /// the panel is wider than that value, for optimal readability.    ///    /// Default: true    pub limit_content_width: Option<bool>,    /// Maximum content width in pixels for the agent panel. Content will be    /// centered when the panel is wider than this value.    ///    /// Default: 850    #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]    pub max_content_width: Option<f32>,    /// The default model to use when creating new chats and for other features when a specific model is not specified.    pub default_model: Option<LanguageModelSelection>,    /// The model to use for subagents spawned via the `spawn_agent` tool. Defaults to the parent agent's model when not specified.    pub subagent_model: Option<LanguageModelSelection>,    /// Favorite models to show at the top of the model selector.    #[serde(default)]    pub favorite_models: Vec<LanguageModelSelection>,    /// Model to use for the inline assistant. Defaults to default_model when not specified.    pub inline_assistant_model: Option<LanguageModelSelection>,    /// Model to use for the inline assistant when streaming tools are enabled.    ///    /// Default: true    pub inline_assistant_use_streaming_tools: Option<bool>,    /// Model to use for generating git commit messages. Defaults to default_model when not specified.    pub commit_message_model: Option<LanguageModelSelection>,    /// Whether to include project rules files (AGENTS.md, CLAUDE.md, .rules, etc.)    /// in the prompt when generating git commit messages.    ///    /// Default: true    pub commit_message_include_project_rules: Option<bool>,    /// Custom instructions to include in the prompt when generating git commit messages.    /// Applied in addition to any project rules files (such as `.rules` or `AGENTS.md`).    pub commit_message_instructions: Option<String>,    /// Model to use for generating thread summaries. Defaults to default_model when not specified.    pub thread_summary_model: Option<LanguageModelSelection>,    /// Additional models with which to generate alternatives when performing inline assists.    pub inline_alternatives: Option<Vec<LanguageModelSelection>>,    /// The default profile to use in the Agent.    ///    /// Default: write    pub default_profile: Option<Arc<str>>,    /// The available agent profiles.    pub profiles: Option<IndexMap<Arc<str>, AgentProfileContent>>,    /// Where to show a popup notification when the agent is waiting for user input.    ///    /// Default: "primary_screen"    pub notify_when_agent_waiting: Option<NotifyWhenAgentWaiting>,    /// When to play a sound when the agent has either completed its response, or needs user input.    ///    /// Default: never    pub play_sound_when_agent_done: Option<PlaySoundWhenAgentDone>,    /// Whether to display agent edits in single-file editors in addition to the review multibuffer pane.    ///    /// Default: false    pub single_file_review: Option<bool>,    /// Additional parameters for language model requests. When making a request    /// to a model, parameters will be taken from the last entry in this list    /// that matches the model's provider and name. In each entry, both provider    /// and model are optional, so that you can specify parameters for either    /// one.    ///    /// Default: []    #[serde(default)]    pub model_parameters: Vec<LanguageModelParameters>,    /// Settings for automatic agent context compaction, which summarizes    /// earlier messages to free up room in the model's context window once the    /// context grows too large.    pub auto_compact: Option<AutoCompactSettingsContent>,    /// Whether to show thumb buttons for feedback in the agent panel.    ///    /// Default: true    pub enable_feedback: Option<bool>,    /// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.    ///    /// Default: true    pub expand_edit_card: Option<bool>,    /// Whether to have terminal cards in the agent panel expanded, showing the whole command output.    ///    /// Default: true    pub expand_terminal_card: Option<bool>,    /// Command to automatically run when Zed creates a Terminal Thread shell in the agent panel.    /// The command is sent to the shell as if typed, so it is interpreted by your    /// configured shell (including on Windows and remote/WSL projects).    /// An empty string disables this behavior.    ///    /// Default: ""    pub terminal_init_command: Option<String>,    /// How thinking blocks should be displayed by default in the agent panel.    ///    /// Default: automatic    pub thinking_display: Option<ThinkingBlockDisplay>,    /// Whether clicking the stop button on a running terminal tool should also cancel the agent's generation.    /// Note that this only applies to the stop button, not to ctrl+c inside the terminal.    ///    /// Default: true    pub cancel_generation_on_terminal_stop: Option<bool>,    /// Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.    ///    /// Default: false    pub use_modifier_to_send: Option<bool>,    /// Minimum number of lines of height the agent message editor should have.    ///    /// Default: 4    pub message_editor_min_lines: Option<usize>,    /// Whether to show turn statistics (elapsed time during generation, final turn duration).    ///    /// Default: false    pub show_turn_stats: Option<bool>,    /// Whether to show the merge conflict indicator in the status bar    /// that offers to resolve conflicts using the agent.    ///    /// Default: true    pub show_merge_conflict_indicator: Option<bool>,    /// Per-tool permission rules for granular control over which tool actions    /// require confirmation.    ///    /// The global `default` applies when no tool-specific rules match.    /// For external agent servers (e.g. Claude Agent) that define their own    /// permission modes, "deny" and "confirm" still take precedence — the    /// external agent's permission system is only used when Zed would allow    /// the action. Per-tool regex patterns (`always_allow`, `always_deny`,    /// `always_confirm`) match against the tool's text input (command, path,    /// URL, etc.).    pub tool_permissions: Option<ToolPermissionsContent>,     /// Persistent sandbox permission grants for agent-run terminal commands.    /// These are populated when choosing "Allow always" from a sandbox    /// escalation prompt.    pub sandbox_permissions: Option<SandboxPermissionsContent>,} impl AgentSettingsContent {    pub fn set_dock(&mut self, dock: DockPosition) {        self.dock = Some(dock);    }     pub fn set_sidebar_side(&mut self, position: SidebarDockPosition) {        self.sidebar_side = Some(position);    }     pub fn set_flexible_size(&mut self, flexible: bool) {        self.flexible = Some(flexible);    }     pub fn set_model(&mut self, language_model: LanguageModelSelection) {        self.default_model = Some(language_model)    }     pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {        self.inline_assistant_model = Some(LanguageModelSelection {            provider: provider.into(),            model,            enable_thinking: false,            effort: None,            speed: None,        });    }     pub fn set_profile(&mut self, profile_id: Arc<str>) {        self.default_profile = Some(profile_id);    }     pub fn add_favorite_model(&mut self, model: LanguageModelSelection) {        // Note: this is intentional to not compare using `PartialEq`here.        // Full equality would treat entries that differ just in thinking/effort/speed        // as distinct and silently produce duplicates.        if !self            .favorite_models            .iter()            .any(|m| m.provider == model.provider && m.model == model.model)        {            self.favorite_models.push(model);        }    }     pub fn remove_favorite_model(&mut self, model: &LanguageModelSelection) {        self.favorite_models            .retain(|m| !(m.provider == model.provider && m.model == model.model));    }     pub fn update_favorite_model<F>(&mut self, provider: &str, model: &str, f: F)    where        F: FnOnce(&mut LanguageModelSelection),    {        if let Some(entry) = self            .favorite_models            .iter_mut()            .find(|m| m.provider.0 == provider && m.model == model)        {            f(entry);        }    }     pub fn set_tool_default_permission(&mut self, tool_id: &str, mode: ToolPermissionMode) {        let tool_permissions = self.tool_permissions.get_or_insert_default();        let tool_rules = tool_permissions            .tools            .entry(Arc::from(tool_id))            .or_default();        tool_rules.default = Some(mode);    }     pub fn add_tool_allow_pattern(&mut self, tool_name: &str, pattern: String) {        let tool_permissions = self.tool_permissions.get_or_insert_default();        let tool_rules = tool_permissions            .tools            .entry(Arc::from(tool_name))            .or_default();        let always_allow = tool_rules.always_allow.get_or_insert_default();        if !always_allow.0.iter().any(|r| r.pattern == pattern) {            always_allow.0.push(ToolRegexRule {                pattern,                case_sensitive: None,            });        }    }     pub fn add_tool_deny_pattern(&mut self, tool_name: &str, pattern: String) {        let tool_permissions = self.tool_permissions.get_or_insert_default();        let tool_rules = tool_permissions            .tools            .entry(Arc::from(tool_name))            .or_default();        let always_deny = tool_rules.always_deny.get_or_insert_default();        if !always_deny.0.iter().any(|r| r.pattern == pattern) {            always_deny.0.push(ToolRegexRule {                pattern,                case_sensitive: None,            });        }    }     pub fn allow_sandbox_all_hosts(&mut self) {        self.sandbox_permissions            .get_or_insert_default()            .allow_all_hosts = Some(true);    }     /// The persisted sandbox network host patterns, as written (callers own    /// parsing/validation).    pub fn sandbox_network_hosts(&self) -> &[String] {        self.sandbox_permissions            .as_ref()            .and_then(|permissions| permissions.network_hosts.as_ref())            .map(|hosts| hosts.0.as_slice())            .unwrap_or_default()    }     /// Replace the persisted sandbox network host patterns. Callers compute    /// the new list (typically the old list plus newly granted hosts, pruned    /// of entries subsumed by wildcards) rather than appending blindly.    pub fn set_sandbox_network_hosts(&mut self, hosts: Vec<String>) {        self.sandbox_permissions            .get_or_insert_default()            .network_hosts = Some(ExtendingVec(hosts));    }     pub fn allow_sandbox_fs_write_all(&mut self) {        self.sandbox_permissions            .get_or_insert_default()            .allow_fs_write_all = Some(true);    }     pub fn allow_sandbox_unsandboxed(&mut self) {        self.sandbox_permissions            .get_or_insert_default()            .allow_unsandboxed = Some(true);    }     pub fn add_sandbox_write_path(&mut self, path: PathBuf) {        let write_paths = &mut self            .sandbox_permissions            .get_or_insert_default()            .write_paths            .get_or_insert_default()

Lines 1–500 of 1191.