use crate::events::AgentEvent;
use crate::llm;
use crate::types::{ToolResult, ToolTier};
use async_trait::async_trait;
use serde_json::Value;
#[derive(Debug, Clone)]
pub enum ToolDecision {
Allow,
Block(String),
RequiresConfirmation(String),
}
#[async_trait]
pub trait AgentHooks: Send + Sync {
async fn pre_tool_use(&self, tool_name: &str, input: &Value, tier: ToolTier) -> ToolDecision {
let _ = input;
match tier {
ToolTier::Observe => ToolDecision::Allow,
ToolTier::Confirm => {
ToolDecision::RequiresConfirmation(format!("Confirm {tool_name}?"))
}
}
}
async fn post_tool_use(&self, _tool_name: &str, _result: &ToolResult) {
}
async fn on_event(&self, _event: &AgentEvent) {
}
async fn on_error(&self, _error: &anyhow::Error) -> bool {
false
}
async fn on_context_compact(&self, _messages: &[llm::Message]) -> Option<String> {
None
}
}
#[derive(Clone, Copy, Default)]
pub struct DefaultHooks;
#[async_trait]
impl AgentHooks for DefaultHooks {}
#[derive(Clone, Copy, Default)]
pub struct AllowAllHooks;
#[async_trait]
impl AgentHooks for AllowAllHooks {
async fn pre_tool_use(
&self,
_tool_name: &str,
_input: &Value,
_tier: ToolTier,
) -> ToolDecision {
ToolDecision::Allow
}
}
#[derive(Clone, Copy, Default)]
pub struct LoggingHooks;
#[async_trait]
impl AgentHooks for LoggingHooks {
async fn pre_tool_use(&self, tool_name: &str, input: &Value, tier: ToolTier) -> ToolDecision {
log::debug!("Pre-tool use tool={tool_name} input={input:?} tier={tier:?}");
DefaultHooks.pre_tool_use(tool_name, input, tier).await
}
async fn post_tool_use(&self, tool_name: &str, result: &ToolResult) {
log::debug!(
"Post-tool use tool={tool_name} success={} duration_ms={:?}",
result.success,
result.duration_ms
);
}
async fn on_event(&self, event: &AgentEvent) {
log::debug!("Agent event {event:?}");
}
async fn on_error(&self, error: &anyhow::Error) -> bool {
log::error!("Agent error {error:?}");
false
}
}