@@ -11,11 +11,16 @@ use tracing::{debug, info, warn};
1111use super :: events:: { HeartbeatEvent , HeartbeatStatus , emit_heartbeat_event, now_ms} ;
1212use crate :: agent:: {
1313 Agent , AgentConfig , HEARTBEAT_OK_TOKEN , SessionStore , build_heartbeat_prompt, is_heartbeat_ok,
14+ tools:: Tool ,
1415} ;
1516use crate :: concurrency:: { TurnGate , WorkspaceLock } ;
1617use crate :: config:: { Config , parse_duration, parse_time} ;
1718use crate :: memory:: MemoryManager ;
1819
20+ /// Factory function type for creating additional tools for the heartbeat agent.
21+ /// This allows the caller (e.g., CLI daemon) to inject dangerous tools like bash, file I/O.
22+ pub type ToolFactory = Box < dyn Fn ( & Config ) -> Result < Vec < Box < dyn Tool > > > + Send + Sync > ;
23+
1924pub struct HeartbeatRunner {
2025 config : Config ,
2126 interval : Duration ,
@@ -28,6 +33,8 @@ pub struct HeartbeatRunner {
2833 turn_gate : Option < TurnGate > ,
2934 /// Cross-process workspace lock
3035 workspace_lock : WorkspaceLock ,
36+ /// Optional tool factory for injecting additional tools (e.g., CLI tools from daemon)
37+ tool_factory : Option < ToolFactory > ,
3138}
3239
3340impl HeartbeatRunner {
@@ -49,6 +56,20 @@ impl HeartbeatRunner {
4956 config : & Config ,
5057 agent_id : & str ,
5158 turn_gate : Option < TurnGate > ,
59+ ) -> Result < Self > {
60+ Self :: new_with_gate_and_tools ( config, agent_id, turn_gate, None )
61+ }
62+
63+ /// Create a new HeartbeatRunner with optional TurnGate and tool factory.
64+ ///
65+ /// The tool_factory allows injecting additional tools (e.g., bash, file I/O from CLI)
66+ /// into the heartbeat agent. This enables the heartbeat to perform filesystem operations
67+ /// and execute commands when running in a CLI/daemon context.
68+ pub fn new_with_gate_and_tools (
69+ config : & Config ,
70+ agent_id : & str ,
71+ turn_gate : Option < TurnGate > ,
72+ tool_factory : Option < ToolFactory > ,
5273 ) -> Result < Self > {
5374 let interval = parse_duration ( & config. heartbeat . interval )
5475 . map_err ( |e| anyhow:: anyhow!( "Invalid heartbeat interval: {}" , e) ) ?;
@@ -82,6 +103,7 @@ impl HeartbeatRunner {
82103 memory,
83104 turn_gate,
84105 workspace_lock,
106+ tool_factory,
85107 } )
86108 }
87109
@@ -319,6 +341,13 @@ impl HeartbeatRunner {
319341 } ;
320342
321343 let mut agent = Agent :: new ( agent_config, & self . config , self . memory . clone ( ) ) . await ?;
344+
345+ // Extend agent with additional tools from factory if provided (e.g., CLI tools from daemon)
346+ if let Some ( ref factory) = self . tool_factory {
347+ let extra_tools = factory ( & self . config ) ?;
348+ agent. extend_tools ( extra_tools) ;
349+ }
350+
322351 agent. new_session ( ) . await ?;
323352
324353 info ! ( name: "Heartbeat" , "Running HEARTBEAT.md" ) ;
0 commit comments