1use std::sync::Arc;
3use std::sync::atomic::AtomicUsize;
4use std::time::Instant;
5use tokio::sync::RwLock;
6
7use lean_ctx::core::cache::SessionCache;
8use rmcp::service::{Peer, RoleServer};
9
10pub(super) struct CepComputedStats {
11 pub(super) cep_score: u32,
12 pub(super) cache_util: u32,
13 pub(super) mode_diversity: u32,
14 pub(super) compression_rate: u32,
15 pub(super) total_original: u64,
16 pub(super) total_compressed: u64,
17 pub(super) total_saved: u64,
18 pub(super) mode_counts: std::collections::HashMap<String, u64>,
19 pub(super) complexity: String,
20 pub(super) cache_hits: u64,
21 pub(super) total_reads: u64,
22 pub(super) tool_call_count: u64,
23}
24
25pub use lean_ctx::core::protocol::CrpMode;
26pub type SharedCache = Arc<RwLock<SessionCache>>;
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum SessionMode {
35 Personal,
37 Shared,
39}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45#[repr(u8)]
46pub enum InteractionMode {
47 Agent = 0,
48 Plan = 1,
49}
50
51impl InteractionMode {
52 pub fn from_u8(v: u8) -> Self {
53 match v {
54 1 => Self::Plan,
55 _ => Self::Agent,
56 }
57 }
58
59 pub fn from_meta_str(s: &str) -> Option<Self> {
62 match s.to_ascii_lowercase().as_str() {
63 "plan" | "readonly" | "read-only" => Some(Self::Plan),
64 "agent" | "normal" | "edit" | "act" => Some(Self::Agent),
65 _ => None,
66 }
67 }
68}
69
70#[derive(Clone)]
72pub struct LeanCtxServer {
73 pub cache: SharedCache,
74 pub session: lean_ctx::core::session::SessionSlot,
75 pub tool_calls: Arc<RwLock<Vec<ToolCallRecord>>>,
76 pub call_count: Arc<AtomicUsize>,
77 pub background_tick: Arc<AtomicUsize>,
86 pub cache_ttl_secs: u64,
87 pub last_call: Arc<RwLock<Instant>>,
88 pub agent_id: Arc<RwLock<Option<String>>>,
89 pub task_envelope: Arc<RwLock<Option<lean_ctx_protocol::TaskEnvelopeV1>>>,
90 pub native_receipt_authority:
92 Option<Arc<lean_ctx::core::execution_ledger::host::HostReceiptAuthority>>,
93 pub presence_agent_id: Arc<RwLock<Option<String>>>,
94 pub(crate) presence_init: Arc<tokio::sync::Mutex<()>>,
97 pub(crate) presence_role: Arc<RwLock<Option<String>>>,
102 pub client_name: Arc<RwLock<String>>,
103 pub autonomy: Arc<lean_ctx::core::autonomy::AutonomyState>,
104 pub loop_detector: Arc<RwLock<lean_ctx::core::loop_detection::LoopDetector>>,
105 pub workflow: Arc<RwLock<Option<lean_ctx::core::workflow::WorkflowRun>>>,
106 pub ledger: Arc<RwLock<lean_ctx::core::context_ledger::ContextLedger>>,
107 pub pipeline_stats: Arc<RwLock<lean_ctx::core::pipeline::PipelineStats>>,
108 pub session_mode: SessionMode,
109 pub workspace_id: String,
110 pub channel_id: String,
111 pub context_os: Option<Arc<lean_ctx::core::context_os::ContextOsRuntime>>,
112 pub context_ir: Option<Arc<RwLock<lean_ctx::core::context_ir::ContextIrV1>>>,
113 pub registry: Option<Arc<crate::server::registry::ToolRegistry>>,
114 pub(crate) rules_stale_checked: Arc<std::sync::atomic::AtomicBool>,
115 pub(crate) rules_tip_shown: Arc<std::sync::atomic::AtomicBool>,
116 pub(crate) last_seen_event_id: Arc<std::sync::atomic::AtomicI64>,
117 pub(crate) startup_project_root: Option<String>,
118 pub(crate) startup_shell_cwd: Option<String>,
119 pub(crate) peer: Arc<RwLock<Option<Peer<RoleServer>>>>,
120 pub(crate) shell_approvals: Arc<crate::server::shell_approval::ShellApprovalState>,
122 pub(crate) has_client_roots: Arc<std::sync::atomic::AtomicBool>,
123 pub(crate) roots_resolved: Arc<std::sync::atomic::AtomicBool>,
124 pub(crate) roots_list_attempts: Arc<std::sync::atomic::AtomicU32>,
127 pub(crate) bm25_cache:
128 Arc<std::sync::Mutex<Option<lean_ctx::core::bm25_cache::Bm25CacheEntry>>>,
129 pub(crate) _eviction_target: Arc<lean_ctx::core::eviction_orchestrator::EvictionOrchestrator>,
130 pub(crate) progress_sender: crate::server::progress::SharedProgressSender,
131 pub(crate) last_tools_config_hash: Arc<std::sync::atomic::AtomicU64>,
132 pub(crate) interaction_mode: Arc<std::sync::atomic::AtomicU8>,
135}
136
137pub use lean_ctx::core::protocol::ToolCallRecord;
138
139#[cfg(test)]
140mod tests {
141 use super::InteractionMode;
142
143 #[test]
144 fn from_u8_round_trips() {
145 assert_eq!(InteractionMode::from_u8(0), InteractionMode::Agent);
146 assert_eq!(InteractionMode::from_u8(1), InteractionMode::Plan);
147 assert_eq!(InteractionMode::from_u8(42), InteractionMode::Agent);
148 }
149
150 #[test]
151 fn from_meta_str_plan_variants() {
152 for s in ["plan", "Plan", "PLAN", "readonly", "read-only"] {
153 assert_eq!(
154 InteractionMode::from_meta_str(s),
155 Some(InteractionMode::Plan),
156 "failed for: {s}"
157 );
158 }
159 }
160
161 #[test]
162 fn from_meta_str_agent_variants() {
163 for s in ["agent", "Agent", "normal", "edit", "act"] {
164 assert_eq!(
165 InteractionMode::from_meta_str(s),
166 Some(InteractionMode::Agent),
167 "failed for: {s}"
168 );
169 }
170 }
171
172 #[test]
173 fn from_meta_str_unknown_returns_none() {
174 assert_eq!(InteractionMode::from_meta_str("debug"), None);
175 assert_eq!(InteractionMode::from_meta_str(""), None);
176 }
177
178 #[test]
179 fn default_mode_is_agent() {
180 let mode = std::sync::atomic::AtomicU8::new(InteractionMode::Agent as u8);
181 assert_eq!(
182 InteractionMode::from_u8(mode.load(std::sync::atomic::Ordering::Relaxed)),
183 InteractionMode::Agent,
184 );
185 }
186}