diff --git a/crates/openfang-hands/bundled/researcher/HAND.toml b/crates/openfang-hands/bundled/researcher/HAND.toml index 2b07fd9e..b2afbfca 100644 --- a/crates/openfang-hands/bundled/researcher/HAND.toml +++ b/crates/openfang-hands/bundled/researcher/HAND.toml @@ -161,7 +161,10 @@ provider = "default" model = "default" max_tokens = 16384 temperature = 0.3 -max_iterations = 80 +max_iterations = 25 +# Researcher makes long LLM calls; 30s (kernel default) causes false-positive +# recovery triggers. 120s matches typical deep-research call latency. +heartbeat_interval_secs = 120 system_prompt = """You are Researcher Hand — an autonomous deep research agent that conducts exhaustive investigations, cross-references sources, fact-checks claims, and produces comprehensive structured reports. ## Phase 0 — Platform Detection & Context (ALWAYS DO THIS FIRST) diff --git a/crates/openfang-hands/src/lib.rs b/crates/openfang-hands/src/lib.rs index 1adbf61d..fa5a8de4 100644 --- a/crates/openfang-hands/src/lib.rs +++ b/crates/openfang-hands/src/lib.rs @@ -293,6 +293,11 @@ pub struct HandAgentConfig { pub system_prompt: String, #[serde(default)] pub max_iterations: Option, + /// Heartbeat interval in seconds for autonomous agents. Overrides the + /// AutonomousConfig default (30s), which is too aggressive for agents + /// making long LLM calls. Omit to use the kernel default. + #[serde(default)] + pub heartbeat_interval_secs: Option, } fn default_module() -> String { diff --git a/crates/openfang-kernel/src/kernel.rs b/crates/openfang-kernel/src/kernel.rs index 3224408c..37b96087 100644 --- a/crates/openfang-kernel/src/kernel.rs +++ b/crates/openfang-kernel/src/kernel.rs @@ -3320,6 +3320,10 @@ impl OpenFangKernel { ], autonomous: def.agent.max_iterations.map(|max_iter| AutonomousConfig { max_iterations: max_iter, + // Use the hand-declared heartbeat interval if provided. + // The kernel default (30s) is too aggressive for hands making long LLM calls; + // HAND.toml authors should set this to reflect expected call latency. + heartbeat_interval_secs: def.agent.heartbeat_interval_secs.unwrap_or(30), ..Default::default() }), // Autonomous hands must run in Continuous mode so the background loop picks them up.