lean_ctx/core/context_os/
mod.rs1use std::path::PathBuf;
2use std::sync::{Arc, OnceLock};
3
4mod shared_sessions;
5pub use shared_sessions::{SharedSessionKey, SharedSessionStore};
6
7mod context_bus;
8pub use context_bus::{ConsistencyLevel, ContextBus, ContextEventKindV1, ContextEventV1};
9
10pub mod redaction;
11pub use redaction::{redact_event_payload, RedactionLevel};
12
13mod metrics;
14pub use metrics::{ContextOsMetrics, MetricsSnapshot};
15
16#[derive(Clone)]
21pub struct ContextOsRuntime {
22 pub shared_sessions: Arc<SharedSessionStore>,
23 pub bus: Arc<ContextBus>,
24 pub metrics: Arc<ContextOsMetrics>,
25}
26
27impl Default for ContextOsRuntime {
28 fn default() -> Self {
29 Self {
30 shared_sessions: Arc::new(SharedSessionStore::new()),
31 bus: Arc::new(ContextBus::new()),
32 metrics: Arc::new(ContextOsMetrics::default()),
33 }
34 }
35}
36
37impl ContextOsRuntime {
38 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn data_dir() -> Option<PathBuf> {
43 crate::core::data_dir::lean_ctx_data_dir().ok()
44 }
45}
46
47static RUNTIME: OnceLock<Arc<ContextOsRuntime>> = OnceLock::new();
48
49pub fn runtime() -> Arc<ContextOsRuntime> {
50 RUNTIME
51 .get_or_init(|| Arc::new(ContextOsRuntime::new()))
52 .clone()
53}
54
55pub fn emit_event(
57 workspace_id: &str,
58 channel_id: &str,
59 kind: &ContextEventKindV1,
60 actor: Option<&str>,
61 payload: serde_json::Value,
62) {
63 let rt = runtime();
64 if rt
65 .bus
66 .append(workspace_id, channel_id, kind, actor, payload)
67 .is_some()
68 {
69 rt.metrics.record_event_appended();
70 }
71}
72
73pub fn secondary_event_kind(tool: &str, action: Option<&str>) -> Option<ContextEventKindV1> {
75 match tool {
76 "ctx_session" => {
77 let a = action.unwrap_or("");
78 if matches!(
79 a,
80 "save"
81 | "set_task"
82 | "task"
83 | "checkpoint"
84 | "finding"
85 | "decision"
86 | "reset"
87 | "import"
88 ) {
89 Some(ContextEventKindV1::SessionMutated)
90 } else {
91 None
92 }
93 }
94 "ctx_handoff" | "ctx_workflow" | "ctx_share" => Some(ContextEventKindV1::SessionMutated),
95 "ctx_knowledge" | "ctx_knowledge_relations" => {
96 let a = action.unwrap_or("");
97 if matches!(
98 a,
99 "remember" | "relate" | "unrelate" | "feedback" | "remove" | "consolidate"
100 ) {
101 Some(ContextEventKindV1::KnowledgeRemembered)
102 } else {
103 None
104 }
105 }
106 "ctx_artifacts" => {
107 let a = action.unwrap_or("");
108 if matches!(a, "store" | "reindex" | "remove") {
109 Some(ContextEventKindV1::ArtifactStored)
110 } else {
111 None
112 }
113 }
114 "ctx_graph" => {
115 let a = action.unwrap_or("");
116 if matches!(
117 a,
118 "index-build"
119 | "index-build-full"
120 | "index-build-background"
121 | "index-build-full-background"
122 ) {
123 Some(ContextEventKindV1::GraphBuilt)
124 } else {
125 None
126 }
127 }
128 "ctx_proof" | "ctx_verify" => {
129 let a = action.unwrap_or("");
130 if matches!(a, "generate" | "export" | "verify") {
131 Some(ContextEventKindV1::ProofAdded)
132 } else {
133 None
134 }
135 }
136 _ => None,
137 }
138}