From 31fa0b4c302fc4ffc0c3683c4dd8be5b51a2eb72 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Fri, 19 Sep 2025 17:30:56 -0400 Subject: [PATCH] terminal proof-of-concept --- crates/acp_thread/src/acp_thread.rs | 58 ++++++++++++++++++++++------- crates/agent2/src/agent.rs | 2 +- crates/agent_servers/src/acp.rs | 9 +++++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index f2327ca70b104de12f44d74aacd1a5a2bb1eca3b..821331a8341cfa1dab258178dcf2cbff19ad3245 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -1949,6 +1949,7 @@ impl AcpThread { extra_env: Vec, cwd: Option, output_byte_limit: Option, + is_display_only: bool, cx: &mut Context, ) -> Task>> { let env = match &cwd { @@ -1989,20 +1990,49 @@ impl AcpThread { ) .redirect_stdin_to_dev_null() .build(Some(command), &args); - let terminal = project - .update(cx, |project, cx| { - project.create_terminal_task( - task::SpawnInTerminal { - command: Some(command.clone()), - args: args.clone(), - cwd: cwd.clone(), - env, - ..Default::default() - }, - cx, - ) - })? - .await?; + + // For display-only terminals, create a terminal that doesn't actually execute + let terminal = if is_display_only { + // Create a display-only terminal that just shows a header + // The actual output will be streamed from the agent + project + .update(cx, |project, cx| { + project.create_terminal_task( + task::SpawnInTerminal { + // Use a simple command that shows the header and waits + // We use sleep to keep the terminal alive for output streaming + command: Some("sh".to_string()), + args: vec![ + "-c".to_string(), + format!( + "echo '\\033[1;34m[Display Terminal]\\033[0m {}' && sleep 86400", + command + ), + ], + cwd: cwd.clone(), + env: Default::default(), // Don't pass environment to avoid printing it + ..Default::default() + }, + cx, + ) + })? + .await? + } else { + project + .update(cx, |project, cx| { + project.create_terminal_task( + task::SpawnInTerminal { + command: Some(command.clone()), + args: args.clone(), + cwd: cwd.clone(), + env, + ..Default::default() + }, + cx, + ) + })? + .await? + }; cx.new(|cx| { Terminal::new( diff --git a/crates/agent2/src/agent.rs b/crates/agent2/src/agent.rs index 86fb50242c64917248df5c620782af066e639b54..0311f63ab9bef0b16dcd0b9585b925b9a1942f55 100644 --- a/crates/agent2/src/agent.rs +++ b/crates/agent2/src/agent.rs @@ -1144,7 +1144,7 @@ impl ThreadEnvironment for AcpThreadEnvironment { cx: &mut AsyncApp, ) -> Task>> { let task = self.acp_thread.update(cx, |thread, cx| { - thread.create_terminal(command, vec![], vec![], cwd, output_byte_limit, cx) + thread.create_terminal(command, vec![], vec![], cwd, output_byte_limit, false, cx) }); let acp_thread = self.acp_thread.clone(); diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index b8c75a01a2e2965c255e32bd3c0746b26d78ecab..04f76ebc857569c585a3ea3364a32e84f12f8b91 100644 --- a/crates/agent_servers/src/acp.rs +++ b/crates/agent_servers/src/acp.rs @@ -606,6 +606,14 @@ impl acp::Client for ClientDelegate { &self, args: acp::CreateTerminalRequest, ) -> Result { + // Check if this is a display-only terminal from the metadata + let is_display_only = args + .meta + .as_ref() + .and_then(|meta| meta.get("display_only")) + .and_then(|v| v.as_bool()) + .unwrap_or(false); + let terminal = self .session_thread(&args.session_id)? .update(&mut self.cx.clone(), |thread, cx| { @@ -615,6 +623,7 @@ impl acp::Client for ClientDelegate { args.env, args.cwd, args.output_byte_limit, + is_display_only, cx, ) })?