1use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::time::Duration;
6
7#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
10pub struct AgentId(pub String);
11
12impl fmt::Display for AgentId {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 f.write_str(&self.0)
15 }
16}
17
18#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
21pub struct TenantId(pub String);
22
23impl fmt::Display for TenantId {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 f.write_str(&self.0)
26 }
27}
28
29#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
31pub struct ProviderId(pub String);
32
33impl fmt::Display for ProviderId {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 f.write_str(&self.0)
36 }
37}
38
39#[derive(Clone, Debug)]
41#[non_exhaustive]
42pub struct AgentMeta {
43 pub id: AgentId,
45 pub role: String,
47 pub version: String,
49 pub identity_pubkey: [u8; 32],
53 pub expected_step_p99: Option<Duration>,
56}
57
58#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
60#[non_exhaustive]
61pub enum RuntimeState {
62 #[default]
64 Running,
65 Draining,
67 Halted,
69}
70
71impl fmt::Display for RuntimeState {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 Self::Running => f.write_str("running"),
75 Self::Draining => f.write_str("draining"),
76 Self::Halted => f.write_str("halted"),
77 }
78 }
79}
80
81#[derive(Clone, Debug, Eq, PartialEq)]
83#[non_exhaustive]
84pub enum BudgetScope {
85 Global,
87 Tenant(TenantId),
89 Provider(ProviderId),
91 TenantProvider(TenantId, ProviderId),
93 Agent(AgentId),
95}
96
97impl fmt::Display for BudgetScope {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 Self::Global => f.write_str("global"),
101 Self::Tenant(t) => write!(f, "tenant:{t}"),
102 Self::Provider(p) => write!(f, "provider:{p}"),
103 Self::TenantProvider(t, p) => write!(f, "tenant:{t}:provider:{p}"),
104 Self::Agent(a) => write!(f, "agent:{a}"),
105 }
106 }
107}
108
109#[derive(Clone, Debug, Serialize, Deserialize)]
111pub struct KillReason(pub String);
112
113#[derive(Clone, Debug, Serialize, Deserialize)]
115#[non_exhaustive]
116pub enum KillTrigger {
117 Programmatic,
119 External(String),
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn agent_id_round_trips_through_string() {
129 let a = AgentId("claims-triage".into());
130 let s = serde_json::to_string(&a).unwrap();
131 let b: AgentId = serde_json::from_str(&s).unwrap();
132 assert_eq!(a, b);
133 }
134
135 #[test]
136 fn runtime_state_default_is_running() {
137 assert_eq!(RuntimeState::default(), RuntimeState::Running);
138 }
139
140 #[test]
141 fn budget_scope_formats_distinctly() {
142 let g = BudgetScope::Global;
143 let t = BudgetScope::Tenant(TenantId("bl_auto".into()));
144 let p = BudgetScope::Provider(ProviderId("anthropic".into()));
145 assert_ne!(format!("{g}"), format!("{t}"));
146 assert_ne!(format!("{t}"), format!("{p}"));
147 }
148}