Skip to main content

zagens_runtime_adapters/tools/
host.rs

1//! Tool host ports wired by the sidecar at engine spawn (D16 E1-a3+).
2//!
3//! Durable manager handles (`TaskManager`, `AutomationManager`, …) stay in
4//! `zagens-cli`; tools call through these ports so `tools/`
5//! can migrate to this crate incrementally.
6
7use std::collections::HashMap;
8use std::path::{Path, PathBuf};
9use std::sync::{Arc, Mutex};
10
11use async_trait::async_trait;
12use serde_json::Value;
13use zagens_core::scratchpad::ScratchpadConfig;
14
15/// Streams incremental tool output to the engine/UI without pulling `Event`
16/// into adapters (avoids cycles with orchestrator event types).
17pub trait ToolProgressEmit: Send + Sync {
18    fn emit_stdout(&self, chunk: &str);
19    fn emit_stderr(&self, chunk: &str);
20}
21
22/// Shell `shell_env` hook injection port (#456).
23pub trait ToolShellEnvHost: Send + Sync {
24    fn collect_shell_env(&self, tool_name: &str, tool_args: &Value) -> HashMap<String, String>;
25}
26
27/// Durable task operations for model-visible `task_*` / PR-attempt tools (D16 E1-a4).
28#[async_trait]
29pub trait ToolTaskHost: Send + Sync {
30    async fn add_task(&self, req: Value) -> Result<Value, String>;
31    async fn list_tasks(&self, limit: Option<usize>) -> Result<Value, String>;
32    async fn get_task(&self, task_id: &str) -> Result<Value, String>;
33    async fn cancel_task(&self, task_id: &str) -> Result<Value, String>;
34    async fn record_tool_metadata(&self, task_id: &str, metadata: &Value) -> Result<(), String>;
35    fn artifact_absolute_path(&self, relative: &Path) -> PathBuf;
36    fn write_task_artifact(
37        &self,
38        task_id: &str,
39        label: &str,
40        content: &str,
41    ) -> Result<PathBuf, String>;
42}
43
44/// Durable automation operations for model-visible `automation_*` tools (D16 E1-a4).
45#[async_trait]
46pub trait ToolAutomationHost: Send + Sync {
47    async fn create_automation(&self, req: Value) -> Result<Value, String>;
48    async fn list_automations(&self) -> Result<Value, String>;
49    async fn get_automation(&self, automation_id: &str) -> Result<Value, String>;
50    async fn list_runs(&self, automation_id: &str, limit: Option<usize>) -> Result<Value, String>;
51    async fn update_automation(&self, automation_id: &str, req: Value) -> Result<Value, String>;
52    async fn pause_automation(&self, automation_id: &str) -> Result<Value, String>;
53    async fn resume_automation(&self, automation_id: &str) -> Result<Value, String>;
54    async fn delete_automation(&self, automation_id: &str) -> Result<Value, String>;
55    async fn run_now(&self, automation_id: &str) -> Result<Value, String>;
56}
57
58/// Durable metadata wired at engine spawn; manager handles stay in sidecar.
59#[derive(Clone)]
60pub struct RuntimeToolHostWire {
61    pub task_data_dir: Option<PathBuf>,
62    pub active_task_id: Option<String>,
63    pub active_thread_id: Option<String>,
64    pub scratchpad_run_id: Arc<Mutex<Option<String>>>,
65    pub persist_scratchpad_run_id: Option<Arc<dyn Fn(String) + Send + Sync>>,
66    pub scratchpad_config: Option<ScratchpadConfig>,
67}
68
69impl Default for RuntimeToolHostWire {
70    fn default() -> Self {
71        Self {
72            task_data_dir: None,
73            active_task_id: None,
74            active_thread_id: None,
75            scratchpad_run_id: Arc::new(Mutex::new(None)),
76            persist_scratchpad_run_id: None,
77            scratchpad_config: None,
78        }
79    }
80}
81
82impl std::fmt::Debug for RuntimeToolHostWire {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        f.debug_struct("RuntimeToolHostWire")
85            .field("task_data_dir", &self.task_data_dir)
86            .field("active_task_id", &self.active_task_id)
87            .field("active_thread_id", &self.active_thread_id)
88            .field(
89                "scratchpad_run_id",
90                &self.scratchpad_run_id.lock().ok().and_then(|g| g.clone()),
91            )
92            .field(
93                "persist_scratchpad_run_id",
94                &self.persist_scratchpad_run_id.is_some(),
95            )
96            .field("scratchpad_config", &self.scratchpad_config.is_some())
97            .finish()
98    }
99}