agent-evaluate-v2-contract 0.1.0

Vendored Evaluate v2 transport contract for agent-infra-sdk
Documentation
//! Bindings: which snapshots an agent or runtime is expected to pass.

use serde::{Deserialize, Serialize};

use crate::catalog::SnapshotRef;
use crate::report::GatePolicy;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TargetKind {
    Agent,
    Runtime,
}

impl TargetKind {
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Agent => "agent",
            Self::Runtime => "runtime",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BindingTarget {
    pub kind: TargetKind,
    pub id: String,
}

impl BindingTarget {
    pub fn agent(id: impl Into<String>) -> Self {
        Self {
            kind: TargetKind::Agent,
            id: id.into(),
        }
    }

    pub fn runtime(id: impl Into<String>) -> Self {
        Self {
            kind: TargetKind::Runtime,
            id: id.into(),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BoundSnapshot {
    #[serde(rename = "ref")]
    pub reference: SnapshotRef,
    /// Non-required snapshots are observed but never gate.
    #[serde(default = "default_required")]
    pub required: bool,
}

fn default_required() -> bool {
    true
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BindingTrigger {
    Manual,
    OnDeploy,
    Scheduled,
}

/// A binding is mutable, unlike everything in the catalogue. Runs freeze the
/// resolved snapshot list at creation, so editing a binding never rewrites
/// history.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct EvalBinding {
    pub tenant_id: String,
    pub project_id: String,
    pub target: BindingTarget,
    pub snapshots: Vec<BoundSnapshot>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub gate_policy: Option<GatePolicy>,
    #[serde(default)]
    pub triggers: Vec<BindingTrigger>,
    /// Optimistic concurrency token.
    pub version: i64,
    pub created_at_ms: i64,
    pub updated_at_ms: i64,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PutBindingRequest {
    pub target: BindingTarget,
    pub snapshots: Vec<BoundSnapshot>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub gate_policy: Option<GatePolicy>,
    #[serde(default)]
    pub triggers: Vec<BindingTrigger>,
    /// Expected current version. Absent means create-only.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub if_version: Option<i64>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GetBindingRequest {
    pub target: BindingTarget,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ListBindingsRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub kind: Option<TargetKind>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub cursor: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub limit: Option<usize>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BindingPage {
    pub items: Vec<EvalBinding>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub next_cursor: Option<String>,
}