agent: Only require confirmation for batch tool when subset of tool calls require confirmation (#28363)

Bennet Bo Fenner created

Release Notes:

- agent: Only require confirmation for batch tool when subset of tool
calls require confirmation

Change summary

crates/agent/src/thread.rs                           |  2 +-
crates/agent/src/tool_use.rs                         |  2 +-
crates/assistant_tool/src/assistant_tool.rs          |  2 +-
crates/assistant_tools/src/bash_tool.rs              |  2 +-
crates/assistant_tools/src/batch_tool.rs             | 13 +++++++++++--
crates/assistant_tools/src/code_symbols_tool.rs      |  2 +-
crates/assistant_tools/src/copy_path_tool.rs         |  2 +-
crates/assistant_tools/src/create_directory_tool.rs  |  2 +-
crates/assistant_tools/src/create_file_tool.rs       |  2 +-
crates/assistant_tools/src/delete_path_tool.rs       |  2 +-
crates/assistant_tools/src/diagnostics_tool.rs       |  2 +-
crates/assistant_tools/src/fetch_tool.rs             |  2 +-
crates/assistant_tools/src/find_replace_file_tool.rs |  2 +-
crates/assistant_tools/src/list_directory_tool.rs    |  2 +-
crates/assistant_tools/src/move_path_tool.rs         |  2 +-
crates/assistant_tools/src/now_tool.rs               |  2 +-
crates/assistant_tools/src/open_tool.rs              |  2 +-
crates/assistant_tools/src/path_search_tool.rs       |  2 +-
crates/assistant_tools/src/read_file_tool.rs         |  2 +-
crates/assistant_tools/src/regex_search_tool.rs      |  2 +-
crates/assistant_tools/src/symbol_info_tool.rs       |  2 +-
crates/assistant_tools/src/thinking_tool.rs          |  2 +-
crates/context_server/src/context_server_tool.rs     |  2 +-
23 files changed, 33 insertions(+), 24 deletions(-)

Detailed changes

crates/agent/src/thread.rs 🔗

@@ -1414,7 +1414,7 @@ impl Thread {
 
         for tool_use in pending_tool_uses.iter() {
             if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
-                if tool.needs_confirmation()
+                if tool.needs_confirmation(&tool_use.input, cx)
                     && !AssistantSettings::get_global(cx).always_allow_tool_actions
                 {
                     self.tool_use.confirm_tool_use(

crates/agent/src/tool_use.rs 🔗

@@ -201,7 +201,7 @@ impl ToolUseState {
 
             let (icon, needs_confirmation) = if let Some(tool) = self.tools.tool(&tool_use.name, cx)
             {
-                (tool.icon(), tool.needs_confirmation())
+                (tool.icon(), tool.needs_confirmation(&tool_use.input, cx))
             } else {
                 (IconName::Cog, false)
             };

crates/assistant_tool/src/assistant_tool.rs 🔗

@@ -48,7 +48,7 @@ pub trait Tool: 'static + Send + Sync {
 
     /// Returns true iff the tool needs the users's confirmation
     /// before having permission to run.
-    fn needs_confirmation(&self) -> bool;
+    fn needs_confirmation(&self, input: &serde_json::Value, cx: &App) -> bool;
 
     /// Returns the JSON schema that describes the tool's input.
     fn input_schema(&self, _: LanguageModelToolSchemaFormat) -> serde_json::Value {

crates/assistant_tools/src/bash_tool.rs 🔗

@@ -29,7 +29,7 @@ impl Tool for BashTool {
         "bash".to_string()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         true
     }
 

crates/assistant_tools/src/batch_tool.rs 🔗

@@ -151,8 +151,17 @@ impl Tool for BatchTool {
         "batch_tool".into()
     }
 
-    fn needs_confirmation(&self) -> bool {
-        true
+    fn needs_confirmation(&self, input: &serde_json::Value, cx: &App) -> bool {
+        serde_json::from_value::<BatchToolInput>(input.clone())
+            .map(|input| {
+                let working_set = ToolWorkingSet::default();
+                input.invocations.iter().any(|invocation| {
+                    working_set
+                        .tool(&invocation.name, cx)
+                        .map_or(false, |tool| tool.needs_confirmation(&invocation.input, cx))
+                })
+            })
+            .unwrap_or(false)
     }
 
     fn description(&self) -> String {

crates/assistant_tools/src/copy_path_tool.rs 🔗

@@ -43,7 +43,7 @@ impl Tool for CopyPathTool {
         "copy_path".into()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         true
     }
 

crates/assistant_tools/src/fetch_tool.rs 🔗

@@ -116,7 +116,7 @@ impl Tool for FetchTool {
         "fetch".to_string()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         true
     }
 

crates/assistant_tools/src/move_path_tool.rs 🔗

@@ -42,7 +42,7 @@ impl Tool for MovePathTool {
         "move_path".into()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         true
     }
 

crates/assistant_tools/src/now_tool.rs 🔗

@@ -33,7 +33,7 @@ impl Tool for NowTool {
         "now".into()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         false
     }
 

crates/assistant_tools/src/open_tool.rs 🔗

@@ -23,7 +23,7 @@ impl Tool for OpenTool {
         "open".to_string()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         true
     }
 

crates/assistant_tools/src/read_file_tool.rs 🔗

@@ -51,7 +51,7 @@ impl Tool for ReadFileTool {
         "read_file".into()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         false
     }
 

crates/assistant_tools/src/thinking_tool.rs 🔗

@@ -24,7 +24,7 @@ impl Tool for ThinkingTool {
         "thinking".to_string()
     }
 
-    fn needs_confirmation(&self) -> bool {
+    fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
         false
     }