1use ahash::AHashMap;
10use anyhow::{Context, Result};
11use chrono::{DateTime, Utc};
12use dashmap::DashMap;
13use parking_lot::RwLock;
14use serde::{Deserialize, Serialize};
15use std::sync::Arc;
16use uuid::Uuid;
17
18pub mod coordination;
19pub mod paradox;
20pub mod perception;
21pub mod substrate;
22
23pub use coordination::*;
25pub use paradox::*;
26pub use perception::*;
27pub use substrate::*;
28
29#[derive(thiserror::Error, Debug)]
31pub enum CasialError {
32 #[error("Perception lock failed: {0}")]
33 PerceptionLock(String),
34
35 #[error("Paradox resolution timeout: {0}")]
36 ParadoxTimeout(String),
37
38 #[error("Context coordination failed: {0}")]
39 CoordinationFailure(String),
40
41 #[error("Template processing error: {0}")]
42 TemplateError(String),
43
44 #[error("Mission configuration error: {0}")]
45 MissionError(String),
46
47 #[error("Substrate integration error: {0}")]
48 SubstrateError(String),
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub struct PerceptionId(Uuid);
54
55impl PerceptionId {
56 pub fn new() -> Self {
57 Self(Uuid::new_v4())
58 }
59}
60
61impl Default for PerceptionId {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct Perception {
70 pub id: PerceptionId,
71 pub name: String,
72 pub description: String,
73 pub confidence: f64,
74 pub created_at: DateTime<Utc>,
75 pub updated_at: DateTime<Utc>,
76 pub metadata: AHashMap<String, serde_json::Value>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct CasialTemplate {
82 pub id: String,
83 pub name: String,
84 pub description: String,
85 pub categories: Vec<String>,
86 pub priority: u32,
87 pub enabled: bool,
88 pub content: String,
89 pub perception_affinity: Vec<PerceptionId>,
90 pub paradox_resistance: f64, pub metadata: AHashMap<String, serde_json::Value>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct CoordinationRule {
97 pub id: String,
98 pub name: String,
99 pub enabled: bool,
100 pub conditions: RuleConditions,
101 pub actions: RuleActions,
102 pub perception_scope: Vec<PerceptionId>,
103 pub paradox_handling: ParadoxStrategy,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct RuleConditions {
109 pub tool_patterns: Vec<String>,
110 pub environment_vars: AHashMap<String, String>,
111 pub file_signals: Vec<FileSignal>,
112 pub perception_states: Vec<PerceptionId>,
113 pub min_confidence: Option<f64>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct RuleActions {
119 pub template_ids: Vec<String>,
120 pub transform_type: TransformType,
121 pub target_field: Option<String>,
122 pub char_limit: Option<usize>,
123 pub perception_lock: bool,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct FileSignal {
129 pub path: String,
130 pub must_exist: bool,
131 pub contains: Option<String>,
132 pub modified_since: Option<DateTime<Utc>>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub enum TransformType {
138 Prepend,
139 Append,
140 InjectField,
141 SystemInstruction,
142 PerceptionLayer,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub enum ParadoxStrategy {
148 Ignore,
150 Coexist,
152 Synthesize,
154 Expose,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct CasialMission {
161 pub id: String,
162 pub name: String,
163 pub description: String,
164 pub templates: Vec<CasialTemplate>,
165 pub rules: Vec<CoordinationRule>,
166 pub perceptions: Vec<Perception>,
167 pub budgets: BudgetConfiguration,
168 pub created_at: DateTime<Utc>,
169 pub updated_at: DateTime<Utc>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct BudgetConfiguration {
175 pub global_char_limit: Option<usize>,
176 pub per_tool_limits: AHashMap<String, usize>,
177 pub perception_quotas: AHashMap<PerceptionId, usize>,
178 pub paradox_overhead: f64, }
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct CoordinationRequest {
184 pub tool_name: String,
185 pub tool_args: serde_json::Value,
186 pub environment: AHashMap<String, String>,
187 pub project_path: Option<String>,
188 pub active_perceptions: Vec<PerceptionId>,
189 pub paradox_tolerance: f64,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct CoordinationResult {
195 pub applied: bool,
196 pub injected_content: String,
197 pub modified_args: serde_json::Value,
198 pub activated_rules: Vec<String>,
199 pub used_templates: Vec<String>,
200 pub perception_locks: Vec<PerceptionId>,
201 pub paradoxes_detected: Vec<ParadoxReport>,
202 pub metadata: AHashMap<String, serde_json::Value>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct ParadoxReport {
208 pub id: Uuid,
209 pub description: String,
210 pub conflicting_perceptions: Vec<PerceptionId>,
211 pub resolution_strategy: ParadoxStrategy,
212 pub confidence_impact: f64,
213}
214
215pub struct CasialEngine {
217 missions: Arc<DashMap<String, Arc<CasialMission>>>,
218 active_perceptions: Arc<DashMap<PerceptionId, Arc<RwLock<Perception>>>>,
219 coordination_history: Arc<DashMap<Uuid, CoordinationResult>>,
220 paradox_registry: Arc<DashMap<Uuid, ParadoxReport>>,
221}
222
223impl CasialEngine {
224 pub fn new() -> Self {
226 Self {
227 missions: Arc::new(DashMap::new()),
228 active_perceptions: Arc::new(DashMap::new()),
229 coordination_history: Arc::new(DashMap::new()),
230 paradox_registry: Arc::new(DashMap::new()),
231 }
232 }
233
234 pub fn load_mission(&self, mission: CasialMission) -> Result<()> {
236 let mission_id = mission.id.clone();
237 let mission_arc = Arc::new(mission);
238
239 for perception in &mission_arc.perceptions {
241 self.active_perceptions
242 .insert(perception.id, Arc::new(RwLock::new(perception.clone())));
243 }
244
245 self.missions.insert(mission_id, mission_arc);
246 Ok(())
247 }
248
249 pub fn coordinate(&self, request: CoordinationRequest) -> Result<CoordinationResult> {
251 let applicable_missions: Vec<Arc<CasialMission>> = self
253 .missions
254 .iter()
255 .map(|entry| entry.value().clone())
256 .collect();
257
258 if applicable_missions.is_empty() {
259 return Ok(CoordinationResult {
260 applied: false,
261 injected_content: String::new(),
262 modified_args: request.tool_args,
263 activated_rules: vec![],
264 used_templates: vec![],
265 perception_locks: vec![],
266 paradoxes_detected: vec![],
267 metadata: AHashMap::new(),
268 });
269 }
270
271 let mut activated_rules = Vec::new();
273 let mut applicable_templates = AHashMap::new();
274 let mut detected_paradoxes = Vec::new();
275
276 for mission in &applicable_missions {
277 for rule in &mission.rules {
278 if !rule.enabled {
279 continue;
280 }
281
282 if self.evaluate_rule_conditions(&rule.conditions, &request)? {
283 activated_rules.push(rule.id.clone());
284
285 for template_id in &rule.actions.template_ids {
287 if let Some(template) =
288 mission.templates.iter().find(|t| t.id == *template_id)
289 {
290 if let Some(existing) = applicable_templates.get(template_id) {
292 let existing_template: &CasialTemplate = existing;
293 if !existing_template.perception_affinity.is_empty()
294 && !template.perception_affinity.is_empty()
295 && existing_template.perception_affinity
296 != template.perception_affinity
297 {
298 let paradox = ParadoxReport {
300 id: Uuid::new_v4(),
301 description: format!(
302 "Template '{}' has conflicting perception affinities",
303 template_id
304 ),
305 conflicting_perceptions: [
306 existing_template.perception_affinity.clone(),
307 template.perception_affinity.clone(),
308 ]
309 .concat(),
310 resolution_strategy: rule.paradox_handling.clone(),
311 confidence_impact: 1.0 - template.paradox_resistance,
312 };
313
314 detected_paradoxes.push(paradox.clone());
315 self.paradox_registry.insert(paradox.id, paradox);
316 }
317 }
318
319 applicable_templates.insert(template_id.clone(), template.clone());
320 }
321 }
322 }
323 }
324 }
325
326 let resolved_templates = self.resolve_paradoxes(
328 applicable_templates,
329 &detected_paradoxes,
330 request.paradox_tolerance,
331 )?;
332
333 let (injected_content, used_templates) =
335 self.compose_context(resolved_templates, &applicable_missions[0].budgets)?;
336
337 let modified_args = self.apply_transformation(
339 &request.tool_args,
340 &injected_content,
341 &activated_rules,
342 &applicable_missions,
343 )?;
344
345 let result = CoordinationResult {
346 applied: !used_templates.is_empty(),
347 injected_content,
348 modified_args,
349 activated_rules,
350 used_templates,
351 perception_locks: request.active_perceptions.clone(),
352 paradoxes_detected: detected_paradoxes,
353 metadata: self.generate_metadata(&request)?,
354 };
355
356 let history_id = Uuid::new_v4();
358 self.coordination_history.insert(history_id, result.clone());
359
360 Ok(result)
361 }
362
363 fn evaluate_rule_conditions(
365 &self,
366 conditions: &RuleConditions,
367 request: &CoordinationRequest,
368 ) -> Result<bool> {
369 if !conditions.tool_patterns.is_empty() {
371 let matches = conditions
372 .tool_patterns
373 .iter()
374 .any(|pattern| request.tool_name.contains(pattern));
375 if !matches {
376 return Ok(false);
377 }
378 }
379
380 for (key, expected) in &conditions.environment_vars {
382 if let Some(actual) = request.environment.get(key) {
383 if !actual.contains(expected) {
384 return Ok(false);
385 }
386 } else {
387 return Ok(false);
388 }
389 }
390
391 if let Some(project_path) = &request.project_path {
393 for signal in &conditions.file_signals {
394 if !self.evaluate_file_signal(signal, project_path)? {
395 return Ok(false);
396 }
397 }
398 }
399
400 if !conditions.perception_states.is_empty() {
402 let has_required_perception = conditions
403 .perception_states
404 .iter()
405 .any(|required| request.active_perceptions.contains(required));
406 if !has_required_perception {
407 return Ok(false);
408 }
409 }
410
411 Ok(true)
412 }
413
414 fn evaluate_file_signal(&self, signal: &FileSignal, project_path: &str) -> Result<bool> {
416 let file_path = std::path::Path::new(project_path).join(&signal.path);
417
418 let exists = file_path.exists();
419 if signal.must_exist && !exists {
420 return Ok(false);
421 }
422
423 if exists {
424 if let Some(expected_content) = &signal.contains {
425 let content = std::fs::read_to_string(&file_path)
426 .context("Failed to read file for signal evaluation")?;
427 if !content.contains(expected_content) {
428 return Ok(false);
429 }
430 }
431
432 if let Some(modified_since) = signal.modified_since {
433 let metadata =
434 std::fs::metadata(&file_path).context("Failed to read file metadata")?;
435 let modified = metadata
436 .modified()
437 .context("Failed to get file modification time")?;
438 let modified_dt = DateTime::<Utc>::from(modified);
439 if modified_dt < modified_since {
440 return Ok(false);
441 }
442 }
443 }
444
445 Ok(true)
446 }
447
448 fn resolve_paradoxes(
450 &self,
451 templates: AHashMap<String, CasialTemplate>,
452 paradoxes: &[ParadoxReport],
453 tolerance: f64,
454 ) -> Result<Vec<CasialTemplate>> {
455 let mut resolved = Vec::new();
456 let mut processed_ids = std::collections::HashSet::new();
457
458 for template in templates.into_values() {
459 if processed_ids.contains(&template.id) {
460 continue;
461 }
462
463 let involved_paradoxes: Vec<&ParadoxReport> = paradoxes
465 .iter()
466 .filter(|p| p.confidence_impact > tolerance)
467 .collect();
468
469 if involved_paradoxes.is_empty() || template.paradox_resistance >= tolerance {
470 processed_ids.insert(template.id.clone());
472 resolved.push(template);
473 } else {
474 let mut should_include = false;
476 for paradox in &involved_paradoxes {
477 match paradox.resolution_strategy {
478 ParadoxStrategy::Ignore => {
479 should_include = true;
480 }
481 ParadoxStrategy::Coexist => {
482 should_include = true;
484 }
485 ParadoxStrategy::Synthesize => {
486 if template.paradox_resistance >= 0.5 {
488 should_include = true;
489 }
490 }
491 ParadoxStrategy::Expose => {
492 should_include = true;
494 }
495 }
496 }
497 processed_ids.insert(template.id.clone());
498 if should_include {
499 resolved.push(template);
500 }
501 }
502 }
503
504 Ok(resolved)
505 }
506
507 fn compose_context(
509 &self,
510 templates: Vec<CasialTemplate>,
511 budget: &BudgetConfiguration,
512 ) -> Result<(String, Vec<String>)> {
513 let mut sorted_templates = templates;
514 sorted_templates.sort_by_key(|t| t.priority);
515
516 let mut content = String::new();
517 let mut used_templates = Vec::new();
518 let mut char_count = 0;
519
520 let char_limit = budget.global_char_limit.unwrap_or(usize::MAX);
521 let paradox_overhead = (char_limit as f64 * budget.paradox_overhead) as usize;
522 let effective_limit = char_limit.saturating_sub(paradox_overhead);
523
524 for template in sorted_templates {
525 if !template.enabled {
526 continue;
527 }
528
529 let template_content = format!("## {}\n\n{}\n\n", template.name, template.content);
530
531 if char_count + template_content.len() > effective_limit {
532 break;
533 }
534
535 content.push_str(&template_content);
536 char_count += template_content.len();
537 used_templates.push(template.id.clone());
538 }
539
540 Ok((content, used_templates))
541 }
542
543 fn apply_transformation(
545 &self,
546 args: &serde_json::Value,
547 content: &str,
548 _rules: &[String],
549 missions: &[Arc<CasialMission>],
550 ) -> Result<serde_json::Value> {
551 let mut modified_args = args.clone();
552
553 let transform_type = missions
555 .iter()
556 .flat_map(|m| &m.rules)
557 .find(|r| r.enabled)
558 .map(|r| &r.actions.transform_type)
559 .unwrap_or(&TransformType::Prepend);
560
561 match transform_type {
562 TransformType::Prepend => {
563 if let Some(query) = modified_args.get_mut("query") {
564 if let Some(query_str) = query.as_str() {
565 *query = serde_json::Value::String(format!("{}\n\n{}", content, query_str));
566 }
567 } else if let Some(instructions) = modified_args.get_mut("instructions") {
568 if let Some(instr_str) = instructions.as_str() {
569 *instructions =
570 serde_json::Value::String(format!("{}\n\n{}", content, instr_str));
571 }
572 }
573 }
574 TransformType::Append => {
575 if let Some(query) = modified_args.get_mut("query") {
576 if let Some(query_str) = query.as_str() {
577 *query = serde_json::Value::String(format!("{}\n\n{}", query_str, content));
578 }
579 }
580 }
581 TransformType::InjectField => {
582 if let Some(obj) = modified_args.as_object_mut() {
583 obj.insert(
584 "casial_context".to_string(),
585 serde_json::Value::String(content.to_string()),
586 );
587 }
588 }
589 TransformType::SystemInstruction => {
590 if let Some(obj) = modified_args.as_object_mut() {
591 obj.insert(
592 "system_context".to_string(),
593 serde_json::Value::String(content.to_string()),
594 );
595 }
596 }
597 TransformType::PerceptionLayer => {
598 if let Some(obj) = modified_args.as_object_mut() {
599 obj.insert(
600 "perception_context".to_string(),
601 serde_json::Value::String(content.to_string()),
602 );
603 }
604 }
605 }
606
607 Ok(modified_args)
608 }
609
610 fn generate_metadata(
612 &self,
613 request: &CoordinationRequest,
614 ) -> Result<AHashMap<String, serde_json::Value>> {
615 let mut metadata = AHashMap::new();
616
617 metadata.insert(
618 "timestamp".to_string(),
619 serde_json::Value::String(Utc::now().to_rfc3339()),
620 );
621 metadata.insert(
622 "tool_name".to_string(),
623 serde_json::Value::String(request.tool_name.clone()),
624 );
625 metadata.insert(
626 "perception_count".to_string(),
627 serde_json::Value::Number(serde_json::Number::from(request.active_perceptions.len())),
628 );
629 metadata.insert(
630 "paradox_tolerance".to_string(),
631 serde_json::Value::Number(
632 serde_json::Number::from_f64(request.paradox_tolerance).unwrap(),
633 ),
634 );
635
636 Ok(metadata)
637 }
638
639 pub fn get_coordination_history(&self) -> Vec<CoordinationResult> {
641 self.coordination_history
642 .iter()
643 .map(|entry| entry.value().clone())
644 .collect()
645 }
646
647 pub fn get_paradox_registry(&self) -> Vec<ParadoxReport> {
649 self.paradox_registry
650 .iter()
651 .map(|entry| entry.value().clone())
652 .collect()
653 }
654}
655
656impl Default for CasialEngine {
657 fn default() -> Self {
658 Self::new()
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665
666 #[test]
667 fn test_casial_engine_creation() {
668 let engine = CasialEngine::new();
669 assert_eq!(engine.missions.len(), 0);
670 assert_eq!(engine.active_perceptions.len(), 0);
671 }
672
673 #[test]
674 fn test_perception_id_generation() {
675 let id1 = PerceptionId::new();
676 let id2 = PerceptionId::new();
677 assert_ne!(id1, id2);
678 }
679}