1pub mod branch;
2pub mod causality;
3pub mod checkpoint;
4pub mod conflict;
5pub mod consolidate;
6pub mod current_fact_resolver;
7pub mod event_builder;
8pub mod evidence;
9pub mod experience;
10pub mod forget;
11pub mod lifecycle;
12pub mod maturity;
13pub mod merge;
14pub mod orientation_cache;
15pub mod poisoning;
16pub mod recall;
17pub mod reflection;
18pub mod remember;
19pub mod replay;
20pub mod retained;
21pub mod retrieval;
22pub mod share;
23
24use std::sync::Arc;
25
26use crate::cache::MemoryCache;
27use crate::embedding::EmbeddingProvider;
28use crate::encryption::ContentEncryption;
29use crate::error::{Error, Result};
30use crate::index::VectorIndex;
31use crate::search::FullTextIndex;
32use crate::storage::StorageBackend;
33use crate::storage::cold::ColdStorage;
34
35const MAX_AGENT_ID_LEN: usize = 256;
36
37pub const MAX_BATCH_QUERY_LIMIT: usize = 10_000;
40
41pub fn validate_agent_id(agent_id: &str) -> Result<()> {
43 if agent_id.is_empty() {
44 return Err(Error::Validation("agent_id cannot be empty".into()));
45 }
46 if agent_id.len() > MAX_AGENT_ID_LEN {
47 return Err(Error::Validation(format!(
48 "agent_id exceeds max length of {MAX_AGENT_ID_LEN}"
49 )));
50 }
51 if !agent_id
52 .chars()
53 .all(|c| c.is_alphanumeric() || c == '-' || c == '_' || c == '.')
54 {
55 return Err(Error::Validation(
56 "agent_id must contain only alphanumeric characters, hyphens, underscores, or dots"
57 .into(),
58 ));
59 }
60 Ok(())
61}
62
63pub struct MnemoEngine {
64 pub storage: Arc<dyn StorageBackend>,
65 pub index: Arc<dyn VectorIndex>,
66 pub embedding: Arc<dyn EmbeddingProvider>,
67 pub full_text: Option<Arc<dyn FullTextIndex>>,
68 pub default_agent_id: String,
69 pub default_org_id: Option<String>,
70 pub encryption: Option<Arc<ContentEncryption>>,
71 pub cold_storage: Option<Arc<dyn ColdStorage>>,
72 pub cache: Option<Arc<MemoryCache>>,
73 pub embed_events: bool,
74 pub ttl_working_seconds: u64,
77 pub procedural_importance_floor: f32,
80 pub poisoning_policy: poisoning::PoisoningPolicy,
84 pub provenance_signer: Option<Arc<crate::provenance::ProvenanceSigner>>,
90 pub orientation_cache_store: Option<Arc<orientation_cache::OrientationCacheStore>>,
98 pub consolidation_policy: maturity::ConsolidationPolicy,
108 pub evidence_scorer: Option<Arc<dyn evidence::EvidenceScorer>>,
117 pub experience_memory_enabled: bool,
125}
126
127pub const DEFAULT_TTL_WORKING_SECONDS: u64 = 3600;
129
130pub const DEFAULT_PROCEDURAL_IMPORTANCE_FLOOR: f32 = 0.8;
132
133impl MnemoEngine {
134 pub fn new(
135 storage: Arc<dyn StorageBackend>,
136 index: Arc<dyn VectorIndex>,
137 embedding: Arc<dyn EmbeddingProvider>,
138 default_agent_id: String,
139 default_org_id: Option<String>,
140 ) -> Self {
141 Self {
142 storage,
143 index,
144 embedding,
145 full_text: None,
146 default_agent_id,
147 default_org_id,
148 encryption: None,
149 cold_storage: None,
150 cache: None,
151 embed_events: false,
152 ttl_working_seconds: DEFAULT_TTL_WORKING_SECONDS,
153 procedural_importance_floor: DEFAULT_PROCEDURAL_IMPORTANCE_FLOOR,
154 poisoning_policy: poisoning::PoisoningPolicy::default(),
155 provenance_signer: None,
156 orientation_cache_store: None,
157 consolidation_policy: maturity::ConsolidationPolicy::default(),
158 evidence_scorer: None,
159 experience_memory_enabled: false,
160 }
161 }
162
163 pub fn with_provenance_signer(
167 mut self,
168 signer: Arc<crate::provenance::ProvenanceSigner>,
169 ) -> Self {
170 self.provenance_signer = Some(signer);
171 self
172 }
173
174 pub fn with_poisoning_policy(mut self, policy: poisoning::PoisoningPolicy) -> Self {
178 self.poisoning_policy = policy;
179 self
180 }
181
182 pub fn with_ttl_working_seconds(mut self, seconds: u64) -> Self {
185 self.ttl_working_seconds = seconds;
186 self
187 }
188
189 pub fn with_procedural_importance_floor(mut self, floor: f32) -> Self {
192 self.procedural_importance_floor = floor.clamp(0.0, 1.0);
193 self
194 }
195
196 pub fn with_full_text(mut self, ft: Arc<dyn FullTextIndex>) -> Self {
197 self.full_text = Some(ft);
198 self
199 }
200
201 pub fn with_encryption(mut self, enc: Arc<ContentEncryption>) -> Self {
202 self.encryption = Some(enc);
203 self
204 }
205
206 pub fn with_cold_storage(mut self, cs: Arc<dyn ColdStorage>) -> Self {
207 self.cold_storage = Some(cs);
208 self
209 }
210
211 pub fn with_cache(mut self, c: Arc<MemoryCache>) -> Self {
212 self.cache = Some(c);
213 self
214 }
215
216 pub fn with_event_embeddings(mut self) -> Self {
217 self.embed_events = true;
218 self
219 }
220
221 pub fn with_orientation_cache_store(
229 mut self,
230 store: Arc<orientation_cache::OrientationCacheStore>,
231 ) -> Self {
232 self.orientation_cache_store = Some(store);
233 self
234 }
235
236 pub fn with_consolidation_policy(mut self, policy: maturity::ConsolidationPolicy) -> Self {
242 self.consolidation_policy = policy;
243 self
244 }
245
246 pub fn with_evidence_scorer(mut self, scorer: Arc<dyn evidence::EvidenceScorer>) -> Self {
253 self.evidence_scorer = Some(scorer);
254 self
255 }
256
257 pub fn with_experience_memory(mut self) -> Self {
264 self.experience_memory_enabled = true;
265 self
266 }
267
268 pub async fn remember(
269 &self,
270 request: remember::RememberRequest,
271 ) -> Result<remember::RememberResponse> {
272 remember::execute(self, request).await
273 }
274
275 pub async fn recall(&self, request: recall::RecallRequest) -> Result<recall::RecallResponse> {
276 recall::execute(self, request).await
277 }
278
279 pub async fn forget(&self, request: forget::ForgetRequest) -> Result<forget::ForgetResponse> {
280 forget::execute(self, request).await
281 }
282
283 pub async fn forget_subject(
286 &self,
287 request: forget::ForgetSubjectRequest,
288 ) -> Result<forget::ForgetSubjectResponse> {
289 forget::forget_subject(self, request).await
290 }
291
292 pub async fn run_ttl_sweep(&self) -> Result<lifecycle::TtlReport> {
295 lifecycle::run_ttl_sweep(self).await
296 }
297
298 pub async fn run_reflection_pass(
302 &self,
303 agent_id: Option<String>,
304 ) -> Result<reflection::ReflectionReport> {
305 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
306 reflection::run_reflection_pass(self, &agent_id).await
307 }
308
309 pub async fn run_reflection_pass_with_mode(
312 &self,
313 agent_id: Option<String>,
314 mode: reflection::ReflectionMode,
315 force: bool,
316 ) -> Result<reflection::ReflectionReport> {
317 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
318 reflection::run_reflection_pass_with_mode(self, &agent_id, mode, force).await
319 }
320
321 pub async fn replay_quarantine(
324 &self,
325 agent_id: Option<String>,
326 since: Option<&str>,
327 ) -> Result<Vec<poisoning::QuarantineReplayEntry>> {
328 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
329 poisoning::replay_quarantine(self, &agent_id, since).await
330 }
331
332 pub async fn share(&self, request: share::ShareRequest) -> Result<share::ShareResponse> {
333 share::execute(self, request).await
334 }
335
336 pub async fn checkpoint(
337 &self,
338 request: checkpoint::CheckpointRequest,
339 ) -> Result<checkpoint::CheckpointResponse> {
340 checkpoint::execute(self, request).await
341 }
342
343 pub async fn branch(&self, request: branch::BranchRequest) -> Result<branch::BranchResponse> {
344 branch::execute(self, request).await
345 }
346
347 pub async fn merge(&self, request: merge::MergeRequest) -> Result<merge::MergeResponse> {
348 merge::execute(self, request).await
349 }
350
351 pub async fn replay(&self, request: replay::ReplayRequest) -> Result<replay::ReplayResponse> {
352 replay::execute(self, request).await
353 }
354
355 pub async fn consolidate(
359 &self,
360 request: consolidate::ConsolidateRequest,
361 ) -> Result<consolidate::ConsolidateResponse> {
362 consolidate::execute(self, request).await
363 }
364
365 pub async fn remember_plan(
371 &self,
372 request: experience::RememberPlanRequest,
373 ) -> Result<experience::RememberPlanResponse> {
374 experience::execute_remember_plan(self, request).await
375 }
376
377 pub async fn recall_plan(
382 &self,
383 request: experience::RecallPlanRequest,
384 ) -> Result<experience::RecallPlanResponse> {
385 experience::execute_recall_plan(self, request).await
386 }
387
388 pub async fn run_decay_pass(
389 &self,
390 agent_id: Option<String>,
391 archive_threshold: f32,
392 forget_threshold: f32,
393 ) -> Result<lifecycle::DecayPassResult> {
394 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
395 lifecycle::run_decay_pass(self, &agent_id, archive_threshold, forget_threshold).await
396 }
397
398 pub async fn run_consolidation(
399 &self,
400 agent_id: Option<String>,
401 min_cluster_size: usize,
402 ) -> Result<lifecycle::ConsolidationResult> {
403 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
404 lifecycle::run_consolidation(self, &agent_id, min_cluster_size).await
405 }
406
407 pub async fn verify_integrity(
408 &self,
409 agent_id: Option<String>,
410 thread_id: Option<&str>,
411 ) -> Result<crate::hash::ChainVerificationResult> {
412 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
413 let records = self
414 .storage
415 .list_memories_by_agent_ordered(&agent_id, thread_id, 10000)
416 .await?;
417 Ok(crate::hash::verify_chain(&records))
418 }
419
420 pub async fn trace_causality(
421 &self,
422 event_id: uuid::Uuid,
423 max_depth: usize,
424 ) -> Result<causality::CausalChain> {
425 causality::trace_causality(
426 self,
427 event_id,
428 max_depth,
429 causality::TraceDirection::Down,
430 None,
431 )
432 .await
433 }
434
435 pub async fn trace_causality_with_options(
436 &self,
437 event_id: uuid::Uuid,
438 max_depth: usize,
439 direction: causality::TraceDirection,
440 event_type_filter: Option<crate::model::event::EventType>,
441 ) -> Result<causality::CausalChain> {
442 causality::trace_causality(self, event_id, max_depth, direction, event_type_filter).await
443 }
444
445 pub async fn verify_event_integrity(
446 &self,
447 agent_id: Option<String>,
448 thread_id: Option<&str>,
449 ) -> Result<crate::hash::ChainVerificationResult> {
450 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
451 let events = if let Some(tid) = thread_id {
452 self.storage.get_events_by_thread(tid, 10000).await?
453 } else {
454 let mut evts = self.storage.list_events(&agent_id, 10000, 0).await?;
456 evts.reverse();
457 evts
458 };
459 Ok(crate::hash::verify_event_chain(&events))
460 }
461
462 pub async fn detect_conflicts(
463 &self,
464 agent_id: Option<String>,
465 threshold: f32,
466 ) -> Result<conflict::ConflictDetectionResult> {
467 let agent_id = agent_id.unwrap_or_else(|| self.default_agent_id.clone());
468 conflict::detect_conflicts(self, &agent_id, threshold).await
469 }
470
471 pub async fn resolve_conflict(
472 &self,
473 conflict_pair: &conflict::ConflictPair,
474 strategy: conflict::ResolutionStrategy,
475 ) -> Result<()> {
476 conflict::resolve_conflict(self, conflict_pair, strategy).await
477 }
478}