Remove thinking tool (#48952)

Richard Feldman created

Closes AI-10

The thinking tool is no longer needed. This removes it from the agent
codebase and updates all tests to use other tools instead.

Release Notes:

- Removed the thinking tool from the Agent, as models now have their own
first-class thinking APIs.

Change summary

crates/agent/src/tests/mod.rs                       | 35 +++-------
crates/agent/src/thread.rs                          |  5 
crates/agent/src/tools.rs                           |  3 
crates/agent/src/tools/subagent_tool.rs             |  4 
crates/agent/src/tools/thinking_tool.rs             | 48 ---------------
crates/eval/src/examples/add_arg_to_trait_method.rs |  1 
6 files changed, 15 insertions(+), 81 deletions(-)

Detailed changes

crates/agent/src/tests/mod.rs 🔗

@@ -2956,12 +2956,12 @@ async fn test_agent_connection(cx: &mut TestAppContext) {
 #[gpui::test]
 async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
     let ThreadTest { thread, model, .. } = setup(cx, TestModel::Fake).await;
-    thread.update(cx, |thread, _cx| thread.add_tool(ThinkingTool, None));
+    thread.update(cx, |thread, _cx| thread.add_tool(EchoTool, None));
     let fake_model = model.as_fake();
 
     let mut events = thread
         .update(cx, |thread, cx| {
-            thread.send(UserMessageId::new(), ["Think"], cx)
+            thread.send(UserMessageId::new(), ["Echo something"], cx)
         })
         .unwrap();
     cx.run_until_parked();
@@ -2971,7 +2971,7 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
     fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
         LanguageModelToolUse {
             id: "1".into(),
-            name: ThinkingTool::NAME.into(),
+            name: EchoTool::NAME.into(),
             raw_input: input.to_string(),
             input,
             is_input_complete: false,
@@ -2980,11 +2980,11 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
     ));
 
     // Input streaming completed
-    let input = json!({ "content": "Thinking hard!" });
+    let input = json!({ "text": "Hello!" });
     fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
         LanguageModelToolUse {
             id: "1".into(),
-            name: "thinking".into(),
+            name: "echo".into(),
             raw_input: input.to_string(),
             input,
             is_input_complete: true,
@@ -2997,13 +2997,9 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
     let tool_call = expect_tool_call(&mut events).await;
     assert_eq!(
         tool_call,
-        acp::ToolCall::new("1", "Thinking")
-            .kind(acp::ToolKind::Think)
+        acp::ToolCall::new("1", "Echo")
             .raw_input(json!({}))
-            .meta(acp::Meta::from_iter([(
-                "tool_name".into(),
-                "thinking".into()
-            )]))
+            .meta(acp::Meta::from_iter([("tool_name".into(), "echo".into())]))
     );
     let update = expect_tool_call_update_fields(&mut events).await;
     assert_eq!(
@@ -3011,9 +3007,9 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
         acp::ToolCallUpdate::new(
             "1",
             acp::ToolCallUpdateFields::new()
-                .title("Thinking")
-                .kind(acp::ToolKind::Think)
-                .raw_input(json!({ "content": "Thinking hard!"}))
+                .title("Echo")
+                .kind(acp::ToolKind::Other)
+                .raw_input(json!({ "text": "Hello!"}))
         )
     );
     let update = expect_tool_call_update_fields(&mut events).await;
@@ -3025,21 +3021,13 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
         )
     );
     let update = expect_tool_call_update_fields(&mut events).await;
-    assert_eq!(
-        update,
-        acp::ToolCallUpdate::new(
-            "1",
-            acp::ToolCallUpdateFields::new().content(vec!["Thinking hard!".into()])
-        )
-    );
-    let update = expect_tool_call_update_fields(&mut events).await;
     assert_eq!(
         update,
         acp::ToolCallUpdate::new(
             "1",
             acp::ToolCallUpdateFields::new()
                 .status(acp::ToolCallStatus::Completed)
-                .raw_output("Finished thinking.")
+                .raw_output("Hello!")
         )
     );
 }
@@ -3340,7 +3328,6 @@ async fn setup(cx: &mut TestAppContext, model: TestModel) -> ThreadTest {
                             ToolRequiringPermission::NAME: true,
                             InfiniteTool::NAME: true,
                             CancellationAwareTool::NAME: true,
-                            ThinkingTool::NAME: true,
                             (TerminalTool::NAME): true,
                         }
                     }

crates/agent/src/thread.rs 🔗

@@ -3,8 +3,8 @@ use crate::{
     DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool, FindPathTool, GrepTool,
     ListDirectoryTool, MovePathTool, NowTool, OpenTool, ProjectSnapshot, ReadFileTool,
     RestoreFileFromDiskTool, SaveFileTool, StreamingEditFileTool, SubagentTool,
-    SystemPromptTemplate, Template, Templates, TerminalTool, ThinkingTool, ToolPermissionDecision,
-    WebSearchTool, decide_permission_from_settings,
+    SystemPromptTemplate, Template, Templates, TerminalTool, ToolPermissionDecision, WebSearchTool,
+    decide_permission_from_settings,
 };
 use acp_thread::{MentionUri, UserMessageId};
 use action_log::ActionLog;
@@ -1343,7 +1343,6 @@ impl Thread {
             TerminalTool::new(self.project.clone(), environment.clone()),
             allowed_tool_names.as_ref(),
         );
-        self.add_tool(ThinkingTool, allowed_tool_names.as_ref());
         self.add_tool(WebSearchTool, allowed_tool_names.as_ref());
 
         if cx.has_flag::<SubagentsFeatureFlag>() && self.depth() < MAX_SUBAGENT_DEPTH {

crates/agent/src/tools.rs 🔗

@@ -17,7 +17,6 @@ mod save_file_tool;
 mod streaming_edit_file_tool;
 mod subagent_tool;
 mod terminal_tool;
-mod thinking_tool;
 mod web_search_tool;
 
 use crate::AgentTool;
@@ -42,7 +41,6 @@ pub use save_file_tool::*;
 pub use streaming_edit_file_tool::*;
 pub use subagent_tool::*;
 pub use terminal_tool::*;
-pub use thinking_tool::*;
 pub use web_search_tool::*;
 
 macro_rules! tools {
@@ -130,6 +128,5 @@ tools! {
     SaveFileTool,
     SubagentTool,
     TerminalTool,
-    ThinkingTool,
     WebSearchTool,
 }

crates/agent/src/tools/subagent_tool.rs 🔗

@@ -238,7 +238,7 @@ mod tests {
                 cx,
             );
             thread.add_tool(crate::NowTool, None);
-            thread.add_tool(crate::ThinkingTool, None);
+            thread.add_tool(crate::WebSearchTool, None);
             thread
         })
     }
@@ -253,7 +253,7 @@ mod tests {
             let valid_tools = Some(vec!["now".to_string()]);
             assert!(SubagentTool::validate_allowed_tools(&valid_tools, &thread, cx).is_ok());
 
-            let both_tools = Some(vec!["now".to_string(), "thinking".to_string()]);
+            let both_tools = Some(vec!["now".to_string(), "web_search".to_string()]);
             assert!(SubagentTool::validate_allowed_tools(&both_tools, &thread, cx).is_ok());
         });
     }

crates/agent/src/tools/thinking_tool.rs 🔗

@@ -1,48 +0,0 @@
-use agent_client_protocol as acp;
-use anyhow::Result;
-use gpui::{App, SharedString, Task};
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use std::sync::Arc;
-
-use crate::{AgentTool, ToolCallEventStream};
-
-/// A tool for thinking through problems, brainstorming ideas, or planning without executing any actions.
-/// Use this tool when you need to work through complex problems, develop strategies, or outline approaches before taking action.
-#[derive(Debug, Serialize, Deserialize, JsonSchema)]
-pub struct ThinkingToolInput {
-    /// Content to think about. This should be a description of what to think about or a problem to solve.
-    content: String,
-}
-
-pub struct ThinkingTool;
-
-impl AgentTool for ThinkingTool {
-    type Input = ThinkingToolInput;
-    type Output = String;
-
-    const NAME: &'static str = "thinking";
-
-    fn kind() -> acp::ToolKind {
-        acp::ToolKind::Think
-    }
-
-    fn initial_title(
-        &self,
-        _input: Result<Self::Input, serde_json::Value>,
-        _cx: &mut App,
-    ) -> SharedString {
-        "Thinking".into()
-    }
-
-    fn run(
-        self: Arc<Self>,
-        input: Self::Input,
-        event_stream: ToolCallEventStream,
-        _cx: &mut App,
-    ) -> Task<Result<String>> {
-        event_stream
-            .update_fields(acp::ToolCallUpdateFields::new().content(vec![input.content.into()]));
-        Task::ready(Ok("Finished thinking.".to_string()))
-    }
-}