agents-kit 0.0.1

Rust agent control Kit for Codex, OpenCode, and provider-backed Custom runtimes
Documentation
use std::collections::BTreeMap;

use crate::KitError;

#[derive(Clone, Debug, serde::Serialize)]
#[serde(transparent)]
pub(crate) struct OpenCodeHistoryRequest {
    high_water: BTreeMap<String, u64>,
}

#[derive(Clone, Debug, serde::Deserialize)]
pub(crate) struct OpenCodeHistoryRow {
    pub(super) id: String,
    pub(super) aggregate_id: String,
    pub(super) seq: u64,
    #[serde(rename = "type")]
    pub(super) event_type: String,
    pub(super) data: serde_json::Map<String, serde_json::Value>,
}

impl OpenCodeHistoryRequest {
    pub(crate) fn new(high_water: BTreeMap<String, u64>) -> Result<Self, KitError> {
        if high_water.keys().any(|id| id.is_empty() || id.trim() != id) {
            return Err(KitError::invalid_state(
                "recovery high-water identity is malformed",
            ));
        }
        Ok(Self { high_water })
    }

    pub(crate) fn empty() -> Self {
        Self {
            high_water: BTreeMap::new(),
        }
    }
}

impl OpenCodeHistoryRow {
    pub(crate) fn encoded_len(&self) -> usize {
        self.id
            .len()
            .saturating_add(self.aggregate_id.len())
            .saturating_add(self.event_type.len())
            .saturating_add(Self::map_encoded_len(&self.data))
            .saturating_add(64)
    }

    pub(crate) fn into_validated(self) -> Result<Self, KitError> {
        self.validate()?;
        Ok(self)
    }

    pub(crate) fn validate(&self) -> Result<(), KitError> {
        let versioned = self
            .event_type
            .rsplit_once('.')
            .is_some_and(|(base, version)| {
                !base.is_empty()
                    && !version.is_empty()
                    && version.bytes().all(|byte| byte.is_ascii_digit())
            });
        if !self.id.starts_with("evt_")
            || self.id.trim() != self.id
            || self.aggregate_id.is_empty()
            || self.aggregate_id.trim() != self.aggregate_id
            || self.event_type.is_empty()
            || self.event_type.trim() != self.event_type
            || !versioned
        {
            return Err(KitError::protocol("history row is malformed"));
        }
        Ok(())
    }

    pub(crate) fn aggregate_id(&self) -> &str {
        &self.aggregate_id
    }

    fn map_encoded_len(values: &serde_json::Map<String, serde_json::Value>) -> usize {
        values.iter().fold(2usize, |length, (key, value)| {
            length
                .saturating_add(Self::string_encoded_len(key))
                .saturating_add(Self::value_encoded_len(value))
                .saturating_add(2)
        })
    }

    fn value_encoded_len(value: &serde_json::Value) -> usize {
        match value {
            serde_json::Value::Null => 4,
            serde_json::Value::Bool(_) => 5,
            serde_json::Value::Number(_) => 32,
            serde_json::Value::String(value) => Self::string_encoded_len(value),
            serde_json::Value::Array(values) => values.iter().fold(2usize, |length, value| {
                length
                    .saturating_add(Self::value_encoded_len(value))
                    .saturating_add(1)
            }),
            serde_json::Value::Object(values) => Self::map_encoded_len(values),
        }
    }

    fn string_encoded_len(value: &str) -> usize {
        value.len().saturating_mul(6).saturating_add(2)
    }
}