use super::EventContext;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AhpEvent {
pub event_type: EventType,
pub session_id: String,
pub agent_id: String,
pub timestamp: String,
pub depth: u32,
pub payload: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<EventContext>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EventType {
Handshake,
PreAction,
PostAction,
PrePrompt,
PostResponse,
SessionStart,
SessionEnd,
Error,
Query,
Heartbeat,
Idle,
ContextPerception,
Success,
MemoryRecall,
Planning,
Reasoning,
RateLimit,
Confirmation,
IntentDetection,
RunLifecycle,
TaskList,
Verification,
}
impl EventType {
pub fn is_blocking(&self) -> bool {
matches!(
self,
EventType::Handshake
| EventType::PreAction
| EventType::PrePrompt
| EventType::Query
| EventType::Idle
| EventType::ContextPerception
| EventType::MemoryRecall
| EventType::Planning
| EventType::Reasoning
| EventType::RateLimit
| EventType::Confirmation
| EventType::IntentDetection
)
}
pub fn uses_specialized_decision(&self) -> bool {
matches!(
self,
EventType::Idle
| EventType::ContextPerception
| EventType::MemoryRecall
| EventType::Planning
| EventType::Reasoning
| EventType::RateLimit
| EventType::Confirmation
| EventType::IntentDetection
)
}
pub fn is_batchable(&self) -> bool {
!matches!(self, EventType::Handshake | EventType::Query)
&& !self.uses_specialized_decision()
}
}
impl std::fmt::Display for EventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EventType::Handshake => write!(f, "handshake"),
EventType::PreAction => write!(f, "pre_action"),
EventType::PostAction => write!(f, "post_action"),
EventType::PrePrompt => write!(f, "pre_prompt"),
EventType::PostResponse => write!(f, "post_response"),
EventType::SessionStart => write!(f, "session_start"),
EventType::SessionEnd => write!(f, "session_end"),
EventType::Error => write!(f, "error"),
EventType::Query => write!(f, "query"),
EventType::Heartbeat => write!(f, "heartbeat"),
EventType::Idle => write!(f, "idle"),
EventType::ContextPerception => write!(f, "context_perception"),
EventType::Success => write!(f, "success"),
EventType::MemoryRecall => write!(f, "memory_recall"),
EventType::Planning => write!(f, "planning"),
EventType::Reasoning => write!(f, "reasoning"),
EventType::RateLimit => write!(f, "rate_limit"),
EventType::Confirmation => write!(f, "confirmation"),
EventType::IntentDetection => write!(f, "intent_detection"),
EventType::RunLifecycle => write!(f, "run_lifecycle"),
EventType::TaskList => write!(f, "task_list"),
EventType::Verification => write!(f, "verification"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum Decision {
Allow {
#[serde(skip_serializing_if = "Option::is_none")]
modified_payload: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<HashMap<String, serde_json::Value>>,
},
Block {
reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<HashMap<String, serde_json::Value>>,
},
Modify {
modified_payload: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<HashMap<String, serde_json::Value>>,
},
Defer {
retry_after_ms: u64,
#[serde(skip_serializing_if = "Option::is_none")]
reason: Option<String>,
},
Escalate {
reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
escalation_target: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HandshakeRequest {
pub protocol_version: String,
pub agent_info: AgentInfo,
pub session_id: String,
pub agent_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentInfo {
pub framework: String,
pub version: String,
pub capabilities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HandshakeResponse {
pub protocol_version: String,
pub harness_info: HarnessInfo,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<HarnessConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarnessInfo {
pub name: String,
pub version: String,
pub capabilities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarnessConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub batch_size: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_depth: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryRequest {
pub session_id: String,
pub agent_id: String,
pub query_type: String,
pub payload: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResponse {
pub answer: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alternatives: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchRequest {
pub events: Vec<AhpEvent>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchResponse {
pub decisions: Vec<Decision>,
}