Remove Agent V2 feature flag (#52792)

Mikayla Maki created

It's happening.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A

Change summary

Cargo.lock                                           |  3 
assets/settings/default.json                         | 10 
crates/agent_settings/src/agent_settings.rs          | 19 --
crates/agent_ui/src/agent_panel.rs                   | 90 -------------
crates/agent_ui/src/agent_ui.rs                      | 61 +-------
crates/agent_ui/src/conversation_view.rs             | 27 ++--
crates/agent_ui/src/conversation_view/thread_view.rs |  7 
crates/agent_ui/src/thread_metadata_store.rs         | 36 -----
crates/feature_flags/src/flags.rs                    | 10 -
crates/platform_title_bar/Cargo.toml                 |  1 
crates/platform_title_bar/src/platform_title_bar.rs  |  3 
crates/settings_ui/Cargo.toml                        |  1 
crates/settings_ui/src/page_data.rs                  | 49 +++----
crates/sidebar/Cargo.toml                            |  2 
crates/sidebar/src/sidebar.rs                        |  6 
crates/sidebar/src/sidebar_tests.rs                  | 11 -
crates/workspace/src/multi_workspace.rs              |  3 
crates/workspace/src/multi_workspace_tests.rs        |  2 
crates/workspace/src/persistence.rs                  |  9 -
crates/zed/src/visual_test_runner.rs                 | 10 -
crates/zed/src/zed.rs                                | 17 --
21 files changed, 65 insertions(+), 312 deletions(-)

Detailed changes

Cargo.lock πŸ”—

@@ -12890,7 +12890,6 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
 name = "platform_title_bar"
 version = "0.1.0"
 dependencies = [
- "feature_flags",
  "gpui",
  "project",
  "settings",
@@ -15923,7 +15922,6 @@ dependencies = [
  "edit_prediction",
  "edit_prediction_ui",
  "editor",
- "feature_flags",
  "fs",
  "futures 0.3.32",
  "fuzzy",
@@ -16073,7 +16071,6 @@ dependencies = [
  "anyhow",
  "chrono",
  "editor",
- "feature_flags",
  "fs",
  "git",
  "gpui",

assets/settings/default.json πŸ”—

@@ -717,7 +717,7 @@
     // Default width of the project panel.
     "default_width": 240,
     // Where to dock the project panel. Can be 'left' or 'right'.
-    "dock": "left",
+    "dock": "right",
     // Spacing between worktree entries in the project panel. Can be 'comfortable' or 'standard'.
     "entry_spacing": "comfortable",
     // Whether to show file icons in the project panel.
@@ -819,7 +819,7 @@
     // Default width of the outline panel.
     "default_width": 300,
     // Where to dock the outline panel. Can be 'left' or 'right'.
-    "dock": "left",
+    "dock": "right",
     // Whether to show file icons in the outline panel.
     "file_icons": true,
     // Whether to show folder icons or chevrons for directories in the outline panel.
@@ -871,7 +871,7 @@
     // Whether to show the collaboration panel button in the status bar.
     "button": true,
     // Where to dock the collaboration panel. Can be 'left' or 'right'.
-    "dock": "left",
+    "dock": "right",
     // Default width of the collaboration panel.
     "default_width": 240,
   },
@@ -879,7 +879,7 @@
     // Whether to show the git panel button in the status bar.
     "button": true,
     // Where to dock the git panel. Can be 'left' or 'right'.
-    "dock": "left",
+    "dock": "right",
     // Default width of the git panel.
     "default_width": 360,
     // Style of the git status indicator in the panel.
@@ -944,7 +944,7 @@
     // Whether to show the agent panel button in the status bar.
     "button": true,
     // Where to dock the agent panel. Can be 'left', 'right' or 'bottom'.
-    "dock": "right",
+    "dock": "left",
     // Whether the agent panel should use flexible (proportional) sizing.
     //
     // Default: true

crates/agent_settings/src/agent_settings.rs πŸ”—

@@ -728,14 +728,6 @@ mod tests {
     use settings::ToolPermissionMode;
     use settings::ToolPermissionsContent;
 
-    fn set_agent_v2_defaults(cx: &mut gpui::App) {
-        SettingsStore::update_global(cx, |store, cx| {
-            store.update_default_settings(cx, |defaults| {
-                PanelLayout::AGENT.write_to(defaults);
-            });
-        });
-    }
-
     #[test]
     fn test_compiled_regex_case_insensitive() {
         let regex = CompiledRegex::new("rm\\s+-rf", false).unwrap();
@@ -1216,9 +1208,6 @@ mod tests {
         project::DisableAiSettings::register(cx);
         AgentSettings::register(cx);
 
-        // Test defaults are editor layout; switch to agent V2.
-        set_agent_v2_defaults(cx);
-
         // Should be Agent with an empty user layout (user hasn't customized).
         let layout = AgentSettings::get_layout(cx);
         let WindowLayout::Agent(Some(user_layout)) = layout else {
@@ -1351,9 +1340,6 @@ mod tests {
             project::DisableAiSettings::register(cx);
             AgentSettings::register(cx);
 
-            // Apply the agent V2 defaults.
-            set_agent_v2_defaults(cx);
-
             // User has agent=left (matches preset) and project_panel=left (does not)
             SettingsStore::update_global(cx, |store, cx| {
                 store
@@ -1442,7 +1428,7 @@ mod tests {
 
         cx.run_until_parked();
 
-        // Read back the file and apply it, then switch to agent V2 defaults.
+        // Read back the file and apply it.
         let written = fs.load(paths::settings_file().as_path()).await.unwrap();
         cx.update(|cx| {
             SettingsStore::update_global(cx, |store, cx| {
@@ -1467,9 +1453,6 @@ mod tests {
             );
             assert_eq!(user_layout.git_panel_dock, Some(DockPosition::Left));
 
-            // Now switch defaults to agent V2.
-            set_agent_v2_defaults(cx);
-
             // Even though defaults are now agent, the backfilled user settings
             // keep everything in the editor layout. The user's experience
             // hasn't changed.

crates/agent_ui/src/agent_panel.rs πŸ”—

@@ -19,7 +19,6 @@ use project::AgentId;
 use serde::{Deserialize, Serialize};
 use settings::{LanguageModelProviderSetting, LanguageModelSelection};
 
-use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt as _};
 use zed_actions::agent::{
     AddSelectionToThread, ConflictContent, ReauthenticateAgent, ResolveConflictedFilesWithAgent,
     ResolveConflictsWithAgent, ReviewBranchDiff,
@@ -923,16 +922,14 @@ impl AgentPanel {
                             panel.selected_agent = agent;
                         }
                         if let Some(ref start_thread_in) = serialized_panel.start_thread_in {
-                            let is_worktree_flag_enabled =
-                                cx.has_flag::<AgentV2FeatureFlag>();
                             let is_valid = match &start_thread_in {
                                 StartThreadIn::LocalProject => true,
                                 StartThreadIn::NewWorktree { .. } => {
                                     let project = panel.project.read(cx);
-                                    is_worktree_flag_enabled && !project.is_via_collab()
+                                    !project.is_via_collab()
                                 }
                                 StartThreadIn::LinkedWorktree { path, .. } => {
-                                    is_worktree_flag_enabled && path.exists()
+                                    path.exists()
                                 }
                             };
                             if is_valid {
@@ -2083,9 +2080,6 @@ impl AgentPanel {
         let new_target = match action {
             StartThreadIn::LocalProject => StartThreadIn::LocalProject,
             StartThreadIn::NewWorktree { .. } => {
-                if !cx.has_flag::<AgentV2FeatureFlag>() {
-                    return;
-                }
                 if !self.project_has_git_repository(cx) {
                     log::error!(
                         "set_start_thread_in: cannot use worktree mode without a git repository"
@@ -2101,9 +2095,6 @@ impl AgentPanel {
                 action.clone()
             }
             StartThreadIn::LinkedWorktree { .. } => {
-                if !cx.has_flag::<AgentV2FeatureFlag>() {
-                    return;
-                }
                 if !self.project_has_git_repository(cx) {
                     log::error!(
                         "set_start_thread_in: cannot use LinkedWorktree without a git repository"
@@ -3371,47 +3362,6 @@ impl AgentPanel {
             })
     }
 
-    fn render_recent_entries_menu(
-        &self,
-        icon: IconName,
-        corner: Corner,
-        cx: &mut Context<Self>,
-    ) -> impl IntoElement {
-        let focus_handle = self.focus_handle(cx);
-
-        PopoverMenu::new("agent-nav-menu")
-            .trigger_with_tooltip(
-                IconButton::new("agent-nav-menu", icon).icon_size(IconSize::Small),
-                {
-                    move |_window, cx| {
-                        Tooltip::for_action_in(
-                            "Toggle Recently Updated Threads",
-                            &ToggleNavigationMenu,
-                            &focus_handle,
-                            cx,
-                        )
-                    }
-                },
-            )
-            .anchor(corner)
-            .with_handle(self.agent_navigation_menu_handle.clone())
-            .menu({
-                let menu = self.agent_navigation_menu.clone();
-                move |window, cx| {
-                    telemetry::event!("View Thread History Clicked");
-
-                    if let Some(menu) = menu.as_ref() {
-                        menu.update(cx, |_, cx| {
-                            cx.defer_in(window, |menu, window, cx| {
-                                menu.rebuild(window, cx);
-                            });
-                        })
-                    }
-                    menu.clone()
-                }
-            })
-    }
-
     fn render_toolbar_back_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
         let focus_handle = self.focus_handle(cx);
 
@@ -3788,8 +3738,6 @@ impl AgentPanel {
             selected_agent.into_any_element()
         };
 
-        let show_history_menu = self.has_history_for_selected_agent(cx);
-        let has_v2_flag = cx.has_flag::<AgentV2FeatureFlag>();
         let is_empty_state = !self.active_thread_has_messages(cx);
 
         let is_in_history_or_config = matches!(
@@ -3814,7 +3762,7 @@ impl AgentPanel {
                 }))
         };
 
-        let use_v2_empty_toolbar = has_v2_flag && is_empty_state && !is_in_history_or_config;
+        let use_v2_empty_toolbar = is_empty_state && !is_in_history_or_config;
 
         let max_content_width = AgentSettings::get_global(cx).max_content_width;
 
@@ -3888,11 +3836,7 @@ impl AgentPanel {
                             |this| this.child(self.render_start_thread_in_selector(cx)),
                         )
                         .when(
-                            has_v2_flag
-                                && matches!(
-                                    self.start_thread_in,
-                                    StartThreadIn::NewWorktree { .. }
-                                ),
+                            matches!(self.start_thread_in, StartThreadIn::NewWorktree { .. }),
                             |this| this.child(self.render_new_worktree_branch_selector(cx)),
                         ),
                 )
@@ -3903,13 +3847,6 @@ impl AgentPanel {
                         .gap_1()
                         .pl_1()
                         .pr_1()
-                        .when(show_history_menu && !has_v2_flag, |this| {
-                            this.child(self.render_recent_entries_menu(
-                                IconName::MenuAltTemp,
-                                Corner::TopRight,
-                                cx,
-                            ))
-                        })
                         .child(full_screen_button)
                         .child(self.render_panel_options_menu(window, cx)),
                 )
@@ -3956,13 +3893,6 @@ impl AgentPanel {
                         .pl_1()
                         .pr_1()
                         .child(new_thread_menu)
-                        .when(show_history_menu && !has_v2_flag, |this| {
-                            this.child(self.render_recent_entries_menu(
-                                IconName::MenuAltTemp,
-                                Corner::TopRight,
-                                cx,
-                            ))
-                        })
                         .child(full_screen_button)
                         .child(self.render_panel_options_menu(window, cx)),
                 )
@@ -4491,7 +4421,6 @@ mod tests {
     };
     use acp_thread::{StubAgentConnection, ThreadStatus};
     use agent_servers::CODEX_ID;
-    use feature_flags::FeatureFlagAppExt;
     use fs::FakeFs;
     use gpui::{TestAppContext, VisualTestContext};
     use project::Project;
@@ -4504,7 +4433,6 @@ mod tests {
     async fn test_active_thread_serialize_and_load_round_trip(cx: &mut TestAppContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });
@@ -4625,7 +4553,6 @@ mod tests {
     async fn test_non_native_thread_without_metadata_is_not_restored(cx: &mut TestAppContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });
@@ -5005,7 +4932,6 @@ mod tests {
     async fn setup_panel(cx: &mut TestAppContext) -> (Entity<AgentPanel>, VisualTestContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });
@@ -5361,7 +5287,6 @@ mod tests {
     async fn test_thread_target_local_project(cx: &mut TestAppContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });
@@ -5469,7 +5394,6 @@ mod tests {
     async fn test_thread_target_serialization_round_trip(cx: &mut TestAppContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });
@@ -5571,7 +5495,6 @@ mod tests {
 
         let fs = FakeFs::new(cx.executor());
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
             <dyn fs::Fs>::set_global(fs.clone(), cx);
@@ -5742,7 +5665,6 @@ mod tests {
         init_test(cx);
 
         let app_state = cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
 
@@ -5909,7 +5831,6 @@ mod tests {
 
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });
@@ -6101,7 +6022,6 @@ mod tests {
     async fn test_new_workspace_inherits_global_last_used_agent(cx: &mut TestAppContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
             // Use an isolated DB so parallel tests can't overwrite our global key.
@@ -6155,7 +6075,6 @@ mod tests {
     async fn test_workspaces_maintain_independent_agent_selection(cx: &mut TestAppContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });
@@ -6248,7 +6167,6 @@ mod tests {
     async fn test_new_thread_uses_workspace_selected_agent(cx: &mut TestAppContext) {
         init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
         });

crates/agent_ui/src/agent_ui.rs πŸ”—

@@ -45,9 +45,9 @@ use ::ui::IconName;
 use agent_client_protocol as acp;
 use agent_settings::{AgentProfileId, AgentSettings};
 use command_palette_hooks::CommandPaletteFilter;
-use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt as _};
+use feature_flags::FeatureFlagAppExt as _;
 use fs::Fs;
-use gpui::{Action, App, Context, Entity, SharedString, UpdateGlobal as _, Window, actions};
+use gpui::{Action, App, Context, Entity, SharedString, Window, actions};
 use language::{
     LanguageRegistry,
     language_settings::{AllLanguageSettings, EditPredictionProvider},
@@ -59,7 +59,7 @@ use project::{AgentId, DisableAiSettings};
 use prompt_store::PromptBuilder;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use settings::{DockPosition, DockSide, LanguageModelSelection, Settings as _, SettingsStore};
+use settings::{LanguageModelSelection, Settings as _, SettingsStore};
 use std::any::TypeId;
 use workspace::Workspace;
 
@@ -80,7 +80,6 @@ use zed_actions;
 
 pub const DEFAULT_THREAD_TITLE: &str = "New Thread";
 const PARALLEL_AGENT_LAYOUT_BACKFILL_KEY: &str = "parallel_agent_layout_backfilled";
-
 actions!(
     agent,
     [
@@ -511,43 +510,10 @@ pub fn init(
     })
     .detach();
 
-    // TODO: remove this field when we're ready remove the feature flag
-    maybe_backfill_editor_layout(fs, is_new_install, false, cx);
-
-    cx.observe_flag::<AgentV2FeatureFlag, _>(|is_enabled, cx| {
-        SettingsStore::update_global(cx, |store, cx| {
-            store.update_default_settings(cx, |defaults| {
-                if is_enabled {
-                    defaults.agent.get_or_insert_default().dock = Some(DockPosition::Left);
-                    defaults.project_panel.get_or_insert_default().dock = Some(DockSide::Right);
-                    defaults.outline_panel.get_or_insert_default().dock = Some(DockSide::Right);
-                    defaults.collaboration_panel.get_or_insert_default().dock =
-                        Some(DockPosition::Right);
-                    defaults.git_panel.get_or_insert_default().dock = Some(DockPosition::Right);
-                } else {
-                    defaults.agent.get_or_insert_default().dock = Some(DockPosition::Right);
-                    defaults.project_panel.get_or_insert_default().dock = Some(DockSide::Left);
-                    defaults.outline_panel.get_or_insert_default().dock = Some(DockSide::Left);
-                    defaults.collaboration_panel.get_or_insert_default().dock =
-                        Some(DockPosition::Left);
-                    defaults.git_panel.get_or_insert_default().dock = Some(DockPosition::Left);
-                }
-            });
-        });
-    })
-    .detach();
+    maybe_backfill_editor_layout(fs, is_new_install, cx);
 }
 
-fn maybe_backfill_editor_layout(
-    fs: Arc<dyn Fs>,
-    is_new_install: bool,
-    should_run: bool,
-    cx: &mut App,
-) {
-    if !should_run {
-        return;
-    }
-
+fn maybe_backfill_editor_layout(fs: Arc<dyn Fs>, is_new_install: bool, cx: &mut App) {
     let kvp = db::kvp::KeyValueStore::global(cx);
     let already_backfilled =
         util::ResultExt::log_err(kvp.read_kvp(PARALLEL_AGENT_LAYOUT_BACKFILL_KEY))
@@ -572,7 +538,7 @@ fn maybe_backfill_editor_layout(
 fn update_command_palette_filter(cx: &mut App) {
     let disable_ai = DisableAiSettings::get_global(cx).disable_ai;
     let agent_enabled = AgentSettings::get_global(cx).enabled;
-    let agent_v2_enabled = cx.has_flag::<AgentV2FeatureFlag>();
+
     let edit_prediction_provider = AllLanguageSettings::get_global(cx)
         .edit_predictions
         .provider;
@@ -641,11 +607,7 @@ fn update_command_palette_filter(cx: &mut App) {
             filter.show_action_types(&[TypeId::of::<zed_actions::OpenZedPredictOnboarding>()]);
         }
 
-        if agent_v2_enabled {
-            filter.show_namespace("multi_workspace");
-        } else {
-            filter.hide_namespace("multi_workspace");
-        }
+        filter.show_namespace("multi_workspace");
     });
 }
 
@@ -714,7 +676,6 @@ mod tests {
     use command_palette_hooks::CommandPaletteFilter;
     use db::kvp::KeyValueStore;
     use editor::actions::AcceptEditPrediction;
-    use feature_flags::FeatureFlagAppExt;
     use gpui::{BorrowAppContext, TestAppContext, px};
     use project::DisableAiSettings;
     use settings::{
@@ -883,7 +844,7 @@ mod tests {
                     .is_none()
             );
 
-            maybe_backfill_editor_layout(fs.clone(), false, true, cx);
+            maybe_backfill_editor_layout(fs.clone(), false, cx);
         });
 
         cx.run_until_parked();
@@ -902,7 +863,7 @@ mod tests {
         let fs = setup_backfill_test(cx).await;
 
         cx.update(|cx| {
-            maybe_backfill_editor_layout(fs.clone(), true, true, cx);
+            maybe_backfill_editor_layout(fs.clone(), true, cx);
         });
 
         cx.run_until_parked();
@@ -924,7 +885,7 @@ mod tests {
         let fs = setup_backfill_test(cx).await;
 
         cx.update(|cx| {
-            maybe_backfill_editor_layout(fs.clone(), false, true, cx);
+            maybe_backfill_editor_layout(fs.clone(), false, cx);
         });
 
         cx.run_until_parked();
@@ -932,7 +893,7 @@ mod tests {
         let after_first = fs.load(paths::settings_file().as_path()).await.unwrap();
 
         cx.update(|cx| {
-            maybe_backfill_editor_layout(fs.clone(), false, true, cx);
+            maybe_backfill_editor_layout(fs.clone(), false, cx);
         });
 
         cx.run_until_parked();

crates/agent_ui/src/conversation_view.rs πŸ”—

@@ -22,7 +22,7 @@ use editor::scroll::Autoscroll;
 use editor::{
     Editor, EditorEvent, EditorMode, MultiBuffer, PathKey, SelectionEffects, SizingBehavior,
 };
-use feature_flags::{AgentSharingFeatureFlag, AgentV2FeatureFlag, FeatureFlagAppExt as _};
+use feature_flags::{AgentSharingFeatureFlag, FeatureFlagAppExt as _};
 use file_icons::FileIcons;
 use fs::Fs;
 use futures::FutureExt as _;
@@ -2645,7 +2645,6 @@ impl ConversationView {
 impl Render for ConversationView {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         self.sync_queued_message_editors(window, cx);
-        let v2_flag = cx.has_flag::<AgentV2FeatureFlag>();
 
         v_flex()
             .track_focus(&self.focus_handle)
@@ -2654,17 +2653,18 @@ impl Render for ConversationView {
             .child(match &self.server_state {
                 ServerState::Loading { .. } => v_flex()
                     .flex_1()
-                    .when(v2_flag, |this| {
-                        this.size_full().items_center().justify_center().child(
-                            Label::new("Loading…").color(Color::Muted).with_animation(
-                                "loading-agent-label",
-                                Animation::new(Duration::from_secs(2))
-                                    .repeat()
-                                    .with_easing(pulsating_between(0.3, 0.7)),
-                                |label, delta| label.alpha(delta),
-                            ),
-                        )
-                    })
+                    .size_full()
+                    .items_center()
+                    .justify_center()
+                    .child(
+                        Label::new("Loading…").color(Color::Muted).with_animation(
+                            "loading-agent-label",
+                            Animation::new(Duration::from_secs(2))
+                                .repeat()
+                                .with_easing(pulsating_between(0.3, 0.7)),
+                            |label, delta| label.alpha(delta),
+                        ),
+                    )
                     .into_any(),
                 ServerState::LoadError { error: e, .. } => v_flex()
                     .flex_1()
@@ -3437,7 +3437,6 @@ pub(crate) mod tests {
         let fs = FakeFs::new(cx.executor());
 
         cx.update(|cx| {
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             agent::ThreadStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);
             <dyn Fs>::set_global(fs.clone(), cx);

crates/agent_ui/src/conversation_view/thread_view.rs πŸ”—

@@ -3141,7 +3141,7 @@ impl ThreadView {
         let editor_bg_color = cx.theme().colors().editor_background;
         let editor_expanded = self.editor_expanded;
         let has_messages = self.list_state.item_count() > 0;
-        let v2_empty_state = cx.has_flag::<AgentV2FeatureFlag>() && !has_messages;
+        let v2_empty_state = !has_messages;
         let (expand_icon, expand_tooltip) = if editor_expanded {
             (IconName::Minimize, "Minimize Message Editor")
         } else {
@@ -5158,7 +5158,7 @@ impl ThreadView {
 
     pub(crate) fn sync_editor_mode_for_empty_state(&mut self, cx: &mut Context<Self>) {
         let has_messages = self.list_state.item_count() > 0;
-        let v2_empty_state = cx.has_flag::<AgentV2FeatureFlag>() && !has_messages;
+        let v2_empty_state = !has_messages;
 
         let mode = if v2_empty_state {
             EditorMode::Full {
@@ -6393,7 +6393,6 @@ impl ThreadView {
                                     .when(is_collapsible || failed_or_canceled, |this| {
                                         let diff_for_discard = if has_revealed_diff
                                             && is_cancelled_edit
-                                            && cx.has_flag::<AgentV2FeatureFlag>()
                                         {
                                             tool_call.diffs().next().cloned()
                                         } else {
@@ -8621,7 +8620,7 @@ impl ThreadView {
 impl Render for ThreadView {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let has_messages = self.list_state.item_count() > 0;
-        let v2_empty_state = cx.has_flag::<AgentV2FeatureFlag>() && !has_messages;
+        let v2_empty_state = !has_messages;
 
         let max_content_width = AgentSettings::get_global(cx).max_content_width;
 

crates/agent_ui/src/thread_metadata_store.rs πŸ”—

@@ -16,7 +16,6 @@ use db::{
     },
     sqlez_macros::sql,
 };
-use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt};
 use futures::{FutureExt as _, future::Shared};
 use gpui::{AppContext as _, Entity, Global, Subscription, Task};
 use project::AgentId;
@@ -28,16 +27,7 @@ use crate::DEFAULT_THREAD_TITLE;
 
 pub fn init(cx: &mut App) {
     ThreadMetadataStore::init_global(cx);
-
-    if cx.has_flag::<AgentV2FeatureFlag>() {
-        migrate_thread_metadata(cx);
-    }
-    cx.observe_flag::<AgentV2FeatureFlag, _>(|has_flag, cx| {
-        if has_flag {
-            migrate_thread_metadata(cx);
-        }
-    })
-    .detach();
+    migrate_thread_metadata(cx);
 }
 
 /// Migrate existing thread metadata from native agent thread store to the new metadata storage.
@@ -343,10 +333,6 @@ impl ThreadMetadataStore {
     }
 
     pub fn save_all(&mut self, metadata: Vec<ThreadMetadata>, cx: &mut Context<Self>) {
-        if !cx.has_flag::<AgentV2FeatureFlag>() {
-            return;
-        }
-
         for metadata in metadata {
             self.save_internal(metadata);
         }
@@ -359,10 +345,6 @@ impl ThreadMetadataStore {
     }
 
     fn save(&mut self, metadata: ThreadMetadata, cx: &mut Context<Self>) {
-        if !cx.has_flag::<AgentV2FeatureFlag>() {
-            return;
-        }
-
         self.save_internal(metadata);
         cx.notify();
     }
@@ -412,10 +394,6 @@ impl ThreadMetadataStore {
         work_dirs: PathList,
         cx: &mut Context<Self>,
     ) {
-        if !cx.has_flag::<AgentV2FeatureFlag>() {
-            return;
-        }
-
         if let Some(thread) = self.threads.get(session_id) {
             self.save_internal(ThreadMetadata {
                 folder_paths: work_dirs,
@@ -490,10 +468,6 @@ impl ThreadMetadataStore {
         archived: bool,
         cx: &mut Context<Self>,
     ) {
-        if !cx.has_flag::<AgentV2FeatureFlag>() {
-            return;
-        }
-
         if let Some(thread) = self.threads.get(session_id) {
             self.save_internal(ThreadMetadata {
                 archived,
@@ -504,10 +478,6 @@ impl ThreadMetadataStore {
     }
 
     pub fn delete(&mut self, session_id: acp::SessionId, cx: &mut Context<Self>) {
-        if !cx.has_flag::<AgentV2FeatureFlag>() {
-            return;
-        }
-
         if let Some(thread) = self.threads.get(&session_id) {
             if let Some(session_ids) = self.threads_by_paths.get_mut(&thread.folder_paths) {
                 session_ids.remove(&session_id);
@@ -997,7 +967,7 @@ mod tests {
     use action_log::ActionLog;
     use agent::DbThread;
     use agent_client_protocol as acp;
-    use feature_flags::FeatureFlagAppExt;
+
     use gpui::TestAppContext;
     use project::FakeFs;
     use project::Project;
@@ -1047,7 +1017,6 @@ mod tests {
         cx.update(|cx| {
             let settings_store = settings::SettingsStore::test(cx);
             cx.set_global(settings_store);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             ThreadMetadataStore::init_global(cx);
             ThreadStore::init_global(cx);
         });
@@ -1088,7 +1057,6 @@ mod tests {
         cx.update(|cx| {
             let settings_store = settings::SettingsStore::test(cx);
             cx.set_global(settings_store);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
             ThreadMetadataStore::init_global(cx);
         });
 

crates/feature_flags/src/flags.rs πŸ”—

@@ -12,16 +12,6 @@ impl FeatureFlag for PanicFeatureFlag {
     const NAME: &'static str = "panic";
 }
 
-pub struct AgentV2FeatureFlag;
-
-impl FeatureFlag for AgentV2FeatureFlag {
-    const NAME: &'static str = "agent-v2";
-
-    fn enabled_for_staff() -> bool {
-        true
-    }
-}
-
 /// A feature flag for granting access to beta ACP features.
 ///
 /// We reuse this feature flag for new betas, so don't delete it if it is not currently in use.

crates/platform_title_bar/Cargo.toml πŸ”—

@@ -13,7 +13,6 @@ path = "src/platform_title_bar.rs"
 doctest = false
 
 [dependencies]
-feature_flags.workspace = true
 gpui.workspace = true
 project.workspace = true
 settings.workspace = true

crates/platform_title_bar/src/platform_title_bar.rs πŸ”—

@@ -1,7 +1,6 @@
 pub mod platforms;
 mod system_window_tabs;
 
-use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt};
 use gpui::{
     Action, AnyElement, App, Context, Decorations, Entity, Hsla, InteractiveElement, IntoElement,
     MouseButton, ParentElement, StatefulInteractiveElement, Styled, WeakEntity, Window,
@@ -111,7 +110,7 @@ impl PlatformTitleBar {
     }
 
     pub fn is_multi_workspace_enabled(cx: &App) -> bool {
-        cx.has_flag::<AgentV2FeatureFlag>() && !DisableAiSettings::get_global(cx).disable_ai
+        !DisableAiSettings::get_global(cx).disable_ai
     }
 }
 

crates/settings_ui/Cargo.toml πŸ”—

@@ -28,7 +28,6 @@ cpal.workspace = true
 edit_prediction.workspace = true
 edit_prediction_ui.workspace = true
 editor.workspace = true
-feature_flags.workspace = true
 fs.workspace = true
 futures.workspace = true
 fuzzy.workspace = true

crates/settings_ui/src/page_data.rs πŸ”—

@@ -1,4 +1,3 @@
-use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt as _};
 use gpui::{Action as _, App};
 use itertools::Itertools as _;
 use settings::{
@@ -7188,7 +7187,7 @@ fn ai_page(cx: &App) -> SettingsPage {
         ]
     }
 
-    fn agent_configuration_section(cx: &App) -> Box<[SettingsPageItem]> {
+    fn agent_configuration_section(_cx: &App) -> Box<[SettingsPageItem]> {
         let mut items = vec![
             SettingsPageItem::SectionHeader("Agent Configuration"),
             SettingsPageItem::SubPageLink(SubPageLink {
@@ -7202,30 +7201,28 @@ fn ai_page(cx: &App) -> SettingsPage {
             }),
         ];
 
-        if cx.has_flag::<AgentV2FeatureFlag>() {
-            items.push(SettingsPageItem::SettingItem(SettingItem {
-                title: "New Thread Location",
-                description: "Whether to start a new thread in the current local project or in a new Git worktree.",
-                field: Box::new(SettingField {
-                    json_path: Some("agent.new_thread_location"),
-                    pick: |settings_content| {
-                        settings_content
-                            .agent
-                            .as_ref()?
-                            .new_thread_location
-                            .as_ref()
-                    },
-                    write: |settings_content, value| {
-                        settings_content
-                            .agent
-                            .get_or_insert_default()
-                            .new_thread_location = value;
-                    },
-                }),
-                metadata: None,
-                files: USER,
-            }));
-        }
+        items.push(SettingsPageItem::SettingItem(SettingItem {
+            title: "New Thread Location",
+            description: "Whether to start a new thread in the current local project or in a new Git worktree.",
+            field: Box::new(SettingField {
+                json_path: Some("agent.new_thread_location"),
+                pick: |settings_content| {
+                    settings_content
+                        .agent
+                        .as_ref()?
+                        .new_thread_location
+                        .as_ref()
+                },
+                write: |settings_content, value| {
+                    settings_content
+                        .agent
+                        .get_or_insert_default()
+                        .new_thread_location = value;
+                },
+            }),
+            metadata: None,
+            files: USER,
+        }));
 
         items.extend([
             SettingsPageItem::SettingItem(SettingItem {

crates/sidebar/Cargo.toml πŸ”—

@@ -24,7 +24,6 @@ agent_ui = { workspace = true, features = ["audio"] }
 anyhow.workspace = true
 chrono.workspace = true
 editor.workspace = true
-feature_flags.workspace = true
 fs.workspace = true
 git.workspace = true
 gpui.workspace = true
@@ -54,7 +53,6 @@ pretty_assertions.workspace = true
 prompt_store.workspace = true
 recent_projects = { workspace = true, features = ["test-support"] }
 serde_json.workspace = true
-feature_flags.workspace = true
 fs = { workspace = true, features = ["test-support"] }
 git.workspace = true
 gpui = { workspace = true, features = ["test-support"] }

crates/sidebar/src/sidebar.rs πŸ”—

@@ -14,7 +14,6 @@ use agent_ui::{
 };
 use chrono::{DateTime, Utc};
 use editor::Editor;
-use feature_flags::{AgentV2FeatureFlag, FeatureFlagViewExt as _};
 use gpui::{
     Action as _, AnyElement, App, Context, Entity, FocusHandle, Focusable, KeyContext, ListState,
     Pixels, Render, SharedString, WeakEntity, Window, WindowHandle, linear_color_stop,
@@ -434,11 +433,6 @@ impl Sidebar {
         })
         .detach();
 
-        cx.observe_flag::<AgentV2FeatureFlag, _>(window, |_is_enabled, this, _window, cx| {
-            this.update_entries(cx);
-        })
-        .detach();
-
         let workspaces: Vec<_> = multi_workspace.read(cx).workspaces().cloned().collect();
         cx.defer_in(window, move |this, window, cx| {
             for workspace in &workspaces {

crates/sidebar/src/sidebar_tests.rs πŸ”—

@@ -6,7 +6,6 @@ use agent_ui::{
     thread_metadata_store::ThreadMetadata,
 };
 use chrono::DateTime;
-use feature_flags::FeatureFlagAppExt as _;
 use fs::FakeFs;
 use gpui::TestAppContext;
 use pretty_assertions::assert_eq;
@@ -24,7 +23,6 @@ fn init_test(cx: &mut TestAppContext) {
         cx.set_global(settings_store);
         theme_settings::init(theme::LoadThemes::JustBase, cx);
         editor::init(cx);
-        cx.update_flags(false, vec!["agent-v2".into()]);
         ThreadStore::init_global(cx);
         ThreadMetadataStore::init_global(cx);
         language_model::LanguageModelRegistry::test(cx);
@@ -1220,7 +1218,6 @@ async fn init_test_project_with_agent_panel(
 ) -> Entity<project::Project> {
     agent_ui::test_support::init_test(cx);
     cx.update(|cx| {
-        cx.update_flags(false, vec!["agent-v2".into()]);
         ThreadStore::init_global(cx);
         ThreadMetadataStore::init_global(cx);
         language_model::LanguageModelRegistry::test(cx);
@@ -2078,7 +2075,7 @@ async fn test_focused_thread_tracks_user_intent(cx: &mut TestAppContext) {
             workspace.panel::<AgentPanel>(cx).is_some(),
             "Agent panel should exist"
         );
-        let dock = workspace.right_dock().read(cx);
+        let dock = workspace.left_dock().read(cx);
         assert!(
             dock.is_open(),
             "Clicking a thread should open the agent panel dock"
@@ -2425,7 +2422,6 @@ async fn test_cmd_n_shows_new_thread_entry_in_absorbed_worktree(cx: &mut TestApp
     // header and highlight it as active.
     agent_ui::test_support::init_test(cx);
     cx.update(|cx| {
-        cx.update_flags(false, vec!["agent-v2".into()]);
         ThreadStore::init_global(cx);
         ThreadMetadataStore::init_global(cx);
         language_model::LanguageModelRegistry::test(cx);
@@ -2974,7 +2970,6 @@ async fn test_absorbed_worktree_running_thread_shows_live_status(cx: &mut TestAp
     // live status (spinner + "(running)") in the sidebar.
     agent_ui::test_support::init_test(cx);
     cx.update(|cx| {
-        cx.update_flags(false, vec!["agent-v2".into()]);
         ThreadStore::init_global(cx);
         ThreadMetadataStore::init_global(cx);
         language_model::LanguageModelRegistry::test(cx);
@@ -3077,7 +3072,6 @@ async fn test_absorbed_worktree_running_thread_shows_live_status(cx: &mut TestAp
 async fn test_absorbed_worktree_completion_triggers_notification(cx: &mut TestAppContext) {
     agent_ui::test_support::init_test(cx);
     cx.update(|cx| {
-        cx.update_flags(false, vec!["agent-v2".into()]);
         ThreadStore::init_global(cx);
         ThreadMetadataStore::init_global(cx);
         language_model::LanguageModelRegistry::test(cx);
@@ -3990,7 +3984,6 @@ async fn test_archive_thread_uses_next_threads_own_workspace(cx: &mut TestAppCon
     // falling back to group_workspace only for Closed workspaces.
     agent_ui::test_support::init_test(cx);
     cx.update(|cx| {
-        cx.update_flags(false, vec!["agent-v2".into()]);
         ThreadStore::init_global(cx);
         ThreadMetadataStore::init_global(cx);
         language_model::LanguageModelRegistry::test(cx);
@@ -4772,7 +4765,6 @@ async fn init_multi_project_test(
 ) -> (Arc<FakeFs>, Entity<project::Project>) {
     agent_ui::test_support::init_test(cx);
     cx.update(|cx| {
-        cx.update_flags(false, vec!["agent-v2".into()]);
         ThreadStore::init_global(cx);
         ThreadMetadataStore::init_global(cx);
         language_model::LanguageModelRegistry::test(cx);
@@ -5652,7 +5644,6 @@ mod property_test {
     ) {
         agent_ui::test_support::init_test(cx);
         cx.update(|cx| {
-            cx.update_flags(false, vec!["agent-v2".into()]);
             ThreadStore::init_global(cx);
             ThreadMetadataStore::init_global(cx);
             language_model::LanguageModelRegistry::test(cx);

crates/workspace/src/multi_workspace.rs πŸ”—

@@ -1,5 +1,4 @@
 use anyhow::Result;
-use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt};
 use gpui::PathPromptOptions;
 use gpui::{
     AnyView, App, Context, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
@@ -438,7 +437,7 @@ impl MultiWorkspace {
     }
 
     pub fn multi_workspace_enabled(&self, cx: &App) -> bool {
-        cx.has_flag::<AgentV2FeatureFlag>() && !DisableAiSettings::get_global(cx).disable_ai
+        !DisableAiSettings::get_global(cx).disable_ai
     }
 
     pub fn toggle_sidebar(&mut self, window: &mut Window, cx: &mut Context<Self>) {

crates/workspace/src/multi_workspace_tests.rs πŸ”—

@@ -1,5 +1,4 @@
 use super::*;
-use feature_flags::FeatureFlagAppExt;
 use fs::FakeFs;
 use gpui::TestAppContext;
 use project::{DisableAiSettings, ProjectGroupKey};
@@ -12,7 +11,6 @@ fn init_test(cx: &mut TestAppContext) {
         cx.set_global(settings_store);
         theme_settings::init(theme::LoadThemes::JustBase, cx);
         DisableAiSettings::register(cx);
-        cx.update_flags(false, vec!["agent-v2".into()]);
     });
 }
 

crates/workspace/src/persistence.rs πŸ”—

@@ -2525,7 +2525,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4082,7 +4081,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4127,7 +4125,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4184,7 +4181,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4275,7 +4271,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4381,7 +4376,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4485,7 +4479,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4542,7 +4535,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());
@@ -4702,7 +4694,6 @@ mod tests {
 
         cx.update(|cx| {
             cx.set_staff(true);
-            cx.update_flags(true, vec!["agent-v2".to_string()]);
         });
 
         let fs = fs::FakeFs::new(cx.executor());

crates/zed/src/visual_test_runner.rs πŸ”—

@@ -2527,11 +2527,6 @@ fn run_multi_workspace_sidebar_visual_tests(
     std::fs::create_dir_all(&workspace1_dir)?;
     std::fs::create_dir_all(&workspace2_dir)?;
 
-    // Enable the agent-v2 feature flag so multi-workspace is active
-    cx.update(|cx| {
-        cx.update_flags(true, vec!["agent-v2".to_string()]);
-    });
-
     // Create both projects upfront so we can build both workspaces during
     // window creation, before the MultiWorkspace entity exists.
     // This avoids a re-entrant read panic that occurs when Workspace::new
@@ -3082,11 +3077,6 @@ fn run_start_thread_in_selector_visual_tests(
 ) -> Result<TestResult> {
     use agent_ui::{AgentPanel, NewWorktreeBranchTarget, StartThreadIn, WorktreeCreationStatus};
 
-    // Enable feature flags so the thread target selector renders
-    cx.update(|cx| {
-        cx.update_flags(true, vec!["agent-v2".to_string()]);
-    });
-
     // Create a temp directory with a real git repo so "New Worktree" is enabled
     let temp_dir = tempfile::tempdir()?;
     let temp_path = temp_dir.keep();

crates/zed/src/zed.rs πŸ”—

@@ -2497,10 +2497,6 @@ mod tests {
     #[gpui::test]
     async fn test_open_paths_action(cx: &mut TestAppContext) {
         let app_state = init_test(cx);
-        cx.update(|cx| {
-            use feature_flags::FeatureFlagAppExt as _;
-            cx.update_flags(false, vec!["agent-v2".to_string()]);
-        });
         app_state
             .fs
             .as_fake()
@@ -5497,10 +5493,6 @@ mod tests {
     #[gpui::test]
     async fn test_open_paths_switches_to_best_workspace(cx: &mut TestAppContext) {
         let app_state = init_test(cx);
-        cx.update(|cx| {
-            use feature_flags::FeatureFlagAppExt as _;
-            cx.update_flags(false, vec!["agent-v2".to_string()]);
-        });
 
         app_state
             .fs
@@ -5702,10 +5694,6 @@ mod tests {
     async fn test_quit_checks_all_workspaces_for_dirty_items(cx: &mut TestAppContext) {
         let app_state = init_test(cx);
         cx.update(init);
-        cx.update(|cx| {
-            use feature_flags::FeatureFlagAppExt as _;
-            cx.update_flags(false, vec!["agent-v2".to_string()]);
-        });
 
         app_state
             .fs
@@ -5995,11 +5983,6 @@ mod tests {
 
         let app_state = init_test(cx);
 
-        cx.update(|cx| {
-            use feature_flags::FeatureFlagAppExt as _;
-            cx.update_flags(false, vec!["agent-v2".to_string()]);
-        });
-
         let dir1 = path!("/dir1");
         let dir2 = path!("/dir2");
         let dir3 = path!("/dir3");