Skip to main content

clawft_kernel/
agency.rs

1//! Agent-first architecture: roles, agency, and agent manifests.
2//!
3//! In WeftOS, agents ARE the OS. Every feature is delivered by an
4//! agent. The kernel boots a root agent which spawns service agents.
5//! Each agent has:
6//!
7//! - A **role** (root, supervisor, service, worker, user)
8//! - An **agency** (ability to spawn child agents)
9//! - A **manifest** (`.agent.toml`) describing its capabilities
10//!
11//! Agency is hierarchical: the root agent has unlimited agency,
12//! supervisors can spawn service/worker agents, services can spawn
13//! workers, and workers have no agency (leaf agents).
14
15use std::collections::HashMap;
16
17use serde::{Deserialize, Serialize};
18
19use crate::capability::AgentCapabilities;
20use crate::process::Pid;
21
22/// Agent role in the OS hierarchy.
23#[non_exhaustive]
24#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub enum AgentRole {
26    /// Superuser agent (user 1). Unlimited capabilities and agency.
27    Root,
28    /// Manages lifecycle of other agents. Can spawn/stop/restart.
29    Supervisor,
30    /// Provides a capability to other agents (memory, cron, API).
31    Service,
32    /// Performs specific tasks, spawned by others. No agency.
33    Worker,
34    /// Represents a human or external system.
35    User,
36    /// Custom role with a label.
37    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/// Agency: the ability to spawn child agents.
54///
55/// Hierarchical: root has unlimited agency, supervisors can spawn
56/// service/worker agents, services can spawn workers only, and
57/// workers have no agency.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct Agency {
60    /// Maximum number of child agents this agent can spawn.
61    /// None = unlimited.
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub max_children: Option<usize>,
64
65    /// Which roles this agent is allowed to spawn.
66    #[serde(default)]
67    pub allowed_roles: Vec<AgentRole>,
68
69    /// Capability ceiling: spawned agents cannot exceed these capabilities.
70    #[serde(default)]
71    pub capability_ceiling: AgentCapabilities,
72
73    /// Currently spawned child PIDs.
74    #[serde(default)]
75    pub children: Vec<Pid>,
76}
77
78impl Agency {
79    /// Root agent: unlimited agency.
80    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    /// Supervisor: can spawn services and workers.
95    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    /// Service: can spawn workers only.
105    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    /// Worker: no agency (leaf agent).
115    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    /// Check if this agency allows spawning a child with the given role.
125    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    /// Record a spawned child.
135    pub fn add_child(&mut self, pid: Pid) {
136        self.children.push(pid);
137    }
138
139    /// Remove a terminated child.
140    pub fn remove_child(&mut self, pid: Pid) {
141        self.children.retain(|&p| p != pid);
142    }
143
144    /// How many children remain before hitting the limit.
145    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/// Agent manifest, parsed from `.agent.toml` files.
158///
159/// Describes what an agent does, what capabilities it needs,
160/// and how to communicate with it. Analogous to systemd unit
161/// files but for AI agents.
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct AgentManifest {
164    /// Agent name (unique within the OS).
165    pub name: String,
166
167    /// Semantic version.
168    #[serde(default = "default_version")]
169    pub version: String,
170
171    /// Human-readable description.
172    #[serde(default)]
173    pub description: String,
174
175    /// Agent role.
176    pub role: AgentRole,
177
178    /// Capabilities this agent needs.
179    #[serde(default)]
180    pub capabilities: AgentCapabilities,
181
182    /// Agency configuration (spawn permissions).
183    #[serde(default)]
184    pub agency: Agency,
185
186    /// Tools this agent is allowed to use.
187    #[serde(default)]
188    pub tools: Vec<String>,
189
190    /// IPC topics this agent publishes to.
191    #[serde(default)]
192    pub topics_publish: Vec<String>,
193
194    /// IPC topics this agent subscribes to.
195    #[serde(default)]
196    pub topics_subscribe: Vec<String>,
197
198    /// Resource limits.
199    #[serde(default)]
200    pub resources: AgentResources,
201
202    /// Communication interface.
203    #[serde(default)]
204    pub interface: AgentInterface,
205
206    /// Health check configuration.
207    #[serde(default)]
208    pub health: AgentHealth,
209
210    /// Dependencies on other agents.
211    #[serde(default)]
212    pub dependencies: AgentDependencies,
213
214    /// Filesystem paths this agent can access.
215    #[serde(default)]
216    pub filesystem_access: Vec<String>,
217
218    /// Additional labels/metadata.
219    #[serde(default)]
220    pub labels: HashMap<String, String>,
221}
222
223fn default_version() -> String {
224    "0.1.0".into()
225}
226
227/// Agent resource configuration.
228#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct AgentResources {
230    /// Maximum memory in MB.
231    #[serde(default = "default_max_memory_mb")]
232    pub max_memory_mb: u64,
233
234    /// Maximum concurrent requests this agent handles.
235    #[serde(default = "default_max_concurrent")]
236    pub max_concurrent_requests: u32,
237
238    /// Priority level.
239    #[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/// Agent priority level for scheduling.
262#[non_exhaustive]
263#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
264pub enum AgentPriority {
265    /// Low priority.
266    Low,
267    /// Normal priority (default).
268    #[default]
269    Normal,
270    /// High priority.
271    High,
272    /// Critical -- must not be preempted.
273    Critical,
274}
275
276/// Agent communication interface.
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct AgentInterface {
279    /// Communication protocol.
280    #[serde(default)]
281    pub protocol: InterfaceProtocol,
282
283    /// Request topic (for IPC protocol).
284    #[serde(default, skip_serializing_if = "Option::is_none")]
285    pub request_topic: Option<String>,
286
287    /// Response mode.
288    #[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/// Interface protocol.
303#[non_exhaustive]
304#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
305pub enum InterfaceProtocol {
306    /// Inter-process communication via kernel IPC.
307    #[default]
308    Ipc,
309    /// REST/HTTP API.
310    Rest,
311    /// gRPC.
312    Grpc,
313    /// Model Context Protocol.
314    Mcp,
315}
316
317/// Response delivery mode.
318#[non_exhaustive]
319#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
320pub enum ResponseMode {
321    /// Reply directly to the sender.
322    #[default]
323    Direct,
324    /// Broadcast response to all subscribers.
325    Broadcast,
326    /// Publish response to a specific topic.
327    Topic(String),
328}
329
330/// Agent health check configuration.
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct AgentHealth {
333    /// Health check interval.
334    #[serde(default = "default_check_interval")]
335    pub check_interval_secs: u64,
336
337    /// Health check timeout.
338    #[serde(default = "default_timeout")]
339    pub timeout_secs: u64,
340
341    /// Restart policy.
342    #[serde(default)]
343    pub restart_policy: AgentRestartPolicy,
344
345    /// Maximum number of restarts before giving up.
346    #[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/// Agent restart policy.
374#[non_exhaustive]
375#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
376pub enum AgentRestartPolicy {
377    /// Never restart.
378    Never,
379    /// Restart on failure only.
380    #[default]
381    OnFailure,
382    /// Always restart.
383    Always,
384}
385
386/// Agent dependencies.
387#[derive(Debug, Clone, Default, Serialize, Deserialize)]
388pub struct AgentDependencies {
389    /// Agents that must be running before this one starts.
390    #[serde(default)]
391    pub requires: Vec<String>,
392
393    /// Agents that should start before this one (soft ordering).
394    #[serde(default)]
395    pub after: Vec<String>,
396}
397
398impl AgentCapabilities {
399    /// Root capabilities: everything allowed.
400    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)); // cannot spawn root
442    }
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)); // at capacity
474        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"); // default
541    }
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}