1use chrono::{DateTime, Utc};
4use indexmap::IndexMap;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub enum MemoryScope {
11 Session,
12 User,
13 Agent,
14 Org,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct SoulProfile {
20 pub name: String,
21 pub mission: String,
22 pub preferences: IndexMap<String, String>,
23 pub updated_at: DateTime<Utc>,
24}
25
26impl Default for SoulProfile {
27 fn default() -> Self {
28 Self {
29 name: "Agent OS agent".to_owned(),
30 mission: "Run tool-mediated work safely and reproducibly".to_owned(),
31 preferences: IndexMap::new(),
32 updated_at: Utc::now(),
33 }
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct Observation {
40 pub observation_id: uuid::Uuid,
41 pub created_at: DateTime<Utc>,
42 pub text: String,
43 pub tags: Vec<String>,
44 pub provenance: Provenance,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Provenance {
50 pub event_start: u64,
52 pub event_end: u64,
54 pub files: Vec<FileProvenance>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct FileProvenance {
61 pub path: String,
62 pub sha256: String,
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn memory_scope_ordering() {
71 assert!(MemoryScope::Session < MemoryScope::User);
72 assert!(MemoryScope::User < MemoryScope::Agent);
73 assert!(MemoryScope::Agent < MemoryScope::Org);
74 }
75
76 #[test]
77 fn memory_scope_serde_roundtrip() {
78 for scope in [
79 MemoryScope::Session,
80 MemoryScope::User,
81 MemoryScope::Agent,
82 MemoryScope::Org,
83 ] {
84 let json = serde_json::to_string(&scope).unwrap();
85 let back: MemoryScope = serde_json::from_str(&json).unwrap();
86 assert_eq!(scope, back);
87 }
88 }
89
90 #[test]
91 fn soul_profile_default() {
92 let soul = SoulProfile::default();
93 assert_eq!(soul.name, "Agent OS agent");
94 assert!(soul.preferences.is_empty());
95 }
96}