1use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use thiserror::Error;
9
10pub trait HookEventKind: Send + Sync + 'static {
15 type Input;
16 type Output;
17}
18
19pub struct OnPromptSubmit;
20pub struct PreLlmRequest;
21pub struct PreToolCall;
22pub struct PostToolCall;
23pub struct OnTurnEnd;
24pub struct OnAbort;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum OnPromptSubmitResult {
28 Continue,
29 Cancel(String),
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum PreLlmRequestResult {
34 Continue,
35 Cancel(String),
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum PreToolCallResult {
40 Continue,
41 Skip,
42 Abort(String),
43 Pause,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum PostToolCallResult {
48 Continue,
49 Abort(String),
50}
51
52#[derive(Debug, Clone)]
53pub enum OnTurnEndResult {
54 Finish,
55 ContinueWithMessages(Vec<crate::Message>),
56 Paused,
57}
58
59use std::sync::Arc;
60
61use crate::tool::{Tool, ToolMeta};
62
63pub struct ToolCallContext {
65 pub call: ToolCall,
67 pub meta: ToolMeta,
69 pub tool: Arc<dyn Tool>,
71}
72
73pub struct PostToolCallContext {
75 pub call: ToolCall,
77 pub result: ToolResult,
79 pub meta: ToolMeta,
81 pub tool: Arc<dyn Tool>,
83}
84
85impl HookEventKind for OnPromptSubmit {
86 type Input = crate::Message;
87 type Output = OnPromptSubmitResult;
88}
89
90impl HookEventKind for PreLlmRequest {
91 type Input = Vec<crate::Message>;
92 type Output = PreLlmRequestResult;
93}
94
95impl HookEventKind for PreToolCall {
96 type Input = ToolCallContext;
97 type Output = PreToolCallResult;
98}
99
100impl HookEventKind for PostToolCall {
101 type Input = PostToolCallContext;
102 type Output = PostToolCallResult;
103}
104
105impl HookEventKind for OnTurnEnd {
106 type Input = Vec<crate::Message>;
107 type Output = OnTurnEndResult;
108}
109
110impl HookEventKind for OnAbort {
111 type Input = String;
112 type Output = ();
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct ToolCall {
124 pub id: String,
126 pub name: String,
128 pub input: Value,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct ToolResult {
137 pub tool_use_id: String,
139 pub content: String,
141 #[serde(default)]
143 pub is_error: bool,
144}
145
146impl ToolResult {
147 pub fn success(tool_use_id: impl Into<String>, content: impl Into<String>) -> Self {
149 Self {
150 tool_use_id: tool_use_id.into(),
151 content: content.into(),
152 is_error: false,
153 }
154 }
155
156 pub fn error(tool_use_id: impl Into<String>, content: impl Into<String>) -> Self {
158 Self {
159 tool_use_id: tool_use_id.into(),
160 content: content.into(),
161 is_error: true,
162 }
163 }
164}
165
166#[derive(Debug, Error)]
172pub enum HookError {
173 #[error("Aborted: {0}")]
175 Aborted(String),
176 #[error("Hook error: {0}")]
178 Internal(String),
179}
180
181#[async_trait]
189pub trait Hook<E: HookEventKind>: Send + Sync {
190 async fn call(&self, input: &mut E::Input) -> Result<E::Output, HookError>;
191}
192
193pub struct HookRegistry {
201 pub(crate) on_prompt_submit: Vec<Box<dyn Hook<OnPromptSubmit>>>,
203 pub(crate) pre_llm_request: Vec<Box<dyn Hook<PreLlmRequest>>>,
205 pub(crate) pre_tool_call: Vec<Box<dyn Hook<PreToolCall>>>,
207 pub(crate) post_tool_call: Vec<Box<dyn Hook<PostToolCall>>>,
209 pub(crate) on_turn_end: Vec<Box<dyn Hook<OnTurnEnd>>>,
211 pub(crate) on_abort: Vec<Box<dyn Hook<OnAbort>>>,
213}
214
215impl Default for HookRegistry {
216 fn default() -> Self {
217 Self::new()
218 }
219}
220
221impl HookRegistry {
222 pub fn new() -> Self {
224 Self {
225 on_prompt_submit: Vec::new(),
226 pre_llm_request: Vec::new(),
227 pre_tool_call: Vec::new(),
228 post_tool_call: Vec::new(),
229 on_turn_end: Vec::new(),
230 on_abort: Vec::new(),
231 }
232 }
233}