use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Permission {
Read,
Write,
Admin,
}
impl Permission {
pub fn can_read(&self) -> bool {
matches!(self, Permission::Read | Permission::Write | Permission::Admin)
}
pub fn can_write(&self) -> bool {
matches!(self, Permission::Write | Permission::Admin)
}
pub fn is_admin(&self) -> bool {
matches!(self, Permission::Admin)
}
}
impl fmt::Display for Permission {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Permission::Read => write!(f, "read"),
Permission::Write => write!(f, "write"),
Permission::Admin => write!(f, "admin"),
}
}
}
impl FromStr for Permission {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"read" => Ok(Permission::Read),
"write" => Ok(Permission::Write),
"admin" => Ok(Permission::Admin),
_ => Err(format!("unknown permission: {}", s)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AclEntry {
pub agent_id: String,
pub namespace: String,
pub permission: Permission,
pub granted_by: String,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MemoryType {
Factual,
Episodic,
Relational,
Emotional,
Procedural,
Opinion,
Causal,
}
impl MemoryType {
pub fn default_importance(&self) -> f64 {
match self {
MemoryType::Factual => 0.3,
MemoryType::Episodic => 0.4,
MemoryType::Relational => 0.6,
MemoryType::Emotional => 0.9,
MemoryType::Procedural => 0.5,
MemoryType::Opinion => 0.3,
MemoryType::Causal => 0.7,
}
}
pub fn default_decay_rate(&self) -> f64 {
match self {
MemoryType::Factual => 0.03,
MemoryType::Episodic => 0.10,
MemoryType::Relational => 0.02,
MemoryType::Emotional => 0.01,
MemoryType::Procedural => 0.01,
MemoryType::Opinion => 0.05,
MemoryType::Causal => 0.02,
}
}
}
impl fmt::Display for MemoryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryType::Factual => write!(f, "factual"),
MemoryType::Episodic => write!(f, "episodic"),
MemoryType::Relational => write!(f, "relational"),
MemoryType::Emotional => write!(f, "emotional"),
MemoryType::Procedural => write!(f, "procedural"),
MemoryType::Opinion => write!(f, "opinion"),
MemoryType::Causal => write!(f, "causal"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MemoryLayer {
Core,
Working,
Archive,
}
impl fmt::Display for MemoryLayer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryLayer::Core => write!(f, "core"),
MemoryLayer::Working => write!(f, "working"),
MemoryLayer::Archive => write!(f, "archive"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryRecord {
pub id: String,
pub content: String,
pub memory_type: MemoryType,
pub layer: MemoryLayer,
pub created_at: DateTime<Utc>,
pub access_times: Vec<DateTime<Utc>>,
pub working_strength: f64,
pub core_strength: f64,
pub importance: f64,
pub pinned: bool,
pub consolidation_count: i32,
pub last_consolidated: Option<DateTime<Utc>>,
pub source: String,
pub contradicts: Option<String>,
pub contradicted_by: Option<String>,
pub superseded_by: Option<String>,
pub metadata: Option<serde_json::Value>,
}
impl MemoryRecord {
pub fn age_hours(&self) -> f64 {
let now = Utc::now();
(now - self.created_at).num_seconds() as f64 / 3600.0
}
pub fn age_days(&self) -> f64 {
self.age_hours() / 24.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecallResult {
pub record: MemoryRecord,
pub activation: f64,
pub confidence: f64,
pub confidence_label: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryStats {
pub total_memories: usize,
pub by_type: std::collections::HashMap<String, TypeStats>,
pub by_layer: std::collections::HashMap<String, LayerStats>,
pub pinned: usize,
pub uptime_hours: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeStats {
pub count: usize,
pub avg_strength: f64,
pub avg_importance: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayerStats {
pub count: usize,
pub avg_working: f64,
pub avg_core: f64,
}
#[derive(Debug, Clone)]
pub struct MergeOutcome {
pub memory_id: String,
pub content_updated: bool,
pub merge_count: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossLink {
pub source_id: String,
pub source_ns: String,
pub target_id: String,
pub target_ns: String,
pub strength: f64,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HebbianLink {
pub source_id: String,
pub target_id: String,
pub strength: f64,
pub coactivation_count: i32,
pub direction: String,
pub created_at: DateTime<Utc>,
pub source_ns: Option<String>,
pub target_ns: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct RecallWithAssociationsResult {
pub memories: Vec<RecallResult>,
pub cross_links: Vec<CrossLink>,
}
#[derive(Debug, thiserror::Error)]
pub enum SupersessionError {
#[error("Memory not found: {0}")]
NotFound(String),
#[error("Cannot supersede a memory with itself: {0}")]
SelfSupersession(String),
#[error("Cross-namespace supersession not allowed: {old_ns} → {new_ns}")]
CrossNamespace { old_ns: String, new_ns: String },
#[error("Bulk supersession failed — invalid IDs: {0:?}")]
InvalidIds(Vec<String>),
#[error("Database error: {0}")]
Db(#[from] rusqlite::Error),
}
#[derive(Debug, Clone)]
pub struct SupersessionInfo {
pub superseded: MemoryRecord,
pub superseded_by_id: String,
pub chain_head: Option<String>,
}
#[derive(Debug, Clone)]
pub struct BulkCorrectionResult {
pub new_id: String,
pub superseded_count: usize,
pub superseded_ids: Vec<String>,
}