1mod acp;
2mod active_thread;
3mod agent_configuration;
4mod agent_diff;
5mod agent_model_selector;
6mod agent_panel;
7mod buffer_codegen;
8mod burn_mode_tooltip;
9mod context_picker;
10mod context_server_configuration;
11mod context_strip;
12mod debug;
13mod inline_assistant;
14mod inline_prompt_editor;
15mod language_model_selector;
16mod message_editor;
17mod profile_selector;
18mod slash_command;
19mod slash_command_picker;
20mod slash_command_settings;
21mod terminal_codegen;
22mod terminal_inline_assistant;
23mod text_thread_editor;
24mod thread_history;
25mod tool_compatibility;
26mod ui;
27
28use std::rc::Rc;
29use std::sync::Arc;
30
31use agent::{Thread, ThreadId};
32use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
33use assistant_slash_command::SlashCommandRegistry;
34use client::Client;
35use command_palette_hooks::CommandPaletteFilter;
36use feature_flags::FeatureFlagAppExt as _;
37use fs::Fs;
38use gpui::{Action, App, Entity, actions};
39use language::LanguageRegistry;
40use language_model::{
41 ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
42};
43use project::DisableAiSettings;
44use prompt_store::PromptBuilder;
45use schemars::JsonSchema;
46use serde::{Deserialize, Serialize};
47use settings::{Settings as _, SettingsStore};
48use std::any::TypeId;
49
50pub use crate::active_thread::ActiveThread;
51use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
52pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
53pub use crate::inline_assistant::InlineAssistant;
54use crate::slash_command_settings::SlashCommandSettings;
55pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
56pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor};
57pub use ui::preview::{all_agent_previews, get_agent_preview};
58use zed_actions;
59
60actions!(
61 agent,
62 [
63 /// Creates a new text-based conversation thread.
64 NewTextThread,
65 /// Toggles the context picker interface for adding files, symbols, or other context.
66 ToggleContextPicker,
67 /// Toggles the navigation menu for switching between threads and views.
68 ToggleNavigationMenu,
69 /// Toggles the options menu for agent settings and preferences.
70 ToggleOptionsMenu,
71 /// Deletes the recently opened thread from history.
72 DeleteRecentlyOpenThread,
73 /// Toggles the profile selector for switching between agent profiles.
74 ToggleProfileSelector,
75 /// Removes all added context from the current conversation.
76 RemoveAllContext,
77 /// Expands the message editor to full size.
78 ExpandMessageEditor,
79 /// Opens the conversation history view.
80 OpenHistory,
81 /// Adds a context server to the configuration.
82 AddContextServer,
83 /// Removes the currently selected thread.
84 RemoveSelectedThread,
85 /// Starts a chat conversation with follow-up enabled.
86 ChatWithFollow,
87 /// Cycles to the next inline assist suggestion.
88 CycleNextInlineAssist,
89 /// Cycles to the previous inline assist suggestion.
90 CyclePreviousInlineAssist,
91 /// Moves focus up in the interface.
92 FocusUp,
93 /// Moves focus down in the interface.
94 FocusDown,
95 /// Moves focus left in the interface.
96 FocusLeft,
97 /// Moves focus right in the interface.
98 FocusRight,
99 /// Removes the currently focused context item.
100 RemoveFocusedContext,
101 /// Accepts the suggested context item.
102 AcceptSuggestedContext,
103 /// Opens the active thread as a markdown file.
104 OpenActiveThreadAsMarkdown,
105 /// Opens the agent diff view to review changes.
106 OpenAgentDiff,
107 /// Keeps the current suggestion or change.
108 Keep,
109 /// Rejects the current suggestion or change.
110 Reject,
111 /// Rejects all suggestions or changes.
112 RejectAll,
113 /// Keeps all suggestions or changes.
114 KeepAll,
115 /// Follows the agent's suggestions.
116 Follow,
117 /// Resets the trial upsell notification.
118 ResetTrialUpsell,
119 /// Resets the trial end upsell notification.
120 ResetTrialEndUpsell,
121 /// Continues the current thread.
122 ContinueThread,
123 /// Continues the thread with burn mode enabled.
124 ContinueWithBurnMode,
125 /// Toggles burn mode for faster responses.
126 ToggleBurnMode,
127 ]
128);
129
130/// Creates a new conversation thread, optionally based on an existing thread.
131#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
132#[action(namespace = agent)]
133#[serde(deny_unknown_fields)]
134pub struct NewThread {
135 #[serde(default)]
136 from_thread_id: Option<ThreadId>,
137}
138
139/// Creates a new external agent conversation thread.
140#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
141#[action(namespace = agent)]
142#[serde(deny_unknown_fields)]
143pub struct NewExternalAgentThread {
144 /// Which agent to use for the conversation.
145 agent: Option<ExternalAgent>,
146}
147
148#[derive(Default, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
149#[serde(rename_all = "snake_case")]
150enum ExternalAgent {
151 #[default]
152 Gemini,
153 ClaudeCode,
154}
155
156impl ExternalAgent {
157 pub fn server(&self) -> Rc<dyn agent_servers::AgentServer> {
158 match self {
159 ExternalAgent::Gemini => Rc::new(agent_servers::Gemini),
160 ExternalAgent::ClaudeCode => Rc::new(agent_servers::ClaudeCode),
161 }
162 }
163}
164
165/// Opens the profile management interface for configuring agent tools and settings.
166#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
167#[action(namespace = agent)]
168#[serde(deny_unknown_fields)]
169pub struct ManageProfiles {
170 #[serde(default)]
171 pub customize_tools: Option<AgentProfileId>,
172}
173
174impl ManageProfiles {
175 pub fn customize_tools(profile_id: AgentProfileId) -> Self {
176 Self {
177 customize_tools: Some(profile_id),
178 }
179 }
180}
181
182#[derive(Clone)]
183pub(crate) enum ModelUsageContext {
184 Thread(Entity<Thread>),
185 InlineAssistant,
186}
187
188impl ModelUsageContext {
189 pub fn configured_model(&self, cx: &App) -> Option<ConfiguredModel> {
190 match self {
191 Self::Thread(thread) => thread.read(cx).configured_model(),
192 Self::InlineAssistant => {
193 LanguageModelRegistry::read_global(cx).inline_assistant_model()
194 }
195 }
196 }
197
198 pub fn language_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> {
199 self.configured_model(cx)
200 .map(|configured_model| configured_model.model)
201 }
202}
203
204/// Initializes the `agent` crate.
205pub fn init(
206 fs: Arc<dyn Fs>,
207 client: Arc<Client>,
208 prompt_builder: Arc<PromptBuilder>,
209 language_registry: Arc<LanguageRegistry>,
210 is_eval: bool,
211 cx: &mut App,
212) {
213 AgentSettings::register(cx);
214 SlashCommandSettings::register(cx);
215
216 assistant_context::init(client.clone(), cx);
217 rules_library::init(cx);
218 if !is_eval {
219 // Initializing the language model from the user settings messes with the eval, so we only initialize them when
220 // we're not running inside of the eval.
221 init_language_model_settings(cx);
222 }
223 assistant_slash_command::init(cx);
224 agent::init(cx);
225 agent_panel::init(cx);
226 context_server_configuration::init(language_registry.clone(), fs.clone(), cx);
227 TextThreadEditor::init(cx);
228
229 register_slash_commands(cx);
230 inline_assistant::init(
231 fs.clone(),
232 prompt_builder.clone(),
233 client.telemetry().clone(),
234 cx,
235 );
236 terminal_inline_assistant::init(
237 fs.clone(),
238 prompt_builder.clone(),
239 client.telemetry().clone(),
240 cx,
241 );
242 indexed_docs::init(cx);
243 cx.observe_new(move |workspace, window, cx| {
244 ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx)
245 })
246 .detach();
247 cx.observe_new(ManageProfilesModal::register).detach();
248
249 // Update command palette filter based on AI settings
250 update_command_palette_filter(cx);
251
252 // Watch for settings changes
253 cx.observe_global::<SettingsStore>(|app_cx| {
254 // When settings change, update the command palette filter
255 update_command_palette_filter(app_cx);
256 })
257 .detach();
258}
259
260fn update_command_palette_filter(cx: &mut App) {
261 let disable_ai = DisableAiSettings::get_global(cx).disable_ai;
262 CommandPaletteFilter::update_global(cx, |filter, _| {
263 if disable_ai {
264 filter.hide_namespace("agent");
265 filter.hide_namespace("assistant");
266 filter.hide_namespace("copilot");
267 filter.hide_namespace("supermaven");
268 filter.hide_namespace("zed_predict_onboarding");
269 filter.hide_namespace("edit_prediction");
270
271 use editor::actions::{
272 AcceptEditPrediction, AcceptPartialEditPrediction, NextEditPrediction,
273 PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction,
274 };
275 let edit_prediction_actions = [
276 TypeId::of::<AcceptEditPrediction>(),
277 TypeId::of::<AcceptPartialEditPrediction>(),
278 TypeId::of::<ShowEditPrediction>(),
279 TypeId::of::<NextEditPrediction>(),
280 TypeId::of::<PreviousEditPrediction>(),
281 TypeId::of::<ToggleEditPrediction>(),
282 ];
283 filter.hide_action_types(&edit_prediction_actions);
284 filter.hide_action_types(&[TypeId::of::<zed_actions::OpenZedPredictOnboarding>()]);
285 } else {
286 filter.show_namespace("agent");
287 filter.show_namespace("assistant");
288 filter.show_namespace("copilot");
289 filter.show_namespace("zed_predict_onboarding");
290
291 filter.show_namespace("edit_prediction");
292
293 use editor::actions::{
294 AcceptEditPrediction, AcceptPartialEditPrediction, NextEditPrediction,
295 PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction,
296 };
297 let edit_prediction_actions = [
298 TypeId::of::<AcceptEditPrediction>(),
299 TypeId::of::<AcceptPartialEditPrediction>(),
300 TypeId::of::<ShowEditPrediction>(),
301 TypeId::of::<NextEditPrediction>(),
302 TypeId::of::<PreviousEditPrediction>(),
303 TypeId::of::<ToggleEditPrediction>(),
304 ];
305 filter.show_action_types(edit_prediction_actions.iter());
306
307 filter
308 .show_action_types([TypeId::of::<zed_actions::OpenZedPredictOnboarding>()].iter());
309 }
310 });
311}
312
313fn init_language_model_settings(cx: &mut App) {
314 update_active_language_model_from_settings(cx);
315
316 cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
317 .detach();
318 cx.subscribe(
319 &LanguageModelRegistry::global(cx),
320 |_, event: &language_model::Event, cx| match event {
321 language_model::Event::ProviderStateChanged
322 | language_model::Event::AddedProvider(_)
323 | language_model::Event::RemovedProvider(_) => {
324 update_active_language_model_from_settings(cx);
325 }
326 _ => {}
327 },
328 )
329 .detach();
330}
331
332fn update_active_language_model_from_settings(cx: &mut App) {
333 let settings = AgentSettings::get_global(cx);
334
335 fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
336 language_model::SelectedModel {
337 provider: LanguageModelProviderId::from(selection.provider.0.clone()),
338 model: LanguageModelId::from(selection.model.clone()),
339 }
340 }
341
342 let default = settings.default_model.as_ref().map(to_selected_model);
343 let inline_assistant = settings
344 .inline_assistant_model
345 .as_ref()
346 .map(to_selected_model);
347 let commit_message = settings
348 .commit_message_model
349 .as_ref()
350 .map(to_selected_model);
351 let thread_summary = settings
352 .thread_summary_model
353 .as_ref()
354 .map(to_selected_model);
355 let inline_alternatives = settings
356 .inline_alternatives
357 .iter()
358 .map(to_selected_model)
359 .collect::<Vec<_>>();
360
361 LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
362 registry.select_default_model(default.as_ref(), cx);
363 registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
364 registry.select_commit_message_model(commit_message.as_ref(), cx);
365 registry.select_thread_summary_model(thread_summary.as_ref(), cx);
366 registry.select_inline_alternative_models(inline_alternatives, cx);
367 });
368}
369
370fn register_slash_commands(cx: &mut App) {
371 let slash_command_registry = SlashCommandRegistry::global(cx);
372
373 slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
374 slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
375 slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
376 slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
377 slash_command_registry
378 .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
379 slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
380 slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
381 slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
382 slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
383 slash_command_registry
384 .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
385 slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
386
387 cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
388 let slash_command_registry = slash_command_registry.clone();
389 move |is_enabled, _cx| {
390 if is_enabled {
391 slash_command_registry.register_command(
392 assistant_slash_commands::StreamingExampleSlashCommand,
393 false,
394 );
395 }
396 }
397 })
398 .detach();
399
400 update_slash_commands_from_settings(cx);
401 cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
402 .detach();
403}
404
405fn update_slash_commands_from_settings(cx: &mut App) {
406 let slash_command_registry = SlashCommandRegistry::global(cx);
407 let settings = SlashCommandSettings::get_global(cx);
408
409 if settings.docs.enabled {
410 slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
411 } else {
412 slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
413 }
414
415 if settings.cargo_workspace.enabled {
416 slash_command_registry
417 .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
418 } else {
419 slash_command_registry
420 .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
421 }
422}