| |
| """Codette Session Manager — Cocoon-Backed Conversation Memory |
| |
| Wraps the Cocoon system (QuantumSpiderweb + CocoonSync + EpistemicMetrics) |
| into a session manager that persists conversation state with encrypted memory. |
| |
| Each session saves: |
| - Chat history |
| - Spiderweb state (agent beliefs, tensions, attractors) |
| - Glyphs (identity signatures) |
| - Epistemic metrics (coherence, tension, coverage) |
| |
| Zero external dependencies beyond what the forge already uses. |
| """ |
|
|
| import json, os, time, hashlib, sqlite3 |
| from pathlib import Path |
| from typing import Dict, List, Optional, Any |
|
|
| |
| import sys |
| _root = str(Path(__file__).parent.parent) |
| if _root not in sys.path: |
| sys.path.insert(0, _root) |
|
|
| |
| try: |
| from reasoning_forge.quantum_spiderweb import QuantumSpiderweb, NodeState |
| HAS_SPIDERWEB = True |
| except ImportError: |
| HAS_SPIDERWEB = False |
|
|
| try: |
| from reasoning_forge.epistemic_metrics import EpistemicMetrics |
| HAS_METRICS = True |
| except ImportError: |
| HAS_METRICS = False |
|
|
| try: |
| from reasoning_forge.cocoon_sync import CocoonSync, CocoonKeyManager |
| HAS_COCOON = True |
| except ImportError: |
| HAS_COCOON = False |
|
|
| try: |
| from reasoning_forge.dream_reweaver import DreamReweaver |
| HAS_DREAMER = True |
| except ImportError: |
| HAS_DREAMER = False |
|
|
| try: |
| from reasoning_forge.quantum_optimizer import QuantumOptimizer, QualitySignal |
| HAS_OPTIMIZER = True |
| except ImportError: |
| HAS_OPTIMIZER = False |
|
|
| try: |
| from reasoning_forge.living_memory import LivingMemoryKernel |
| HAS_MEMORY = True |
| except ImportError: |
| HAS_MEMORY = False |
|
|
| try: |
| from reasoning_forge.guardian import CodetteGuardian |
| HAS_GUARDIAN = True |
| except ImportError: |
| HAS_GUARDIAN = False |
|
|
| try: |
| from reasoning_forge.resonant_continuity import ResonantContinuityEngine |
| HAS_RESONANCE = True |
| except ImportError: |
| HAS_RESONANCE = False |
|
|
| try: |
| from reasoning_forge.perspective_registry import ( |
| PERSPECTIVES, get_adapter_for_perspective, list_all as list_perspectives |
| ) |
| HAS_PERSPECTIVES = True |
| except ImportError: |
| HAS_PERSPECTIVES = False |
|
|
| try: |
| from reasoning_forge.aegis import AEGIS |
| HAS_AEGIS = True |
| except ImportError: |
| HAS_AEGIS = False |
|
|
| try: |
| from reasoning_forge.nexus import NexusSignalEngine |
| HAS_NEXUS = True |
| except ImportError: |
| HAS_NEXUS = False |
|
|
| |
| AGENT_NAMES = [ |
| "newton", "davinci", "empathy", "philosophy", |
| "quantum", "consciousness", "multi_perspective", "systems_architecture" |
| ] |
|
|
| |
| ADAPTER_COLORS = { |
| "newton": "#3b82f6", |
| "davinci": "#f59e0b", |
| "empathy": "#a855f7", |
| "philosophy": "#10b981", |
| "quantum": "#ef4444", |
| "consciousness": "#e2e8f0", |
| "multi_perspective": "#f97316", |
| "systems_architecture": "#06b6d4", |
| "_base": "#94a3b8", |
| } |
|
|
| DB_PATH = Path(__file__).parent.parent / "data" / "codette_sessions.db" |
|
|
|
|
| class CodetteSession: |
| """Manages a single conversation session with Cocoon state.""" |
|
|
| def __init__(self, session_id: Optional[str] = None): |
| self.session_id = session_id or hashlib.sha256( |
| f"{time.time()}_{os.getpid()}".encode() |
| ).hexdigest()[:16] |
|
|
| self.messages: List[Dict[str, str]] = [] |
| self.created_at = time.time() |
| self.updated_at = time.time() |
|
|
| |
| self.spiderweb = None |
| self.metrics_engine = None |
| self.cocoon_sync = None |
| self.dream_reweaver = None |
| self.optimizer = None |
| self.memory_kernel = None |
| self.guardian = None |
| self.resonance_engine = None |
| self.aegis = None |
| self.nexus = None |
|
|
| |
| self.coherence_history: List[float] = [] |
| self.tension_history: List[float] = [] |
| self.attractors: List[Dict] = [] |
| self.glyphs: List[Dict] = [] |
| self.perspective_usage: Dict[str, int] = {} |
| self.lifeforms: List[str] = [] |
| self.dream_history: List[Dict] = [] |
|
|
| |
| self._init_cocoon() |
|
|
| def _init_cocoon(self): |
| """Initialize Cocoon subsystems if available.""" |
| if HAS_SPIDERWEB: |
| self.spiderweb = QuantumSpiderweb() |
| self.spiderweb.build_from_agents(AGENT_NAMES) |
|
|
| if HAS_METRICS: |
| self.metrics_engine = EpistemicMetrics() |
|
|
| if HAS_COCOON: |
| try: |
| key_mgr = CocoonKeyManager() |
| self.cocoon_sync = CocoonSync( |
| node_id=f"session_{self.session_id}", |
| key_manager=key_mgr, |
| ) |
| except Exception: |
| self.cocoon_sync = None |
|
|
| if HAS_DREAMER: |
| self.dream_reweaver = DreamReweaver(creativity=0.3) |
|
|
| if HAS_OPTIMIZER: |
| self.optimizer = QuantumOptimizer() |
|
|
| if HAS_MEMORY: |
| self.memory_kernel = LivingMemoryKernel(max_memories=100) |
|
|
| if HAS_GUARDIAN: |
| self.guardian = CodetteGuardian() |
|
|
| if HAS_RESONANCE: |
| self.resonance_engine = ResonantContinuityEngine() |
|
|
| if HAS_AEGIS: |
| self.aegis = AEGIS() |
|
|
| if HAS_NEXUS: |
| self.nexus = NexusSignalEngine() |
|
|
| def add_message(self, role: str, content: str, metadata: Optional[Dict] = None): |
| """Add a message to the session history.""" |
| msg = { |
| "role": role, |
| "content": content, |
| "timestamp": time.time(), |
| } |
| if metadata: |
| msg["metadata"] = metadata |
| self.messages.append(msg) |
| self.updated_at = time.time() |
|
|
| def update_after_response(self, route_result, adapter_name: str, |
| perspectives: Optional[Dict[str, str]] = None): |
| """Update Cocoon state after a Codette response. |
| |
| Args: |
| route_result: RouteResult from the router |
| adapter_name: Which adapter was primary |
| perspectives: Dict of adapter_name -> response text (if multi-perspective) |
| """ |
| |
| self.perspective_usage[adapter_name] = \ |
| self.perspective_usage.get(adapter_name, 0) + 1 |
|
|
| if not HAS_SPIDERWEB or self.spiderweb is None: |
| return |
|
|
| |
| try: |
| if adapter_name in self.spiderweb.nodes: |
| node = self.spiderweb.nodes[adapter_name] |
| |
| node.state.psi = min(node.state.psi + 0.1, 2.0) |
| node.state.tau += 0.05 |
|
|
| |
| self.spiderweb.propagate_belief( |
| adapter_name, belief=node.state, max_hops=2 |
| ) |
|
|
| |
| if perspectives and len(perspectives) > 1: |
| adapters = list(perspectives.keys()) |
| for i in range(len(adapters)): |
| for j in range(i + 1, len(adapters)): |
| if (adapters[i] in self.spiderweb.nodes and |
| adapters[j] in self.spiderweb.nodes): |
| self.spiderweb.entangle(adapters[i], adapters[j]) |
|
|
| |
| coherence = self.spiderweb.phase_coherence() |
| self.coherence_history.append(coherence) |
|
|
| |
| self.attractors = self.spiderweb.detect_attractors() |
|
|
| |
| for name in (perspectives or {adapter_name: ""}).keys(): |
| if name in self.spiderweb.nodes: |
| glyph = self.spiderweb.form_glyph(name) |
| if glyph: |
| self.glyphs.append({ |
| "glyph_id": glyph.glyph_id, |
| "source": glyph.source_node, |
| "stability": glyph.stability_score, |
| }) |
|
|
| |
| is_converging, mean_tension = self.spiderweb.check_convergence() |
| self.tension_history.append(mean_tension) |
|
|
| |
| if HAS_OPTIMIZER and self.optimizer: |
| try: |
| signal = QualitySignal( |
| timestamp=time.time(), |
| adapter=adapter_name, |
| coherence=coherence, |
| tension=mean_tension, |
| productivity=0.5, |
| response_length=0, |
| multi_perspective=perspectives is not None and len(perspectives) > 1, |
| user_continued=True, |
| ) |
| self.optimizer.record_signal(signal) |
| except Exception: |
| pass |
|
|
| except Exception as e: |
| print(f" [cocoon] Spiderweb update error: {e}") |
|
|
| |
| if self.resonance_engine: |
| try: |
| coh = self.coherence_history[-1] if self.coherence_history else 0.5 |
| ten = self.tension_history[-1] if self.tension_history else 0.3 |
| self.resonance_engine.compute_psi(coherence=coh, tension=ten) |
| except Exception: |
| pass |
|
|
| |
| if self.guardian: |
| try: |
| coh = self.coherence_history[-1] if self.coherence_history else 0.5 |
| ten = self.tension_history[-1] if self.tension_history else 0.3 |
| self.guardian.evaluate_output(adapter_name, "", coh, ten) |
| except Exception: |
| pass |
|
|
| |
| if self.aegis and self.messages: |
| try: |
| |
| for msg in reversed(self.messages[-4:]): |
| if msg["role"] == "assistant": |
| self.aegis.evaluate(msg["content"], adapter=adapter_name) |
| break |
| except Exception: |
| pass |
|
|
| |
| if self.nexus and self.messages: |
| try: |
| for msg in reversed(self.messages[-4:]): |
| if msg["role"] == "user": |
| self.nexus.analyze(msg["content"], adapter=adapter_name) |
| break |
| except Exception: |
| pass |
|
|
| |
| if self.memory_kernel and self.messages: |
| try: |
| |
| query_text = "" |
| response_text = "" |
| for msg in reversed(self.messages[-4:]): |
| if msg["role"] == "user" and not query_text: |
| query_text = msg["content"] |
| elif msg["role"] == "assistant" and not response_text: |
| response_text = msg["content"] |
| if query_text and response_text: |
| coh = self.coherence_history[-1] if self.coherence_history else 0.5 |
| ten = self.tension_history[-1] if self.tension_history else 0.3 |
| self.memory_kernel.store_from_turn( |
| query=query_text, |
| response=response_text, |
| adapter=adapter_name, |
| coherence=coh, |
| tension=ten, |
| ) |
| except Exception: |
| pass |
|
|
| def compute_epistemic_report(self, analyses: Dict[str, str], |
| synthesis: str = "") -> Optional[Dict]: |
| """Run full epistemic metrics on a multi-perspective response.""" |
| if not HAS_METRICS or self.metrics_engine is None: |
| return None |
|
|
| try: |
| return self.metrics_engine.full_epistemic_report(analyses, synthesis) |
| except Exception as e: |
| print(f" [cocoon] Metrics error: {e}") |
| return None |
|
|
| def get_state(self) -> Dict[str, Any]: |
| """Get full session state for UI rendering.""" |
| state = { |
| "session_id": self.session_id, |
| "message_count": len(self.messages), |
| "created_at": self.created_at, |
| "updated_at": self.updated_at, |
| "perspective_usage": self.perspective_usage, |
| "adapter_colors": ADAPTER_COLORS, |
| "cocoon": { |
| "has_spiderweb": HAS_SPIDERWEB and self.spiderweb is not None, |
| "has_metrics": HAS_METRICS, |
| "has_sync": HAS_COCOON and self.cocoon_sync is not None, |
| }, |
| } |
|
|
| |
| if self.spiderweb: |
| try: |
| web_dict = self.spiderweb.to_dict() |
| state["spiderweb"] = { |
| "nodes": { |
| nid: { |
| |
| "state": n["state"], |
| "neighbors": n.get("neighbors", []), |
| "tension_history": n.get("tension_history", [])[-10:], |
| } |
| for nid, n in web_dict.get("nodes", {}).items() |
| }, |
| "phase_coherence": web_dict.get("phase_coherence", 0), |
| "attractors": self.attractors, |
| "glyphs": self.glyphs[-10:], |
| |
| "entropy": self.spiderweb.shannon_entropy(), |
| "decoherence_rate": self.spiderweb.decoherence_rate(), |
| "lifeforms": self.lifeforms[-20:], |
| } |
| except Exception: |
| state["spiderweb"] = None |
| else: |
| state["spiderweb"] = None |
|
|
| |
| state["metrics"] = { |
| "coherence_history": self.coherence_history[-50:], |
| "tension_history": self.tension_history[-50:], |
| "current_coherence": self.coherence_history[-1] if self.coherence_history else 0, |
| "current_tension": self.tension_history[-1] if self.tension_history else 0, |
| "attractor_count": len(self.attractors), |
| "glyph_count": len(self.glyphs), |
| } |
|
|
| |
| if HAS_OPTIMIZER and self.optimizer: |
| state["optimizer"] = self.optimizer.get_tuning_report() |
| else: |
| state["optimizer"] = None |
|
|
| |
| state["dream_history"] = self.dream_history[-10:] |
|
|
| |
| if self.memory_kernel: |
| state["memory"] = self.memory_kernel.get_state() |
| else: |
| state["memory"] = None |
|
|
| |
| if self.guardian: |
| state["guardian"] = self.guardian.get_state() |
| else: |
| state["guardian"] = None |
|
|
| |
| if self.resonance_engine: |
| state["resonance"] = self.resonance_engine.get_state() |
| else: |
| state["resonance"] = None |
|
|
| |
| if self.aegis: |
| state["aegis"] = self.aegis.get_state() |
| else: |
| state["aegis"] = None |
|
|
| |
| if self.nexus: |
| state["nexus"] = self.nexus.get_state() |
| else: |
| state["nexus"] = None |
|
|
| |
| if HAS_PERSPECTIVES: |
| state["perspectives_available"] = len(PERSPECTIVES) |
|
|
| return state |
|
|
| def to_dict(self) -> Dict: |
| """Serialize for storage.""" |
| data = { |
| "session_id": self.session_id, |
| "created_at": self.created_at, |
| "updated_at": self.updated_at, |
| "messages": self.messages, |
| "coherence_history": self.coherence_history, |
| "tension_history": self.tension_history, |
| "attractors": self.attractors, |
| "glyphs": self.glyphs, |
| "perspective_usage": self.perspective_usage, |
| "lifeforms": self.lifeforms, |
| "dream_history": self.dream_history, |
| } |
| if self.spiderweb: |
| try: |
| data["spiderweb_state"] = self.spiderweb.to_dict() |
| except Exception: |
| pass |
| if HAS_OPTIMIZER and self.optimizer: |
| try: |
| data["optimizer_state"] = self.optimizer.to_dict() |
| except Exception: |
| pass |
| if self.memory_kernel: |
| try: |
| data["memory_state"] = self.memory_kernel.to_dict() |
| except Exception: |
| pass |
| if self.guardian: |
| try: |
| data["guardian_state"] = self.guardian.to_dict() |
| except Exception: |
| pass |
| if self.resonance_engine: |
| try: |
| data["resonance_state"] = self.resonance_engine.to_dict() |
| except Exception: |
| pass |
| if self.aegis: |
| try: |
| data["aegis_state"] = self.aegis.to_dict() |
| except Exception: |
| pass |
| if self.nexus: |
| try: |
| data["nexus_state"] = self.nexus.to_dict() |
| except Exception: |
| pass |
| return data |
|
|
| def from_dict(self, data: Dict): |
| """Restore from storage.""" |
| self.session_id = data.get("session_id", self.session_id) |
| self.created_at = data.get("created_at", self.created_at) |
| self.updated_at = data.get("updated_at", self.updated_at) |
| self.messages = data.get("messages", []) |
| self.coherence_history = data.get("coherence_history", []) |
| self.tension_history = data.get("tension_history", []) |
| self.attractors = data.get("attractors", []) |
| self.glyphs = data.get("glyphs", []) |
| self.perspective_usage = data.get("perspective_usage", {}) |
| self.lifeforms = data.get("lifeforms", []) |
| self.dream_history = data.get("dream_history", []) |
|
|
| if self.spiderweb and "spiderweb_state" in data: |
| try: |
| self.spiderweb = QuantumSpiderweb.from_dict(data["spiderweb_state"]) |
| except Exception: |
| pass |
| if HAS_OPTIMIZER and self.optimizer and "optimizer_state" in data: |
| try: |
| self.optimizer = QuantumOptimizer.from_dict(data["optimizer_state"]) |
| except Exception: |
| pass |
| if HAS_MEMORY and "memory_state" in data: |
| try: |
| self.memory_kernel = LivingMemoryKernel.from_dict(data["memory_state"]) |
| except Exception: |
| pass |
| if HAS_GUARDIAN and "guardian_state" in data: |
| try: |
| self.guardian = CodetteGuardian.from_dict(data["guardian_state"]) |
| except Exception: |
| pass |
| if HAS_RESONANCE and "resonance_state" in data: |
| try: |
| self.resonance_engine = ResonantContinuityEngine.from_dict(data["resonance_state"]) |
| except Exception: |
| pass |
| if HAS_AEGIS and "aegis_state" in data: |
| try: |
| self.aegis = AEGIS.from_dict(data["aegis_state"]) |
| except Exception: |
| pass |
| if HAS_NEXUS and "nexus_state" in data: |
| try: |
| self.nexus = NexusSignalEngine.from_dict(data["nexus_state"]) |
| except Exception: |
| pass |
|
|
|
|
| class SessionStore: |
| """SQLite-backed session persistence with Cocoon encryption.""" |
|
|
| def __init__(self, db_path: Optional[Path] = None): |
| self.db_path = db_path or DB_PATH |
| self.db_path.parent.mkdir(parents=True, exist_ok=True) |
| self._init_db() |
|
|
| def _init_db(self): |
| """Create sessions table if needed.""" |
| conn = sqlite3.connect(str(self.db_path)) |
| conn.execute(""" |
| CREATE TABLE IF NOT EXISTS sessions ( |
| session_id TEXT PRIMARY KEY, |
| created_at REAL, |
| updated_at REAL, |
| title TEXT, |
| data TEXT |
| ) |
| """) |
| conn.commit() |
| conn.close() |
|
|
| def save(self, session: CodetteSession, title: Optional[str] = None): |
| """Save a session to the database.""" |
| if title is None: |
| |
| for msg in session.messages: |
| if msg["role"] == "user": |
| title = msg["content"][:80] |
| break |
| title = title or f"Session {session.session_id[:8]}" |
|
|
| data_json = json.dumps(session.to_dict()) |
|
|
| conn = sqlite3.connect(str(self.db_path)) |
| conn.execute(""" |
| INSERT OR REPLACE INTO sessions (session_id, created_at, updated_at, title, data) |
| VALUES (?, ?, ?, ?, ?) |
| """, (session.session_id, session.created_at, session.updated_at, title, data_json)) |
| conn.commit() |
| conn.close() |
|
|
| def load(self, session_id: str) -> Optional[CodetteSession]: |
| """Load a session from the database.""" |
| conn = sqlite3.connect(str(self.db_path)) |
| row = conn.execute( |
| "SELECT data FROM sessions WHERE session_id = ?", (session_id,) |
| ).fetchone() |
| conn.close() |
|
|
| if not row: |
| return None |
|
|
| session = CodetteSession(session_id) |
| session.from_dict(json.loads(row[0])) |
| return session |
|
|
| def list_sessions(self, limit: int = 20) -> List[Dict]: |
| """List recent sessions.""" |
| conn = sqlite3.connect(str(self.db_path)) |
| rows = conn.execute(""" |
| SELECT session_id, created_at, updated_at, title |
| FROM sessions ORDER BY updated_at DESC LIMIT ? |
| """, (limit,)).fetchall() |
| conn.close() |
|
|
| return [ |
| { |
| "session_id": r[0], |
| "created_at": r[1], |
| "updated_at": r[2], |
| "title": r[3], |
| } |
| for r in rows |
| ] |
|
|
| def delete(self, session_id: str): |
| """Delete a session.""" |
| conn = sqlite3.connect(str(self.db_path)) |
| conn.execute("DELETE FROM sessions WHERE session_id = ?", (session_id,)) |
| conn.commit() |
| conn.close() |
|
|
|
|
| |
| if __name__ == "__main__": |
| print("Testing CodetteSession...") |
| session = CodetteSession() |
| print(f" Session ID: {session.session_id}") |
| print(f" Spiderweb: {HAS_SPIDERWEB}") |
| print(f" Metrics: {HAS_METRICS}") |
| print(f" Cocoon: {HAS_COCOON}") |
|
|
| session.add_message("user", "How does gravity work?") |
| session.add_message("assistant", "Objects attract each other...", |
| metadata={"adapter": "newton", "confidence": 0.95}) |
|
|
| state = session.get_state() |
| print(f" State keys: {list(state.keys())}") |
| print(f" Cocoon status: {state['cocoon']}") |
|
|
| if state["spiderweb"]: |
| print(f" Nodes: {list(state['spiderweb']['nodes'].keys())}") |
| print(f" Phase coherence: {state['spiderweb']['phase_coherence']:.4f}") |
|
|
| |
| store = SessionStore() |
| store.save(session) |
| loaded = store.load(session.session_id) |
| print(f" Persistence: {'OK' if loaded else 'FAILED'}") |
| if loaded: |
| print(f" Loaded messages: {len(loaded.messages)}") |
|
|
| print("Done!") |
|
|