Skip to main content

crabtalk_runtime/
host.rs

1//! Host — trait for server-specific tool dispatch.
2//!
3//! The runtime crate defines this trait. The daemon implements it to provide
4//! `ask_user`, `delegate`, and per-conversation CWD resolution. Embedded users
5//! get [`NoHost`] with no-op defaults.
6
7use std::path::PathBuf;
8
9/// Trait for server-specific tool dispatch that the runtime cannot handle locally.
10pub trait Host: Send + Sync + Clone {
11    /// Handle `ask_user` — block until user replies.
12    ///
13    /// Returns `Ok` for a normal reply, `Err` for a failure (not available,
14    /// timeout, cancelled, invalid args).
15    fn dispatch_ask_user(
16        &self,
17        args: &str,
18        conversation_id: Option<u64>,
19    ) -> impl std::future::Future<Output = Result<String, String>> + Send {
20        let _ = (args, conversation_id);
21        async { Err("ask_user is not available in this runtime mode".to_owned()) }
22    }
23
24    /// Handle `delegate` — spawn sub-agent tasks.
25    ///
26    /// Returns `Ok` for successful delegation output, `Err` for failure.
27    fn dispatch_delegate(
28        &self,
29        args: &str,
30        agent: &str,
31    ) -> impl std::future::Future<Output = Result<String, String>> + Send {
32        let _ = (args, agent);
33        async { Err("delegate is not available in this runtime mode".to_owned()) }
34    }
35
36    /// Resolve the working directory for a conversation.
37    /// Returns `None` to fall back to the runtime's base cwd.
38    fn conversation_cwd(&self, _conversation_id: u64) -> Option<PathBuf> {
39        None
40    }
41
42    /// Called when an agent event occurs. The daemon uses this to broadcast
43    /// protobuf events to console subscribers. Default: no-op.
44    fn on_agent_event(&self, _agent: &str, _conversation_id: u64, _event: &wcore::AgentEvent) {}
45
46    /// Deliver a user reply to a pending `ask_user` tool call.
47    /// Returns `true` if a pending ask was found and resolved.
48    fn reply_to_ask(
49        &self,
50        _session: u64,
51        _content: String,
52    ) -> impl std::future::Future<Output = anyhow::Result<bool>> + Send {
53        async { Ok(false) }
54    }
55
56    /// Set the working directory override for a conversation.
57    fn set_conversation_cwd(
58        &self,
59        _conversation: u64,
60        _cwd: PathBuf,
61    ) -> impl std::future::Future<Output = ()> + Send {
62        async {}
63    }
64
65    /// Clear all per-conversation state (pending asks, CWD overrides).
66    fn clear_conversation_state(
67        &self,
68        _conversation: u64,
69    ) -> impl std::future::Future<Output = ()> + Send {
70        async {}
71    }
72
73    /// Subscribe to agent events. Returns `None` if event broadcasting
74    /// is not supported by this host.
75    fn subscribe_events(
76        &self,
77    ) -> Option<tokio::sync::broadcast::Receiver<wcore::protocol::message::AgentEventMsg>> {
78        None
79    }
80
81    /// Handle a tool call not matched by the built-in dispatch table.
82    /// Downstream hosts override this to inject private tools.
83    ///
84    /// Returns `Ok` on success, `Err` for unknown tools or failures.
85    fn dispatch_custom_tool(
86        &self,
87        name: &str,
88        _args: &str,
89        _agent: &str,
90        _conversation_id: Option<u64>,
91    ) -> impl std::future::Future<Output = Result<String, String>> + Send {
92        async move { Err(format!("tool not available: {name}")) }
93    }
94}
95
96/// No-op host for embedded use.
97#[derive(Clone)]
98pub struct NoHost;
99
100impl Host for NoHost {}