use serde::{Deserialize, Serialize};
use std::fmt;
use std::time::Duration;
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct AgentId(pub String);
impl fmt::Display for AgentId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct TenantId(pub String);
impl fmt::Display for TenantId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct ProviderId(pub String);
impl fmt::Display for ProviderId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct AgentMeta {
pub id: AgentId,
pub role: String,
pub version: String,
pub identity_pubkey: [u8; 32],
pub expected_step_p99: Option<Duration>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum RuntimeState {
#[default]
Running,
Draining,
Halted,
}
impl fmt::Display for RuntimeState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Running => f.write_str("running"),
Self::Draining => f.write_str("draining"),
Self::Halted => f.write_str("halted"),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum BudgetScope {
Global,
Tenant(TenantId),
Provider(ProviderId),
TenantProvider(TenantId, ProviderId),
Agent(AgentId),
}
impl fmt::Display for BudgetScope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Global => f.write_str("global"),
Self::Tenant(t) => write!(f, "tenant:{t}"),
Self::Provider(p) => write!(f, "provider:{p}"),
Self::TenantProvider(t, p) => write!(f, "tenant:{t}:provider:{p}"),
Self::Agent(a) => write!(f, "agent:{a}"),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct KillReason(pub String);
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum KillTrigger {
Programmatic,
External(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn agent_id_round_trips_through_string() {
let a = AgentId("claims-triage".into());
let s = serde_json::to_string(&a).unwrap();
let b: AgentId = serde_json::from_str(&s).unwrap();
assert_eq!(a, b);
}
#[test]
fn runtime_state_default_is_running() {
assert_eq!(RuntimeState::default(), RuntimeState::Running);
}
#[test]
fn budget_scope_formats_distinctly() {
let g = BudgetScope::Global;
let t = BudgetScope::Tenant(TenantId("bl_auto".into()));
let p = BudgetScope::Provider(ProviderId("anthropic".into()));
assert_ne!(format!("{g}"), format!("{t}"));
assert_ne!(format!("{t}"), format!("{p}"));
}
}