1use super::EventContext;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct AhpEvent {
8 pub event_type: EventType,
9 pub session_id: String,
10 pub agent_id: String,
11 pub timestamp: String,
12 pub depth: u32,
13 pub payload: serde_json::Value,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub context: Option<EventContext>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub metadata: Option<HashMap<String, serde_json::Value>>,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum EventType {
25 Handshake,
26 PreAction,
27 PostAction,
28 PrePrompt,
29 PostResponse,
30 SessionStart,
31 SessionEnd,
32 Error,
33 Query,
34 Heartbeat,
35 Idle,
37 ContextPerception,
39 Success,
41 MemoryRecall,
43 Planning,
45 Reasoning,
47 RateLimit,
49 Confirmation,
51 IntentDetection,
53 RunLifecycle,
55 TaskList,
57 Verification,
59}
60
61impl EventType {
62 pub fn is_blocking(&self) -> bool {
64 matches!(
65 self,
66 EventType::Handshake
67 | EventType::PreAction
68 | EventType::PrePrompt
69 | EventType::Query
70 | EventType::Idle
71 | EventType::ContextPerception
72 | EventType::MemoryRecall
73 | EventType::Planning
74 | EventType::Reasoning
75 | EventType::RateLimit
76 | EventType::Confirmation
77 | EventType::IntentDetection
78 )
79 }
80
81 pub fn uses_specialized_decision(&self) -> bool {
83 matches!(
84 self,
85 EventType::Idle
86 | EventType::ContextPerception
87 | EventType::MemoryRecall
88 | EventType::Planning
89 | EventType::Reasoning
90 | EventType::RateLimit
91 | EventType::Confirmation
92 | EventType::IntentDetection
93 )
94 }
95
96 pub fn is_batchable(&self) -> bool {
98 !matches!(self, EventType::Handshake | EventType::Query)
99 && !self.uses_specialized_decision()
100 }
101}
102
103impl std::fmt::Display for EventType {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 match self {
106 EventType::Handshake => write!(f, "handshake"),
107 EventType::PreAction => write!(f, "pre_action"),
108 EventType::PostAction => write!(f, "post_action"),
109 EventType::PrePrompt => write!(f, "pre_prompt"),
110 EventType::PostResponse => write!(f, "post_response"),
111 EventType::SessionStart => write!(f, "session_start"),
112 EventType::SessionEnd => write!(f, "session_end"),
113 EventType::Error => write!(f, "error"),
114 EventType::Query => write!(f, "query"),
115 EventType::Heartbeat => write!(f, "heartbeat"),
116 EventType::Idle => write!(f, "idle"),
117 EventType::ContextPerception => write!(f, "context_perception"),
118 EventType::Success => write!(f, "success"),
119 EventType::MemoryRecall => write!(f, "memory_recall"),
120 EventType::Planning => write!(f, "planning"),
121 EventType::Reasoning => write!(f, "reasoning"),
122 EventType::RateLimit => write!(f, "rate_limit"),
123 EventType::Confirmation => write!(f, "confirmation"),
124 EventType::IntentDetection => write!(f, "intent_detection"),
125 EventType::RunLifecycle => write!(f, "run_lifecycle"),
126 EventType::TaskList => write!(f, "task_list"),
127 EventType::Verification => write!(f, "verification"),
128 }
129 }
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134#[serde(tag = "decision", rename_all = "lowercase")]
135pub enum Decision {
136 Allow {
137 #[serde(skip_serializing_if = "Option::is_none")]
138 modified_payload: Option<serde_json::Value>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 metadata: Option<HashMap<String, serde_json::Value>>,
141 },
142 Block {
143 reason: String,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 metadata: Option<HashMap<String, serde_json::Value>>,
146 },
147 Modify {
148 modified_payload: serde_json::Value,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 metadata: Option<HashMap<String, serde_json::Value>>,
151 },
152 Defer {
153 retry_after_ms: u64,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 reason: Option<String>,
156 },
157 Escalate {
158 reason: String,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 escalation_target: Option<String>,
161 },
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct HandshakeRequest {
167 pub protocol_version: String,
168 pub agent_info: AgentInfo,
169 pub session_id: String,
170 pub agent_id: String,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct AgentInfo {
176 pub framework: String,
177 pub version: String,
178 pub capabilities: Vec<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct HandshakeResponse {
184 pub protocol_version: String,
185 pub harness_info: HarnessInfo,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub session_token: Option<String>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub config: Option<HarnessConfig>,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct HarnessInfo {
195 pub name: String,
196 pub version: String,
197 pub capabilities: Vec<String>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct HarnessConfig {
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub timeout_ms: Option<u64>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub batch_size: Option<usize>,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub max_depth: Option<u32>,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct QueryRequest {
214 pub session_id: String,
215 pub agent_id: String,
216 pub query_type: String,
217 pub payload: serde_json::Value,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct QueryResponse {
223 pub answer: serde_json::Value,
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub reason: Option<String>,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub alternatives: Option<Vec<String>>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct BatchRequest {
233 pub events: Vec<AhpEvent>,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct BatchResponse {
239 pub decisions: Vec<Decision>,
240}