use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BlastRadius {
ReadOnly,
SingleService,
SingleHost,
MultiHost,
Irreversible,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Decision {
Allow,
Deny,
RequireApproval,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Reversibility {
Reversible,
PartiallyReversible,
Irreversible,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Confidence {
pub score: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub basis: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostEstimate {
pub currency: String,
pub amount: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub amount_max: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub basis: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f64>,
}
pub const STRICTNESS_STRICT_LITERAL: &str = "strict_literal";
pub const STRICTNESS_STRICT_TEMPLATE: &str = "strict_template";
fn default_strictness() -> String {
STRICTNESS_STRICT_LITERAL.to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Step {
pub tool: String,
pub args: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub expected: Option<serde_json::Value>,
pub blast_radius: BlastRadius,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Context {
pub rationale: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub triggered_by: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub derived_from: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Proposal {
pub aae_version: String,
pub proposal_id: String,
pub agent_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_chain: Option<Vec<String>>,
pub tenant_id: String,
pub intent: String,
pub context: Context,
pub steps: Vec<Step>,
pub submitted_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Effect {
#[serde(rename = "type")]
pub effect_type: String,
pub target: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "from")]
pub from_state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepPreview {
pub step_index: usize,
pub predicted_effects: Vec<Effect>,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_duration_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub diff: Option<String>,
#[serde(default)]
pub warnings: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preview_unsupported_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ext: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Preview {
pub aae_version: String,
pub preview_id: String,
pub proposal_id: String,
pub step_previews: Vec<StepPreview>,
pub aggregate_blast_radius: BlastRadius,
pub preview_unsupported: bool,
pub generated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicyDecision {
pub aae_version: String,
pub decision_id: String,
pub proposal_id: String,
pub decision: Decision,
pub policy_version: String,
pub rules_evaluated: Vec<String>,
pub reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub required_approvers: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<DateTime<Utc>>,
#[serde(default = "default_strictness")]
pub strictness: String,
pub decided_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ext: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityScope {
pub tool: String,
pub approved_steps_hash: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_constraints: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityToken {
pub aae_version: String,
pub token_id: String,
pub proposal_id: String,
pub decision_id: String,
pub scope: CapabilityScope,
pub iat: i64,
pub exp: i64,
pub iss: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub: Option<String>,
pub max_uses: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventSignature {
pub alg: String,
pub key_id: String,
pub value: String,
}
pub const EVENT_TYPE_ARTIFACT_ATTESTED: &str = "artifact_attested";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
pub aae_version: String,
pub event_id: String,
pub event_type: String,
pub proposal_id: String,
pub tenant_id: String,
pub agent_id: String,
pub actor: String,
pub ts: DateTime<Utc>,
pub payload: serde_json::Value,
pub prev_event_hash: Option<String>,
pub this_event_hash: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub signature: Option<EventSignature>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolRegistration {
pub aae_version: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub plan_schema: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub preview_schema: Option<serde_json::Value>,
pub preview_supported: bool,
pub default_blast_radius: BlastRadius,
pub aae_required: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub data_classes_touched: Vec<String>,
#[serde(default)]
pub compliance_relevant: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub systems_of_record: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reversibility: Option<Reversibility>,
}