acp: Disable external agents over SSH (#37402)

Cole Miller created

Follow-up to #37377 

Show a clearer error here until SSH support is implemented.

Release Notes:

- N/A

Change summary

Cargo.toml                             |  2 +-
crates/agent_ui/src/acp/thread_view.rs |  4 +---
crates/agent_ui/src/agent_panel.rs     | 16 +++++++---------
crates/git_ui/src/branch_picker.rs     |  1 -
crates/vim/src/visual.rs               |  1 -
5 files changed, 9 insertions(+), 15 deletions(-)

Detailed changes

Cargo.toml 🔗

@@ -847,7 +847,7 @@ too_many_arguments = "allow"
 large_enum_variant = "allow"
 
 # Boolean expressions can be hard to read, requiring only the minimal form gets in the way
-nonminimal_bol = "allow"
+nonminimal_bool = "allow"
 
 [workspace.metadata.cargo-machete]
 ignored = [

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -420,9 +420,7 @@ impl AcpThreadView {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> ThreadState {
-        if project.read(cx).is_via_collab()
-            && agent.clone().downcast::<NativeAgentServer>().is_none()
-        {
+        if !project.read(cx).is_local() && agent.clone().downcast::<NativeAgentServer>().is_none() {
             return ThreadState::LoadError(LoadError::Other(
                 "External agents are not yet supported for remote projects.".into(),
             ));

crates/agent_ui/src/agent_panel.rs 🔗

@@ -1091,7 +1091,7 @@ impl AgentPanel {
         let workspace = self.workspace.clone();
         let project = self.project.clone();
         let fs = self.fs.clone();
-        let is_via_collab = self.project.read(cx).is_via_collab();
+        let is_not_local = !self.project.read(cx).is_local();
 
         const LAST_USED_EXTERNAL_AGENT_KEY: &str = "agent_panel__last_used_external_agent";
 
@@ -1123,7 +1123,7 @@ impl AgentPanel {
                     agent
                 }
                 None => {
-                    if is_via_collab {
+                    if is_not_local {
                         ExternalAgent::NativeAgent
                     } else {
                         cx.background_spawn(async move {
@@ -2532,10 +2532,8 @@ impl AgentPanel {
             .with_handle(self.new_thread_menu_handle.clone())
             .menu({
                 let workspace = self.workspace.clone();
-                let is_via_collab = workspace
-                    .update(cx, |workspace, cx| {
-                        workspace.project().read(cx).is_via_collab()
-                    })
+                let is_not_local = workspace
+                    .update(cx, |workspace, cx| !workspace.project().read(cx).is_local())
                     .unwrap_or_default();
 
                 move |window, cx| {
@@ -2627,7 +2625,7 @@ impl AgentPanel {
                                     ContextMenuEntry::new("New Gemini CLI Thread")
                                         .icon(IconName::AiGemini)
                                         .icon_color(Color::Muted)
-                                        .disabled(is_via_collab)
+                                        .disabled(is_not_local)
                                         .handler({
                                             let workspace = workspace.clone();
                                             move |window, cx| {
@@ -2654,7 +2652,7 @@ impl AgentPanel {
                                 menu.item(
                                     ContextMenuEntry::new("New Claude Code Thread")
                                         .icon(IconName::AiClaude)
-                                        .disabled(is_via_collab)
+                                        .disabled(is_not_local)
                                         .icon_color(Color::Muted)
                                         .handler({
                                             let workspace = workspace.clone();
@@ -2687,7 +2685,7 @@ impl AgentPanel {
                                         ContextMenuEntry::new(format!("New {} Thread", agent_name))
                                             .icon(IconName::Terminal)
                                             .icon_color(Color::Muted)
-                                            .disabled(is_via_collab)
+                                            .disabled(is_not_local)
                                             .handler({
                                                 let workspace = workspace.clone();
                                                 let agent_name = agent_name.clone();

crates/git_ui/src/branch_picker.rs 🔗

@@ -341,7 +341,6 @@ impl PickerDelegate for BranchListDelegate {
             };
             picker
                 .update(cx, |picker, _| {
-                    #[allow(clippy::nonminimal_bool)]
                     if !query.is_empty()
                         && !matches
                             .first()

crates/vim/src/visual.rs 🔗

@@ -216,7 +216,6 @@ impl Vim {
                         // If the file ends with a newline (which is common) we don't do this.
                         // so that if you go to the end of such a file you can use "up" to go
                         // to the previous line and have it work somewhat as expected.
-                        #[allow(clippy::nonminimal_bool)]
                         if !selection.reversed
                             && !selection.is_empty()
                             && !(selection.end.column() == 0 && selection.end == map.max_point())