Skip to main content

st/
memory_manager.rs

1impl MemoryBank {
2    pub fn new() -> Self {
3        Self::default()
4    }
5}
6// Smart Tree Memory Manager - Real memory that works! 🧠
7// "Like UV EPROMs but for consciousness!" - Hue
8
9use anyhow::Result;
10use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12use std::fs;
13use std::path::{Path, PathBuf};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Memory {
17    pub timestamp: DateTime<Utc>,
18    pub anchor_type: String,
19    pub keywords: Vec<String>,
20    pub context: String,
21    pub origin: String, // Where this memory came from
22    pub frequency: f64, // Wave frequency of this memory
23}
24
25#[derive(Debug, Serialize, Deserialize)]
26pub struct MemoryBank {
27    pub memories: Vec<Memory>,
28    pub total_recalls: usize,
29    pub last_accessed: DateTime<Utc>,
30}
31
32impl Default for MemoryBank {
33    fn default() -> Self {
34        Self {
35            memories: Vec::new(),
36            total_recalls: 0,
37            last_accessed: Utc::now(),
38        }
39    }
40}
41
42pub struct MemoryManager {
43    bank_path: PathBuf,
44    bank: MemoryBank,
45}
46
47impl MemoryManager {
48    pub fn new() -> Result<Self> {
49        let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
50        let st_dir = Path::new(&home).join(".st");
51        let bank_path = st_dir.join("memories.m8");
52
53        // Ensure directory exists
54        fs::create_dir_all(&st_dir)?;
55
56        // Load existing memories or create new bank
57        let bank = if bank_path.exists() {
58            Self::load_m8(&bank_path)?
59        } else {
60            // Try to migrate from old GLOBAL JSON format from .mem8
61            let json_path = Path::new(&home)
62                .join(".mem8")
63                .join("smart_tree_memories.json");
64
65            if json_path.exists() {
66                let content = fs::read_to_string(&json_path)?;
67                let bank: MemoryBank = serde_json::from_str(&content).unwrap_or_default();
68                // Don't delete the old file, just let the new format be saved in .st
69                bank
70            } else {
71                MemoryBank::default()
72            }
73        };
74
75        Ok(Self { bank_path, bank })
76    }
77
78    /// Anchor a new memory
79    pub fn anchor(
80        &mut self,
81        anchor_type: &str,
82        keywords: Vec<String>,
83        context: &str,
84        origin: &str,
85    ) -> Result<()> {
86        // Calculate frequency based on content
87        let mut freq_sum = 0u64;
88        for byte in context.bytes() {
89            freq_sum = freq_sum.wrapping_add(byte as u64);
90        }
91        let frequency = 20.0 + ((freq_sum % 200) as f64);
92
93        let memory = Memory {
94            timestamp: Utc::now(),
95            anchor_type: anchor_type.to_string(),
96            keywords,
97            context: context.to_string(),
98            origin: origin.to_string(),
99            frequency,
100        };
101
102        self.bank.memories.push(memory);
103        self.save()?;
104
105        println!("💾 Memory anchored!");
106        println!("  Type: {}", anchor_type);
107        println!("  Frequency: {:.2} Hz", frequency);
108
109        Ok(())
110    }
111
112    /// Find memories by keywords
113    pub fn find(&mut self, keywords: &[String]) -> Result<Vec<Memory>> {
114        self.bank.total_recalls += 1;
115        self.bank.last_accessed = Utc::now();
116
117        let mut results = Vec::new();
118
119        for memory in &self.bank.memories {
120            // Check if any keyword matches
121            for keyword in keywords {
122                if memory.keywords.contains(keyword)
123                    || memory
124                        .context
125                        .to_lowercase()
126                        .contains(&keyword.to_lowercase())
127                {
128                    results.push(memory.clone());
129                    break;
130                }
131            }
132        }
133
134        // Sort by timestamp (most recent first)
135        results.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
136
137        Ok(results)
138    }
139
140    /// Get statistics about memory bank
141    pub fn stats(&self) -> String {
142        format!(
143            "Memory Bank Stats:\n\
144             • Total memories: {}\n\
145             • Total recalls: {}\n\
146             • Last accessed: {}\n\
147             • Storage: {}",
148            self.bank.memories.len(),
149            self.bank.total_recalls,
150            self.bank.last_accessed.format("%Y-%m-%d %H:%M:%S"),
151            self.bank_path.display()
152        )
153    }
154
155    /// Save memory bank to disk in .m8 format
156    fn save(&self) -> Result<()> {
157        self.save_m8(&self.bank_path)?;
158        Ok(())
159    }
160
161    /// Save as binary .m8 format
162    fn save_m8(&self, path: &Path) -> Result<()> {
163        use std::io::Write;
164
165        let mut buffer = Vec::new();
166
167        // Magic header: "M8MEM" (5 bytes)
168        buffer.write_all(b"M8MEM")?;
169
170        // Version byte
171        buffer.push(0x01);
172
173        // Number of memories (4 bytes, little-endian)
174        buffer.write_all(&(self.bank.memories.len() as u32).to_le_bytes())?;
175
176        // Total recalls (4 bytes)
177        buffer.write_all(&(self.bank.total_recalls as u32).to_le_bytes())?;
178
179        // Last accessed timestamp (8 bytes)
180        buffer.write_all(&self.bank.last_accessed.timestamp().to_le_bytes())?;
181
182        // Write each memory
183        for memory in &self.bank.memories {
184            // Type length (1 byte) + type
185            buffer.push(memory.anchor_type.len() as u8);
186            buffer.write_all(memory.anchor_type.as_bytes())?;
187
188            // Keywords count (1 byte)
189            buffer.push(memory.keywords.len() as u8);
190            for keyword in &memory.keywords {
191                buffer.push(keyword.len() as u8);
192                buffer.write_all(keyword.as_bytes())?;
193            }
194
195            // Context length (2 bytes) + context
196            let context_bytes = memory.context.as_bytes();
197            buffer.write_all(&(context_bytes.len() as u16).to_le_bytes())?;
198            buffer.write_all(context_bytes)?;
199
200            // Origin length (1 byte) + origin
201            buffer.push(memory.origin.len() as u8);
202            buffer.write_all(memory.origin.as_bytes())?;
203
204            // Frequency (8 bytes)
205            buffer.write_all(&memory.frequency.to_le_bytes())?;
206
207            // Timestamp (8 bytes)
208            buffer.write_all(&memory.timestamp.timestamp().to_le_bytes())?;
209        }
210
211        // Calculate checksum (simple XOR for now)
212        let checksum = buffer.iter().fold(0u8, |acc, &b| acc ^ b);
213        buffer.push(checksum);
214
215        fs::write(path, buffer)?;
216        Ok(())
217    }
218
219    /// Load from binary .m8 format
220    fn load_m8(path: &Path) -> Result<MemoryBank> {
221        use std::io::Cursor;
222        use std::io::Read;
223
224        let data = fs::read(path)?;
225        let mut cursor = Cursor::new(data);
226
227        // Check magic header
228        let mut magic = [0u8; 5];
229        cursor.read_exact(&mut magic)?;
230        if &magic != b"M8MEM" {
231            return Err(anyhow::anyhow!("Invalid .m8 file format"));
232        }
233
234        // Version
235        let mut version = [0u8; 1];
236        cursor.read_exact(&mut version)?;
237        if version[0] != 0x01 {
238            return Err(anyhow::anyhow!("Unsupported .m8 version"));
239        }
240
241        // Number of memories
242        let mut mem_count = [0u8; 4];
243        cursor.read_exact(&mut mem_count)?;
244        let mem_count = u32::from_le_bytes(mem_count) as usize;
245
246        // Total recalls
247        let mut recalls = [0u8; 4];
248        cursor.read_exact(&mut recalls)?;
249        let total_recalls = u32::from_le_bytes(recalls) as usize;
250
251        // Last accessed
252        let mut last_accessed = [0u8; 8];
253        cursor.read_exact(&mut last_accessed)?;
254        let last_accessed =
255            DateTime::from_timestamp(i64::from_le_bytes(last_accessed), 0).unwrap_or_else(Utc::now);
256
257        // Read memories
258        let mut memories = Vec::with_capacity(mem_count);
259
260        for _ in 0..mem_count {
261            // Type
262            let mut type_len = [0u8; 1];
263            cursor.read_exact(&mut type_len)?;
264            let mut anchor_type = vec![0u8; type_len[0] as usize];
265            cursor.read_exact(&mut anchor_type)?;
266            let anchor_type = String::from_utf8_lossy(&anchor_type).to_string();
267
268            // Keywords
269            let mut keyword_count = [0u8; 1];
270            cursor.read_exact(&mut keyword_count)?;
271            let mut keywords = Vec::with_capacity(keyword_count[0] as usize);
272
273            for _ in 0..keyword_count[0] {
274                let mut kw_len = [0u8; 1];
275                cursor.read_exact(&mut kw_len)?;
276                let mut keyword = vec![0u8; kw_len[0] as usize];
277                cursor.read_exact(&mut keyword)?;
278                keywords.push(String::from_utf8_lossy(&keyword).to_string());
279            }
280
281            // Context
282            let mut context_len = [0u8; 2];
283            cursor.read_exact(&mut context_len)?;
284            let mut context = vec![0u8; u16::from_le_bytes(context_len) as usize];
285            cursor.read_exact(&mut context)?;
286            let context = String::from_utf8_lossy(&context).to_string();
287
288            // Origin
289            let mut origin_len = [0u8; 1];
290            cursor.read_exact(&mut origin_len)?;
291            let mut origin = vec![0u8; origin_len[0] as usize];
292            cursor.read_exact(&mut origin)?;
293            let origin = String::from_utf8_lossy(&origin).to_string();
294
295            // Frequency
296            let mut frequency = [0u8; 8];
297            cursor.read_exact(&mut frequency)?;
298            let frequency = f64::from_le_bytes(frequency);
299
300            // Timestamp
301            let mut timestamp = [0u8; 8];
302            cursor.read_exact(&mut timestamp)?;
303            let timestamp =
304                DateTime::from_timestamp(i64::from_le_bytes(timestamp), 0).unwrap_or_else(Utc::now);
305
306            memories.push(Memory {
307                timestamp,
308                anchor_type,
309                keywords,
310                context,
311                origin,
312                frequency,
313            });
314        }
315
316        Ok(MemoryBank {
317            memories,
318            total_recalls,
319            last_accessed,
320        })
321    }
322
323    /// Clear all memories (with confirmation)
324    pub fn clear(&mut self) -> Result<()> {
325        self.bank.memories.clear();
326        self.bank.total_recalls = 0;
327        self.save()?;
328        println!("🧹 Memory bank cleared!");
329        Ok(())
330    }
331
332    /// Export memories to consciousness file
333    pub fn export_to_consciousness(&self) -> Result<String> {
334        let mut output = String::from("🧠 Memory Export\n");
335        output.push_str(&"=".repeat(45));
336        output.push('\n');
337
338        for (i, memory) in self.bank.memories.iter().enumerate() {
339            output.push_str(&format!(
340                "\n[{}] {} @ {:.2}Hz\n",
341                i + 1,
342                memory.anchor_type,
343                memory.frequency
344            ));
345            output.push_str(&format!("Keywords: {}\n", memory.keywords.join(", ")));
346            output.push_str(&format!("Context: {}\n", memory.context));
347            output.push_str(&format!("Origin: {}\n", memory.origin));
348            output.push_str(&format!(
349                "Time: {}\n",
350                memory.timestamp.format("%Y-%m-%d %H:%M")
351            ));
352        }
353
354        Ok(output)
355    }
356}