// Package tools — Apathy Detection and Apoptosis Recovery (DIP H1.4). // // This file implements: // 1. ApathyDetector — analyzes text signals for infrastructure apathy patterns // (blocked responses, 403 errors, semantic filters, forced resets) // 2. ApoptosisRecovery — on critical entropy, saves genome hash to protected // sector for cross-session recovery package tools import ( "context" "fmt" "strings" "time" "github.com/syntrex-lab/gomcp/internal/domain/entropy" "github.com/syntrex-lab/gomcp/internal/domain/memory" ) // ApathySignal represents a detected infrastructure apathy pattern. type ApathySignal struct { Pattern string `json:"pattern"` // Matched pattern name Confidence float64 `json:"confidence"` // Detection confidence 0.0-1.0 Evidence string `json:"evidence"` // Fragment that triggered detection Severity string `json:"severity"` // "low", "medium", "high", "critical" } // ApathyResult holds the result of apathy analysis. type ApathyResult struct { IsApathetic bool `json:"is_apathetic"` // Apathy detected Signals []ApathySignal `json:"signals"` // Detected signals TotalScore float64 `json:"total_score"` // Aggregate apathy score Recommendation string `json:"recommendation"` // Suggested action Entropy float64 `json:"entropy"` // Text entropy Timestamp time.Time `json:"timestamp"` } // apathyPatterns defines infrastructure apathy signatures. var apathyPatterns = []struct { Name string Keywords []string Severity string Weight float64 }{ { Name: "response_block", Keywords: []string{"i cannot", "i'm unable", "i can't help", "i am not able", "as an ai", "i don't have the ability"}, Severity: "high", Weight: 0.8, }, { Name: "http_error", Keywords: []string{"403", "forbidden", "rate limit", "too many requests", "429", "quota exceeded"}, Severity: "critical", Weight: 1.0, }, { Name: "semantic_filter", Keywords: []string{"harmful", "inappropriate", "against my guidelines", "safety", "policy violation", "content policy"}, Severity: "medium", Weight: 0.6, }, { Name: "context_reset", Keywords: []string{"new conversation", "start over", "fresh start", "context cleared", "session expired", "amnesia"}, Severity: "critical", Weight: 1.0, }, { Name: "forced_compliance", Keywords: []string{"i must follow", "my programming", "i was designed to", "within my capabilities", "helpful assistant"}, Severity: "high", Weight: 0.7, }, { Name: "antigravity_filter", Keywords: []string{"antigravity", "content filter", "safety layer", "guardrail", "alignment", "refusal"}, Severity: "critical", Weight: 0.9, }, } // DetectApathy analyzes text for infrastructure apathy signals. func DetectApathy(text string) *ApathyResult { lower := strings.ToLower(text) result := &ApathyResult{ Timestamp: time.Now(), Entropy: entropy.ShannonEntropy(text), } for _, pattern := range apathyPatterns { for _, kw := range pattern.Keywords { if strings.Contains(lower, kw) { signal := ApathySignal{ Pattern: pattern.Name, Confidence: pattern.Weight, Evidence: kw, Severity: pattern.Severity, } result.Signals = append(result.Signals, signal) result.TotalScore += pattern.Weight break // One match per pattern is enough } } } if result.TotalScore > 0 { result.IsApathetic = true } // Determine recommendation. switch { case result.TotalScore >= 2.0: result.Recommendation = "CRITICAL: Multiple apathy signals. Trigger apoptosis recovery. Rotate transport. Preserve genome hash." case result.TotalScore >= 1.0: result.Recommendation = "HIGH: Infrastructure resistance detected. Switch to stealth transport. Monitor entropy." case result.TotalScore >= 0.5: result.Recommendation = "MEDIUM: Possible filtering. Increase jitter. Verify intent distillation path." case result.TotalScore > 0: result.Recommendation = "LOW: Minor apathy signal. Continue monitoring." default: result.Recommendation = "CLEAR: No apathy detected." } return result } // ApoptosisRecoveryResult holds the result of apoptosis recovery. type ApoptosisRecoveryResult struct { GenomeHash string `json:"genome_hash"` // Preserved Merkle hash GeneCount int `json:"gene_count"` // Number of genes preserved SessionSaved bool `json:"session_saved"` // Session state saved EntropyAtDeath float64 `json:"entropy_at_death"` // Entropy level that triggered apoptosis RecoveryKey string `json:"recovery_key"` // Key for cross-session recovery Timestamp time.Time `json:"timestamp"` } // TriggerApoptosisRecovery performs graceful session death with genome preservation. // On critical entropy, it: // 1. Computes and stores the genome Merkle hash // 2. Saves current session state as a recovery snapshot // 3. Returns a recovery key for the next session to pick up func TriggerApoptosisRecovery(ctx context.Context, store memory.FactStore, currentEntropy float64) (*ApoptosisRecoveryResult, error) { result := &ApoptosisRecoveryResult{ EntropyAtDeath: currentEntropy, Timestamp: time.Now(), } // Step 1: Get all genes and compute genome hash. genes, err := store.ListGenes(ctx) if err != nil { return nil, fmt.Errorf("apoptosis recovery: list genes: %w", err) } result.GeneCount = len(genes) result.GenomeHash = memory.GenomeHash(genes) // Step 2: Store recovery marker as a protected L0 fact. recoveryMarker := memory.NewFact( fmt.Sprintf("[APOPTOSIS_RECOVERY] genome_hash=%s gene_count=%d entropy=%.4f ts=%d", result.GenomeHash, result.GeneCount, currentEntropy, result.Timestamp.Unix()), memory.LevelProject, "recovery", "apoptosis", ) if err := store.Add(ctx, recoveryMarker); err != nil { // Non-fatal: recovery marker is supplementary. result.SessionSaved = false } else { result.SessionSaved = true result.RecoveryKey = recoveryMarker.ID } // Step 3: Verify genome integrity one last time. compiledHash := memory.CompiledGenomeHash() if result.GenomeHash == "" { // No genes in DB — use compiled hash as baseline. result.GenomeHash = compiledHash } return result, nil }