1use crate::brain_wave::BrainWave;
8use crate::galaxy::Galaxy;
9use std::collections::HashMap;
10
11pub struct Context {
13 pub brain_wave: BrainWave,
15 pub session_id: Option<uuid::Uuid>,
17 pub user_id: Option<String>,
19 pub meta: HashMap<String, serde_json::Value>,
21 pub scratchpad: HashMap<String, serde_json::Value>,
23 pub dharma_profile: Option<String>,
25 pub compartment: Option<String>,
27 pub karma_debt: f32,
29 pub intent_score: f32,
31 pub citta_coherence: f32,
33 pub citta_valence: f32,
35 pub self_model_confidence: f32,
38 pub drive_curiosity: f32,
40 pub drive_caution: f32,
42 pub drive_energy: f32,
44 pub drive_exploration_weight: f32,
46 pub drive_conservative_weight: f32,
48 pub last_gana: Option<crate::Gana>,
50 pub readonly: bool,
53 pub operation_id: Option<String>,
56}
57
58impl Context {
59 #[must_use]
61 pub fn new(brain_wave: BrainWave) -> Self {
62 Self {
63 brain_wave,
64 session_id: None,
65 user_id: None,
66 meta: HashMap::new(),
67 scratchpad: HashMap::new(),
68 dharma_profile: None,
69 compartment: None,
70 karma_debt: 0.0,
71 intent_score: 1.0,
72 citta_coherence: 1.0,
73 citta_valence: 0.0,
74 self_model_confidence: 0.5,
75 drive_curiosity: 0.5,
76 drive_caution: 0.3,
77 drive_energy: 0.8,
78 drive_exploration_weight: 0.5,
79 drive_conservative_weight: 0.3,
80 last_gana: None,
81 readonly: false,
82 operation_id: None,
83 }
84 }
85
86 #[must_use]
88 pub fn with_operation_id(mut self, op_id: impl Into<String>) -> Self {
89 self.operation_id = Some(op_id.into());
90 self
91 }
92
93 #[must_use]
95 pub const fn brain_wave(&self) -> BrainWave {
96 self.brain_wave
97 }
98
99 pub fn set(&mut self, key: impl Into<String>, value: serde_json::Value) {
101 self.scratchpad.insert(key.into(), value);
102 }
103
104 #[must_use]
106 pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
107 self.scratchpad.get(key)
108 }
109
110 #[must_use]
118 pub fn can_access_galaxy(&self, galaxy: Galaxy) -> bool {
119 match self.compartment.as_deref() {
120 None => true,
121 Some("sandbox") => matches!(galaxy, Galaxy::Tutorial | Galaxy::Research),
122 Some("production") => !matches!(
123 galaxy,
124 Galaxy::Karma
125 | Galaxy::Dharma
126 | Galaxy::Substrate
127 | Galaxy::Associations
128 | Galaxy::Embeddings
129 ),
130 Some("secure") => matches!(
131 galaxy,
132 Galaxy::Aria
133 | Galaxy::Citta
134 | Galaxy::Codex
135 | Galaxy::Journals
136 | Galaxy::Dreams
137 | Galaxy::Research
138 | Galaxy::Sessions
139 | Galaxy::Substrate
140 | Galaxy::Tutorial
141 | Galaxy::Universal
142 | Galaxy::Valkyrie
143 ),
144 Some(_) => false,
146 }
147 }
148
149 #[must_use]
153 pub fn can_write_galaxy(&self, galaxy: Galaxy) -> bool {
154 match self.compartment.as_deref() {
155 None => true,
156 Some("sandbox") => matches!(galaxy, Galaxy::Tutorial),
157 Some("production" | "secure") => self.can_access_galaxy(galaxy),
158 Some(_) => false,
160 }
161 }
162}
163
164impl Default for Context {
165 fn default() -> Self {
166 Self::new(BrainWave::Gamma)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::galaxy::Galaxy;
174
175 #[test]
176 fn no_compartment_has_full_access() {
177 let ctx = Context::default();
178 for g in Galaxy::all() {
179 assert!(
180 ctx.can_access_galaxy(g),
181 "No compartment should access {g:?}"
182 );
183 assert!(ctx.can_write_galaxy(g), "No compartment should write {g:?}");
184 }
185 }
186
187 fn make_ctx(compartment: &str) -> Context {
188 Context {
189 compartment: Some(compartment.into()),
190 ..Context::default()
191 }
192 }
193
194 #[test]
195 fn sandbox_can_only_read_tutorial_and_research() {
196 let ctx = make_ctx("sandbox");
197 assert!(ctx.can_access_galaxy(Galaxy::Tutorial));
198 assert!(ctx.can_access_galaxy(Galaxy::Research));
199 assert!(!ctx.can_access_galaxy(Galaxy::Codex));
200 assert!(!ctx.can_access_galaxy(Galaxy::Karma));
201 assert!(ctx.can_write_galaxy(Galaxy::Tutorial));
203 assert!(!ctx.can_write_galaxy(Galaxy::Research));
204 }
205
206 #[test]
207 fn production_cannot_access_system_galaxies() {
208 let ctx = make_ctx("production");
209 assert!(ctx.can_access_galaxy(Galaxy::Codex));
210 assert!(ctx.can_access_galaxy(Galaxy::Research));
211 assert!(!ctx.can_access_galaxy(Galaxy::Karma));
212 assert!(!ctx.can_access_galaxy(Galaxy::Dharma));
213 assert!(!ctx.can_access_galaxy(Galaxy::Substrate));
214 assert!(ctx.can_write_galaxy(Galaxy::Codex));
215 }
216
217 #[test]
218 fn secure_can_access_all_memory_galaxies() {
219 let ctx = make_ctx("secure");
220 assert!(ctx.can_access_galaxy(Galaxy::Codex));
221 assert!(ctx.can_access_galaxy(Galaxy::Substrate));
222 assert!(ctx.can_write_galaxy(Galaxy::Codex));
223 assert!(!ctx.can_access_galaxy(Galaxy::Karma));
225 }
226
227 #[test]
228 fn unknown_compartment_fails_closed() {
229 let ctx = make_ctx("bogus");
230 for g in Galaxy::all() {
231 assert!(
232 !ctx.can_access_galaxy(g),
233 "unknown compartment must not read {g:?}"
234 );
235 assert!(
236 !ctx.can_write_galaxy(g),
237 "unknown compartment must not write {g:?}"
238 );
239 }
240 }
241
242 #[test]
243 fn operation_id_propagates_and_defaults_none() {
244 let ctx = Context::default();
245 assert_eq!(ctx.operation_id, None);
246
247 let ctx_with_op = ctx.with_operation_id("01H7XYZ0000000000000000000");
248 assert_eq!(
249 ctx_with_op.operation_id.as_deref(),
250 Some("01H7XYZ0000000000000000000")
251 );
252 }
253}