agent_sdk/agent/hooks.rs
1use crate::error::AgentId;
2use crate::types::task::Task;
3
4/// Events that can trigger hooks.
5#[derive(Debug, Clone)]
6pub enum HookEvent {
7 /// A teammate has finished its current work and is about to go idle.
8 /// Return `HookResult::Reject` with feedback to keep it working.
9 TeammateIdle {
10 agent_id: AgentId,
11 name: String,
12 tasks_completed: usize,
13 },
14
15 /// A task is being created in the task store.
16 /// Return `HookResult::Reject` to prevent creation.
17 TaskCreated {
18 task: Task,
19 },
20
21 /// A task is being marked as completed.
22 /// Return `HookResult::Reject` to prevent completion and send feedback.
23 TaskCompleted {
24 task: Task,
25 agent_id: AgentId,
26 },
27}
28
29/// Result of a hook evaluation.
30#[derive(Debug, Clone)]
31pub enum HookResult {
32 /// Allow the action to proceed.
33 Continue,
34 /// Reject the action with feedback (equivalent to exit code 2 in Claude Code).
35 Reject { feedback: String },
36}
37
38/// Trait for implementing quality gates and lifecycle hooks.
39///
40/// Hooks run synchronously during agent team execution. They can inspect
41/// events and either allow them to proceed or reject them with feedback.
42pub trait Hook: Send + Sync {
43 fn on_event(&self, event: &HookEvent) -> HookResult;
44}
45
46/// A collection of hooks that are evaluated in order.
47pub struct HookRegistry {
48 hooks: Vec<Box<dyn Hook>>,
49}
50
51impl HookRegistry {
52 pub fn new() -> Self {
53 Self { hooks: Vec::new() }
54 }
55
56 pub fn add(&mut self, hook: impl Hook + 'static) {
57 self.hooks.push(Box::new(hook));
58 }
59
60 /// Evaluate all hooks for an event. Returns `Reject` on the first rejection.
61 pub fn evaluate(&self, event: &HookEvent) -> HookResult {
62 for hook in &self.hooks {
63 if let HookResult::Reject { feedback } = hook.on_event(event) {
64 return HookResult::Reject { feedback };
65 }
66 }
67 HookResult::Continue
68 }
69
70 pub fn is_empty(&self) -> bool {
71 self.hooks.is_empty()
72 }
73}
74
75impl Default for HookRegistry {
76 fn default() -> Self {
77 Self::new()
78 }
79}