funera-core 0.2.2

Core LLM agent engine — ReAct loop, providers, tools, skills, middleware, security
Documentation
use chrono::Utc;
use serde::Serialize;
use tokio::sync::broadcast;

#[derive(Debug, Clone, Serialize)]
pub enum AuditEvent {
    ToolExecuted {
        tool_name: String,
        duration_ms: u64,
        success: bool,
        error: Option<String>,
        timestamp: i64,
    },
    ToolDenied {
        tool_name: String,
        reason: String,
        timestamp: i64,
    },
    PolicyViolated {
        detail: String,
        timestamp: i64,
    },
    SandboxApplied {
        tool_name: String,
        policy_summary: String,
        timestamp: i64,
    },
    SandboxSkipped {
        tool_name: String,
        timestamp: i64,
    },
}

impl AuditEvent {
    pub fn tool_executed(
        tool_name: &str,
        duration_ms: u64,
        success: bool,
        error: Option<String>,
    ) -> Self {
        Self::ToolExecuted {
            tool_name: tool_name.to_string(),
            duration_ms,
            success,
            error,
            timestamp: Utc::now().timestamp(),
        }
    }

    pub fn tool_denied(tool_name: &str, reason: String) -> Self {
        Self::ToolDenied {
            tool_name: tool_name.to_string(),
            reason,
            timestamp: Utc::now().timestamp(),
        }
    }

    pub fn policy_violated(detail: String) -> Self {
        Self::PolicyViolated {
            detail,
            timestamp: Utc::now().timestamp(),
        }
    }

    pub fn sandbox_applied(tool_name: &str, policy_summary: &str) -> Self {
        Self::SandboxApplied {
            tool_name: tool_name.to_string(),
            policy_summary: policy_summary.to_string(),
            timestamp: Utc::now().timestamp(),
        }
    }

    pub fn sandbox_skipped(tool_name: &str) -> Self {
        Self::SandboxSkipped {
            tool_name: tool_name.to_string(),
            timestamp: Utc::now().timestamp(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct AuditBus {
    tx: broadcast::Sender<AuditEvent>,
}

impl AuditBus {
    pub fn new(capacity: usize) -> Self {
        let (tx, _) = broadcast::channel(capacity);
        Self { tx }
    }

    pub fn report(&self, event: AuditEvent) {
        let _ = self.tx.send(event);
    }

    pub fn subscribe(&self) -> broadcast::Receiver<AuditEvent> {
        self.tx.subscribe()
    }

    pub fn sender(&self) -> broadcast::Sender<AuditEvent> {
        self.tx.clone()
    }
}

impl Default for AuditBus {
    fn default() -> Self {
        Self::new(1024)
    }
}