crabtalk_runtime/hook.rs
1//! Hook trait — lifecycle callbacks and tool dispatch for subsystems.
2//!
3//! Each tool/subsystem implements `Hook` to participate in the runtime
4//! lifecycle: provide schemas, inject context before runs, observe
5//! events, preprocess messages, and dispatch tool calls.
6
7use crabllm_core::Tool;
8use wcore::{AgentConfig, AgentEvent, ToolDispatch, ToolFuture, model::HistoryEntry};
9
10/// A pluggable subsystem that participates in the agent lifecycle.
11///
12/// All methods have default no-op implementations so subsystems only
13/// override what they need.
14pub trait Hook: Send + Sync {
15 /// Tool schemas this hook provides.
16 fn schema(&self) -> Vec<Tool> {
17 vec![]
18 }
19
20 /// System prompt fragment appended to agent configs at build time.
21 fn system_prompt(&self) -> Option<String> {
22 None
23 }
24
25 /// Called by `Runtime::add_agent()` before building the `Agent`.
26 fn on_build_agent(&self, config: AgentConfig) -> AgentConfig {
27 config
28 }
29
30 /// Inject context entries before each agent run.
31 fn on_before_run(
32 &self,
33 _agent: &str,
34 _conversation_id: u64,
35 _history: &[HistoryEntry],
36 ) -> Vec<HistoryEntry> {
37 Vec::new()
38 }
39
40 /// Called by Runtime after each agent step during execution.
41 fn on_event(&self, _agent: &str, _conversation_id: u64, _event: &AgentEvent) {}
42
43 /// Preprocess user content before it becomes a message.
44 /// Return `Some(modified)` to transform, `None` to pass through.
45 fn preprocess(&self, _agent: &str, _content: &str) -> Option<String> {
46 None
47 }
48
49 /// Tools to include when building a scoped agent's whitelist, plus an
50 /// optional scope prompt line (e.g. `"skills: foo, bar"`).
51 ///
52 /// Default: include all tools from `schema()` unconditionally, no
53 /// scope line. Override to gate inclusion on agent config fields.
54 fn scoped_tools(&self, _config: &AgentConfig) -> (Vec<String>, Option<String>) {
55 let tools = self
56 .schema()
57 .iter()
58 .map(|t| t.function.name.clone())
59 .collect();
60 (tools, None)
61 }
62
63 /// Dispatch a tool call by name. Return `None` if this hook doesn't
64 /// own the tool — Env will try the next hook or the legacy entries.
65 fn dispatch<'a>(&'a self, _name: &'a str, _call: ToolDispatch) -> Option<ToolFuture<'a>> {
66 None
67 }
68}
69
70/// No-op Hook for tests.
71impl Hook for () {}