1use crate::events::AgentEvent;
17use crate::llm;
18use crate::types::{ToolResult, ToolTier};
19use async_trait::async_trait;
20use serde_json::Value;
21
22#[derive(Debug, Clone)]
24pub enum ToolDecision {
25 Allow,
27 Block(String),
29 RequiresConfirmation(String),
31 RequiresPin(String),
33}
34
35#[async_trait]
38pub trait AgentHooks: Send + Sync {
39 async fn pre_tool_use(&self, tool_name: &str, input: &Value, tier: ToolTier) -> ToolDecision {
42 let _ = input;
45 match tier {
46 ToolTier::Observe => ToolDecision::Allow,
47 ToolTier::Confirm => {
48 ToolDecision::RequiresConfirmation(format!("Confirm {tool_name}?"))
49 }
50 ToolTier::RequiresPin => {
51 ToolDecision::RequiresPin(format!("{tool_name} requires PIN verification"))
52 }
53 }
54 }
55
56 async fn post_tool_use(&self, _tool_name: &str, _result: &ToolResult) {
58 }
60
61 async fn on_event(&self, _event: &AgentEvent) {
64 }
66
67 async fn on_error(&self, _error: &anyhow::Error) -> bool {
70 false
72 }
73
74 async fn on_context_compact(&self, _messages: &[llm::Message]) -> Option<String> {
77 None
79 }
80}
81
82#[derive(Clone, Copy, Default)]
84pub struct DefaultHooks;
85
86#[async_trait]
87impl AgentHooks for DefaultHooks {}
88
89#[derive(Clone, Copy, Default)]
91pub struct AllowAllHooks;
92
93#[async_trait]
94impl AgentHooks for AllowAllHooks {
95 async fn pre_tool_use(
96 &self,
97 _tool_name: &str,
98 _input: &Value,
99 _tier: ToolTier,
100 ) -> ToolDecision {
101 ToolDecision::Allow
102 }
103}
104
105#[derive(Clone, Copy, Default)]
107pub struct LoggingHooks;
108
109#[async_trait]
110impl AgentHooks for LoggingHooks {
111 async fn pre_tool_use(&self, tool_name: &str, input: &Value, tier: ToolTier) -> ToolDecision {
112 tracing::debug!(tool = tool_name, ?input, ?tier, "Pre-tool use");
113 DefaultHooks.pre_tool_use(tool_name, input, tier).await
114 }
115
116 async fn post_tool_use(&self, tool_name: &str, result: &ToolResult) {
117 tracing::debug!(
118 tool = tool_name,
119 success = result.success,
120 duration_ms = result.duration_ms,
121 "Post-tool use"
122 );
123 }
124
125 async fn on_event(&self, event: &AgentEvent) {
126 tracing::debug!(?event, "Agent event");
127 }
128
129 async fn on_error(&self, error: &anyhow::Error) -> bool {
130 tracing::error!(?error, "Agent error");
131 false
132 }
133}