1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use crate::{ExecutionResult, Message, PlanStep};
5
6#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
7#[serde(rename_all = "snake_case")]
8pub enum HookKind {
9 PlanStart,
10 PlanEnd,
11 BeforeExecute,
12 StepEnd,
13}
14
15#[derive(Debug, Deserialize, Serialize, Clone)]
16pub struct HookContext {
17 pub agent_id: String,
18 pub thread_id: String,
19 pub task_id: String,
20 pub run_id: String,
21}
22
23impl HookContext {
24 pub fn from_parts(agent_id: &str, thread_id: &str, task_id: &str, run_id: &str) -> Self {
25 Self {
26 agent_id: agent_id.to_string(),
27 thread_id: thread_id.to_string(),
28 task_id: task_id.to_string(),
29 run_id: run_id.to_string(),
30 }
31 }
32}
33
34#[derive(Debug, Deserialize, Serialize, Clone)]
35pub struct HookMutation {
36 #[serde(default)]
37 pub dynamic_values: HashMap<String, serde_json::Value>,
38}
39
40impl HookMutation {
41 pub fn none() -> Self {
42 Self {
43 dynamic_values: HashMap::new(),
44 }
45 }
46}
47
48#[derive(Debug, Deserialize, Serialize, Clone)]
49pub struct InlineHookRequest {
50 pub hook: String,
51 pub hook_id: String,
52 pub context: HookContext,
53 pub timeout_ms: u64,
54 #[serde(default)]
55 pub fire_and_forget: bool,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub message: Option<Message>,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub plan: Option<Vec<PlanStep>>,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub result: Option<ExecutionResult>,
62}
63
64#[derive(Debug, Deserialize, Serialize, Clone)]
65pub struct InlineHookResponse {
66 pub hook_id: String,
67 pub mutation: HookMutation,
68}