Port terminal
tool to agent2 (#35918)
Release Notes: - N/A --------- Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
This commit is contained in:
parent
422e0a2eb7
commit
086ea3c619
13 changed files with 882 additions and 112 deletions
|
@ -32,6 +32,7 @@ serde.workspace = true
|
|||
serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
terminal.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace-hack.workspace = true
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
mod connection;
|
||||
mod diff;
|
||||
mod terminal;
|
||||
|
||||
pub use connection::*;
|
||||
pub use diff::*;
|
||||
pub use terminal::*;
|
||||
|
||||
use action_log::ActionLog;
|
||||
use agent_client_protocol as acp;
|
||||
|
@ -147,6 +149,14 @@ impl AgentThreadEntry {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn terminals(&self) -> impl Iterator<Item = &Entity<Terminal>> {
|
||||
if let AgentThreadEntry::ToolCall(call) = self {
|
||||
itertools::Either::Left(call.terminals())
|
||||
} else {
|
||||
itertools::Either::Right(std::iter::empty())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn locations(&self) -> Option<&[acp::ToolCallLocation]> {
|
||||
if let AgentThreadEntry::ToolCall(ToolCall { locations, .. }) = self {
|
||||
Some(locations)
|
||||
|
@ -250,8 +260,17 @@ impl ToolCall {
|
|||
|
||||
pub fn diffs(&self) -> impl Iterator<Item = &Entity<Diff>> {
|
||||
self.content.iter().filter_map(|content| match content {
|
||||
ToolCallContent::ContentBlock { .. } => None,
|
||||
ToolCallContent::Diff { diff } => Some(diff),
|
||||
ToolCallContent::Diff(diff) => Some(diff),
|
||||
ToolCallContent::ContentBlock(_) => None,
|
||||
ToolCallContent::Terminal(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn terminals(&self) -> impl Iterator<Item = &Entity<Terminal>> {
|
||||
self.content.iter().filter_map(|content| match content {
|
||||
ToolCallContent::Terminal(terminal) => Some(terminal),
|
||||
ToolCallContent::ContentBlock(_) => None,
|
||||
ToolCallContent::Diff(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -387,8 +406,9 @@ impl ContentBlock {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum ToolCallContent {
|
||||
ContentBlock { content: ContentBlock },
|
||||
Diff { diff: Entity<Diff> },
|
||||
ContentBlock(ContentBlock),
|
||||
Diff(Entity<Diff>),
|
||||
Terminal(Entity<Terminal>),
|
||||
}
|
||||
|
||||
impl ToolCallContent {
|
||||
|
@ -398,19 +418,20 @@ impl ToolCallContent {
|
|||
cx: &mut App,
|
||||
) -> Self {
|
||||
match content {
|
||||
acp::ToolCallContent::Content { content } => Self::ContentBlock {
|
||||
content: ContentBlock::new(content, &language_registry, cx),
|
||||
},
|
||||
acp::ToolCallContent::Diff { diff } => Self::Diff {
|
||||
diff: cx.new(|cx| Diff::from_acp(diff, language_registry, cx)),
|
||||
},
|
||||
acp::ToolCallContent::Content { content } => {
|
||||
Self::ContentBlock(ContentBlock::new(content, &language_registry, cx))
|
||||
}
|
||||
acp::ToolCallContent::Diff { diff } => {
|
||||
Self::Diff(cx.new(|cx| Diff::from_acp(diff, language_registry, cx)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_markdown(&self, cx: &App) -> String {
|
||||
match self {
|
||||
Self::ContentBlock { content } => content.to_markdown(cx).to_string(),
|
||||
Self::Diff { diff } => diff.read(cx).to_markdown(cx),
|
||||
Self::ContentBlock(content) => content.to_markdown(cx).to_string(),
|
||||
Self::Diff(diff) => diff.read(cx).to_markdown(cx),
|
||||
Self::Terminal(terminal) => terminal.read(cx).to_markdown(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -419,6 +440,7 @@ impl ToolCallContent {
|
|||
pub enum ToolCallUpdate {
|
||||
UpdateFields(acp::ToolCallUpdate),
|
||||
UpdateDiff(ToolCallUpdateDiff),
|
||||
UpdateTerminal(ToolCallUpdateTerminal),
|
||||
}
|
||||
|
||||
impl ToolCallUpdate {
|
||||
|
@ -426,6 +448,7 @@ impl ToolCallUpdate {
|
|||
match self {
|
||||
Self::UpdateFields(update) => &update.id,
|
||||
Self::UpdateDiff(diff) => &diff.id,
|
||||
Self::UpdateTerminal(terminal) => &terminal.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -448,6 +471,18 @@ pub struct ToolCallUpdateDiff {
|
|||
pub diff: Entity<Diff>,
|
||||
}
|
||||
|
||||
impl From<ToolCallUpdateTerminal> for ToolCallUpdate {
|
||||
fn from(terminal: ToolCallUpdateTerminal) -> Self {
|
||||
Self::UpdateTerminal(terminal)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ToolCallUpdateTerminal {
|
||||
pub id: acp::ToolCallId,
|
||||
pub terminal: Entity<Terminal>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Plan {
|
||||
pub entries: Vec<PlanEntry>,
|
||||
|
@ -760,7 +795,13 @@ impl AcpThread {
|
|||
current_call.content.clear();
|
||||
current_call
|
||||
.content
|
||||
.push(ToolCallContent::Diff { diff: update.diff });
|
||||
.push(ToolCallContent::Diff(update.diff));
|
||||
}
|
||||
ToolCallUpdate::UpdateTerminal(update) => {
|
||||
current_call.content.clear();
|
||||
current_call
|
||||
.content
|
||||
.push(ToolCallContent::Terminal(update.terminal));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
87
crates/acp_thread/src/terminal.rs
Normal file
87
crates/acp_thread/src/terminal.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use gpui::{App, AppContext, Context, Entity};
|
||||
use language::LanguageRegistry;
|
||||
use markdown::Markdown;
|
||||
use std::{path::PathBuf, process::ExitStatus, sync::Arc, time::Instant};
|
||||
|
||||
pub struct Terminal {
|
||||
command: Entity<Markdown>,
|
||||
working_dir: Option<PathBuf>,
|
||||
terminal: Entity<terminal::Terminal>,
|
||||
started_at: Instant,
|
||||
output: Option<TerminalOutput>,
|
||||
}
|
||||
|
||||
pub struct TerminalOutput {
|
||||
pub ended_at: Instant,
|
||||
pub exit_status: Option<ExitStatus>,
|
||||
pub was_content_truncated: bool,
|
||||
pub original_content_len: usize,
|
||||
pub content_line_count: usize,
|
||||
pub finished_with_empty_output: bool,
|
||||
}
|
||||
|
||||
impl Terminal {
|
||||
pub fn new(
|
||||
command: String,
|
||||
working_dir: Option<PathBuf>,
|
||||
terminal: Entity<terminal::Terminal>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
command: cx
|
||||
.new(|cx| Markdown::new(command.into(), Some(language_registry.clone()), None, cx)),
|
||||
working_dir,
|
||||
terminal,
|
||||
started_at: Instant::now(),
|
||||
output: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(
|
||||
&mut self,
|
||||
exit_status: Option<ExitStatus>,
|
||||
original_content_len: usize,
|
||||
truncated_content_len: usize,
|
||||
content_line_count: usize,
|
||||
finished_with_empty_output: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.output = Some(TerminalOutput {
|
||||
ended_at: Instant::now(),
|
||||
exit_status,
|
||||
was_content_truncated: truncated_content_len < original_content_len,
|
||||
original_content_len,
|
||||
content_line_count,
|
||||
finished_with_empty_output,
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn command(&self) -> &Entity<Markdown> {
|
||||
&self.command
|
||||
}
|
||||
|
||||
pub fn working_dir(&self) -> &Option<PathBuf> {
|
||||
&self.working_dir
|
||||
}
|
||||
|
||||
pub fn started_at(&self) -> Instant {
|
||||
self.started_at
|
||||
}
|
||||
|
||||
pub fn output(&self) -> Option<&TerminalOutput> {
|
||||
self.output.as_ref()
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &Entity<terminal::Terminal> {
|
||||
&self.terminal
|
||||
}
|
||||
|
||||
pub fn to_markdown(&self, cx: &App) -> String {
|
||||
format!(
|
||||
"Terminal:\n```\n{}\n```\n",
|
||||
self.terminal.read(cx).get_content()
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue