use async_trait::async_trait;
use std::time::Duration;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct GateRequest {
pub tool_name: String,
pub args: serde_json::Value,
}
impl GateRequest {
pub fn new(tool_name: impl Into<String>, args: serde_json::Value) -> Self {
Self {
tool_name: tool_name.into(),
args,
}
}
}
pub type PolicyCode = String;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum GateDecision {
Allow,
Deny {
code: PolicyCode,
reason: String,
},
RequireApproval {
ticket: String,
quorum: u8,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ApprovalError {
#[error("approval timed out after {millis}ms")]
TimedOut {
millis: u64,
},
#[error("approval denied: {0}")]
Denied(String),
#[error("halted")]
Halted,
#[error("gate does not support wait_for_approval")]
NotSupported,
#[error("approver verification failed: {0}")]
VerificationFailed(String),
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ApprovalOutcome {
Allow,
Deny {
code: String,
reason: String,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub enum ApprovalTimeoutPolicy {
Deny,
RequeueAndEscalate {
max_requeues: u8,
},
}
impl Default for ApprovalTimeoutPolicy {
fn default() -> Self {
Self::RequeueAndEscalate { max_requeues: 3 }
}
}
#[async_trait]
pub trait Gate: Send + Sync {
async fn evaluate(&self, req: GateRequest) -> GateDecision;
fn name(&self) -> &'static str;
fn may_require_approval(&self) -> bool {
false
}
async fn wait_for_approval(
&self,
_ticket: String,
_timeout: Duration,
) -> Result<ApprovalOutcome, ApprovalError> {
Err(ApprovalError::NotSupported)
}
}