use serde::{Deserialize, Serialize}; /// The six action categories that always require human approval before an /// agent may execute them (spec ยง15, acceptance-blocking). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum GatedCategory { /// Outbound messages and emails leaving the workspace. OutboundMessage, /// Sharing keys, secrets, or credentials. SecretSharing, /// Access, permission, or sharing changes. AccessChange, /// Financial transactions and credit purchases. FinancialTransaction, /// File deletion. FileDeletion, /// Granting or extending external-infrastructure access. InfraAccessGrant, } impl GatedCategory { /// Every gated category, in spec order. The safety layer iterates this /// to prove exhaustive coverage in tests. pub const ALL: [GatedCategory; 6] = [ GatedCategory::OutboundMessage, GatedCategory::SecretSharing, GatedCategory::AccessChange, GatedCategory::FinancialTransaction, GatedCategory::FileDeletion, GatedCategory::InfraAccessGrant, ]; /// Storage form matching the `approvals.category` CHECK constraint. pub fn as_str(&self) -> &'static str { match self { GatedCategory::OutboundMessage => "outbound_message", GatedCategory::SecretSharing => "secret_sharing", GatedCategory::AccessChange => "access_change", GatedCategory::FinancialTransaction => "financial_transaction", GatedCategory::FileDeletion => "file_deletion", GatedCategory::InfraAccessGrant => "infra_access_grant", } } } impl std::str::FromStr for GatedCategory { type Err = String; fn from_str(s: &str) -> Result { match s { "outbound_message" => Ok(GatedCategory::OutboundMessage), "secret_sharing" => Ok(GatedCategory::SecretSharing), "access_change" => Ok(GatedCategory::AccessChange), "financial_transaction" => Ok(GatedCategory::FinancialTransaction), "file_deletion" => Ok(GatedCategory::FileDeletion), "infra_access_grant" => Ok(GatedCategory::InfraAccessGrant), other => Err(format!("unknown gated category: {other}")), } } }