1use aios_protocol::mode::{GatingProfile, OperatingMode};
8use serde::{Deserialize, Serialize};
9
10use crate::economic::{EconomicMode, EconomicState, ModelTier};
11use crate::hysteresis::HysteresisGate;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct EconomicGates {
16 pub economic_mode: EconomicMode,
18 pub max_tokens_next_turn: Option<u32>,
20 pub preferred_model: Option<ModelTier>,
22 pub allow_expensive_tools: bool,
24 pub allow_replication: bool,
26}
27
28impl Default for EconomicGates {
29 fn default() -> Self {
30 Self {
31 economic_mode: EconomicMode::Sovereign,
32 max_tokens_next_turn: None,
33 preferred_model: None,
34 allow_expensive_tools: true,
35 allow_replication: true,
36 }
37 }
38}
39
40#[derive(Debug, Clone, Default, Serialize, Deserialize)]
45pub struct AutonomicGatingProfile {
46 pub operational: GatingProfile,
48 pub economic: EconomicGates,
50 pub rationale: Vec<String>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct OperationalState {
57 pub mode: OperatingMode,
59 pub error_streak: u32,
61 pub total_errors: u32,
63 pub total_successes: u32,
65 pub last_tick_ms: u64,
67}
68
69impl Default for OperationalState {
70 fn default() -> Self {
71 Self {
72 mode: OperatingMode::Execute,
73 error_streak: 0,
74 total_errors: 0,
75 total_successes: 0,
76 last_tick_ms: 0,
77 }
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct CognitiveState {
84 pub total_tokens_used: u64,
86 pub tokens_remaining: u64,
88 pub context_pressure: f32,
90 pub turns_completed: u32,
92 pub tool_density: f64,
94 pub turns_since_compact: u32,
96 pub dilation_gate: HysteresisGate,
99}
100
101impl Default for CognitiveState {
102 fn default() -> Self {
103 Self {
104 total_tokens_used: 0,
105 tokens_remaining: 120_000,
106 context_pressure: 0.0,
107 turns_completed: 0,
108 tool_density: 0.0,
109 turns_since_compact: 0,
110 dilation_gate: HysteresisGate::new(0.60, 0.45, 0),
113 }
114 }
115}
116
117#[derive(Debug, Clone, Default, Serialize, Deserialize)]
123pub struct StrategyState {
124 pub drift_alerts: u32,
126 pub decisions_logged: u32,
128 pub critiques_completed: u32,
130 pub last_strategy_event_ms: u64,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct EvalState {
140 pub inline_eval_count: u32,
142 pub async_eval_count: u32,
144 pub aggregate_quality_score: f64,
146 pub quality_trend: f64,
148 pub last_eval_ms: u64,
150}
151
152impl Default for EvalState {
153 fn default() -> Self {
154 Self {
155 inline_eval_count: 0,
156 async_eval_count: 0,
157 aggregate_quality_score: 1.0, quality_trend: 0.0,
159 last_eval_ms: 0,
160 }
161 }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct BeliefState {
171 pub capability_count: u32,
173 pub trust_peer_count: u32,
175 pub average_trust: f64,
177 pub min_trust: f64,
179 pub reputation_score: f64,
181 pub violations: u64,
183 pub last_belief_event_ms: u64,
185}
186
187impl Default for BeliefState {
188 fn default() -> Self {
189 Self {
190 capability_count: 0,
191 trust_peer_count: 0,
192 average_trust: 1.0, min_trust: 1.0,
194 reputation_score: 1.0, violations: 0,
196 last_belief_event_ms: 0,
197 }
198 }
199}
200
201#[derive(Debug, Clone, Default, Serialize, Deserialize)]
206pub struct HomeostaticState {
207 pub agent_id: String,
209 pub operational: OperationalState,
211 pub cognitive: CognitiveState,
213 pub economic: EconomicState,
215 pub strategy: StrategyState,
217 pub eval: EvalState,
219 pub belief: BeliefState,
221 pub last_event_seq: u64,
223 pub last_event_ms: u64,
225}
226
227impl HomeostaticState {
228 pub fn for_agent(agent_id: impl Into<String>) -> Self {
230 Self {
231 agent_id: agent_id.into(),
232 ..Default::default()
233 }
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn autonomic_gating_profile_default() {
243 let profile = AutonomicGatingProfile::default();
244 assert!(profile.operational.allow_side_effects);
245 assert!(profile.economic.allow_expensive_tools);
246 assert_eq!(profile.economic.economic_mode, EconomicMode::Sovereign);
247 assert!(profile.rationale.is_empty());
248 }
249
250 #[test]
251 fn autonomic_gating_profile_serde_roundtrip() {
252 let profile = AutonomicGatingProfile {
253 operational: GatingProfile::default(),
254 economic: EconomicGates {
255 economic_mode: EconomicMode::Conserving,
256 max_tokens_next_turn: Some(4096),
257 preferred_model: Some(ModelTier::Budget),
258 allow_expensive_tools: false,
259 allow_replication: false,
260 },
261 rationale: vec!["balance low".into(), "reducing spend".into()],
262 };
263 let json = serde_json::to_string(&profile).unwrap();
264 let back: AutonomicGatingProfile = serde_json::from_str(&json).unwrap();
265 assert_eq!(back.economic.economic_mode, EconomicMode::Conserving);
266 assert_eq!(back.economic.max_tokens_next_turn, Some(4096));
267 assert!(!back.economic.allow_expensive_tools);
268 assert_eq!(back.rationale.len(), 2);
269 }
270
271 #[test]
272 fn homeostatic_state_for_agent() {
273 let state = HomeostaticState::for_agent("agent-1");
274 assert_eq!(state.agent_id, "agent-1");
275 assert_eq!(state.operational.mode, OperatingMode::Execute);
276 assert_eq!(state.economic.mode, EconomicMode::Sovereign);
277 }
278
279 #[test]
280 fn strategy_state_default_is_zeroed() {
281 let strategy = StrategyState::default();
282 assert_eq!(strategy.drift_alerts, 0);
283 assert_eq!(strategy.decisions_logged, 0);
284 assert_eq!(strategy.critiques_completed, 0);
285 assert_eq!(strategy.last_strategy_event_ms, 0);
286 }
287
288 #[test]
289 fn homeostatic_state_includes_strategy() {
290 let state = HomeostaticState::for_agent("agent-1");
291 assert_eq!(state.strategy.drift_alerts, 0);
292 assert_eq!(state.strategy.decisions_logged, 0);
293 assert_eq!(state.strategy.critiques_completed, 0);
294 assert_eq!(state.strategy.last_strategy_event_ms, 0);
295 }
296
297 #[test]
298 fn strategy_state_serde_roundtrip() {
299 let strategy = StrategyState {
300 drift_alerts: 5,
301 decisions_logged: 12,
302 critiques_completed: 3,
303 last_strategy_event_ms: 1_700_000_000_000,
304 };
305 let json = serde_json::to_string(&strategy).unwrap();
306 let back: StrategyState = serde_json::from_str(&json).unwrap();
307 assert_eq!(back.drift_alerts, 5);
308 assert_eq!(back.decisions_logged, 12);
309 assert_eq!(back.critiques_completed, 3);
310 assert_eq!(back.last_strategy_event_ms, 1_700_000_000_000);
311 }
312
313 #[test]
314 fn eval_state_default_optimistic() {
315 let eval = EvalState::default();
316 assert_eq!(eval.inline_eval_count, 0);
317 assert_eq!(eval.async_eval_count, 0);
318 assert!((eval.aggregate_quality_score - 1.0).abs() < f64::EPSILON);
319 assert!((eval.quality_trend).abs() < f64::EPSILON);
320 assert_eq!(eval.last_eval_ms, 0);
321 }
322
323 #[test]
324 fn eval_state_serde_roundtrip() {
325 let eval = EvalState {
326 inline_eval_count: 15,
327 async_eval_count: 3,
328 aggregate_quality_score: 0.78,
329 quality_trend: -0.02,
330 last_eval_ms: 1_700_000_000_000,
331 };
332 let json = serde_json::to_string(&eval).unwrap();
333 let back: EvalState = serde_json::from_str(&json).unwrap();
334 assert_eq!(back.inline_eval_count, 15);
335 assert_eq!(back.async_eval_count, 3);
336 assert!((back.aggregate_quality_score - 0.78).abs() < f64::EPSILON);
337 assert!((back.quality_trend - (-0.02)).abs() < f64::EPSILON);
338 }
339
340 #[test]
341 fn homeostatic_state_includes_eval() {
342 let state = HomeostaticState::for_agent("test");
343 assert_eq!(state.eval.inline_eval_count, 0);
344 assert!((state.eval.aggregate_quality_score - 1.0).abs() < f64::EPSILON);
345 }
346
347 #[test]
348 fn belief_state_default_optimistic() {
349 let belief = BeliefState::default();
350 assert_eq!(belief.capability_count, 0);
351 assert_eq!(belief.trust_peer_count, 0);
352 assert!((belief.average_trust - 1.0).abs() < f64::EPSILON);
353 assert!((belief.min_trust - 1.0).abs() < f64::EPSILON);
354 assert!((belief.reputation_score - 1.0).abs() < f64::EPSILON);
355 assert_eq!(belief.violations, 0);
356 assert_eq!(belief.last_belief_event_ms, 0);
357 }
358
359 #[test]
360 fn belief_state_serde_roundtrip() {
361 let belief = BeliefState {
362 capability_count: 5,
363 trust_peer_count: 3,
364 average_trust: 0.72,
365 min_trust: 0.45,
366 reputation_score: 0.88,
367 violations: 2,
368 last_belief_event_ms: 1_700_000_000_000,
369 };
370 let json = serde_json::to_string(&belief).unwrap();
371 let back: BeliefState = serde_json::from_str(&json).unwrap();
372 assert_eq!(back.capability_count, 5);
373 assert_eq!(back.trust_peer_count, 3);
374 assert!((back.average_trust - 0.72).abs() < f64::EPSILON);
375 assert!((back.min_trust - 0.45).abs() < f64::EPSILON);
376 assert!((back.reputation_score - 0.88).abs() < f64::EPSILON);
377 assert_eq!(back.violations, 2);
378 }
379
380 #[test]
381 fn homeostatic_state_includes_belief() {
382 let state = HomeostaticState::for_agent("test");
383 assert_eq!(state.belief.capability_count, 0);
384 assert_eq!(state.belief.violations, 0);
385 assert!((state.belief.reputation_score - 1.0).abs() < f64::EPSILON);
386 }
387
388 #[test]
389 fn cognitive_state_has_dilation_gate() {
390 let cog = CognitiveState::default();
391 assert!(!cog.dilation_gate.active);
392 assert!((cog.dilation_gate.threshold_enter - 0.60).abs() < f64::EPSILON);
393 assert!((cog.dilation_gate.threshold_exit - 0.45).abs() < f64::EPSILON);
394 }
395
396 #[test]
397 fn cognitive_state_has_compression_signals() {
398 let mut cog = CognitiveState::default();
399 assert_eq!(cog.tool_density, 0.0);
400 assert_eq!(cog.turns_since_compact, 0);
401 cog.tool_density = 3.5;
402 cog.turns_since_compact = 12;
403 assert!((cog.tool_density - 3.5).abs() < f64::EPSILON);
404 assert_eq!(cog.turns_since_compact, 12);
405 }
406
407 #[test]
408 fn cognitive_state_compression_signals_serde() {
409 let cog = CognitiveState {
410 tool_density: 2.5,
411 turns_since_compact: 8,
412 ..Default::default()
413 };
414 let json = serde_json::to_string(&cog).unwrap();
415 let back: CognitiveState = serde_json::from_str(&json).unwrap();
416 assert!((back.tool_density - 2.5).abs() < f64::EPSILON);
417 assert_eq!(back.turns_since_compact, 8);
418 }
419}