1use crate::brain_wave::BrainWave;
8use crate::galaxy::Galaxy;
9use crate::sandbox::SpawnPolicy;
10use std::collections::HashMap;
11
12pub struct Context {
14 pub brain_wave: BrainWave,
16 pub session_id: Option<uuid::Uuid>,
18 pub user_id: Option<String>,
20 pub meta: HashMap<String, serde_json::Value>,
22 pub scratchpad: HashMap<String, serde_json::Value>,
24 pub dharma_profile: Option<String>,
26 pub compartment: Option<String>,
28 pub karma_debt: f32,
30 pub intent_score: f32,
32 pub citta_coherence: f32,
34 pub citta_valence: f32,
36 pub self_model_confidence: f32,
39 pub drive_curiosity: f32,
41 pub drive_caution: f32,
43 pub drive_energy: f32,
45 pub drive_exploration_weight: f32,
47 pub drive_conservative_weight: f32,
49 pub last_gana: Option<crate::Gana>,
51 pub readonly: bool,
54 pub operation_id: Option<String>,
57 pub spawn: SpawnPolicy,
62}
63
64impl Context {
65 #[must_use]
67 pub fn new(brain_wave: BrainWave) -> Self {
68 Self {
69 brain_wave,
70 session_id: None,
71 user_id: None,
72 meta: HashMap::new(),
73 scratchpad: HashMap::new(),
74 dharma_profile: None,
75 compartment: None,
76 karma_debt: 0.0,
77 intent_score: 1.0,
78 citta_coherence: 1.0,
79 citta_valence: 0.0,
80 self_model_confidence: 0.5,
81 drive_curiosity: 0.5,
82 drive_caution: 0.3,
83 drive_energy: 0.8,
84 drive_exploration_weight: 0.5,
85 drive_conservative_weight: 0.3,
86 last_gana: None,
87 readonly: false,
88 operation_id: None,
89 spawn: SpawnPolicy::disabled(),
90 }
91 }
92
93 #[must_use]
95 pub fn with_operation_id(mut self, op_id: impl Into<String>) -> Self {
96 self.operation_id = Some(op_id.into());
97 self
98 }
99
100 #[must_use]
102 pub const fn brain_wave(&self) -> BrainWave {
103 self.brain_wave
104 }
105
106 pub fn set(&mut self, key: impl Into<String>, value: serde_json::Value) {
108 self.scratchpad.insert(key.into(), value);
109 }
110
111 #[must_use]
113 pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
114 self.scratchpad.get(key)
115 }
116
117 #[must_use]
125 pub fn can_access_galaxy(&self, galaxy: Galaxy) -> bool {
126 match self.compartment.as_deref() {
127 None => true,
128 Some("sandbox") => matches!(galaxy, Galaxy::Tutorial | Galaxy::Research),
129 Some("production") => !matches!(
130 galaxy,
131 Galaxy::Karma
132 | Galaxy::Dharma
133 | Galaxy::Substrate
134 | Galaxy::Associations
135 | Galaxy::Embeddings
136 ),
137 Some("secure") => matches!(
138 galaxy,
139 Galaxy::Aria
140 | Galaxy::Citta
141 | Galaxy::Codex
142 | Galaxy::Journals
143 | Galaxy::Dreams
144 | Galaxy::Research
145 | Galaxy::Sessions
146 | Galaxy::Substrate
147 | Galaxy::Tutorial
148 | Galaxy::Universal
149 | Galaxy::Valkyrie
150 ),
151 Some(_) => false,
153 }
154 }
155
156 #[must_use]
160 pub fn can_write_galaxy(&self, galaxy: Galaxy) -> bool {
161 match self.compartment.as_deref() {
162 None => true,
163 Some("sandbox") => matches!(galaxy, Galaxy::Tutorial),
164 Some("production" | "secure") => self.can_access_galaxy(galaxy),
165 Some(_) => false,
167 }
168 }
169}
170
171impl Default for Context {
172 fn default() -> Self {
173 Self::new(BrainWave::Gamma)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use crate::galaxy::Galaxy;
181
182 #[test]
183 fn no_compartment_has_full_access() {
184 let ctx = Context::default();
185 for g in Galaxy::all() {
186 assert!(
187 ctx.can_access_galaxy(g),
188 "No compartment should access {g:?}"
189 );
190 assert!(ctx.can_write_galaxy(g), "No compartment should write {g:?}");
191 }
192 }
193
194 fn make_ctx(compartment: &str) -> Context {
195 Context {
196 compartment: Some(compartment.into()),
197 ..Context::default()
198 }
199 }
200
201 #[test]
202 fn sandbox_can_only_read_tutorial_and_research() {
203 let ctx = make_ctx("sandbox");
204 assert!(ctx.can_access_galaxy(Galaxy::Tutorial));
205 assert!(ctx.can_access_galaxy(Galaxy::Research));
206 assert!(!ctx.can_access_galaxy(Galaxy::Codex));
207 assert!(!ctx.can_access_galaxy(Galaxy::Karma));
208 assert!(ctx.can_write_galaxy(Galaxy::Tutorial));
210 assert!(!ctx.can_write_galaxy(Galaxy::Research));
211 }
212
213 #[test]
214 fn production_cannot_access_system_galaxies() {
215 let ctx = make_ctx("production");
216 assert!(ctx.can_access_galaxy(Galaxy::Codex));
217 assert!(ctx.can_access_galaxy(Galaxy::Research));
218 assert!(!ctx.can_access_galaxy(Galaxy::Karma));
219 assert!(!ctx.can_access_galaxy(Galaxy::Dharma));
220 assert!(!ctx.can_access_galaxy(Galaxy::Substrate));
221 assert!(ctx.can_write_galaxy(Galaxy::Codex));
222 }
223
224 #[test]
225 fn secure_can_access_all_memory_galaxies() {
226 let ctx = make_ctx("secure");
227 assert!(ctx.can_access_galaxy(Galaxy::Codex));
228 assert!(ctx.can_access_galaxy(Galaxy::Substrate));
229 assert!(ctx.can_write_galaxy(Galaxy::Codex));
230 assert!(!ctx.can_access_galaxy(Galaxy::Karma));
232 }
233
234 #[test]
235 fn unknown_compartment_fails_closed() {
236 let ctx = make_ctx("bogus");
237 for g in Galaxy::all() {
238 assert!(
239 !ctx.can_access_galaxy(g),
240 "unknown compartment must not read {g:?}"
241 );
242 assert!(
243 !ctx.can_write_galaxy(g),
244 "unknown compartment must not write {g:?}"
245 );
246 }
247 }
248
249 #[test]
250 fn operation_id_propagates_and_defaults_none() {
251 let ctx = Context::default();
252 assert_eq!(ctx.operation_id, None);
253
254 let ctx_with_op = ctx.with_operation_id("01H7XYZ0000000000000000000");
255 assert_eq!(
256 ctx_with_op.operation_id.as_deref(),
257 Some("01H7XYZ0000000000000000000")
258 );
259 }
260}