use crate::{content::Content, error::HookError};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HookPoint {
PreInference,
PostInference,
PreToolUse,
PostToolUse,
ExitCheck,
ToolExecutionUpdate,
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HookContext {
pub point: HookPoint,
pub tool_name: Option<String>,
pub tool_input: Option<serde_json::Value>,
pub tool_result: Option<String>,
pub model_output: Option<Content>,
pub tokens_used: u64,
pub cost: rust_decimal::Decimal,
pub turns_completed: u32,
pub elapsed: crate::duration::DurationMs,
pub tool_chunk: Option<String>,
}
impl HookContext {
pub fn new(point: HookPoint) -> Self {
Self {
point,
tool_name: None,
tool_input: None,
tool_result: None,
model_output: None,
tokens_used: 0,
cost: rust_decimal::Decimal::ZERO,
turns_completed: 0,
elapsed: crate::duration::DurationMs::ZERO,
tool_chunk: None,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "action", rename_all = "snake_case")]
pub enum HookAction {
Continue,
Halt {
reason: String,
},
SkipTool {
reason: String,
},
ModifyToolInput {
new_input: serde_json::Value,
},
ModifyToolOutput {
new_output: serde_json::Value,
},
}
#[async_trait]
pub trait Hook: Send + Sync {
fn points(&self) -> &[HookPoint];
async fn on_event(&self, ctx: &HookContext) -> Result<HookAction, HookError>;
}