diff --git a/app.js b/app.js index 085ed8f..63a6b1b 100644 --- a/app.js +++ b/app.js @@ -41,6 +41,7 @@ let harnessPulseMesh; let powerMeterBars = []; let particles, dustParticles; let debugOverlay; +let spatialSchema; // Project Mnemosyne — Spatial Memory Schema let frameCount = 0, lastFPSTime = 0, fps = 0; let chatOpen = true; let loadProgress = 0; @@ -592,6 +593,192 @@ let pseLayer; let metaLayer, neuroBridge, cbr, symbolicPlanner, knowledgeGraph, blackboard, symbolicEngine, calibrator; let agentFSMs = {}; + +// ═══════════════════════════════════════════ +// PROJECT MNEMOSYNE — SPATIAL MEMORY SCHEMA +// ═══════════════════════════════════════════ + +/** + * SpatialMemorySchema — Maps memory categories to persistent 3D world regions. + * Each memory type has a dedicated "zone" in the Nexus, so recalled memories + * always appear in their neighborhood. Zones persist via localStorage. + */ +class SpatialMemorySchema { + constructor() { + this.zones = new Map(); + this.zoneVisuals = []; + this.STORAGE_KEY = 'nexus_spatial_memory_zones'; + + // Default zones mapped to Nexus regions + this._defineDefaultZones(); + this._loadPersistedZones(); + } + + _defineDefaultZones() { + // Workshop area — conversations and chat memories + this.registerZone('conversations', new THREE.Vector3(0, 0, -18), 6, 0x4af0c0, { + description: 'Chat history and conversation memories', + icon: '💬' + }); + + // Archive region — skills and procedures + this.registerZone('skills', new THREE.Vector3(23, 0, 0), 6, 0x0066ff, { + description: 'Learned skills and procedural knowledge', + icon: '⚙️' + }); + + // Central hub — environment facts + this.registerZone('facts', new THREE.Vector3(0, 0, 0), 5, 0xffd700, { + description: 'Environmental facts and stable knowledge', + icon: '📋' + }); + + // Near player start — user preferences and corrections + this.registerZone('preferences', new THREE.Vector3(-8, 0, 8), 4, 0x7b5cff, { + description: 'User preferences and corrections', + icon: '🎯' + }); + + // Outer ring — transient/session data + this.registerZone('transient', new THREE.Vector3(0, 0, 20), 8, 0xff4466, { + description: 'Session data and temporary memories (fades quickly)', + icon: '⏳' + }); + + // Chapel area — deep/sacred memories + this.registerZone('deep', new THREE.Vector3(-20, 0, 0), 5, 0xff8800, { + description: 'Deep memories, insights, and important events', + icon: '🔮' + }); + } + + registerZone(name, center, radius, color, metadata = {}) { + this.zones.set(name, { + name, + center: center.clone(), + radius, + color, + metadata, + orbCount: 0, + createdAt: Date.now() + }); + } + + /** + * Route a memory object to its zone based on type. + * @param {object} memoryObj — { type, source, content, score, ... } + * @returns {{ zone: string, position: THREE.Vector3, color: number }} + */ + assignMemory(memoryObj) { + const type = (memoryObj.type || 'facts').toLowerCase(); + const zone = this.zones.get(type) || this.zones.get('facts'); + zone.orbCount++; + + // Jittered position within zone — spread orbs so they don't stack + const angle = Math.random() * Math.PI * 2; + const dist = Math.random() * zone.radius * 0.8; + const position = new THREE.Vector3( + zone.center.x + Math.cos(angle) * dist, + 1.5 + Math.random() * 2, + zone.center.z + Math.sin(angle) * dist + ); + + return { zone: zone.name, position, color: zone.color }; + } + + getZonePosition(type) { + const zone = this.zones.get(type) || this.zones.get('facts'); + return zone.center.clone(); + } + + listZones() { + const result = []; + this.zones.forEach((z, name) => { + result.push({ + name, + center: { x: z.center.x, y: z.center.y, z: z.center.z }, + radius: z.radius, + color: '#' + z.color.toString(16).padStart(6, '0'), + orbCount: z.orbCount, + description: z.metadata.description + }); + }); + return result; + } + + /** + * Draw subtle ground rings at each zone boundary. + * Call once after scene is initialized. + */ + visualizeZones() { + if (typeof scene === 'undefined') return; + + this.zones.forEach((zone, name) => { + // Ground ring + const ringGeo = new THREE.RingGeometry(zone.radius - 0.15, zone.radius + 0.15, 64); + const ringMat = new THREE.MeshBasicMaterial({ + color: zone.color, + transparent: true, + opacity: 0.25, + side: THREE.DoubleSide + }); + const ring = new THREE.Mesh(ringGeo, ringMat); + ring.rotation.x = -Math.PI / 2; + ring.position.copy(zone.center); + ring.position.y = 0.02; + scene.add(ring); + + // Inner glow disc + const discGeo = new THREE.CircleGeometry(zone.radius, 64); + const discMat = new THREE.MeshBasicMaterial({ + color: zone.color, + transparent: true, + opacity: 0.04, + side: THREE.DoubleSide + }); + const disc = new THREE.Mesh(discGeo, discMat); + disc.rotation.x = -Math.PI / 2; + disc.position.copy(zone.center); + disc.position.y = 0.01; + scene.add(disc); + + this.zoneVisuals.push(ring, disc); + }); + + console.info('[Mnemosyne] Zone visualization created for', this.zones.size, 'zones'); + } + + _persistZones() { + try { + const data = {}; + this.zones.forEach((z, name) => { + data[name] = { + center: { x: z.center.x, y: z.center.y, z: z.center.z }, + radius: z.radius, + orbCount: z.orbCount + }; + }); + localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data)); + } catch (e) { /* storage full or unavailable */ } + } + + _loadPersistedZones() { + try { + const raw = localStorage.getItem(this.STORAGE_KEY); + if (!raw) return; + const data = JSON.parse(raw); + Object.entries(data).forEach(([name, saved]) => { + const zone = this.zones.get(name); + if (zone && saved.orbCount) { + zone.orbCount = saved.orbCount; + } + }); + } catch (e) { /* corrupt or missing */ } + } +} + + + function setupGOFAI() { knowledgeGraph = new KnowledgeGraph(); blackboard = new Blackboard(); @@ -606,6 +793,10 @@ function setupGOFAI() { pseLayer = new PSELayer(); calibrator = new AdaptiveCalibrator('nexus-v1', { base_rate: 0.05 }); + // Initialize Spatial Memory Schema (Project Mnemosyne) + spatialSchema = new SpatialMemorySchema(); + console.info('[Mnemosyne] Spatial Memory Schema initialized with', spatialSchema.zones.size, 'zones'); + // Setup initial facts symbolicEngine.addFact('energy', 100); symbolicEngine.addFact('stability', 1.0); @@ -2936,6 +3127,11 @@ function updateAshStorm(delta, elapsed) { init().then(() => { createAshStorm(); createPortalTunnel(); + + // Visualize memory zones if schema is ready + if (spatialSchema) { + spatialSchema.visualizeZones(); + } fetchGiteaData(); setInterval(fetchGiteaData, 30000); runWeeklyAudit(); diff --git a/spatial-memory-schema.json b/spatial-memory-schema.json new file mode 100644 index 0000000..d92ff55 --- /dev/null +++ b/spatial-memory-schema.json @@ -0,0 +1,312 @@ +{ + "version": "1.0.0", + "project": "Mnemosyne", + "description": "Spatial memory schema for holographic memory visualization", + "rooms": { + "library": { + "name": "The Library", + "category": "user_pref", + "description": "User preferences and personal settings", + "visual_theme": { + "lighting": "soft_ambient", + "colors": { + "primary": "#8B4513", + "secondary": "#DAA520", + "accent": "#FFD700", + "particle": "#FFE4B5" + }, + "materials": { + "floor": "dark_wood", + "walls": "bookshelf", + "ceiling": "vaulted_stone" + }, + "particle_effects": [ + "dust_motes", + "book_sparkles" + ] + }, + "spatial_bounds": { + "center": [ + 0, + 0, + 0 + ], + "dimensions": [ + 20, + 10, + 20 + ], + "orb_density": 0.7 + }, + "object_types": { + "preference": { + "shape": "sphere", + "base_size": 0.3, + "glow_intensity": 0.8 + }, + "setting": { + "shape": "cube", + "base_size": 0.4, + "glow_intensity": 0.6 + } + } + }, + "workshop": { + "name": "The Workshop", + "category": "project", + "description": "Active projects and development work", + "visual_theme": { + "lighting": "bright_work", + "colors": { + "primary": "#4682B4", + "secondary": "#B0C4DE", + "accent": "#00BFFF", + "particle": "#87CEEB" + }, + "materials": { + "floor": "polished_concrete", + "walls": "blueprint_paper", + "ceiling": "industrial_metal" + }, + "particle_effects": [ + "blueprint_lines", + "tool_sparks" + ] + }, + "spatial_bounds": { + "center": [ + 30, + 0, + 0 + ], + "dimensions": [ + 25, + 12, + 25 + ], + "orb_density": 0.8 + }, + "object_types": { + "project": { + "shape": "pyramid", + "base_size": 0.5, + "glow_intensity": 0.9 + }, + "task": { + "shape": "cube", + "base_size": 0.3, + "glow_intensity": 0.7 + } + } + }, + "armory": { + "name": "The Armory", + "category": "tool", + "description": "Tools, skills, and capabilities", + "visual_theme": { + "lighting": "neon_glow", + "colors": { + "primary": "#2E8B57", + "secondary": "#3CB371", + "accent": "#00FF7F", + "particle": "#98FB98" + }, + "materials": { + "floor": "chrome_grid", + "walls": "server_rack", + "ceiling": "neon_tube" + }, + "particle_effects": [ + "data_streams", + "circuit_traces" + ] + }, + "spatial_bounds": { + "center": [ + 0, + 0, + 30 + ], + "dimensions": [ + 15, + 8, + 15 + ], + "orb_density": 0.6 + }, + "object_types": { + "tool": { + "shape": "octahedron", + "base_size": 0.4, + "glow_intensity": 1.0 + }, + "skill": { + "shape": "sphere", + "base_size": 0.35, + "glow_intensity": 0.85 + } + } + }, + "commons": { + "name": "The Commons", + "category": "general", + "description": "General knowledge and miscellaneous facts", + "visual_theme": { + "lighting": "natural_daylight", + "colors": { + "primary": "#9370DB", + "secondary": "#BA55D3", + "accent": "#DA70D6", + "particle": "#E6E6FA" + }, + "materials": { + "floor": "grass", + "walls": "floating_islands", + "ceiling": "open_sky" + }, + "particle_effects": [ + "floating_pollen", + "lightning_bugs" + ] + }, + "spatial_bounds": { + "center": [ + 30, + 0, + 30 + ], + "dimensions": [ + 30, + 15, + 30 + ], + "orb_density": 0.5 + }, + "object_types": { + "fact": { + "shape": "sphere", + "base_size": 0.25, + "glow_intensity": 0.7 + }, + "memory": { + "shape": "dodecahedron", + "base_size": 0.3, + "glow_intensity": 0.65 + } + } + } + }, + "object_properties": { + "trust_mapping": { + "description": "Maps trust score (0.0-1.0) to visual properties", + "glow_intensity": { + "min": 0.2, + "max": 1.0, + "curve": "linear" + }, + "opacity": { + "min": 0.3, + "max": 1.0, + "curve": "ease_in_out" + } + }, + "importance_mapping": { + "description": "Maps importance (relation count) to visual properties", + "scale": { + "min": 0.2, + "max": 2.0, + "curve": "logarithmic" + }, + "particle_density": { + "min": 10, + "max": 100, + "curve": "linear" + } + }, + "lifecycle_events": { + "FACT_CREATED": { + "animation": "fade_in", + "duration": 1.5, + "particle_effect": "spawn_burst" + }, + "FACT_UPDATED": { + "animation": "pulse_glow", + "duration": 0.8, + "particle_effect": "update_ripple" + }, + "FACT_REMOVED": { + "animation": "dissolve", + "duration": 2.0, + "particle_effect": "scatter" + }, + "FACT_RECALLED": { + "animation": "beam_light", + "duration": 1.0, + "particle_effect": "recall_beam" + } + } + }, + "connections": { + "holographic_threads": { + "description": "Visual connections between related memory orbs", + "material": "transparent_glow", + "colors": { + "strong_relation": "#00FFFF", + "medium_relation": "#00CED1", + "weak_relation": "#5F9EA0" + }, + "thickness": { + "min": 0.02, + "max": 0.1, + "curve": "linear" + } + }, + "cross_room_portals": { + "description": "Portals connecting different memory rooms", + "effect": "swirling_vortex", + "colors": { + "library_workshop": "#FFD700", + "workshop_armory": "#00BFFF", + "armory_commons": "#00FF7F", + "commons_library": "#DA70D6" + } + } + }, + "rag_integration": { + "retrieval_visualization": { + "description": "How RAG retrieval results are visualized", + "highlight_effect": "golden_glow", + "spiral_arrangement": { + "radius": 3.0, + "height_step": 0.5, + "rotation_step": 0.618033988749895 + }, + "relevance_scoring": { + "high": { + "color": "#FFD700", + "size_multiplier": 1.5 + }, + "medium": { + "color": "#FFA500", + "size_multiplier": 1.2 + }, + "low": { + "color": "#FF8C00", + "size_multiplier": 1.0 + } + } + }, + "query_beam": { + "description": "Beam from user to relevant memory orbs", + "color": "#FFFFFF", + "opacity": 0.8, + "pulse_frequency": 2.0 + } + }, + "animation_timing": { + "orb_spawn_delay": 0.1, + "room_transition_duration": 2.0, + "connection_draw_speed": 0.5, + "particle_fade_time": 1.5 + } +} \ No newline at end of file