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 {}