diff --git a/crates/agent/src/agent.rs b/crates/agent/src/agent.rs index 4e8691c0b5..3906da3605 100644 --- a/crates/agent/src/agent.rs +++ b/crates/agent/src/agent.rs @@ -50,7 +50,6 @@ use std::any::Any; use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::Arc; -use std::time::Duration; use util::ResultExt; use util::rel_path::RelPath; @@ -1586,7 +1585,6 @@ impl NativeThreadEnvironment { parent_thread_entity: Entity, label: String, initial_prompt: String, - timeout: Option, cx: &mut App, ) -> Result> { let parent_thread = parent_thread_entity.read(cx); @@ -1617,7 +1615,6 @@ impl NativeThreadEnvironment { acp_thread, parent_thread_entity, initial_prompt, - timeout, cx, ) } @@ -1627,7 +1624,6 @@ impl NativeThreadEnvironment { parent_thread_entity: Entity, session_id: acp::SessionId, follow_up_prompt: String, - timeout: Option, cx: &mut App, ) -> Result> { let (subagent_thread, acp_thread) = agent.update(cx, |agent, _cx| { @@ -1644,7 +1640,6 @@ impl NativeThreadEnvironment { acp_thread, parent_thread_entity, follow_up_prompt, - timeout, cx, ) } @@ -1655,7 +1650,6 @@ impl NativeThreadEnvironment { acp_thread: Entity, parent_thread_entity: Entity, prompt: String, - timeout: Option, cx: &mut App, ) -> Result> { parent_thread_entity.update(cx, |parent_thread, _cx| { @@ -1666,33 +1660,15 @@ impl NativeThreadEnvironment { acp_thread.send(vec![prompt.into()], cx) }); - let timeout_timer = timeout.map(|d| cx.background_executor().timer(d)); let wait_for_prompt_to_complete = cx .background_spawn(async move { - if let Some(timer) = timeout_timer { - futures::select! { - _ = timer.fuse() => SubagentInitialPromptResult::Timeout, - response = task.fuse() => { - let response = response.log_err().flatten(); - if response.is_some_and(|response| { - response.stop_reason == acp::StopReason::Cancelled - }) - { - SubagentInitialPromptResult::Cancelled - } else { - SubagentInitialPromptResult::Completed - } - }, - } + let response = task.await.log_err().flatten(); + if response + .is_some_and(|response| response.stop_reason == acp::StopReason::Cancelled) + { + SubagentInitialPromptResult::Cancelled } else { - let response = task.await.log_err().flatten(); - if response - .is_some_and(|response| response.stop_reason == acp::StopReason::Cancelled) - { - SubagentInitialPromptResult::Cancelled - } else { - SubagentInitialPromptResult::Completed - } + SubagentInitialPromptResult::Completed } }) .shared(); @@ -1745,7 +1721,6 @@ impl ThreadEnvironment for NativeThreadEnvironment { parent_thread_entity: Entity, label: String, initial_prompt: String, - timeout: Option, cx: &mut App, ) -> Result> { Self::create_subagent_thread( @@ -1753,7 +1728,6 @@ impl ThreadEnvironment for NativeThreadEnvironment { parent_thread_entity, label, initial_prompt, - timeout, cx, ) } @@ -1763,7 +1737,6 @@ impl ThreadEnvironment for NativeThreadEnvironment { parent_thread_entity: Entity, session_id: acp::SessionId, follow_up_prompt: String, - timeout: Option, cx: &mut App, ) -> Result> { Self::resume_subagent_thread( @@ -1771,7 +1744,6 @@ impl ThreadEnvironment for NativeThreadEnvironment { parent_thread_entity, session_id, follow_up_prompt, - timeout, cx, ) } @@ -1780,7 +1752,6 @@ impl ThreadEnvironment for NativeThreadEnvironment { #[derive(Debug, Clone, Copy)] enum SubagentInitialPromptResult { Completed, - Timeout, Cancelled, } @@ -1811,10 +1782,6 @@ impl SubagentHandle for NativeSubagentHandle { .map(|m| m.to_markdown()) .context("No response from subagent") }), - SubagentInitialPromptResult::Timeout => { - thread.update(cx, |thread, cx| thread.cancel(cx)).await; - Err(anyhow!("The time to complete the task was exceeded.")) - } SubagentInitialPromptResult::Cancelled => Err(anyhow!("User cancelled")), }; diff --git a/crates/agent/src/tests/mod.rs b/crates/agent/src/tests/mod.rs index 3df64794c5..2c412561ba 100644 --- a/crates/agent/src/tests/mod.rs +++ b/crates/agent/src/tests/mod.rs @@ -207,7 +207,6 @@ impl crate::ThreadEnvironment for FakeThreadEnvironment { _parent_thread: Entity, _label: String, _initial_prompt: String, - _timeout_ms: Option, _cx: &mut App, ) -> Result> { Ok(self @@ -253,7 +252,6 @@ impl crate::ThreadEnvironment for MultiTerminalEnvironment { _parent_thread: Entity, _label: String, _initial_prompt: String, - _timeout: Option, _cx: &mut App, ) -> Result> { unimplemented!() @@ -4234,7 +4232,6 @@ async fn test_subagent_tool_call_end_to_end(cx: &mut TestAppContext) { label: "label".to_string(), message: "subagent task prompt".to_string(), session_id: None, - timeout_secs: None, }; let subagent_tool_use = LanguageModelToolUse { id: "subagent_1".into(), @@ -4378,7 +4375,6 @@ async fn test_subagent_tool_call_cancellation_during_task_prompt(cx: &mut TestAp label: "label".to_string(), message: "subagent task prompt".to_string(), session_id: None, - timeout_secs: None, }; let subagent_tool_use = LanguageModelToolUse { id: "subagent_1".into(), @@ -4516,7 +4512,6 @@ async fn test_subagent_tool_resume_session(cx: &mut TestAppContext) { label: "initial task".to_string(), message: "do the first task".to_string(), session_id: None, - timeout_secs: None, }; let subagent_tool_use = LanguageModelToolUse { id: "subagent_1".into(), @@ -4578,7 +4573,6 @@ async fn test_subagent_tool_resume_session(cx: &mut TestAppContext) { label: "follow-up task".to_string(), message: "do the follow-up task".to_string(), session_id: Some(subagent_session_id.clone()), - timeout_secs: None, }; let resume_tool_use = LanguageModelToolUse { id: "subagent_2".into(), @@ -4836,90 +4830,6 @@ async fn test_parent_cancel_stops_subagent(cx: &mut TestAppContext) { }); } -#[gpui::test] -async fn test_subagent_tool_includes_cancellation_notice_when_timeout_is_exceeded( - cx: &mut TestAppContext, -) { - init_test(cx); - - always_allow_tools(cx); - - cx.update(|cx| { - cx.update_flags(true, vec!["subagents".to_string()]); - }); - - let fs = FakeFs::new(cx.executor()); - fs.insert_tree(path!("/test"), json!({})).await; - let project = Project::test(fs.clone(), [path!("/test").as_ref()], cx).await; - let project_context = cx.new(|_cx| ProjectContext::default()); - let context_server_store = project.read_with(cx, |project, _| project.context_server_store()); - let context_server_registry = - cx.new(|cx| ContextServerRegistry::new(context_server_store.clone(), cx)); - cx.update(LanguageModelRegistry::test); - let model = Arc::new(FakeLanguageModel::default()); - let thread_store = cx.new(|cx| ThreadStore::new(cx)); - let native_agent = NativeAgent::new( - project.clone(), - thread_store, - Templates::new(), - None, - fs, - &mut cx.to_async(), - ) - .await - .unwrap(); - let parent_thread = cx.new(|cx| { - Thread::new( - project.clone(), - project_context, - context_server_registry, - Templates::new(), - Some(model.clone()), - cx, - ) - }); - - let subagent_handle = cx - .update(|cx| { - NativeThreadEnvironment::create_subagent_thread( - native_agent.downgrade(), - parent_thread.clone(), - "some title".to_string(), - "task prompt".to_string(), - Some(Duration::from_secs(1)), - cx, - ) - }) - .expect("Failed to create subagent"); - - let summary_task = subagent_handle.wait_for_output(&cx.to_async()); - - cx.run_until_parked(); - - { - let messages = model.pending_completions().last().unwrap().messages.clone(); - // Ensure that model received a system prompt - assert_eq!(messages[0].role, Role::System); - // Ensure that model received a task prompt - assert_eq!( - messages[1].content, - vec![MessageContent::Text("task prompt".to_string())] - ); - } - - // Don't complete the initial model stream — let the timeout expire instead. - cx.executor().advance_clock(Duration::from_secs(2)); - cx.run_until_parked(); - - model.end_last_completion_stream(); - - let error = summary_task.await.unwrap_err(); - assert_eq!( - error.to_string(), - "The time to complete the task was exceeded." - ); -} - #[gpui::test] async fn test_edit_file_tool_deny_rule_blocks_edit(cx: &mut TestAppContext) { init_test(cx); diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index cd5c8f370a..e40ab834a8 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -618,7 +618,6 @@ pub trait ThreadEnvironment { parent_thread: Entity, label: String, initial_prompt: String, - timeout: Option, cx: &mut App, ) -> Result>; @@ -627,7 +626,6 @@ pub trait ThreadEnvironment { _parent_thread: Entity, _session_id: acp::SessionId, _follow_up_prompt: String, - _timeout: Option, _cx: &mut App, ) -> Result> { Err(anyhow::anyhow!( diff --git a/crates/agent/src/tools/spawn_agent_tool.rs b/crates/agent/src/tools/spawn_agent_tool.rs index 61446e1d43..7392a85cfe 100644 --- a/crates/agent/src/tools/spawn_agent_tool.rs +++ b/crates/agent/src/tools/spawn_agent_tool.rs @@ -5,8 +5,8 @@ use gpui::{App, SharedString, Task, WeakEntity}; use language_model::LanguageModelToolResultContent; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::rc::Rc; use std::sync::Arc; -use std::{rc::Rc, time::Duration}; use crate::{AgentTool, Thread, ThreadEnvironment, ToolCallEventStream}; @@ -37,9 +37,6 @@ pub struct SpawnAgentToolInput { /// Optional session ID of an existing agent session to continue a conversation with. When provided, the message is sent as a follow-up to that session instead of creating a new one. Use this to ask clarifying questions, request changes based on previous output, or retry after errors. #[serde(default)] pub session_id: Option, - /// Optional maximum runtime in seconds. The purpose of this timeout is to prevent the agent from getting stuck in infinite loops, NOT to estimate task duration. Be generous if setting. If not set, the agent runs until it completes or is cancelled. - #[serde(default)] - pub timeout_secs: Option, } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -114,21 +111,11 @@ impl AgentTool for SpawnAgentTool { }; let subagent = if let Some(session_id) = input.session_id { - self.environment.resume_subagent( - parent_thread_entity, - session_id, - input.message, - input.timeout_secs.map(Duration::from_secs), - cx, - ) + self.environment + .resume_subagent(parent_thread_entity, session_id, input.message, cx) } else { - self.environment.create_subagent( - parent_thread_entity, - input.label, - input.message, - input.timeout_secs.map(Duration::from_secs), - cx, - ) + self.environment + .create_subagent(parent_thread_entity, input.label, input.message, cx) }; let subagent = match subagent { Ok(subagent) => subagent, diff --git a/crates/eval/src/instance.rs b/crates/eval/src/instance.rs index 3fc8d23a69..cbc5cd1568 100644 --- a/crates/eval/src/instance.rs +++ b/crates/eval/src/instance.rs @@ -685,7 +685,6 @@ impl agent::ThreadEnvironment for EvalThreadEnvironment { _parent_thread: Entity, _label: String, _initial_prompt: String, - _timeout_ms: Option, _cx: &mut App, ) -> Result> { unimplemented!()