From de2d3e3a1144bc3a5daa0f019d92ac6df101e03a Mon Sep 17 00:00:00 2001 From: iven Date: Wed, 8 Apr 2026 20:44:52 +0800 Subject: [PATCH] fix(runtime): add 30s timeout to tool execution in AgentLoop Tool execution (ShellExec, WebFetch, etc.) had no timeout, causing the entire streaming response to hang indefinitely when a tool fails or stalls. Now wraps execute_tool calls in tokio::time::timeout(30s) with a graceful error message on timeout. --- crates/zclaw-runtime/src/loop_runner.rs | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/zclaw-runtime/src/loop_runner.rs b/crates/zclaw-runtime/src/loop_runner.rs index 09817de..e323738 100644 --- a/crates/zclaw-runtime/src/loop_runner.rs +++ b/crates/zclaw-runtime/src/loop_runner.rs @@ -429,10 +429,17 @@ impl AgentLoop { continue; } middleware::ToolCallDecision::ReplaceInput(new_input) => { - // Execute with replaced input - let tool_result = match self.execute_tool(&name, new_input, &tool_context).await { - Ok(result) => result, - Err(e) => serde_json::json!({ "error": e.to_string() }), + // Execute with replaced input (with timeout) + let tool_result = match tokio::time::timeout( + std::time::Duration::from_secs(30), + self.execute_tool(&name, new_input, &tool_context), + ).await { + Ok(Ok(result)) => result, + Ok(Err(e)) => serde_json::json!({ "error": e.to_string() }), + Err(_) => { + tracing::warn!("[AgentLoop] Tool '{}' (replaced input) timed out after 30s", name); + serde_json::json!({ "error": format!("工具 '{}' 执行超时(30秒),请重试", name) }) + } }; messages.push(Message::tool_result(id, zclaw_types::ToolId::new(&name), tool_result, false)); continue; @@ -471,9 +478,16 @@ impl AgentLoop { } } - let tool_result = match self.execute_tool(&name, input, &tool_context).await { - Ok(result) => result, - Err(e) => serde_json::json!({ "error": e.to_string() }), + let tool_result = match tokio::time::timeout( + std::time::Duration::from_secs(30), + self.execute_tool(&name, input, &tool_context), + ).await { + Ok(Ok(result)) => result, + Ok(Err(e)) => serde_json::json!({ "error": e.to_string() }), + Err(_) => { + tracing::warn!("[AgentLoop] Tool '{}' timed out after 30s", name); + serde_json::json!({ "error": format!("工具 '{}' 执行超时(30秒),请重试", name) }) + } }; // Check if this is a clarification response — terminate loop immediately