Detailed changes
@@ -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<Thread>,
label: String,
initial_prompt: String,
- timeout: Option<Duration>,
cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
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<Thread>,
session_id: acp::SessionId,
follow_up_prompt: String,
- timeout: Option<Duration>,
cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
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<acp_thread::AcpThread>,
parent_thread_entity: Entity<Thread>,
prompt: String,
- timeout: Option<Duration>,
cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
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<Thread>,
label: String,
initial_prompt: String,
- timeout: Option<Duration>,
cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
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<Thread>,
session_id: acp::SessionId,
follow_up_prompt: String,
- timeout: Option<Duration>,
cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
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")),
};
@@ -207,7 +207,6 @@ impl crate::ThreadEnvironment for FakeThreadEnvironment {
_parent_thread: Entity<Thread>,
_label: String,
_initial_prompt: String,
- _timeout_ms: Option<Duration>,
_cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
Ok(self
@@ -253,7 +252,6 @@ impl crate::ThreadEnvironment for MultiTerminalEnvironment {
_parent_thread: Entity<Thread>,
_label: String,
_initial_prompt: String,
- _timeout: Option<Duration>,
_cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
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);
@@ -618,7 +618,6 @@ pub trait ThreadEnvironment {
parent_thread: Entity<Thread>,
label: String,
initial_prompt: String,
- timeout: Option<Duration>,
cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>>;
@@ -627,7 +626,6 @@ pub trait ThreadEnvironment {
_parent_thread: Entity<Thread>,
_session_id: acp::SessionId,
_follow_up_prompt: String,
- _timeout: Option<Duration>,
_cx: &mut App,
) -> Result<Rc<dyn SubagentHandle>> {
Err(anyhow::anyhow!(
@@ -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<acp::SessionId>,
- /// 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<u64>,
}
#[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,
@@ -685,7 +685,6 @@ impl agent::ThreadEnvironment for EvalThreadEnvironment {
_parent_thread: Entity<agent::Thread>,
_label: String,
_initial_prompt: String,
- _timeout_ms: Option<Duration>,
_cx: &mut App,
) -> Result<Rc<dyn agent::SubagentHandle>> {
unimplemented!()