agent_servers: Use agent display_name for session titles (#50092)

Ben Brandt created

Use a separate `display_name` field (distinct from `server_name`) so
that session titles show a human-readable name. For custom agents this
resolves to the configured display name; for built-ins it falls back to
the server name.

Release Notes:

- N/A

Change summary

crates/agent_servers/src/acp.rs    | 19 ++++++++++++++++---
crates/agent_servers/src/claude.rs |  1 +
crates/agent_servers/src/codex.rs  |  1 +
crates/agent_servers/src/custom.rs |  6 ++++++
crates/agent_servers/src/gemini.rs |  1 +
5 files changed, 25 insertions(+), 3 deletions(-)

Detailed changes

crates/agent_servers/src/acp.rs 🔗

@@ -36,6 +36,7 @@ pub struct UnsupportedVersion;
 
 pub struct AcpConnection {
     server_name: SharedString,
+    display_name: SharedString,
     telemetry_id: SharedString,
     connection: Rc<acp::ClientSideConnection>,
     sessions: Rc<RefCell<HashMap<acp::SessionId, AcpSession>>>,
@@ -158,6 +159,7 @@ impl AgentSessionList for AcpSessionList {
 
 pub async fn connect(
     server_name: SharedString,
+    display_name: SharedString,
     command: AgentServerCommand,
     root_dir: &Path,
     default_mode: Option<acp::SessionModeId>,
@@ -168,6 +170,7 @@ pub async fn connect(
 ) -> Result<Rc<dyn AgentConnection>> {
     let conn = AcpConnection::stdio(
         server_name,
+        display_name,
         command.clone(),
         root_dir,
         default_mode,
@@ -185,6 +188,7 @@ const MINIMUM_SUPPORTED_VERSION: acp::ProtocolVersion = acp::ProtocolVersion::V1
 impl AcpConnection {
     pub async fn stdio(
         server_name: SharedString,
+        display_name: SharedString,
         command: AgentServerCommand,
         root_dir: &Path,
         default_mode: Option<acp::SessionModeId>,
@@ -330,6 +334,7 @@ impl AcpConnection {
             root_dir: root_dir.to_owned(),
             connection,
             server_name,
+            display_name,
             telemetry_id,
             sessions,
             agent_capabilities: response.agent_capabilities,
@@ -550,7 +555,7 @@ impl AgentConnection for AcpConnection {
             let thread: Entity<AcpThread> = cx.new(|cx| {
                 AcpThread::new(
                     None,
-                    self.server_name.clone(),
+                    self.display_name.clone(),
                     self.clone(),
                     project,
                     action_log,
@@ -603,10 +608,14 @@ impl AgentConnection for AcpConnection {
         let cwd = cwd.to_path_buf();
         let mcp_servers = mcp_servers_for_project(&project, cx);
         let action_log = cx.new(|_| ActionLog::new(project.clone()));
+        let title = session
+            .title
+            .clone()
+            .unwrap_or_else(|| self.display_name.clone());
         let thread: Entity<AcpThread> = cx.new(|cx| {
             AcpThread::new(
                 None,
-                self.server_name.clone(),
+                title,
                 self.clone(),
                 project,
                 action_log,
@@ -676,10 +685,14 @@ impl AgentConnection for AcpConnection {
         let cwd = cwd.to_path_buf();
         let mcp_servers = mcp_servers_for_project(&project, cx);
         let action_log = cx.new(|_| ActionLog::new(project.clone()));
+        let title = session
+            .title
+            .clone()
+            .unwrap_or_else(|| self.display_name.clone());
         let thread: Entity<AcpThread> = cx.new(|cx| {
             AcpThread::new(
                 None,
-                self.server_name.clone(),
+                title,
                 self.clone(),
                 project,
                 action_log,

crates/agent_servers/src/claude.rs 🔗

@@ -244,6 +244,7 @@ impl AgentServer for ClaudeCode {
                 })??
                 .await?;
             let connection = crate::acp::connect(
+                name.clone(),
                 name,
                 command,
                 root_dir.as_ref(),

crates/agent_servers/src/codex.rs 🔗

@@ -248,6 +248,7 @@ impl AgentServer for Codex {
                 .await?;
 
             let connection = crate::acp::connect(
+                name.clone(),
                 name,
                 command,
                 root_dir.as_ref(),

crates/agent_servers/src/custom.rs 🔗

@@ -332,6 +332,11 @@ impl AgentServer for CustomAgentServer {
         cx: &mut App,
     ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
         let name = self.name();
+        let display_name = delegate
+            .store
+            .read(cx)
+            .agent_display_name(&ExternalAgentServerName(name.clone()))
+            .unwrap_or_else(|| name.clone());
         let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
         let is_remote = delegate.project.read(cx).is_via_remote_server();
         let default_mode = self.default_mode(cx);
@@ -399,6 +404,7 @@ impl AgentServer for CustomAgentServer {
                 .await?;
             let connection = crate::acp::connect(
                 name,
+                display_name,
                 command,
                 root_dir.as_ref(),
                 default_mode,