use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::time::Instant;
use tokio::sync::RwLock;
use lean_ctx::core::cache::SessionCache;
use rmcp::service::{Peer, RoleServer};
pub(super) struct CepComputedStats {
pub(super) cep_score: u32,
pub(super) cache_util: u32,
pub(super) mode_diversity: u32,
pub(super) compression_rate: u32,
pub(super) total_original: u64,
pub(super) total_compressed: u64,
pub(super) total_saved: u64,
pub(super) mode_counts: std::collections::HashMap<String, u64>,
pub(super) complexity: String,
pub(super) cache_hits: u64,
pub(super) total_reads: u64,
pub(super) tool_call_count: u64,
}
pub use lean_ctx::core::protocol::CrpMode;
pub type SharedCache = Arc<RwLock<SessionCache>>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SessionMode {
Personal,
Shared,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum InteractionMode {
Agent = 0,
Plan = 1,
}
impl InteractionMode {
pub fn from_u8(v: u8) -> Self {
match v {
1 => Self::Plan,
_ => Self::Agent,
}
}
pub fn from_meta_str(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"plan" | "readonly" | "read-only" => Some(Self::Plan),
"agent" | "normal" | "edit" | "act" => Some(Self::Agent),
_ => None,
}
}
}
#[derive(Clone)]
pub struct LeanCtxServer {
pub cache: SharedCache,
pub session: lean_ctx::core::session::SessionSlot,
pub tool_calls: Arc<RwLock<Vec<ToolCallRecord>>>,
pub call_count: Arc<AtomicUsize>,
pub background_tick: Arc<AtomicUsize>,
pub cache_ttl_secs: u64,
pub last_call: Arc<RwLock<Instant>>,
pub agent_id: Arc<RwLock<Option<String>>>,
pub task_envelope: Arc<RwLock<Option<lean_ctx_protocol::TaskEnvelopeV1>>>,
pub native_receipt_authority:
Option<Arc<lean_ctx::core::execution_ledger::host::HostReceiptAuthority>>,
pub presence_agent_id: Arc<RwLock<Option<String>>>,
pub(crate) presence_init: Arc<tokio::sync::Mutex<()>>,
pub(crate) presence_role: Arc<RwLock<Option<String>>>,
pub client_name: Arc<RwLock<String>>,
pub autonomy: Arc<lean_ctx::core::autonomy::AutonomyState>,
pub loop_detector: Arc<RwLock<lean_ctx::core::loop_detection::LoopDetector>>,
pub workflow: Arc<RwLock<Option<lean_ctx::core::workflow::WorkflowRun>>>,
pub ledger: Arc<RwLock<lean_ctx::core::context_ledger::ContextLedger>>,
pub pipeline_stats: Arc<RwLock<lean_ctx::core::pipeline::PipelineStats>>,
pub session_mode: SessionMode,
pub workspace_id: String,
pub channel_id: String,
pub context_os: Option<Arc<lean_ctx::core::context_os::ContextOsRuntime>>,
pub context_ir: Option<Arc<RwLock<lean_ctx::core::context_ir::ContextIrV1>>>,
pub registry: Option<Arc<crate::server::registry::ToolRegistry>>,
pub(crate) rules_stale_checked: Arc<std::sync::atomic::AtomicBool>,
pub(crate) rules_tip_shown: Arc<std::sync::atomic::AtomicBool>,
pub(crate) last_seen_event_id: Arc<std::sync::atomic::AtomicI64>,
pub(crate) startup_project_root: Option<String>,
pub(crate) startup_shell_cwd: Option<String>,
pub(crate) peer: Arc<RwLock<Option<Peer<RoleServer>>>>,
pub(crate) shell_approvals: Arc<crate::server::shell_approval::ShellApprovalState>,
pub(crate) has_client_roots: Arc<std::sync::atomic::AtomicBool>,
pub(crate) roots_resolved: Arc<std::sync::atomic::AtomicBool>,
pub(crate) roots_list_attempts: Arc<std::sync::atomic::AtomicU32>,
pub(crate) bm25_cache:
Arc<std::sync::Mutex<Option<lean_ctx::core::bm25_cache::Bm25CacheEntry>>>,
pub(crate) _eviction_target: Arc<lean_ctx::core::eviction_orchestrator::EvictionOrchestrator>,
pub(crate) progress_sender: crate::server::progress::SharedProgressSender,
pub(crate) last_tools_config_hash: Arc<std::sync::atomic::AtomicU64>,
pub(crate) interaction_mode: Arc<std::sync::atomic::AtomicU8>,
}
pub use lean_ctx::core::protocol::ToolCallRecord;
#[cfg(test)]
mod tests {
use super::InteractionMode;
#[test]
fn from_u8_round_trips() {
assert_eq!(InteractionMode::from_u8(0), InteractionMode::Agent);
assert_eq!(InteractionMode::from_u8(1), InteractionMode::Plan);
assert_eq!(InteractionMode::from_u8(42), InteractionMode::Agent);
}
#[test]
fn from_meta_str_plan_variants() {
for s in ["plan", "Plan", "PLAN", "readonly", "read-only"] {
assert_eq!(
InteractionMode::from_meta_str(s),
Some(InteractionMode::Plan),
"failed for: {s}"
);
}
}
#[test]
fn from_meta_str_agent_variants() {
for s in ["agent", "Agent", "normal", "edit", "act"] {
assert_eq!(
InteractionMode::from_meta_str(s),
Some(InteractionMode::Agent),
"failed for: {s}"
);
}
}
#[test]
fn from_meta_str_unknown_returns_none() {
assert_eq!(InteractionMode::from_meta_str("debug"), None);
assert_eq!(InteractionMode::from_meta_str(""), None);
}
#[test]
fn default_mode_is_agent() {
let mode = std::sync::atomic::AtomicU8::new(InteractionMode::Agent as u8);
assert_eq!(
InteractionMode::from_u8(mode.load(std::sync::atomic::Ordering::Relaxed)),
InteractionMode::Agent,
);
}
}