Skip to main content

agentic_contracts/
types.rs

1//! Shared types used across all sisters.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use uuid::Uuid;
7
8/// All sister types in the ecosystem.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum SisterType {
12    // Foundation sisters
13    Memory,
14    Vision,
15    Codebase,
16    Identity,
17    Time,
18    Contract,
19
20    // Cognitive sisters
21    Comm,
22    Planning,
23    Cognition,
24    Reality,
25
26    // Future sisters (reserved)
27    Attention,
28    Affect,
29    Motivation,
30    Learning,
31    Bond,
32    Meaning,
33    Wonder,
34    Imagination,
35    Conscience,
36    Meta,
37    Duration,
38}
39
40impl SisterType {
41    /// Get the file extension for this sister type.
42    pub fn file_extension(&self) -> &'static str {
43        match self {
44            Self::Memory => "amem",
45            Self::Vision => "avis",
46            Self::Codebase => "acb",
47            Self::Identity => "aid",
48            Self::Time => "atime",
49            Self::Contract => "acon",
50            Self::Comm => "acomm",
51            Self::Planning => "aplan",
52            Self::Cognition => "acog",
53            Self::Reality => "areal",
54            Self::Attention => "aatt",
55            Self::Affect => "aaff",
56            Self::Motivation => "amot",
57            Self::Learning => "alrn",
58            Self::Bond => "abond",
59            Self::Meaning => "amean",
60            Self::Wonder => "awond",
61            Self::Imagination => "aimag",
62            Self::Conscience => "acons",
63            Self::Meta => "ameta",
64            Self::Duration => "adur",
65        }
66    }
67
68    /// Get the MCP tool prefix for this sister type.
69    pub fn mcp_prefix(&self) -> &'static str {
70        match self {
71            Self::Memory => "memory",
72            Self::Vision => "vision",
73            Self::Codebase => "codebase",
74            Self::Identity => "identity",
75            Self::Time => "time",
76            Self::Contract => "contract",
77            Self::Comm => "comm",
78            Self::Planning => "planning",
79            Self::Cognition => "cognition",
80            Self::Reality => "reality",
81            Self::Attention => "attention",
82            Self::Affect => "affect",
83            Self::Motivation => "motivation",
84            Self::Learning => "learning",
85            Self::Bond => "bond",
86            Self::Meaning => "meaning",
87            Self::Wonder => "wonder",
88            Self::Imagination => "imagination",
89            Self::Conscience => "conscience",
90            Self::Meta => "meta",
91            Self::Duration => "duration",
92        }
93    }
94
95    /// Get the byte identifier for file format headers.
96    pub fn to_byte(&self) -> u8 {
97        match self {
98            Self::Memory => 0x01,
99            Self::Vision => 0x02,
100            Self::Codebase => 0x03,
101            Self::Identity => 0x04,
102            Self::Time => 0x05,
103            Self::Contract => 0x06,
104            Self::Comm => 0x07,
105            Self::Planning => 0x08,
106            Self::Cognition => 0x09,
107            Self::Reality => 0x0A,
108            Self::Attention => 0x0B,
109            Self::Affect => 0x0C,
110            Self::Motivation => 0x0D,
111            Self::Learning => 0x0E,
112            Self::Bond => 0x0F,
113            Self::Meaning => 0x10,
114            Self::Wonder => 0x11,
115            Self::Imagination => 0x12,
116            Self::Conscience => 0x13,
117            Self::Meta => 0x14,
118            Self::Duration => 0x15,
119        }
120    }
121
122    /// Get sister type from byte identifier.
123    pub fn from_byte(byte: u8) -> Option<Self> {
124        match byte {
125            0x01 => Some(Self::Memory),
126            0x02 => Some(Self::Vision),
127            0x03 => Some(Self::Codebase),
128            0x04 => Some(Self::Identity),
129            0x05 => Some(Self::Time),
130            0x06 => Some(Self::Contract),
131            0x07 => Some(Self::Comm),
132            0x08 => Some(Self::Planning),
133            0x09 => Some(Self::Cognition),
134            0x0A => Some(Self::Reality),
135            0x0B => Some(Self::Attention),
136            0x0C => Some(Self::Affect),
137            0x0D => Some(Self::Motivation),
138            0x0E => Some(Self::Learning),
139            0x0F => Some(Self::Bond),
140            0x10 => Some(Self::Meaning),
141            0x11 => Some(Self::Wonder),
142            0x12 => Some(Self::Imagination),
143            0x13 => Some(Self::Conscience),
144            0x14 => Some(Self::Meta),
145            0x15 => Some(Self::Duration),
146            _ => None,
147        }
148    }
149}
150
151impl std::fmt::Display for SisterType {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        write!(f, "{}", self.mcp_prefix())
154    }
155}
156
157/// Semantic version.
158#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
159pub struct Version {
160    pub major: u8,
161    pub minor: u8,
162    pub patch: u8,
163}
164
165impl Version {
166    pub fn new(major: u8, minor: u8, patch: u8) -> Self {
167        Self {
168            major,
169            minor,
170            patch,
171        }
172    }
173
174    /// Check if this version is compatible with another.
175    /// Compatible means same major version.
176    pub fn is_compatible_with(&self, other: &Version) -> bool {
177        self.major == other.major
178    }
179
180    /// Check if this version can read files from another version.
181    /// We can always read older versions (backward compatible).
182    pub fn can_read(&self, file_version: &Version) -> bool {
183        self.major >= file_version.major
184    }
185}
186
187impl std::fmt::Display for Version {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
190    }
191}
192
193impl From<(u8, u8, u8)> for Version {
194    fn from((major, minor, patch): (u8, u8, u8)) -> Self {
195        Self {
196            major,
197            minor,
198            patch,
199        }
200    }
201}
202
203/// Sister status.
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
205#[serde(rename_all = "snake_case")]
206pub enum Status {
207    Starting,
208    Ready,
209    Busy,
210    Degraded,
211    ShuttingDown,
212    Error,
213}
214
215impl std::fmt::Display for Status {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        match self {
218            Self::Starting => write!(f, "starting"),
219            Self::Ready => write!(f, "ready"),
220            Self::Busy => write!(f, "busy"),
221            Self::Degraded => write!(f, "degraded"),
222            Self::ShuttingDown => write!(f, "shutting_down"),
223            Self::Error => write!(f, "error"),
224        }
225    }
226}
227
228/// Capability that a sister provides.
229#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
230pub struct Capability {
231    pub name: String,
232    pub description: String,
233}
234
235impl Capability {
236    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
237        Self {
238            name: name.into(),
239            description: description.into(),
240        }
241    }
242}
243
244/// Resource usage metrics.
245#[derive(Debug, Clone, Default, Serialize, Deserialize)]
246pub struct ResourceUsage {
247    pub memory_bytes: usize,
248    pub disk_bytes: usize,
249    pub open_handles: usize,
250}
251
252/// Health status returned by all sisters.
253#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct HealthStatus {
255    /// Is the sister operational?
256    pub healthy: bool,
257
258    /// Current status.
259    pub status: Status,
260
261    /// Time since initialization.
262    #[serde(with = "duration_serde")]
263    pub uptime: std::time::Duration,
264
265    /// Resource usage.
266    pub resources: ResourceUsage,
267
268    /// Any warnings (non-fatal issues).
269    pub warnings: Vec<String>,
270
271    /// Last error if any.
272    pub last_error: Option<String>,
273}
274
275impl Default for HealthStatus {
276    fn default() -> Self {
277        Self {
278            healthy: true,
279            status: Status::Ready,
280            uptime: std::time::Duration::ZERO,
281            resources: ResourceUsage::default(),
282            warnings: vec![],
283            last_error: None,
284        }
285    }
286}
287
288/// Generic metadata map.
289pub type Metadata = HashMap<String, serde_json::Value>;
290
291/// Unique identifier (UUID-based).
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
293pub struct UniqueId(pub Uuid);
294
295impl UniqueId {
296    pub fn new() -> Self {
297        Self(Uuid::new_v4())
298    }
299
300    pub fn from_uuid(uuid: Uuid) -> Self {
301        Self(uuid)
302    }
303
304    pub fn nil() -> Self {
305        Self(Uuid::nil())
306    }
307}
308
309impl Default for UniqueId {
310    fn default() -> Self {
311        Self::new()
312    }
313}
314
315impl std::fmt::Display for UniqueId {
316    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317        write!(f, "{}", self.0)
318    }
319}
320
321impl From<Uuid> for UniqueId {
322    fn from(uuid: Uuid) -> Self {
323        Self(uuid)
324    }
325}
326
327/// Timestamp wrapper for consistency.
328#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
329pub struct Timestamp(pub DateTime<Utc>);
330
331impl Timestamp {
332    pub fn now() -> Self {
333        Self(Utc::now())
334    }
335
336    pub fn from_datetime(dt: DateTime<Utc>) -> Self {
337        Self(dt)
338    }
339}
340
341impl Default for Timestamp {
342    fn default() -> Self {
343        Self::now()
344    }
345}
346
347impl std::fmt::Display for Timestamp {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        write!(f, "{}", self.0.to_rfc3339())
350    }
351}
352
353// Duration serialization helper
354mod duration_serde {
355    use serde::{Deserialize, Deserializer, Serialize, Serializer};
356    use std::time::Duration;
357
358    pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
359    where
360        S: Serializer,
361    {
362        duration.as_secs_f64().serialize(serializer)
363    }
364
365    pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
366    where
367        D: Deserializer<'de>,
368    {
369        let secs = f64::deserialize(deserializer)?;
370        Ok(Duration::from_secs_f64(secs))
371    }
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377
378    #[test]
379    fn test_sister_type_byte_roundtrip() {
380        for sister in [
381            SisterType::Memory,
382            SisterType::Vision,
383            SisterType::Codebase,
384            SisterType::Identity,
385        ] {
386            let byte = sister.to_byte();
387            let recovered = SisterType::from_byte(byte).unwrap();
388            assert_eq!(sister, recovered);
389        }
390    }
391
392    #[test]
393    fn test_version_compatibility() {
394        let v1 = Version::new(1, 0, 0);
395        let v1_1 = Version::new(1, 1, 0);
396        let v2 = Version::new(2, 0, 0);
397
398        assert!(v1.is_compatible_with(&v1_1));
399        assert!(!v1.is_compatible_with(&v2));
400        assert!(v2.can_read(&v1));
401        assert!(!v1.can_read(&v2));
402    }
403}