use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AhpRequest {
pub jsonrpc: String,
pub id: String,
pub method: String,
pub params: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AhpResponse {
pub jsonrpc: String,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<AhpErrorObject>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AhpNotification {
pub jsonrpc: String,
pub method: String,
pub params: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AhpErrorObject {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[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,
}
impl EventType {
pub fn is_blocking(&self) -> bool {
matches!(
self,
EventType::Handshake
| EventType::PreAction
| EventType::PrePrompt
| EventType::Query
| EventType::ContextPerception
| EventType::MemoryRecall
| EventType::Planning
| EventType::Reasoning
| EventType::Confirmation
| EventType::IntentDetection
)
}
}
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"),
}
}
}
#[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>,
}
impl AhpRequest {
pub fn new(method: impl Into<String>, params: serde_json::Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id: uuid::Uuid::new_v4().to_string(),
method: method.into(),
params,
}
}
}
impl AhpNotification {
pub fn new(method: impl Into<String>, params: serde_json::Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
method: method.into(),
params,
}
}
}
impl AhpResponse {
pub fn success(id: impl Into<String>, result: serde_json::Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id: id.into(),
result: Some(result),
error: None,
}
}
pub fn error(id: impl Into<String>, code: i32, message: impl Into<String>) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id: id.into(),
result: None,
error: Some(AhpErrorObject {
code,
message: message.into(),
data: None,
}),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdleEvent {
pub idle_duration_ms: u64,
pub idle_reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_event_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suggested_action: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HeartbeatEvent {
pub uptime_ms: u64,
pub total_events_processed: u64,
pub current_state: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_percent: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_bytes: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub active_tools: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pending_actions: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub queue_depth: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tokens_used: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventContext {
#[serde(skip_serializing_if = "Option::is_none")]
pub recent_facts: Option<Vec<Fact>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_summary: Option<MemorySummary>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_stats: Option<SessionStats>,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_task: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capabilities: Option<std::collections::HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Fact {
pub content: String,
pub source: String,
pub confidence: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemorySummary {
pub memory_type: String,
pub total_items: usize,
pub recent_topics: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionStats {
pub total_actions: usize,
pub total_tokens: i32,
pub duration_ms: u64,
pub error_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum IdleDecision {
Allow,
Defer {
#[serde(skip_serializing_if = "Option::is_none")]
reason: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PerceptionIntent {
Recognize,
Understand,
Locate,
Retrieve,
Explore,
Reason,
Validate,
Compare,
Track,
}
impl std::fmt::Display for PerceptionIntent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PerceptionIntent::Recognize => write!(f, "recognize"),
PerceptionIntent::Understand => write!(f, "understand"),
PerceptionIntent::Locate => write!(f, "locate"),
PerceptionIntent::Retrieve => write!(f, "retrieve"),
PerceptionIntent::Explore => write!(f, "explore"),
PerceptionIntent::Reason => write!(f, "reason"),
PerceptionIntent::Validate => write!(f, "validate"),
PerceptionIntent::Compare => write!(f, "compare"),
PerceptionIntent::Track => write!(f, "track"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PerceptionTarget {
Entity {
name: String,
entity_type: String,
identifier: Option<String>,
},
Location {
path: String,
location_type: String,
},
Event {
description: String,
event_type: String,
time_range: Option<TimeRange>,
},
Relation {
entities: Vec<String>,
relation_type: String,
},
Rule {
name: String,
rule_type: String,
scope: Option<String>,
},
State {
target: String,
aspect: String,
include_history: bool,
},
Resource {
name: String,
resource_type: String,
constraints: Option<serde_json::Value>,
},
Pattern {
pattern: String,
pattern_type: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeRange {
pub from: Option<i64>,
pub to: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub relative: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PerceptionDomain {
Coding,
Writing,
DataAnalysis,
Research,
ProjectManagement,
Conversation,
Operations,
Security,
General,
}
impl Default for PerceptionDomain {
fn default() -> Self {
PerceptionDomain::General
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PerceptionModality {
Text,
Code,
StructuredData,
Table,
Chart,
Image,
Audio,
Video,
Any,
}
impl Default for PerceptionModality {
fn default() -> Self {
PerceptionModality::Any
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PerceptionUrgency {
Critical,
High,
Normal,
Low,
}
impl Default for PerceptionUrgency {
fn default() -> Self {
PerceptionUrgency::Normal
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PerceptionFreshness {
Realtime,
Recent,
Static,
}
impl Default for PerceptionFreshness {
fn default() -> Self {
PerceptionFreshness::Static
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerceptionConstraints {
#[serde(default)]
pub max_results: Option<usize>,
#[serde(default)]
pub max_context_length: Option<usize>,
#[serde(default = "default_include_sources")]
pub include_sources: bool,
}
fn default_include_sources() -> bool {
true
}
impl Default for PerceptionConstraints {
fn default() -> Self {
Self {
max_results: None,
max_context_length: None,
include_sources: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerceptionContext {
pub workspace: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_task: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub query: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub relevant_history: Option<Vec<HistoryItem>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoryItem {
pub item_type: String,
pub content: String,
pub timestamp: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContextPerceptionEvent {
pub session_id: String,
pub intent: PerceptionIntent,
pub target: PerceptionTarget,
#[serde(default)]
pub domain: PerceptionDomain,
#[serde(default)]
pub preferred_modality: PerceptionModality,
#[serde(default)]
pub urgency: PerceptionUrgency,
#[serde(default)]
pub freshness: PerceptionFreshness,
pub context: PerceptionContext,
#[serde(default)]
pub constraints: PerceptionConstraints,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InjectedContext {
pub facts: Vec<Fact>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_contents: Option<Vec<FileContentSnippet>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub project_summary: Option<ProjectSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
pub knowledge: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suggestions: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileContentSnippet {
pub path: String,
pub snippet: String,
pub relevance_score: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectSummary {
pub project_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub key_files: Option<Vec<String>>,
pub structure_description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum ContextPerceptionDecision {
Allow {
injected_context: InjectedContext,
#[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>>,
},
Refine {
refined_intent: Option<PerceptionIntent>,
refined_target: Option<PerceptionTarget>,
scope_hints: Vec<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryRecallEvent {
pub session_id: String,
pub query: String,
pub memory_type: String,
pub max_results: usize,
pub working_directory: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum MemoryRecallDecision {
Allow {
injected_facts: Vec<Fact>,
#[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>>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PlanningStrategy {
None,
StepByStep,
TreeOfThoughts,
GraphPlanning,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlanningEvent {
pub session_id: String,
pub task_description: String,
pub available_strategies: Vec<PlanningStrategy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub constraints: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum PlanningDecision {
Allow {
selected_strategy: PlanningStrategy,
#[serde(skip_serializing_if = "Option::is_none")]
planning_template: 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_task: String,
#[serde(skip_serializing_if = "Option::is_none")]
hints: Option<Vec<String>>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReasoningType {
ChainOfThought,
TreeOfThoughts,
ReAct,
Reflexion,
Other(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReasoningEvent {
pub session_id: String,
pub reasoning_type: ReasoningType,
pub problem_statement: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub hints: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum ReasoningDecision {
Allow {
#[serde(skip_serializing_if = "Option::is_none")]
hints: Option<Vec<String>>,
#[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>>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuccessEvent {
pub session_id: String,
pub action_type: String,
pub action_summary: String,
pub duration_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RateLimitType {
LlmTokenLimit,
LlmRequestLimit,
ApiRequestLimit,
ToolExecutionLimit,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitEvent {
pub session_id: String,
pub limit_type: RateLimitType,
pub retry_after_ms: u64,
pub current_usage: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum RateLimitDecision {
Retry {
retry_after_ms: u64,
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<HashMap<String, serde_json::Value>>,
},
Queue,
Skip { reason: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ConfirmationType {
SafetyConfirm,
UserConfirm,
CostConfirm,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfirmationEvent {
pub session_id: String,
pub confirmation_type: ConfirmationType,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum ConfirmationDecision {
Escalate,
Approve,
Reject { reason: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IntentDetectionEvent {
pub session_id: String,
pub prompt: String,
pub workspace: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub language_hint: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TargetHints {
#[serde(skip_serializing_if = "Option::is_none")]
pub target_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "lowercase")]
pub enum IntentDetectionDecision {
Allow {
detected_intent: String,
confidence: f32,
#[serde(skip_serializing_if = "Option::is_none")]
target_hints: Option<TargetHints>,
},
Block { reason: String },
}