1use std::sync::Arc;
2
3use collections::IndexMap;
4use convert_case::{Case, Casing as _};
5use fs::Fs;
6use gpui::{App, SharedString};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use settings::{Settings as _, update_settings_file};
10use util::ResultExt as _;
11
12use crate::AgentSettings;
13
14pub mod builtin_profiles {
15 use super::AgentProfileId;
16
17 pub const WRITE: &str = "write";
18 pub const ASK: &str = "ask";
19 pub const MINIMAL: &str = "minimal";
20
21 pub fn is_builtin(profile_id: &AgentProfileId) -> bool {
22 profile_id.as_str() == WRITE || profile_id.as_str() == ASK || profile_id.as_str() == MINIMAL
23 }
24}
25
26#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
27pub struct AgentProfileId(pub Arc<str>);
28
29impl AgentProfileId {
30 pub fn as_str(&self) -> &str {
31 &self.0
32 }
33}
34
35impl std::fmt::Display for AgentProfileId {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(f, "{}", self.0)
38 }
39}
40
41impl Default for AgentProfileId {
42 fn default() -> Self {
43 Self("write".into())
44 }
45}
46
47#[derive(Clone, Debug, Eq, PartialEq)]
48pub struct AgentProfile {
49 id: AgentProfileId,
50}
51
52pub type AvailableProfiles = IndexMap<AgentProfileId, SharedString>;
53
54impl AgentProfile {
55 pub fn new(id: AgentProfileId) -> Self {
56 Self { id }
57 }
58
59 pub fn id(&self) -> &AgentProfileId {
60 &self.id
61 }
62
63 /// Saves a new profile to the settings.
64 pub fn create(
65 name: String,
66 base_profile_id: Option<AgentProfileId>,
67 fs: Arc<dyn Fs>,
68 cx: &App,
69 ) -> AgentProfileId {
70 let id = AgentProfileId(name.to_case(Case::Kebab).into());
71
72 let base_profile =
73 base_profile_id.and_then(|id| AgentSettings::get_global(cx).profiles.get(&id).cloned());
74
75 let profile_settings = AgentProfileSettings {
76 name: name.into(),
77 tools: base_profile
78 .as_ref()
79 .map(|profile| profile.tools.clone())
80 .unwrap_or_default(),
81 enable_all_context_servers: base_profile
82 .as_ref()
83 .map(|profile| profile.enable_all_context_servers)
84 .unwrap_or_default(),
85 context_servers: base_profile
86 .map(|profile| profile.context_servers)
87 .unwrap_or_default(),
88 };
89
90 update_settings_file::<AgentSettings>(fs, cx, {
91 let id = id.clone();
92 move |settings, _cx| {
93 settings.create_profile(id, profile_settings).log_err();
94 }
95 });
96
97 id
98 }
99
100 /// Returns a map of AgentProfileIds to their names
101 pub fn available_profiles(cx: &App) -> AvailableProfiles {
102 let mut profiles = AvailableProfiles::default();
103 for (id, profile) in AgentSettings::get_global(cx).profiles.iter() {
104 profiles.insert(id.clone(), profile.name.clone());
105 }
106 profiles
107 }
108}
109
110/// A profile for the Zed Agent that controls its behavior.
111#[derive(Debug, Clone)]
112pub struct AgentProfileSettings {
113 /// The name of the profile.
114 pub name: SharedString,
115 pub tools: IndexMap<Arc<str>, bool>,
116 pub enable_all_context_servers: bool,
117 pub context_servers: IndexMap<Arc<str>, ContextServerPreset>,
118}
119
120impl AgentProfileSettings {
121 pub fn is_tool_enabled(&self, tool_name: &str) -> bool {
122 self.tools.get(tool_name) == Some(&true)
123 }
124
125 pub fn is_context_server_tool_enabled(&self, server_id: &str, tool_name: &str) -> bool {
126 self.enable_all_context_servers
127 || self
128 .context_servers
129 .get(server_id)
130 .is_some_and(|preset| preset.tools.get(tool_name) == Some(&true))
131 }
132}
133
134#[derive(Debug, Clone, Default)]
135pub struct ContextServerPreset {
136 pub tools: IndexMap<Arc<str>, bool>,
137}