From 9bfe7a1798181fdc3170e696e2e55284b6d26387 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Wed, 17 Sep 2025 16:45:47 -0400 Subject: [PATCH] acp: Fix agent servers sometimes not being registered when Zed starts (#38330) In local projects, initialize the list of agents in the agent server store immediately. Previously we were initializing the list only after a delay, in an attempt to avoid sending the `ExternalAgentsUpdated` message to the downstream client (if any) before its handlers were initialized. But we already have a separate codepath for that situation, in the `AgentServerStore::shared`, and we can insert the delay in that place instead. Release Notes: - acp: Fixed a bug where starting an external agent thread soon after Zed starts up would show a "not registered" error. --------- Co-authored-by: Michael Co-authored-by: Agus --- crates/project/src/agent_server_store.rs | 43 +++++++++----------- crates/project/src/git_store/conflict_set.rs | 6 +-- crates/remote_server/src/headless_project.rs | 2 +- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/crates/project/src/agent_server_store.rs b/crates/project/src/agent_server_store.rs index bdb2297624e4a404cb3c918f07eab15004944f97..abe27710d8969d6e365d9a2539855fd625645134 100644 --- a/crates/project/src/agent_server_store.rs +++ b/crates/project/src/agent_server_store.rs @@ -234,7 +234,7 @@ impl AgentServerStore { let subscription = cx.observe_global::(|this, cx| { this.agent_servers_settings_changed(cx); }); - let this = Self { + let mut this = Self { state: AgentServerStoreState::Local { node_runtime, fs, @@ -245,14 +245,7 @@ impl AgentServerStore { }, external_agents: Default::default(), }; - cx.spawn(async move |this, cx| { - cx.background_executor().timer(Duration::from_secs(1)).await; - this.update(cx, |this, cx| { - this.agent_servers_settings_changed(cx); - }) - .ok(); - }) - .detach(); + this.agent_servers_settings_changed(cx); this } @@ -305,22 +298,29 @@ impl AgentServerStore { } } - pub fn shared(&mut self, project_id: u64, client: AnyProtoClient) { + pub fn shared(&mut self, project_id: u64, client: AnyProtoClient, cx: &mut Context) { match &mut self.state { AgentServerStoreState::Local { downstream_client, .. } => { - client - .send(proto::ExternalAgentsUpdated { - project_id, - names: self - .external_agents + *downstream_client = Some((project_id, client.clone())); + // Send the current list of external agents downstream, but only after a delay, + // to avoid having the message arrive before the downstream project's agent server store + // sets up its handlers. + cx.spawn(async move |this, cx| { + cx.background_executor().timer(Duration::from_secs(1)).await; + let names = this.update(cx, |this, _| { + this.external_agents .keys() .map(|name| name.to_string()) - .collect(), - }) - .log_err(); - *downstream_client = Some((project_id, client)); + .collect() + })?; + client + .send(proto::ExternalAgentsUpdated { project_id, names }) + .log_err(); + anyhow::Ok(()) + }) + .detach(); } AgentServerStoreState::Remote { .. } => { debug_panic!( @@ -721,11 +721,6 @@ struct RemoteExternalAgentServer { new_version_available_tx: Option>>, } -// new method: status_updated -// does nothing in the all-local case -// for RemoteExternalAgentServer, sends on the stored tx -// etc. - impl ExternalAgentServer for RemoteExternalAgentServer { fn get_command( &mut self, diff --git a/crates/project/src/git_store/conflict_set.rs b/crates/project/src/git_store/conflict_set.rs index 313a1e90adc2fde8a62dbe6aa60b4d3a366af22c..2bcfc75b32da3c5a4860cc72f3266bff38f022e3 100644 --- a/crates/project/src/git_store/conflict_set.rs +++ b/crates/project/src/git_store/conflict_set.rs @@ -257,7 +257,7 @@ impl EventEmitter for ConflictSet {} mod tests { use std::{path::Path, sync::mpsc}; - use crate::{Project, project_settings::ProjectSettings}; + use crate::Project; use super::*; use fs::FakeFs; @@ -484,7 +484,7 @@ mod tests { cx.update(|cx| { settings::init(cx); WorktreeSettings::register(cx); - ProjectSettings::register(cx); + Project::init_settings(cx); AllLanguageSettings::register(cx); }); let initial_text = " @@ -585,7 +585,7 @@ mod tests { cx.update(|cx| { settings::init(cx); WorktreeSettings::register(cx); - ProjectSettings::register(cx); + Project::init_settings(cx); AllLanguageSettings::register(cx); }); diff --git a/crates/remote_server/src/headless_project.rs b/crates/remote_server/src/headless_project.rs index bdfd46002e63069b68b5661f1733060818a291e7..f60f859481cccb3de9ea89ebb374d19ef9812690 100644 --- a/crates/remote_server/src/headless_project.rs +++ b/crates/remote_server/src/headless_project.rs @@ -198,7 +198,7 @@ impl HeadlessProject { let agent_server_store = cx.new(|cx| { let mut agent_server_store = AgentServerStore::local(node_runtime.clone(), fs.clone(), environment, cx); - agent_server_store.shared(REMOTE_SERVER_PROJECT_ID, session.clone()); + agent_server_store.shared(REMOTE_SERVER_PROJECT_ID, session.clone(), cx); agent_server_store });