Add back DeletePathTool to agent2 (#36168)

This was probably removed accidentally as a result of a merge conflict.

Release Notes:

- N/A
This commit is contained in:
Antonio Scandurra 2025-08-14 11:11:27 +02:00 committed by GitHub
parent 8e4f30abcb
commit b3d048d6dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 57 additions and 80 deletions

View file

@ -1,8 +1,8 @@
use crate::{AgentResponseEvent, Thread, templates::Templates}; use crate::{AgentResponseEvent, Thread, templates::Templates};
use crate::{ use crate::{
ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DiagnosticsTool, EditFileTool, ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DeletePathTool, DiagnosticsTool,
FetchTool, FindPathTool, GrepTool, ListDirectoryTool, MovePathTool, NowTool, OpenTool, EditFileTool, FetchTool, FindPathTool, GrepTool, ListDirectoryTool, MovePathTool, NowTool,
ReadFileTool, TerminalTool, ThinkingTool, ToolCallAuthorization, UserMessageContent, OpenTool, ReadFileTool, TerminalTool, ThinkingTool, ToolCallAuthorization, UserMessageContent,
WebSearchTool, WebSearchTool,
}; };
use acp_thread::AgentModelSelector; use acp_thread::AgentModelSelector;
@ -583,22 +583,22 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
default_model, default_model,
cx, cx,
); );
thread.add_tool(CreateDirectoryTool::new(project.clone()));
thread.add_tool(CopyPathTool::new(project.clone())); thread.add_tool(CopyPathTool::new(project.clone()));
thread.add_tool(CreateDirectoryTool::new(project.clone()));
thread.add_tool(DeletePathTool::new(project.clone(), action_log.clone()));
thread.add_tool(DiagnosticsTool::new(project.clone())); thread.add_tool(DiagnosticsTool::new(project.clone()));
thread.add_tool(MovePathTool::new(project.clone()));
thread.add_tool(ListDirectoryTool::new(project.clone()));
thread.add_tool(OpenTool::new(project.clone()));
thread.add_tool(ThinkingTool);
thread.add_tool(FindPathTool::new(project.clone()));
thread.add_tool(FetchTool::new(project.read(cx).client().http_client()));
thread.add_tool(GrepTool::new(project.clone()));
thread.add_tool(ReadFileTool::new(project.clone(), action_log));
thread.add_tool(EditFileTool::new(cx.entity())); thread.add_tool(EditFileTool::new(cx.entity()));
thread.add_tool(FetchTool::new(project.read(cx).client().http_client()));
thread.add_tool(FindPathTool::new(project.clone()));
thread.add_tool(GrepTool::new(project.clone()));
thread.add_tool(ListDirectoryTool::new(project.clone()));
thread.add_tool(MovePathTool::new(project.clone()));
thread.add_tool(NowTool); thread.add_tool(NowTool);
thread.add_tool(OpenTool::new(project.clone()));
thread.add_tool(ReadFileTool::new(project.clone(), action_log));
thread.add_tool(TerminalTool::new(project.clone(), cx)); thread.add_tool(TerminalTool::new(project.clone(), cx));
// TODO: Needs to be conditional based on zed model or not thread.add_tool(ThinkingTool);
thread.add_tool(WebSearchTool); thread.add_tool(WebSearchTool); // TODO: Enable this only if it's a zed model.
thread thread
}); });

View file

@ -411,7 +411,7 @@ pub struct Thread {
/// Survives across multiple requests as the model performs tool calls and /// Survives across multiple requests as the model performs tool calls and
/// we run tools, report their results. /// we run tools, report their results.
running_turn: Option<Task<()>>, running_turn: Option<Task<()>>,
pending_agent_message: Option<AgentMessage>, pending_message: Option<AgentMessage>,
tools: BTreeMap<SharedString, Arc<dyn AnyAgentTool>>, tools: BTreeMap<SharedString, Arc<dyn AnyAgentTool>>,
context_server_registry: Entity<ContextServerRegistry>, context_server_registry: Entity<ContextServerRegistry>,
profile_id: AgentProfileId, profile_id: AgentProfileId,
@ -437,7 +437,7 @@ impl Thread {
messages: Vec::new(), messages: Vec::new(),
completion_mode: CompletionMode::Normal, completion_mode: CompletionMode::Normal,
running_turn: None, running_turn: None,
pending_agent_message: None, pending_message: None,
tools: BTreeMap::default(), tools: BTreeMap::default(),
context_server_registry, context_server_registry,
profile_id, profile_id,
@ -463,7 +463,7 @@ impl Thread {
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub fn last_message(&self) -> Option<Message> { pub fn last_message(&self) -> Option<Message> {
if let Some(message) = self.pending_agent_message.clone() { if let Some(message) = self.pending_message.clone() {
Some(Message::Agent(message)) Some(Message::Agent(message))
} else { } else {
self.messages.last().cloned() self.messages.last().cloned()
@ -485,7 +485,7 @@ impl Thread {
pub fn cancel(&mut self) { pub fn cancel(&mut self) {
// TODO: do we need to emit a stop::cancel for ACP? // TODO: do we need to emit a stop::cancel for ACP?
self.running_turn.take(); self.running_turn.take();
self.flush_pending_agent_message(); self.flush_pending_message();
} }
pub fn truncate(&mut self, message_id: UserMessageId) -> Result<()> { pub fn truncate(&mut self, message_id: UserMessageId) -> Result<()> {
@ -521,74 +521,58 @@ impl Thread {
mpsc::unbounded::<Result<AgentResponseEvent, LanguageModelCompletionError>>(); mpsc::unbounded::<Result<AgentResponseEvent, LanguageModelCompletionError>>();
let event_stream = AgentResponseEventStream(events_tx); let event_stream = AgentResponseEventStream(events_tx);
let user_message_ix = self.messages.len();
self.messages.push(Message::User(UserMessage { self.messages.push(Message::User(UserMessage {
id: message_id, id: message_id.clone(),
content, content,
})); }));
log::info!("Total messages in thread: {}", self.messages.len()); log::info!("Total messages in thread: {}", self.messages.len());
self.running_turn = Some(cx.spawn(async move |thread, cx| { self.running_turn = Some(cx.spawn(async move |this, cx| {
log::info!("Starting agent turn execution"); log::info!("Starting agent turn execution");
let turn_result = async { let turn_result = async {
// Perform one request, then keep looping if the model makes tool calls.
let mut completion_intent = CompletionIntent::UserPrompt; let mut completion_intent = CompletionIntent::UserPrompt;
'outer: loop { loop {
log::debug!( log::debug!(
"Building completion request with intent: {:?}", "Building completion request with intent: {:?}",
completion_intent completion_intent
); );
let request = thread.update(cx, |thread, cx| { let request = this.update(cx, |this, cx| {
thread.build_completion_request(completion_intent, cx) this.build_completion_request(completion_intent, cx)
})?; })?;
// Stream events, appending to messages and collecting up tool uses.
log::info!("Calling model.stream_completion"); log::info!("Calling model.stream_completion");
let mut events = model.stream_completion(request, cx).await?; let mut events = model.stream_completion(request, cx).await?;
log::debug!("Stream completion started successfully"); log::debug!("Stream completion started successfully");
let mut tool_uses = FuturesUnordered::new(); let mut tool_uses = FuturesUnordered::new();
while let Some(event) = events.next().await { while let Some(event) = events.next().await {
match event { match event? {
Ok(LanguageModelCompletionEvent::Stop(reason)) => { LanguageModelCompletionEvent::Stop(reason) => {
event_stream.send_stop(reason); event_stream.send_stop(reason);
if reason == StopReason::Refusal { if reason == StopReason::Refusal {
thread.update(cx, |thread, _cx| { this.update(cx, |this, _cx| this.truncate(message_id))??;
thread.pending_agent_message = None; return Ok(());
thread.messages.truncate(user_message_ix);
})?;
break 'outer;
} }
} }
Ok(event) => { event => {
log::trace!("Received completion event: {:?}", event); log::trace!("Received completion event: {:?}", event);
thread this.update(cx, |this, cx| {
.update(cx, |thread, cx| { tool_uses.extend(this.handle_streamed_completion_event(
tool_uses.extend(thread.handle_streamed_completion_event( event,
event, &event_stream,
&event_stream, cx,
cx, ));
)); })
}) .ok();
.ok();
}
Err(error) => {
log::error!("Error in completion stream: {:?}", error);
event_stream.send_error(error);
break;
} }
} }
} }
// If there are no tool uses, the turn is done.
if tool_uses.is_empty() { if tool_uses.is_empty() {
log::info!("No tool uses found, completing turn"); log::info!("No tool uses found, completing turn");
break; return Ok(());
} }
log::info!("Found {} tool uses to execute", tool_uses.len()); log::info!("Found {} tool uses to execute", tool_uses.len());
// As tool results trickle in, insert them in the last user
// message so that they can be sent on the next tick of the
// agentic loop.
while let Some(tool_result) = tool_uses.next().await { while let Some(tool_result) = tool_uses.next().await {
log::info!("Tool finished {:?}", tool_result); log::info!("Tool finished {:?}", tool_result);
@ -604,29 +588,21 @@ impl Thread {
..Default::default() ..Default::default()
}, },
); );
thread this.update(cx, |this, _cx| {
.update(cx, |thread, _cx| { this.pending_message()
thread .tool_results
.pending_agent_message() .insert(tool_result.tool_use_id.clone(), tool_result);
.tool_results })
.insert(tool_result.tool_use_id.clone(), tool_result); .ok();
})
.ok();
} }
thread.update(cx, |thread, _cx| thread.flush_pending_agent_message())?; this.update(cx, |this, _| this.flush_pending_message())?;
completion_intent = CompletionIntent::ToolResults; completion_intent = CompletionIntent::ToolResults;
} }
Ok(())
} }
.await; .await;
thread this.update(cx, |this, _| this.flush_pending_message()).ok();
.update(cx, |thread, _cx| thread.flush_pending_agent_message())
.ok();
if let Err(error) = turn_result { if let Err(error) = turn_result {
log::error!("Turn execution failed: {:?}", error); log::error!("Turn execution failed: {:?}", error);
event_stream.send_error(error); event_stream.send_error(error);
@ -668,7 +644,8 @@ impl Thread {
match event { match event {
StartMessage { .. } => { StartMessage { .. } => {
self.messages.push(Message::Agent(AgentMessage::default())); self.flush_pending_message();
self.pending_message = Some(AgentMessage::default());
} }
Text(new_text) => self.handle_text_event(new_text, event_stream, cx), Text(new_text) => self.handle_text_event(new_text, event_stream, cx),
Thinking { text, signature } => { Thinking { text, signature } => {
@ -706,7 +683,7 @@ impl Thread {
) { ) {
events_stream.send_text(&new_text); events_stream.send_text(&new_text);
let last_message = self.pending_agent_message(); let last_message = self.pending_message();
if let Some(AgentMessageContent::Text(text)) = last_message.content.last_mut() { if let Some(AgentMessageContent::Text(text)) = last_message.content.last_mut() {
text.push_str(&new_text); text.push_str(&new_text);
} else { } else {
@ -727,7 +704,7 @@ impl Thread {
) { ) {
event_stream.send_thinking(&new_text); event_stream.send_thinking(&new_text);
let last_message = self.pending_agent_message(); let last_message = self.pending_message();
if let Some(AgentMessageContent::Thinking { text, signature }) = if let Some(AgentMessageContent::Thinking { text, signature }) =
last_message.content.last_mut() last_message.content.last_mut()
{ {
@ -744,7 +721,7 @@ impl Thread {
} }
fn handle_redacted_thinking_event(&mut self, data: String, cx: &mut Context<Self>) { fn handle_redacted_thinking_event(&mut self, data: String, cx: &mut Context<Self>) {
let last_message = self.pending_agent_message(); let last_message = self.pending_message();
last_message last_message
.content .content
.push(AgentMessageContent::RedactedThinking(data)); .push(AgentMessageContent::RedactedThinking(data));
@ -768,7 +745,7 @@ impl Thread {
} }
// Ensure the last message ends in the current tool use // Ensure the last message ends in the current tool use
let last_message = self.pending_agent_message(); let last_message = self.pending_message();
let push_new_tool_use = last_message.content.last_mut().map_or(true, |content| { let push_new_tool_use = last_message.content.last_mut().map_or(true, |content| {
if let AgentMessageContent::ToolUse(last_tool_use) = content { if let AgentMessageContent::ToolUse(last_tool_use) = content {
if last_tool_use.id == tool_use.id { if last_tool_use.id == tool_use.id {
@ -871,12 +848,12 @@ impl Thread {
} }
} }
fn pending_agent_message(&mut self) -> &mut AgentMessage { fn pending_message(&mut self) -> &mut AgentMessage {
self.pending_agent_message.get_or_insert_default() self.pending_message.get_or_insert_default()
} }
fn flush_pending_agent_message(&mut self) { fn flush_pending_message(&mut self) {
let Some(mut message) = self.pending_agent_message.take() else { let Some(mut message) = self.pending_message.take() else {
return; return;
}; };
@ -997,7 +974,7 @@ impl Thread {
} }
} }
if let Some(message) = self.pending_agent_message.as_ref() { if let Some(message) = self.pending_message.as_ref() {
messages.extend(message.to_request()); messages.extend(message.to_request());
} }
@ -1013,7 +990,7 @@ impl Thread {
markdown.push_str(&message.to_markdown()); markdown.push_str(&message.to_markdown());
} }
if let Some(message) = self.pending_agent_message.as_ref() { if let Some(message) = self.pending_message.as_ref() {
markdown.push('\n'); markdown.push('\n');
markdown.push_str(&message.to_markdown()); markdown.push_str(&message.to_markdown());
} }