perspt-tui 0.6.2

Ratatui-based TUI for Perspt
Documentation
//! Telemetry module for real-time orchestrator updates
//!
//! Provides event types and channels for dashboard communication.

use tokio::sync::mpsc;

/// Telemetry event types from the orchestrator
#[derive(Debug, Clone)]
pub enum TelemetryEvent {
    /// Lyapunov Energy update for a node
    EnergyUpdate {
        node_id: String,
        energy: f32,
        components: EnergyComponents,
    },
    /// Task status change
    TaskStatusChange {
        node_id: String,
        status: TaskStatus,
        goal: String,
    },
    /// Token usage update
    TokensUsed { count: usize, total: usize },
    /// Log message for activity feed
    Log(LogEntry),
    /// Session state change
    SessionState(SessionState),
}

/// Energy components for detailed display
#[derive(Debug, Clone, Default)]
pub struct EnergyComponents {
    /// Syntactic energy (LSP diagnostics)
    pub v_syn: f32,
    /// Structural energy (contract violations)
    pub v_str: f32,
    /// Logic energy (test failures)
    pub v_log: f32,
    /// Bootstrap energy
    pub v_boot: f32,
    /// Sheaf validation energy
    pub v_sheaf: f32,
    /// Total composite energy
    pub total: f32,
}

impl EnergyComponents {
    /// Build from the five PSP-8 component rollups. The rollups already carry the
    /// squared, weighted residual energy (`V_comp = Σ_{e∈comp} w_e‖r_e‖²`), so the
    /// total is their plain sum — there is no separate `α/β/γ` aggregation pass.
    pub fn new(v_syn: f32, v_str: f32, v_log: f32, v_boot: f32, v_sheaf: f32) -> Self {
        Self {
            v_syn,
            v_str,
            v_log,
            v_boot,
            v_sheaf,
            total: v_syn + v_str + v_log + v_boot + v_sheaf,
        }
    }

    pub fn is_stable(&self, epsilon: f32) -> bool {
        self.total < epsilon
    }
}

/// Task status enum
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskStatus {
    Pending,
    Running,
    Verifying,
    Completed,
    Failed,
    Escalated,
}

impl TaskStatus {
    pub fn icon(&self) -> &'static str {
        match self {
            TaskStatus::Pending => "",
            TaskStatus::Running => "",
            TaskStatus::Verifying => "",
            TaskStatus::Completed => "",
            TaskStatus::Failed => "",
            TaskStatus::Escalated => "",
        }
    }

    pub fn as_str(&self) -> &'static str {
        match self {
            TaskStatus::Pending => "pending",
            TaskStatus::Running => "running",
            TaskStatus::Verifying => "verifying",
            TaskStatus::Completed => "completed",
            TaskStatus::Failed => "failed",
            TaskStatus::Escalated => "escalated",
        }
    }
}

/// Log entry for activity feed
#[derive(Debug, Clone)]
pub struct LogEntry {
    pub level: LogLevel,
    pub message: String,
    pub timestamp: std::time::Instant,
}

impl LogEntry {
    pub fn info(message: impl Into<String>) -> Self {
        Self {
            level: LogLevel::Info,
            message: message.into(),
            timestamp: std::time::Instant::now(),
        }
    }

    pub fn warning(message: impl Into<String>) -> Self {
        Self {
            level: LogLevel::Warning,
            message: message.into(),
            timestamp: std::time::Instant::now(),
        }
    }

    pub fn error(message: impl Into<String>) -> Self {
        Self {
            level: LogLevel::Error,
            message: message.into(),
            timestamp: std::time::Instant::now(),
        }
    }

    pub fn success(message: impl Into<String>) -> Self {
        Self {
            level: LogLevel::Success,
            message: message.into(),
            timestamp: std::time::Instant::now(),
        }
    }
}

/// Log level
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
    Info,
    Warning,
    Error,
    Success,
}

/// Session state
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SessionState {
    Planning,
    Executing,
    Paused,
    Completed,
    Failed,
}

/// Type alias for telemetry sender
pub type TelemetrySender = mpsc::UnboundedSender<TelemetryEvent>;

/// Type alias for telemetry receiver
pub type TelemetryReceiver = mpsc::UnboundedReceiver<TelemetryEvent>;

/// Create a new telemetry channel
pub fn create_telemetry_channel() -> (TelemetrySender, TelemetryReceiver) {
    mpsc::unbounded_channel()
}