1use std::collections::HashMap;
16
17use serde::{Deserialize, Serialize};
18
19use crate::capability::AgentCapabilities;
20use crate::process::Pid;
21
22#[non_exhaustive]
24#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub enum AgentRole {
26 Root,
28 Supervisor,
30 Service,
32 Worker,
34 User,
36 Custom(String),
38}
39
40impl std::fmt::Display for AgentRole {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match self {
43 AgentRole::Root => write!(f, "root"),
44 AgentRole::Supervisor => write!(f, "supervisor"),
45 AgentRole::Service => write!(f, "service"),
46 AgentRole::Worker => write!(f, "worker"),
47 AgentRole::User => write!(f, "user"),
48 AgentRole::Custom(name) => write!(f, "custom({name})"),
49 }
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct Agency {
60 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub max_children: Option<usize>,
64
65 #[serde(default)]
67 pub allowed_roles: Vec<AgentRole>,
68
69 #[serde(default)]
71 pub capability_ceiling: AgentCapabilities,
72
73 #[serde(default)]
75 pub children: Vec<Pid>,
76}
77
78impl Agency {
79 pub fn root() -> Self {
81 Self {
82 max_children: None,
83 allowed_roles: vec![
84 AgentRole::Supervisor,
85 AgentRole::Service,
86 AgentRole::Worker,
87 AgentRole::User,
88 ],
89 capability_ceiling: AgentCapabilities::root(),
90 children: Vec::new(),
91 }
92 }
93
94 pub fn supervisor(max_children: usize) -> Self {
96 Self {
97 max_children: Some(max_children),
98 allowed_roles: vec![AgentRole::Service, AgentRole::Worker],
99 capability_ceiling: AgentCapabilities::default(),
100 children: Vec::new(),
101 }
102 }
103
104 pub fn service(max_workers: usize) -> Self {
106 Self {
107 max_children: Some(max_workers),
108 allowed_roles: vec![AgentRole::Worker],
109 capability_ceiling: AgentCapabilities::default(),
110 children: Vec::new(),
111 }
112 }
113
114 pub fn none() -> Self {
116 Self {
117 max_children: Some(0),
118 allowed_roles: Vec::new(),
119 capability_ceiling: AgentCapabilities::default(),
120 children: Vec::new(),
121 }
122 }
123
124 pub fn can_spawn(&self, role: &AgentRole) -> bool {
126 if let Some(max) = self.max_children
127 && self.children.len() >= max
128 {
129 return false;
130 }
131 self.allowed_roles.contains(role)
132 }
133
134 pub fn add_child(&mut self, pid: Pid) {
136 self.children.push(pid);
137 }
138
139 pub fn remove_child(&mut self, pid: Pid) {
141 self.children.retain(|&p| p != pid);
142 }
143
144 pub fn remaining_capacity(&self) -> Option<usize> {
146 self.max_children
147 .map(|max| max.saturating_sub(self.children.len()))
148 }
149}
150
151impl Default for Agency {
152 fn default() -> Self {
153 Self::none()
154 }
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct AgentManifest {
164 pub name: String,
166
167 #[serde(default = "default_version")]
169 pub version: String,
170
171 #[serde(default)]
173 pub description: String,
174
175 pub role: AgentRole,
177
178 #[serde(default)]
180 pub capabilities: AgentCapabilities,
181
182 #[serde(default)]
184 pub agency: Agency,
185
186 #[serde(default)]
188 pub tools: Vec<String>,
189
190 #[serde(default)]
192 pub topics_publish: Vec<String>,
193
194 #[serde(default)]
196 pub topics_subscribe: Vec<String>,
197
198 #[serde(default)]
200 pub resources: AgentResources,
201
202 #[serde(default)]
204 pub interface: AgentInterface,
205
206 #[serde(default)]
208 pub health: AgentHealth,
209
210 #[serde(default)]
212 pub dependencies: AgentDependencies,
213
214 #[serde(default)]
216 pub filesystem_access: Vec<String>,
217
218 #[serde(default)]
220 pub labels: HashMap<String, String>,
221}
222
223fn default_version() -> String {
224 "0.1.0".into()
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct AgentResources {
230 #[serde(default = "default_max_memory_mb")]
232 pub max_memory_mb: u64,
233
234 #[serde(default = "default_max_concurrent")]
236 pub max_concurrent_requests: u32,
237
238 #[serde(default)]
240 pub priority: AgentPriority,
241}
242
243fn default_max_memory_mb() -> u64 {
244 256
245}
246
247fn default_max_concurrent() -> u32 {
248 100
249}
250
251impl Default for AgentResources {
252 fn default() -> Self {
253 Self {
254 max_memory_mb: default_max_memory_mb(),
255 max_concurrent_requests: default_max_concurrent(),
256 priority: AgentPriority::default(),
257 }
258 }
259}
260
261#[non_exhaustive]
263#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
264pub enum AgentPriority {
265 Low,
267 #[default]
269 Normal,
270 High,
272 Critical,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct AgentInterface {
279 #[serde(default)]
281 pub protocol: InterfaceProtocol,
282
283 #[serde(default, skip_serializing_if = "Option::is_none")]
285 pub request_topic: Option<String>,
286
287 #[serde(default)]
289 pub response_mode: ResponseMode,
290}
291
292impl Default for AgentInterface {
293 fn default() -> Self {
294 Self {
295 protocol: InterfaceProtocol::Ipc,
296 request_topic: None,
297 response_mode: ResponseMode::Direct,
298 }
299 }
300}
301
302#[non_exhaustive]
304#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
305pub enum InterfaceProtocol {
306 #[default]
308 Ipc,
309 Rest,
311 Grpc,
313 Mcp,
315}
316
317#[non_exhaustive]
319#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
320pub enum ResponseMode {
321 #[default]
323 Direct,
324 Broadcast,
326 Topic(String),
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct AgentHealth {
333 #[serde(default = "default_check_interval")]
335 pub check_interval_secs: u64,
336
337 #[serde(default = "default_timeout")]
339 pub timeout_secs: u64,
340
341 #[serde(default)]
343 pub restart_policy: AgentRestartPolicy,
344
345 #[serde(default = "default_max_restarts")]
347 pub max_restarts: u32,
348}
349
350fn default_check_interval() -> u64 {
351 30
352}
353
354fn default_timeout() -> u64 {
355 5
356}
357
358fn default_max_restarts() -> u32 {
359 5
360}
361
362impl Default for AgentHealth {
363 fn default() -> Self {
364 Self {
365 check_interval_secs: default_check_interval(),
366 timeout_secs: default_timeout(),
367 restart_policy: AgentRestartPolicy::default(),
368 max_restarts: default_max_restarts(),
369 }
370 }
371}
372
373#[non_exhaustive]
375#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
376pub enum AgentRestartPolicy {
377 Never,
379 #[default]
381 OnFailure,
382 Always,
384}
385
386#[derive(Debug, Clone, Default, Serialize, Deserialize)]
388pub struct AgentDependencies {
389 #[serde(default)]
391 pub requires: Vec<String>,
392
393 #[serde(default)]
395 pub after: Vec<String>,
396}
397
398impl AgentCapabilities {
399 pub fn root() -> Self {
401 Self {
402 can_spawn: true,
403 can_ipc: true,
404 can_exec_tools: true,
405 can_network: true,
406 ipc_scope: crate::capability::IpcScope::All,
407 resource_limits: crate::capability::ResourceLimits {
408 max_memory_bytes: u64::MAX,
409 max_cpu_time_ms: u64::MAX,
410 max_tool_calls: u64::MAX,
411 max_messages: u64::MAX,
412 #[cfg(feature = "os-patterns")]
413 max_disk_bytes: u64::MAX,
414 },
415 }
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 #[test]
424 fn agent_role_display() {
425 assert_eq!(AgentRole::Root.to_string(), "root");
426 assert_eq!(AgentRole::Service.to_string(), "service");
427 assert_eq!(AgentRole::Worker.to_string(), "worker");
428 assert_eq!(
429 AgentRole::Custom("analytics".into()).to_string(),
430 "custom(analytics)"
431 );
432 }
433
434 #[test]
435 fn root_agency_unlimited() {
436 let agency = Agency::root();
437 assert!(agency.max_children.is_none());
438 assert!(agency.can_spawn(&AgentRole::Service));
439 assert!(agency.can_spawn(&AgentRole::Worker));
440 assert!(agency.can_spawn(&AgentRole::User));
441 assert!(!agency.can_spawn(&AgentRole::Root)); }
443
444 #[test]
445 fn supervisor_agency() {
446 let agency = Agency::supervisor(10);
447 assert_eq!(agency.max_children, Some(10));
448 assert!(agency.can_spawn(&AgentRole::Service));
449 assert!(agency.can_spawn(&AgentRole::Worker));
450 assert!(!agency.can_spawn(&AgentRole::Root));
451 assert!(!agency.can_spawn(&AgentRole::User));
452 }
453
454 #[test]
455 fn service_agency() {
456 let agency = Agency::service(5);
457 assert!(agency.can_spawn(&AgentRole::Worker));
458 assert!(!agency.can_spawn(&AgentRole::Service));
459 }
460
461 #[test]
462 fn worker_has_no_agency() {
463 let agency = Agency::none();
464 assert!(!agency.can_spawn(&AgentRole::Worker));
465 assert_eq!(agency.remaining_capacity(), Some(0));
466 }
467
468 #[test]
469 fn agency_child_tracking() {
470 let mut agency = Agency::service(2);
471 agency.add_child(100);
472 agency.add_child(101);
473 assert!(!agency.can_spawn(&AgentRole::Worker)); assert_eq!(agency.remaining_capacity(), Some(0));
475
476 agency.remove_child(100);
477 assert!(agency.can_spawn(&AgentRole::Worker));
478 assert_eq!(agency.remaining_capacity(), Some(1));
479 }
480
481 #[test]
482 fn agency_serde_roundtrip() {
483 let agency = Agency::supervisor(8);
484 let json = serde_json::to_string(&agency).unwrap();
485 let restored: Agency = serde_json::from_str(&json).unwrap();
486 assert_eq!(restored.max_children, Some(8));
487 assert!(restored.allowed_roles.contains(&AgentRole::Service));
488 }
489
490 #[test]
491 fn root_capabilities() {
492 let caps = AgentCapabilities::root();
493 assert!(caps.can_spawn);
494 assert!(caps.can_ipc);
495 assert!(caps.can_exec_tools);
496 assert!(caps.can_network);
497 assert_eq!(caps.resource_limits.max_memory_bytes, u64::MAX);
498 }
499
500 #[test]
501 fn agent_manifest_serde_roundtrip() {
502 let manifest = AgentManifest {
503 name: "memory-service".into(),
504 version: "0.1.0".into(),
505 description: "Persistent memory storage".into(),
506 role: AgentRole::Service,
507 capabilities: AgentCapabilities::default(),
508 agency: Agency::service(10),
509 tools: vec!["memory_store".into(), "memory_retrieve".into()],
510 topics_publish: vec!["memory.stored".into()],
511 topics_subscribe: vec!["memory.request".into()],
512 resources: AgentResources::default(),
513 interface: AgentInterface {
514 protocol: InterfaceProtocol::Ipc,
515 request_topic: Some("memory.request".into()),
516 response_mode: ResponseMode::Direct,
517 },
518 health: AgentHealth::default(),
519 dependencies: AgentDependencies {
520 requires: vec![],
521 after: vec!["kernel-init".into()],
522 },
523 filesystem_access: vec!["data/memory/".into()],
524 labels: HashMap::new(),
525 };
526 let json = serde_json::to_string_pretty(&manifest).unwrap();
527 let restored: AgentManifest = serde_json::from_str(&json).unwrap();
528 assert_eq!(restored.name, "memory-service");
529 assert_eq!(restored.role, AgentRole::Service);
530 assert_eq!(restored.tools.len(), 2);
531 }
532
533 #[test]
534 fn minimal_manifest_serde() {
535 let json = r#"{"name":"worker-1","role":"Worker"}"#;
536 let manifest: AgentManifest = serde_json::from_str(json).unwrap();
537 assert_eq!(manifest.name, "worker-1");
538 assert_eq!(manifest.role, AgentRole::Worker);
539 assert!(manifest.tools.is_empty());
540 assert_eq!(manifest.version, "0.1.0"); }
542
543 #[test]
544 fn agent_priority_ordering() {
545 assert!(AgentPriority::Low < AgentPriority::Normal);
546 assert!(AgentPriority::Normal < AgentPriority::High);
547 assert!(AgentPriority::High < AgentPriority::Critical);
548 }
549
550 #[test]
551 fn agent_health_defaults() {
552 let health = AgentHealth::default();
553 assert_eq!(health.check_interval_secs, 30);
554 assert_eq!(health.timeout_secs, 5);
555 assert_eq!(health.max_restarts, 5);
556 assert_eq!(health.restart_policy, AgentRestartPolicy::OnFailure);
557 }
558
559 #[test]
560 fn interface_protocol_default() {
561 assert_eq!(InterfaceProtocol::default(), InterfaceProtocol::Ipc);
562 }
563
564 #[test]
565 fn response_mode_serde() {
566 let modes = vec![
567 ResponseMode::Direct,
568 ResponseMode::Broadcast,
569 ResponseMode::Topic("events".into()),
570 ];
571 for mode in &modes {
572 let json = serde_json::to_string(mode).unwrap();
573 let _restored: ResponseMode = serde_json::from_str(&json).unwrap();
574 }
575 }
576
577 #[test]
578 fn agent_dependencies_serde() {
579 let deps = AgentDependencies {
580 requires: vec!["message-bus".into()],
581 after: vec!["kernel-init".into(), "message-bus".into()],
582 };
583 let json = serde_json::to_string(&deps).unwrap();
584 let restored: AgentDependencies = serde_json::from_str(&json).unwrap();
585 assert_eq!(restored.requires, vec!["message-bus"]);
586 assert_eq!(restored.after.len(), 2);
587 }
588
589 #[test]
590 fn agent_resources_defaults() {
591 let res = AgentResources::default();
592 assert_eq!(res.max_memory_mb, 256);
593 assert_eq!(res.max_concurrent_requests, 100);
594 assert_eq!(res.priority, AgentPriority::Normal);
595 }
596}