2026-04-12 16:20:03 +00:00
import ResonanceVisualizer from './nexus/components/resonance-visualizer.js' ; \ nimport * as THREE from 'three' ;
2026-03-28 20:50:56 +00:00
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js' ;
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js' ;
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js' ;
import { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js' ;
2026-04-10 07:41:13 +00:00
import { SpatialMemory } from './nexus/components/spatial-memory.js' ;
2026-04-12 12:52:39 -04:00
import { SpatialAudio } from './nexus/components/spatial-audio.js' ;
2026-04-11 19:48:46 +00:00
import { MemoryBirth } from './nexus/components/memory-birth.js' ;
2026-04-11 01:39:58 +00:00
import { MemoryOptimizer } from './nexus/components/memory-optimizer.js' ;
2026-04-11 21:17:42 +00:00
import { MemoryInspect } from './nexus/components/memory-inspect.js' ;
2026-04-12 06:45:25 +00:00
import { MemoryPulse } from './nexus/components/memory-pulse.js' ;
2026-04-13 18:23:05 -04:00
import { ReasoningTrace } from './nexus/components/reasoning-trace.js' ;
2026-03-28 20:50:56 +00:00
// ═══════════════════════════════════════════
// NEXUS v1.1 — Portal System Update
// ═══════════════════════════════════════════
const NEXUS = {
colors : {
primary : 0x4af0c0 ,
secondary : 0x7b5cff ,
bg : 0x050510 ,
panelBg : 0x0a0f28 ,
nebula1 : 0x1a0a3e ,
nebula2 : 0x0a1a3e ,
gold : 0xffd700 ,
danger : 0xff4466 ,
gridLine : 0x1a2a4a ,
}
} ;
// ═══ STATE ═══
let camera , scene , renderer , composer ;
let clock , playerPos , playerRot ;
let keys = { } ;
let mouseDown = false ;
let batcaveTerminals = [ ] ;
let portals = [ ] ; // Registry of active portals
let visionPoints = [ ] ; // Registry of vision points
let agents = [ ] ; // Registry of agent presences
let activePortal = null ; // Portal currently in proximity
let activeVisionPoint = null ; // Vision point currently in proximity
let portalOverlayActive = false ;
let visionOverlayActive = false ;
let atlasOverlayActive = false ;
let thoughtStreamMesh ;
let harnessPulseMesh ;
let powerMeterBars = [ ] ;
let particles , dustParticles ;
let debugOverlay ;
let frameCount = 0 , lastFPSTime = 0 , fps = 0 ;
let chatOpen = true ;
2026-04-11 01:35:13 +00:00
let memoryFeedEntries = [ ] ; // Mnemosyne: recent memory events for feed panel
2026-04-11 04:10:49 +00:00
let _memoryFilterOpen = false ; // Mnemosyne: filter panel state
2026-04-11 21:17:42 +00:00
let _clickStartX = 0 , _clickStartY = 0 ; // Mnemosyne: click-vs-drag detection
2026-03-28 20:50:56 +00:00
let loadProgress = 0 ;
let performanceTier = 'high' ;
2026-04-12 12:13:40 -04:00
/** Escape HTML entities for safe innerHTML insertion. */
function escHtml ( s ) {
2026-04-13 08:30:22 +00:00
return String ( s ) . replace ( /&/g , '&' ) . replace ( /</g , '<' ) . replace ( />/g , '>' ) . replace ( /"/g , '"' ) . replace ( /'/g , ''' ) ;
2026-04-12 12:13:40 -04:00
}
2026-03-28 20:50:56 +00:00
// ═══ HERMES WS STATE ═══
let hermesWs = null ;
let wsReconnectTimer = null ;
let wsConnected = false ;
2026-04-12 11:54:27 -04:00
// ═══ EVENNIA ROOM STATE ═══
let evenniaRoom = null ; // {title, desc, exits[], objects[], occupants[], timestamp, roomKey}
let evenniaConnected = false ;
let evenniaStaleTimer = null ;
const EVENNIA _STALE _MS = 60000 ; // mark stale after 60s without update
2026-03-28 20:50:56 +00:00
let recentToolOutputs = [ ] ;
2026-04-12 12:13:40 -04:00
let actionStreamEntries = [ ] ; // Evennia command/result flow for action stream panel
let actionStreamRoom = '' ; // Current room from movement events
2026-03-28 20:50:56 +00:00
let workshopPanelCtx = null ;
let workshopPanelTexture = null ;
let workshopPanelCanvas = null ;
let workshopScanMat = null ;
let workshopPanelRefreshTimer = 0 ;
let lastFocusedPortal = null ;
2026-04-12 11:52:13 -04:00
// ═══ VISITOR / OPERATOR MODE ═══
let uiMode = 'visitor' ; // 'visitor' | 'operator'
2026-03-28 20:50:56 +00:00
// ═══ NAVIGATION SYSTEM ═══
const NAV _MODES = [ 'walk' , 'orbit' , 'fly' ] ;
let navModeIdx = 0 ;
const orbitState = {
target : new THREE . Vector3 ( 0 , 2 , 0 ) ,
radius : 14 ,
theta : Math . PI ,
phi : Math . PI / 6 ,
minR : 3 ,
maxR : 40 ,
lastX : 0 ,
lastY : 0 ,
} ;
let flyY = 2 ;
// ═══ INIT ═══
2026-03-30 03:04:27 +00:00
2026-04-12 23:28:40 +00:00
import {
SymbolicEngine , AgentFSM , KnowledgeGraph , Blackboard ,
SymbolicPlanner , HTNPlanner , CaseBasedReasoner ,
NeuroSymbolicBridge , MetaReasoningLayer
} from './nexus/symbolic-engine.js' ;
2026-03-30 03:04:27 +00:00
// ═══ SOVEREIGN SYMBOLIC ENGINE (GOFAI) ═══
class SymbolicEngine {
constructor ( ) {
this . facts = new Map ( ) ;
this . factIndices = new Map ( ) ;
this . factMask = 0 n ;
this . rules = [ ] ;
this . reasoningLog = [ ] ;
}
addFact ( key , value ) {
this . facts . set ( key , value ) ;
if ( ! this . factIndices . has ( key ) ) {
this . factIndices . set ( key , BigInt ( this . factIndices . size ) ) ;
}
const bitIndex = this . factIndices . get ( key ) ;
if ( value ) {
this . factMask |= ( 1 n << bitIndex ) ;
} else {
this . factMask &= ~ ( 1 n << bitIndex ) ;
}
}
2026-04-13 00:26:36 +00:00
addRule ( condition , action , description , triggerFacts = [ ] ) {
this . rules . push ( { condition , action , description , triggerFacts } ) ;
2026-03-30 03:04:27 +00:00
}
reason ( ) {
this . rules . forEach ( rule => {
if ( rule . condition ( this . facts ) ) {
const result = rule . action ( this . facts ) ;
if ( result ) {
this . logReasoning ( rule . description , result ) ;
}
}
} ) ;
}
logReasoning ( ruleDesc , outcome ) {
const entry = { timestamp : Date . now ( ) , rule : ruleDesc , outcome : outcome } ;
this . reasoningLog . unshift ( entry ) ;
if ( this . reasoningLog . length > 5 ) this . reasoningLog . pop ( ) ;
const container = document . getElementById ( 'symbolic-log-content' ) ;
if ( container ) {
const logDiv = document . createElement ( 'div' ) ;
logDiv . className = 'symbolic-log-entry' ;
logDiv . innerHTML = ` <span class="symbolic-rule">[RULE] ${ ruleDesc } </span><span class="symbolic-outcome">→ ${ outcome } </span> ` ;
container . prepend ( logDiv ) ;
if ( container . children . length > 5 ) container . lastElementChild . remove ( ) ;
}
}
}
class AgentFSM {
constructor ( agentId , initialState ) {
this . agentId = agentId ;
this . state = initialState ;
this . transitions = { } ;
}
addTransition ( fromState , toState , condition ) {
if ( ! this . transitions [ fromState ] ) this . transitions [ fromState ] = [ ] ;
this . transitions [ fromState ] . push ( { toState , condition } ) ;
}
update ( facts ) {
const possibleTransitions = this . transitions [ this . state ] || [ ] ;
for ( const transition of possibleTransitions ) {
if ( transition . condition ( facts ) ) {
console . log ( ` [FSM] Agent ${ this . agentId } transitioning: ${ this . state } -> ${ transition . toState } ` ) ;
this . state = transition . toState ;
return true ;
}
}
return false ;
}
}
class KnowledgeGraph {
constructor ( ) {
this . nodes = new Map ( ) ;
this . edges = [ ] ;
}
addNode ( id , type , metadata = { } ) {
this . nodes . set ( id , { id , type , ... metadata } ) ;
}
addEdge ( from , to , relation ) {
this . edges . push ( { from , to , relation } ) ;
}
query ( from , relation ) {
return this . edges
. filter ( e => e . from === from && e . relation === relation )
. map ( e => this . nodes . get ( e . to ) ) ;
}
}
class Blackboard {
constructor ( ) {
this . data = { } ;
this . subscribers = [ ] ;
}
write ( key , value , source ) {
const oldValue = this . data [ key ] ;
this . data [ key ] = value ;
this . notify ( key , value , oldValue , source ) ;
}
read ( key ) { return this . data [ key ] ; }
subscribe ( callback ) { this . subscribers . push ( callback ) ; }
notify ( key , value , oldValue , source ) {
this . subscribers . forEach ( sub => sub ( key , value , oldValue , source ) ) ;
const container = document . getElementById ( 'blackboard-log-content' ) ;
if ( container ) {
const entry = document . createElement ( 'div' ) ;
entry . className = 'blackboard-entry' ;
entry . innerHTML = ` <span class="bb-source">[ ${ source } ]</span> <span class="bb-key"> ${ key } </span>: <span class="bb-value"> ${ JSON . stringify ( value ) } </span> ` ;
container . prepend ( entry ) ;
if ( container . children . length > 8 ) container . lastElementChild . remove ( ) ;
}
}
}
class SymbolicPlanner {
constructor ( ) {
this . actions = [ ] ;
this . currentPlan = [ ] ;
}
addAction ( name , preconditions , effects ) {
this . actions . push ( { name , preconditions , effects } ) ;
}
heuristic ( state , goal ) {
let h = 0 ;
for ( let key in goal ) {
if ( state [ key ] !== goal [ key ] ) {
h += Math . abs ( ( state [ key ] || 0 ) - ( goal [ key ] || 0 ) ) ;
}
}
return h ;
}
findPlan ( initialState , goalState ) {
let openSet = [ { state : initialState , plan : [ ] , g : 0 , h : this . heuristic ( initialState , goalState ) } ] ;
let visited = new Map ( ) ;
visited . set ( JSON . stringify ( initialState ) , 0 ) ;
while ( openSet . length > 0 ) {
openSet . sort ( ( a , b ) => ( a . g + a . h ) - ( b . g + b . h ) ) ;
let { state , plan , g } = openSet . shift ( ) ;
if ( this . isGoalReached ( state , goalState ) ) return plan ;
for ( let action of this . actions ) {
if ( this . arePreconditionsMet ( state , action . preconditions ) ) {
let nextState = { ... state , ... action . effects } ;
let stateStr = JSON . stringify ( nextState ) ;
let nextG = g + 1 ;
if ( ! visited . has ( stateStr ) || nextG < visited . get ( stateStr ) ) {
visited . set ( stateStr , nextG ) ;
openSet . push ( {
state : nextState ,
plan : [ ... plan , action . name ] ,
g : nextG ,
h : this . heuristic ( nextState , goalState )
} ) ;
}
}
}
}
return null ;
}
isGoalReached ( state , goal ) {
for ( let key in goal ) {
if ( state [ key ] !== goal [ key ] ) return false ;
}
return true ;
}
arePreconditionsMet ( state , preconditions ) {
for ( let key in preconditions ) {
if ( state [ key ] < preconditions [ key ] ) return false ;
}
return true ;
}
logPlan ( plan ) {
this . currentPlan = plan ;
const container = document . getElementById ( 'planner-log-content' ) ;
if ( container ) {
container . innerHTML = '' ;
if ( ! plan || plan . length === 0 ) {
container . innerHTML = '<div class="planner-empty">NO ACTIVE PLAN</div>' ;
return ;
}
plan . forEach ( ( step , i ) => {
const div = document . createElement ( 'div' ) ;
div . className = 'planner-step' ;
div . innerHTML = ` <span class="step-num"> ${ i + 1 } .</span> ${ step } ` ;
container . appendChild ( div ) ;
} ) ;
}
}
}
class HTNPlanner {
constructor ( ) {
this . methods = { } ;
this . primitiveTasks = { } ;
}
addMethod ( taskName , preconditions , subtasks ) {
if ( ! this . methods [ taskName ] ) this . methods [ taskName ] = [ ] ;
this . methods [ taskName ] . push ( { preconditions , subtasks } ) ;
}
addPrimitiveTask ( taskName , preconditions , effects ) {
this . primitiveTasks [ taskName ] = { preconditions , effects } ;
}
findPlan ( initialState , tasks ) {
return this . decompose ( initialState , tasks , [ ] ) ;
}
decompose ( state , tasks , plan ) {
if ( tasks . length === 0 ) return plan ;
const [ task , ... remainingTasks ] = tasks ;
if ( this . primitiveTasks [ task ] ) {
const { preconditions , effects } = this . primitiveTasks [ task ] ;
if ( this . arePreconditionsMet ( state , preconditions ) ) {
const nextState = { ... state , ... effects } ;
return this . decompose ( nextState , remainingTasks , [ ... plan , task ] ) ;
}
return null ;
}
const methods = this . methods [ task ] || [ ] ;
for ( const method of methods ) {
if ( this . arePreconditionsMet ( state , method . preconditions ) ) {
const result = this . decompose ( state , [ ... method . subtasks , ... remainingTasks ] , plan ) ;
if ( result ) return result ;
}
}
return null ;
}
arePreconditionsMet ( state , preconditions ) {
for ( const key in preconditions ) {
if ( state [ key ] < ( preconditions [ key ] || 0 ) ) return false ;
}
return true ;
}
}
class CaseBasedReasoner {
constructor ( ) {
this . caseLibrary = [ ] ;
}
addCase ( situation , action , outcome ) {
this . caseLibrary . push ( { situation , action , outcome , timestamp : Date . now ( ) } ) ;
}
findSimilarCase ( currentSituation ) {
let bestMatch = null ;
let maxSimilarity = - 1 ;
this . caseLibrary . forEach ( c => {
let similarity = this . calculateSimilarity ( currentSituation , c . situation ) ;
if ( similarity > maxSimilarity ) {
maxSimilarity = similarity ;
bestMatch = c ;
}
} ) ;
return maxSimilarity > 0.7 ? bestMatch : null ;
}
calculateSimilarity ( s1 , s2 ) {
let score = 0 , total = 0 ;
for ( let key in s1 ) {
if ( s2 [ key ] !== undefined ) {
score += 1 - Math . abs ( s1 [ key ] - s2 [ key ] ) ;
total += 1 ;
}
}
return total > 0 ? score / total : 0 ;
}
logCase ( c ) {
const container = document . getElementById ( 'cbr-log-content' ) ;
if ( container ) {
const div = document . createElement ( 'div' ) ;
div . className = 'cbr-entry' ;
div . innerHTML = `
< div class = "cbr-match" > SIMILAR CASE FOUND ( $ { ( this . calculateSimilarity ( symbolicEngine . facts , c . situation ) * 100 ) . toFixed ( 0 ) } % ) < / d i v >
< div class = "cbr-action" > SUGGESTED : $ { c . action } < / d i v >
< div class = "cbr-outcome" > PREVIOUS OUTCOME : $ { c . outcome } < / d i v >
` ;
container . prepend ( div ) ;
if ( container . children . length > 3 ) container . lastElementChild . remove ( ) ;
}
}
}
class NeuroSymbolicBridge {
constructor ( symbolicEngine , blackboard ) {
this . engine = symbolicEngine ;
this . blackboard = blackboard ;
this . perceptionLog = [ ] ;
}
perceive ( rawState ) {
2026-04-13 00:26:36 +00:00
Object . entries ( rawState ) . forEach ( ( [ key , value ] ) => this . engine . addFact ( key , value ) ) ;
2026-03-30 03:04:27 +00:00
const concepts = [ ] ;
if ( rawState . stability < 0.4 && rawState . energy > 60 ) concepts . push ( 'UNSTABLE_OSCILLATION' ) ;
if ( rawState . energy < 30 && rawState . activePortals > 2 ) concepts . push ( 'CRITICAL_DRAIN_PATTERN' ) ;
concepts . forEach ( concept => {
this . engine . addFact ( concept , true ) ;
this . logPerception ( concept ) ;
} ) ;
return concepts ;
}
logPerception ( concept ) {
const container = document . getElementById ( 'neuro-bridge-log-content' ) ;
if ( container ) {
const div = document . createElement ( 'div' ) ;
div . className = 'neuro-bridge-entry' ;
div . innerHTML = ` <span class="neuro-icon">🧠</span> <span class="neuro-concept"> ${ concept } </span> ` ;
container . prepend ( div ) ;
if ( container . children . length > 5 ) container . lastElementChild . remove ( ) ;
}
}
}
class MetaReasoningLayer {
constructor ( planner , blackboard ) {
this . planner = planner ;
this . blackboard = blackboard ;
this . reasoningCache = new Map ( ) ;
this . performanceMetrics = { totalReasoningTime : 0 , calls : 0 } ;
}
getCachedPlan ( stateKey ) {
const cached = this . reasoningCache . get ( stateKey ) ;
if ( cached && ( Date . now ( ) - cached . timestamp < 10000 ) ) return cached . plan ;
return null ;
}
cachePlan ( stateKey , plan ) {
this . reasoningCache . set ( stateKey , { plan , timestamp : Date . now ( ) } ) ;
}
reflect ( ) {
const avgTime = this . performanceMetrics . totalReasoningTime / ( this . performanceMetrics . calls || 1 ) ;
const container = document . getElementById ( 'meta-log-content' ) ;
if ( container ) {
container . innerHTML = `
< div class = "meta-stat" > CACHE SIZE : $ { this . reasoningCache . size } < / d i v >
< div class = "meta-stat" > AVG LATENCY : $ { avgTime . toFixed ( 2 ) } ms < / d i v >
< div class = "meta-stat" > STATUS : $ { avgTime > 50 ? 'OPTIMIZING' : 'NOMINAL' } < / d i v >
` ;
}
}
track ( startTime ) {
const duration = performance . now ( ) - startTime ;
this . performanceMetrics . totalReasoningTime += duration ;
this . performanceMetrics . calls ++ ;
}
}
// ═══ ADAPTIVE CALIBRATOR (LOCAL EFFICIENCY) ═══
class AdaptiveCalibrator {
constructor ( modelId , initialParams ) {
this . model = modelId ;
this . weights = {
'input_tokens' : 0.0 ,
'complexity_score' : 0.0 ,
'task_type_indicator' : 0.0 ,
'bias' : initialParams . base _rate || 0.0
} ;
this . learningRate = 0.01 ;
this . history = [ ] ;
}
predict ( features ) {
let prediction = this . weights [ 'bias' ] ;
for ( let feature in features ) {
if ( this . weights [ feature ] !== undefined ) {
prediction += this . weights [ feature ] * features [ feature ] ;
}
}
return Math . max ( 0 , prediction ) ;
}
update ( features , actualCost ) {
const predicted = this . predict ( features ) ;
const error = actualCost - predicted ;
for ( let feature in features ) {
if ( this . weights [ feature ] !== undefined ) {
this . weights [ feature ] += this . learningRate * error * features [ feature ] ;
}
}
this . history . push ( { predicted , actual : actualCost , timestamp : Date . now ( ) } ) ;
const container = document . getElementById ( 'calibrator-log-content' ) ;
if ( container ) {
const div = document . createElement ( 'div' ) ;
div . className = 'calibrator-entry' ;
div . innerHTML = ` <span class="cal-label">CALIBRATED:</span> <span class="cal-val"> ${ predicted . toFixed ( 4 ) } </span> <span class="cal-err">ERR: ${ error . toFixed ( 4 ) } </span> ` ;
container . prepend ( div ) ;
if ( container . children . length > 5 ) container . lastElementChild . remove ( ) ;
}
}
}
2026-03-30 03:07:37 +00:00
// ═══ NOSTR AGENT REGISTRATION ═══
class NostrAgent {
constructor ( pubkey ) {
this . pubkey = pubkey ;
this . relays = [ 'wss://relay.damus.io' , 'wss://nos.lol' ] ;
}
async announce ( metadata ) {
console . log ( ` [NOSTR] Announcing agent ${ this . pubkey } ... ` ) ;
const event = {
kind : 0 ,
pubkey : this . pubkey ,
created _at : Math . floor ( Date . now ( ) / 1000 ) ,
tags : [ ] ,
content : JSON . stringify ( metadata ) ,
id : 'mock_id' ,
sig : 'mock_sig'
} ;
this . relays . forEach ( url => {
console . log ( ` [NOSTR] Publishing to ${ url } : ` , event ) ;
} ) ;
const container = document . getElementById ( 'nostr-log-content' ) ;
if ( container ) {
const div = document . createElement ( 'div' ) ;
div . className = 'nostr-entry' ;
div . innerHTML = ` <span class="nostr-pubkey">[ ${ this . pubkey . substring ( 0 , 8 ) } ...]</span> <span class="nostr-status">ANNOUNCED</span> ` ;
container . prepend ( div ) ;
}
}
}
// ═══ L402 CLIENT LOGIC ═══
class L402Client {
async fetchWithL402 ( url ) {
console . log ( ` [L402] Fetching ${ url } ... ` ) ;
const response = await fetch ( url ) ;
if ( response . status === 402 ) {
const authHeader = response . headers . get ( 'WWW-Authenticate' ) ;
console . log ( ` [L402] Challenge received: ${ authHeader } ` ) ;
const container = document . getElementById ( 'l402-log-content' ) ;
if ( container ) {
const div = document . createElement ( 'div' ) ;
div . className = 'l402-entry' ;
div . innerHTML = ` <span class="l402-status">CHALLENGE</span> <span class="l402-msg">Payment Required</span> ` ;
container . prepend ( div ) ;
}
return { status : 402 , challenge : authHeader } ;
}
return response . json ( ) ;
}
}
let nostrAgent , l402Client ;
2026-03-30 03:10:15 +00:00
// ═══ PARALLEL SYMBOLIC EXECUTION (PSE) ═══
class PSELayer {
constructor ( ) {
this . worker = new Worker ( 'gofai_worker.js' ) ;
this . worker . onmessage = ( e ) => this . handleWorkerMessage ( e ) ;
}
handleWorkerMessage ( e ) {
const { type , results , plan } = e . data ;
if ( type === 'REASON_RESULT' ) {
results . forEach ( res => symbolicEngine . logReasoning ( res . rule , res . outcome ) ) ;
} else if ( type === 'PLAN_RESULT' ) {
symbolicPlanner . logPlan ( plan ) ;
}
}
offloadReasoning ( facts , rules ) {
this . worker . postMessage ( { type : 'REASON' , data : { facts , rules } } ) ;
}
offloadPlanning ( initialState , goalState , actions ) {
this . worker . postMessage ( { type : 'PLAN' , data : { initialState , goalState , actions } } ) ;
}
}
let pseLayer ;
2026-04-12 16:20:03 +00:00
let resonanceViz , metaLayer , neuroBridge , cbr , symbolicPlanner , knowledgeGraph , blackboard , symbolicEngine , calibrator ;
2026-03-30 03:04:27 +00:00
let agentFSMs = { } ;
function setupGOFAI ( ) {
knowledgeGraph = new KnowledgeGraph ( ) ;
blackboard = new Blackboard ( ) ;
symbolicEngine = new SymbolicEngine ( ) ;
symbolicPlanner = new SymbolicPlanner ( ) ;
cbr = new CaseBasedReasoner ( ) ;
neuroBridge = new NeuroSymbolicBridge ( symbolicEngine , blackboard ) ;
metaLayer = new MetaReasoningLayer ( symbolicPlanner , blackboard ) ;
2026-03-30 03:07:37 +00:00
nostrAgent = new NostrAgent ( "npub1..." ) ;
l402Client = new L402Client ( ) ;
nostrAgent . announce ( { name : "Timmy Nexus Agent" , capabilities : [ "GOFAI" , "L402" ] } ) ;
2026-03-30 03:10:15 +00:00
pseLayer = new PSELayer ( ) ;
2026-04-12 23:28:40 +00:00
calibrator = new AdaptiveCalibrator ( 'nexus-v1' , { base _rate : 0.05 } ) ; \ n MemoryOptimizer . blackboard = blackboard ;
2026-03-30 03:04:27 +00:00
// Setup initial facts
symbolicEngine . addFact ( 'energy' , 100 ) ;
symbolicEngine . addFact ( 'stability' , 1.0 ) ;
// Setup FSM
agentFSMs [ 'timmy' ] = new AgentFSM ( 'timmy' , 'IDLE' ) ;
agentFSMs [ 'timmy' ] . addTransition ( 'IDLE' , 'ANALYZING' , ( facts ) => facts . get ( 'activePortals' ) > 0 ) ;
2026-04-13 00:26:36 +00:00
symbolicEngine . addRule ( ( facts ) => facts . get ( 'UNSTABLE_OSCILLATION' ) , ( ) => 'STABILIZE MATRIX' , 'Unstable oscillation demands stabilization' , [ 'UNSTABLE_OSCILLATION' ] ) ;
symbolicEngine . addRule ( ( facts ) => facts . get ( 'CRITICAL_DRAIN_PATTERN' ) , ( ) => 'SHED PORTAL LOAD' , 'Critical drain demands portal shedding' , [ 'CRITICAL_DRAIN_PATTERN' ] ) ;
2026-03-30 03:04:27 +00:00
// Setup Planner
symbolicPlanner . addAction ( 'Stabilize Matrix' , { energy : 50 } , { stability : 1.0 } ) ;
2026-04-13 01:34:45 +00:00
symbolicPlanner . addAction ( 'Shed Portal Load' , { activePortals : 1 } , { activePortals : 0 , stability : 0.8 } ) ;
2026-03-30 03:04:27 +00:00
}
2026-04-12 20:46:29 -04:00
function deriveGOFAIState ( elapsed ) {
const activeBars = powerMeterBars . reduce ( ( n , _ , i ) => n + ( ( ( ( Math . sin ( elapsed * 2 + i * 0.5 ) * 0.5 ) + 0.5 ) > ( i / Math . max ( powerMeterBars . length , 1 ) ) ) ? 1 : 0 ) , 0 ) ;
const energy = Math . round ( ( activeBars / Math . max ( powerMeterBars . length , 1 ) ) * 100 ) ;
const stability = Math . max ( 0.1 , Math . min ( 1 , ( wsConnected ? 0.55 : 0.2 ) + ( agents . length * 0.05 ) - ( portals . length * 0.03 ) - ( activePortal ? 0.1 : 0 ) - ( portalOverlayActive ? 0.05 : 0 ) ) ) ;
return { stability , energy , activePortals : activePortal ? 1 : 0 } ;
}
2026-04-13 01:34:45 +00:00
function deriveGOFAIGoal ( facts ) {
if ( facts . get ( 'CRITICAL_DRAIN_PATTERN' ) ) return { activePortals : 0 , stability : 0.8 } ;
if ( facts . get ( 'UNSTABLE_OSCILLATION' ) ) return { stability : 1.0 } ;
return { stability : Math . max ( 0.7 , facts . get ( 'stability' ) || 0.7 ) } ;
}
2026-03-30 03:04:27 +00:00
function updateGOFAI ( delta , elapsed ) {
const startTime = performance . now ( ) ;
2026-04-12 20:46:29 -04:00
neuroBridge . perceive ( deriveGOFAIState ( elapsed ) ) ;
2026-04-13 00:26:36 +00:00
agentFSMs [ 'timmy' ] ? . update ( symbolicEngine . facts ) ;
2026-03-30 03:04:27 +00:00
// Run reasoning
if ( Math . floor ( elapsed * 2 ) > Math . floor ( ( elapsed - delta ) * 2 ) ) {
symbolicEngine . reason ( ) ;
2026-04-13 01:34:45 +00:00
pseLayer . offloadReasoning ( Array . from ( symbolicEngine . facts . entries ( ) ) , symbolicEngine . rules . map ( ( r ) => ( { description : r . description , triggerFacts : r . triggerFacts , workerOutcome : r . action ( symbolicEngine . facts ) , confidence : 0.9 } ) ) ) ;
pseLayer . offloadPlanning ( Object . fromEntries ( symbolicEngine . facts ) , deriveGOFAIGoal ( symbolicEngine . facts ) , symbolicPlanner . actions ) ;
2026-03-30 03:10:15 +00:00
document . getElementById ( "pse-task-count" ) . innerText = parseInt ( document . getElementById ( "pse-task-count" ) . innerText ) + 1 ;
2026-03-30 03:04:27 +00:00
metaLayer . reflect ( ) ;
// Simulate calibration update
calibrator . update ( { input _tokens : 100 , complexity _score : 0.5 } , 0.06 ) ;
2026-03-30 03:07:37 +00:00
if ( Math . random ( ) > 0.95 ) l402Client . fetchWithL402 ( "http://localhost:8080/api/cost-estimate" ) ;
2026-03-30 03:04:27 +00:00
}
metaLayer . track ( startTime ) ;
}
2026-03-28 20:50:56 +00:00
async function init ( ) {
clock = new THREE . Clock ( ) ;
playerPos = new THREE . Vector3 ( 0 , 2 , 12 ) ;
playerRot = new THREE . Euler ( 0 , 0 , 0 , 'YXZ' ) ;
const canvas = document . getElementById ( 'nexus-canvas' ) ;
renderer = new THREE . WebGLRenderer ( { canvas , antialias : true } ) ;
renderer . setSize ( window . innerWidth , window . innerHeight ) ;
renderer . toneMapping = THREE . ACESFilmicToneMapping ;
renderer . toneMappingExposure = 1.2 ;
renderer . shadowMap . enabled = true ;
renderer . shadowMap . type = THREE . PCFSoftShadowMap ;
performanceTier = detectPerformanceTier ( ) ;
updateLoad ( 10 ) ;
scene = new THREE . Scene ( ) ;
scene . fog = new THREE . FogExp2 ( 0x050510 , 0.012 ) ;
2026-04-12 16:20:03 +00:00
setupGOFAI ( ) ; \ n resonanceViz = new ResonanceVisualizer ( scene ) ;
2026-03-28 20:50:56 +00:00
camera = new THREE . PerspectiveCamera ( 65 , window . innerWidth / window . innerHeight , 0.1 , 1000 ) ;
camera . position . copy ( playerPos ) ;
updateLoad ( 20 ) ;
createSkybox ( ) ;
updateLoad ( 30 ) ;
createLighting ( ) ;
updateLoad ( 40 ) ;
createFloor ( ) ;
updateLoad ( 50 ) ;
createBatcaveTerminal ( ) ;
updateLoad ( 60 ) ;
// Load Portals from Registry
try {
const response = await fetch ( './portals.json' ) ;
const portalData = await response . json ( ) ;
createPortals ( portalData ) ;
} catch ( e ) {
console . error ( 'Failed to load portals.json:' , e ) ;
addChatMessage ( 'error' , 'Portal registry offline. Check logs.' ) ;
}
// Load Vision Points
try {
const response = await fetch ( './vision.json' ) ;
const visionData = await response . json ( ) ;
createVisionPoints ( visionData ) ;
} catch ( e ) {
console . error ( 'Failed to load vision.json:' , e ) ;
}
updateLoad ( 80 ) ;
createParticles ( ) ;
createDustParticles ( ) ;
updateLoad ( 85 ) ;
2026-04-12 11:51:40 -04:00
if ( performanceTier !== "low" ) createAmbientStructures ( ) ;
2026-03-28 20:50:56 +00:00
createAgentPresences ( ) ;
2026-04-12 11:51:40 -04:00
if ( performanceTier !== "low" ) createThoughtStream ( ) ;
2026-03-28 20:50:56 +00:00
createHarnessPulse ( ) ;
createSessionPowerMeter ( ) ;
createWorkshopTerminal ( ) ;
2026-04-12 11:51:40 -04:00
if ( performanceTier !== "low" ) createAshStorm ( ) ;
2026-04-10 07:41:13 +00:00
SpatialMemory . init ( scene ) ;
2026-04-11 19:48:46 +00:00
MemoryBirth . init ( scene ) ;
MemoryBirth . wrapSpatialMemory ( SpatialMemory ) ;
2026-04-11 05:06:02 +00:00
SpatialMemory . setCamera ( camera ) ;
2026-04-12 12:52:39 -04:00
SpatialAudio . init ( camera , scene ) ;
SpatialAudio . bindSpatialMemory ( SpatialMemory ) ;
2026-04-11 21:17:42 +00:00
MemoryInspect . init ( { onNavigate : _navigateToMemory } ) ;
2026-04-12 06:45:25 +00:00
MemoryPulse . init ( SpatialMemory ) ;
2026-04-13 18:23:05 -04:00
ReasoningTrace . init ( ) ;
2026-03-28 20:50:56 +00:00
updateLoad ( 90 ) ;
loadSession ( ) ;
connectHermes ( ) ;
2026-04-11 01:39:58 +00:00
// Mnemosyne: Periodic GOFAI Optimization
setInterval ( ( ) => {
console . info ( '[Mnemosyne] Running periodic optimization...' ) ;
MemoryOptimizer . optimize ( SpatialMemory ) ;
} , 1000 * 60 * 10 ) ; // Every 10 minutes
2026-03-28 20:50:56 +00:00
fetchGiteaData ( ) ;
setInterval ( fetchGiteaData , 30000 ) ; // Refresh every 30s
2026-04-12 11:51:40 -04:00
// Quality-tier feature gating: only enable heavy post-processing on medium/high
if ( performanceTier !== 'low' ) {
composer = new EffectComposer ( renderer ) ;
composer . addPass ( new RenderPass ( scene , camera ) ) ;
const bloomStrength = performanceTier === 'high' ? 0.6 : 0.35 ;
const bloom = new UnrealBloomPass (
new THREE . Vector2 ( window . innerWidth , window . innerHeight ) ,
bloomStrength , 0.4 , 0.85
) ;
composer . addPass ( bloom ) ;
composer . addPass ( new SMAAPass ( window . innerWidth , window . innerHeight ) ) ;
} else {
composer = null ;
}
2026-03-28 20:50:56 +00:00
updateLoad ( 95 ) ;
setupControls ( ) ;
window . addEventListener ( 'resize' , onResize ) ;
debugOverlay = document . getElementById ( 'debug-overlay' ) ;
updateLoad ( 100 ) ;
setTimeout ( ( ) => {
document . getElementById ( 'loading-screen' ) . classList . add ( 'fade-out' ) ;
const enterPrompt = document . getElementById ( 'enter-prompt' ) ;
enterPrompt . style . display = 'flex' ;
enterPrompt . addEventListener ( 'click' , ( ) => {
enterPrompt . classList . add ( 'fade-out' ) ;
2026-04-12 11:52:13 -04:00
document . body . classList . add ( 'visitor-mode' ) ;
2026-03-28 20:50:56 +00:00
document . getElementById ( 'hud' ) . style . display = 'block' ;
2026-04-12 11:54:27 -04:00
const erpPanel = document . getElementById ( 'evennia-room-panel' ) ;
if ( erpPanel ) erpPanel . style . display = 'block' ;
2026-03-28 20:50:56 +00:00
setTimeout ( ( ) => { enterPrompt . remove ( ) ; } , 600 ) ;
} , { once : true } ) ;
setTimeout ( ( ) => { document . getElementById ( 'loading-screen' ) . remove ( ) ; } , 900 ) ;
} , 600 ) ;
requestAnimationFrame ( gameLoop ) ;
}
function updateLoad ( pct ) {
loadProgress = pct ;
const fill = document . getElementById ( 'load-progress' ) ;
if ( fill ) fill . style . width = pct + '%' ;
}
// ═══ PERFORMANCE BUDGET ═══
function detectPerformanceTier ( ) {
const isMobile = /Mobi|Android|iPhone|iPad/i . test ( navigator . userAgent ) || window . innerWidth < 768 ;
const cores = navigator . hardwareConcurrency || 4 ;
if ( isMobile ) {
renderer . setPixelRatio ( 1 ) ;
renderer . shadowMap . enabled = false ;
return 'low' ;
} else if ( cores < 8 ) {
renderer . setPixelRatio ( Math . min ( window . devicePixelRatio , 1.5 ) ) ;
renderer . shadowMap . type = THREE . BasicShadowMap ;
return 'medium' ;
} else {
renderer . setPixelRatio ( Math . min ( window . devicePixelRatio , 2 ) ) ;
return 'high' ;
}
}
function particleCount ( base ) {
if ( performanceTier === 'low' ) return Math . floor ( base * 0.25 ) ;
if ( performanceTier === 'medium' ) return Math . floor ( base * 0.6 ) ;
return base ;
}
// ═══ SKYBOX ═══
function createSkybox ( ) {
const skyGeo = new THREE . SphereGeometry ( 400 , 64 , 64 ) ;
const skyMat = new THREE . ShaderMaterial ( {
uniforms : {
uTime : { value : 0 } ,
uColor1 : { value : new THREE . Color ( 0x0a0520 ) } ,
uColor2 : { value : new THREE . Color ( 0x1a0a3e ) } ,
uColor3 : { value : new THREE . Color ( 0x0a1a3e ) } ,
uStarDensity : { value : 0.97 } ,
} ,
vertexShader : `
varying vec3 vPos ;
void main ( ) {
vPos = position ;
gl _Position = projectionMatrix * modelViewMatrix * vec4 ( position , 1.0 ) ;
}
` ,
fragmentShader : `
uniform float uTime ;
uniform vec3 uColor1 ;
uniform vec3 uColor2 ;
uniform vec3 uColor3 ;
uniform float uStarDensity ;
varying vec3 vPos ;
float hash ( vec3 p ) {
p = fract ( p * vec3 ( 443.897 , 441.423 , 437.195 ) ) ;
p += dot ( p , p . yzx + 19.19 ) ;
return fract ( ( p . x + p . y ) * p . z ) ;
}
float noise ( vec3 p ) {
vec3 i = floor ( p ) ;
vec3 f = fract ( p ) ;
f = f * f * ( 3.0 - 2.0 * f ) ;
return mix (
mix ( mix ( hash ( i ) , hash ( i + vec3 ( 1 , 0 , 0 ) ) , f . x ) ,
mix ( hash ( i + vec3 ( 0 , 1 , 0 ) ) , hash ( i + vec3 ( 1 , 1 , 0 ) ) , f . x ) , f . y ) ,
mix ( mix ( hash ( i + vec3 ( 0 , 0 , 1 ) ) , hash ( i + vec3 ( 1 , 0 , 1 ) ) , f . x ) ,
mix ( hash ( i + vec3 ( 0 , 1 , 1 ) ) , hash ( i + vec3 ( 1 , 1 , 1 ) ) , f . x ) , f . y ) ,
f . z
) ;
}
float fbm ( vec3 p ) {
float v = 0.0 ;
float a = 0.5 ;
for ( int i = 0 ; i < 5 ; i ++ ) {
v += a * noise ( p ) ;
p *= 2.0 ;
a *= 0.5 ;
}
return v ;
}
void main ( ) {
vec3 dir = normalize ( vPos ) ;
float n1 = fbm ( dir * 3.0 + uTime * 0.02 ) ;
float n2 = fbm ( dir * 5.0 - uTime * 0.015 + 100.0 ) ;
float n3 = fbm ( dir * 2.0 + uTime * 0.01 + 200.0 ) ;
vec3 col = uColor1 ;
col = mix ( col , uColor2 , smoothstep ( 0.3 , 0.7 , n1 ) ) ;
col = mix ( col , uColor3 , smoothstep ( 0.4 , 0.8 , n2 ) * 0.5 ) ;
float glow = pow ( n1 * n2 , 2.0 ) * 1.5 ;
col += vec3 ( 0.15 , 0.05 , 0.25 ) * glow ;
col += vec3 ( 0.05 , 0.15 , 0.25 ) * pow ( n3 , 3.0 ) ;
float starField = hash ( dir * 800.0 ) ;
float stars = step ( uStarDensity , starField ) * ( 0.5 + 0.5 * hash ( dir * 1600.0 ) ) ;
float twinkle = 0.7 + 0.3 * sin ( uTime * 2.0 + hash ( dir * 400.0 ) * 6.28 ) ;
col += vec3 ( stars * twinkle ) ;
float bigStar = step ( 0.998 , starField ) ;
col += vec3 ( 0.8 , 0.9 , 1.0 ) * bigStar * twinkle ;
gl _FragColor = vec4 ( col , 1.0 ) ;
}
` ,
side : THREE . BackSide ,
} ) ;
const sky = new THREE . Mesh ( skyGeo , skyMat ) ;
sky . name = 'skybox' ;
scene . add ( sky ) ;
}
// ═══ LIGHTING ═══
function createLighting ( ) {
const ambient = new THREE . AmbientLight ( 0x1a1a3a , 0.4 ) ;
scene . add ( ambient ) ;
const dirLight = new THREE . DirectionalLight ( 0x4466aa , 0.6 ) ;
dirLight . position . set ( 10 , 20 , 10 ) ;
dirLight . castShadow = renderer . shadowMap . enabled ;
const shadowRes = performanceTier === 'high' ? 2048 : performanceTier === 'medium' ? 1024 : 512 ;
dirLight . shadow . mapSize . set ( shadowRes , shadowRes ) ;
scene . add ( dirLight ) ;
const tealLight = new THREE . PointLight ( NEXUS . colors . primary , 2 , 30 , 1.5 ) ;
tealLight . position . set ( 0 , 1 , - 5 ) ;
scene . add ( tealLight ) ;
const purpleLight = new THREE . PointLight ( NEXUS . colors . secondary , 1.5 , 25 , 1.5 ) ;
purpleLight . position . set ( - 8 , 3 , - 8 ) ;
scene . add ( purpleLight ) ;
}
// ═══ FLOOR ═══
function createFloor ( ) {
const platGeo = new THREE . CylinderGeometry ( 25 , 25 , 0.3 , 6 ) ;
const platMat = new THREE . MeshStandardMaterial ( {
color : 0x0a0f1a ,
roughness : 0.8 ,
metalness : 0.3 ,
} ) ;
const platform = new THREE . Mesh ( platGeo , platMat ) ;
platform . position . y = - 0.15 ;
platform . receiveShadow = true ;
scene . add ( platform ) ;
const gridHelper = new THREE . GridHelper ( 50 , 50 , NEXUS . colors . gridLine , NEXUS . colors . gridLine ) ;
gridHelper . material . opacity = 0.15 ;
gridHelper . material . transparent = true ;
gridHelper . position . y = 0.02 ;
scene . add ( gridHelper ) ;
const ringGeo = new THREE . RingGeometry ( 24.5 , 25.2 , 6 ) ;
const ringMat = new THREE . MeshBasicMaterial ( {
color : NEXUS . colors . primary ,
transparent : true ,
opacity : 0.4 ,
side : THREE . DoubleSide ,
} ) ;
const ring = new THREE . Mesh ( ringGeo , ringMat ) ;
ring . rotation . x = Math . PI / 2 ;
ring . position . y = 0.05 ;
scene . add ( ring ) ;
}
// ═══ BATCAVE TERMINAL ═══
function createBatcaveTerminal ( ) {
const terminalGroup = new THREE . Group ( ) ;
terminalGroup . position . set ( 0 , 0 , - 8 ) ;
const panelData = [
{ title : 'NEXUS COMMAND' , color : NEXUS . colors . primary , rot : - 0.4 , x : - 6 , y : 3 , lines : [ '> STATUS: NOMINAL' , '> UPTIME: 142.4h' , '> HARNESS: STABLE' , '> MODE: SOVEREIGN' ] } ,
{ title : 'DEV QUEUE' , color : NEXUS . colors . gold , rot : - 0.2 , x : - 3 , y : 3 , lines : [ '> ISSUE #4: CORE' , '> ISSUE #5: PORTAL' , '> ISSUE #6: TERMINAL' , '> ISSUE #7: TIMMY' ] } ,
{ title : 'METRICS' , color : NEXUS . colors . secondary , rot : 0 , x : 0 , y : 3 , lines : [ '> CPU: 12% [||....]' , '> MEM: 4.2GB' , '> COMMITS: 842' , '> ACTIVE LOOPS: 5' ] } ,
{ title : 'SOVEREIGNTY' , color : NEXUS . colors . gold , rot : 0.2 , x : 3 , y : 3 , lines : [ 'REPLIT: GRADE: A' , 'PERPLEXITY: GRADE: A-' , 'HERMES: GRADE: B+' , 'KIMI: GRADE: B' , 'CLAUDE: GRADE: B+' ] } ,
{ title : 'AGENT STATUS' , color : NEXUS . colors . primary , rot : 0.4 , x : 6 , y : 3 , lines : [ '> TIMMY: ● RUNNING' , '> KIMI: ○ STANDBY' , '> CLAUDE: ● ACTIVE' , '> PERPLEXITY: ○' ] } ,
] ;
panelData . forEach ( data => {
const terminal = createTerminalPanel ( terminalGroup , data . x , data . y , data . rot , data . title , data . color , data . lines ) ;
batcaveTerminals . push ( terminal ) ;
} ) ;
scene . add ( terminalGroup ) ;
}
// ═══ WORKSHOP TERMINAL ═══
function createWorkshopTerminal ( ) {
const w = 6 , h = 4 ;
const group = new THREE . Group ( ) ;
group . position . set ( - 14 , 3 , 0 ) ;
group . rotation . y = Math . PI / 4 ;
workshopPanelCanvas = document . createElement ( 'canvas' ) ;
workshopPanelCanvas . width = 1024 ;
workshopPanelCanvas . height = 512 ;
workshopPanelCtx = workshopPanelCanvas . getContext ( '2d' ) ;
workshopPanelTexture = new THREE . CanvasTexture ( workshopPanelCanvas ) ;
workshopPanelTexture . minFilter = THREE . LinearFilter ;
const panelGeo = new THREE . PlaneGeometry ( w , h ) ;
const panelMat = new THREE . MeshBasicMaterial ( {
map : workshopPanelTexture ,
transparent : true ,
opacity : 0.9 ,
side : THREE . DoubleSide
} ) ;
const panel = new THREE . Mesh ( panelGeo , panelMat ) ;
group . add ( panel ) ;
const scanGeo = new THREE . PlaneGeometry ( w + 0.1 , h + 0.1 ) ;
workshopScanMat = new THREE . ShaderMaterial ( {
transparent : true ,
uniforms : { uTime : { value : 0 } } ,
vertexShader : ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } ` ,
fragmentShader : `
uniform float uTime ;
varying vec2 vUv ;
void main ( ) {
float scan = sin ( vUv . y * 200.0 + uTime * 10.0 ) * 0.05 ;
float noise = fract ( sin ( dot ( vUv , vec2 ( 12.9898 , 78.233 ) ) ) * 43758.5453 ) * 0.05 ;
gl _FragColor = vec4 ( 0.0 , 0.1 , 0.2 , scan + noise ) ;
}
`
} ) ;
const scan = new THREE . Mesh ( scanGeo , workshopScanMat ) ;
scan . position . z = 0.01 ;
group . add ( scan ) ;
scene . add ( group ) ;
refreshWorkshopPanel ( ) ;
}
function refreshWorkshopPanel ( ) {
if ( ! workshopPanelCtx ) return ;
const ctx = workshopPanelCtx ;
const w = 1024 , h = 512 ;
ctx . clearRect ( 0 , 0 , w , h ) ;
ctx . fillStyle = 'rgba(10, 15, 40, 0.8)' ;
ctx . fillRect ( 0 , 0 , w , h ) ;
ctx . fillStyle = '#4af0c0' ;
ctx . font = 'bold 40px "Orbitron", sans-serif' ;
ctx . fillText ( 'WORKSHOP TERMINAL v1.0' , 40 , 60 ) ;
ctx . fillRect ( 40 , 80 , 944 , 4 ) ;
ctx . font = '24px "JetBrains Mono", monospace' ;
ctx . fillStyle = wsConnected ? '#4af0c0' : '#ff4466' ;
ctx . fillText ( ` HERMES STATUS: ${ wsConnected ? 'ONLINE' : 'OFFLINE' } ` , 40 , 120 ) ;
ctx . fillStyle = '#7b5cff' ;
const contextName = activePortal ? activePortal . name . toUpperCase ( ) : 'NEXUS CORE' ;
ctx . fillText ( ` CONTEXT: ${ contextName } ` , 40 , 160 ) ;
ctx . fillStyle = '#a0b8d0' ;
ctx . font = 'bold 20px "Orbitron", sans-serif' ;
ctx . fillText ( 'TOOL OUTPUT STREAM' , 40 , 220 ) ;
ctx . fillRect ( 40 , 230 , 400 , 2 ) ;
ctx . font = '16px "JetBrains Mono", monospace' ;
recentToolOutputs . slice ( - 10 ) . forEach ( ( out , i ) => {
ctx . fillStyle = out . type === 'call' ? '#ffd700' : '#4af0c0' ;
const text = ` [ ${ out . agent } ] ${ out . content . substring ( 0 , 80 ) } ${ out . content . length > 80 ? '...' : '' } ` ;
ctx . fillText ( text , 40 , 260 + i * 24 ) ;
} ) ;
workshopPanelTexture . needsUpdate = true ;
}
function createTerminalPanel ( parent , x , y , rot , title , color , lines ) {
const w = 2.8 , h = 3.5 ;
const group = new THREE . Group ( ) ;
group . position . set ( x , y , 0 ) ;
group . rotation . y = rot ;
const bgGeo = new THREE . PlaneGeometry ( w , h ) ;
const bgMat = new THREE . MeshPhysicalMaterial ( {
color : NEXUS . colors . panelBg ,
transparent : true ,
opacity : 0.6 ,
roughness : 0.1 ,
metalness : 0.5 ,
side : THREE . DoubleSide ,
} ) ;
const bg = new THREE . Mesh ( bgGeo , bgMat ) ;
group . add ( bg ) ;
const borderMat = new THREE . MeshBasicMaterial ( { color : color , transparent : true , opacity : 0.3 , side : THREE . DoubleSide } ) ;
const border = new THREE . Mesh ( new THREE . PlaneGeometry ( w + 0.05 , h + 0.05 ) , borderMat ) ;
border . position . z = - 0.01 ;
group . add ( border ) ;
const textCanvas = document . createElement ( 'canvas' ) ;
textCanvas . width = 512 ;
textCanvas . height = 640 ;
const ctx = textCanvas . getContext ( '2d' ) ;
const textTexture = new THREE . CanvasTexture ( textCanvas ) ;
textTexture . minFilter = THREE . LinearFilter ;
function updatePanelText ( newLines ) {
ctx . clearRect ( 0 , 0 , 512 , 640 ) ;
ctx . fillStyle = '#' + new THREE . Color ( color ) . getHexString ( ) ;
ctx . font = 'bold 32px "Orbitron", sans-serif' ;
ctx . fillText ( title , 20 , 45 ) ;
ctx . fillRect ( 20 , 55 , 472 , 2 ) ;
ctx . font = '20px "JetBrains Mono", monospace' ;
ctx . fillStyle = '#a0b8d0' ;
const displayLines = newLines || lines ;
displayLines . forEach ( ( line , i ) => {
let fillColor = '#a0b8d0' ;
if ( line . includes ( '● RUNNING' ) || line . includes ( '● ACTIVE' ) || line . includes ( 'ONLINE' ) ) fillColor = '#4af0c0' ;
else if ( line . includes ( '○ STANDBY' ) || line . includes ( 'OFFLINE' ) ) fillColor = '#5a6a8a' ;
else if ( line . includes ( 'NOMINAL' ) ) fillColor = '#4af0c0' ;
ctx . fillStyle = fillColor ;
ctx . fillText ( line , 20 , 100 + i * 40 ) ;
} ) ;
textTexture . needsUpdate = true ;
}
updatePanelText ( ) ;
const textMat = new THREE . MeshBasicMaterial ( {
map : textTexture ,
transparent : true ,
side : THREE . DoubleSide ,
depthWrite : false ,
} ) ;
const textMesh = new THREE . Mesh ( new THREE . PlaneGeometry ( w * 0.95 , h * 0.95 ) , textMat ) ;
textMesh . position . z = 0.01 ;
group . add ( textMesh ) ;
const scanGeo = new THREE . PlaneGeometry ( w , h ) ;
const scanMat = new THREE . ShaderMaterial ( {
transparent : true ,
depthWrite : false ,
uniforms : { uTime : { value : 0 } , uColor : { value : new THREE . Color ( color ) } } ,
vertexShader : `
varying vec2 vUv ;
void main ( ) { vUv = uv ; gl _Position = projectionMatrix * modelViewMatrix * vec4 ( position , 1.0 ) ; }
` ,
fragmentShader : `
uniform float uTime ;
uniform vec3 uColor ;
varying vec2 vUv ;
void main ( ) {
float scanline = sin ( vUv . y * 200.0 + uTime * 2.0 ) * 0.5 + 0.5 ;
scanline = pow ( scanline , 8.0 ) ;
float sweep = smoothstep ( 0.0 , 0.02 , abs ( fract ( vUv . y - uTime * 0.1 ) - 0.5 ) ) ;
sweep = 1.0 - ( 1.0 - sweep ) * 0.3 ;
float alpha = scanline * 0.04 + ( 1.0 - sweep ) * 0.08 ;
gl _FragColor = vec4 ( uColor , alpha ) ;
}
` ,
side : THREE . DoubleSide ,
} ) ;
const scanMesh = new THREE . Mesh ( scanGeo , scanMat ) ;
scanMesh . position . z = 0.02 ;
group . add ( scanMesh ) ;
parent . add ( group ) ;
return { group , scanMat , borderMat , updatePanelText , title } ;
}
// ═══ GITEA DATA INTEGRATION ═══
async function fetchGiteaData ( ) {
try {
const [ issuesRes , stateRes ] = await Promise . all ( [
2026-04-05 21:05:20 +00:00
fetch ( 'https://forge.alexanderwhitestone.com/api/v1/repos/Timmy_Foundation/the-nexus/issues?state=all&limit=20' ) ,
2026-04-13 04:10:01 -04:00
fetch ( 'https://forge.alexanderwhitestone.com/api/v1/repos/Timmy_Foundation/the-nexus/contents/vision.json' )
2026-03-28 20:50:56 +00:00
] ) ;
if ( issuesRes . ok ) {
const issues = await issuesRes . json ( ) ;
updateDevQueue ( issues ) ;
updateAgentStatus ( issues ) ;
}
if ( stateRes . ok ) {
const content = await stateRes . json ( ) ;
const worldState = JSON . parse ( atob ( content . content ) ) ;
updateNexusCommand ( worldState ) ;
2026-04-05 21:05:20 +00:00
updateSovereignHealth ( ) ;
2026-03-28 20:50:56 +00:00
}
} catch ( e ) {
console . error ( 'Failed to fetch Gitea data:' , e ) ;
}
}
function updateAgentStatus ( issues ) {
const terminal = batcaveTerminals . find ( t => t . title === 'AGENT STATUS' ) ;
if ( ! terminal ) return ;
// Check for Morrowind issues
const morrowindIssues = issues . filter ( i => i . title . toLowerCase ( ) . includes ( 'morrowind' ) && i . state === 'open' ) ;
const perplexityStatus = morrowindIssues . length > 0 ? '● MORROWIND' : '○ STANDBY' ;
const lines = [
'> TIMMY: ● RUNNING' ,
'> KIMI: ○ STANDBY' ,
'> CLAUDE: ● ACTIVE' ,
` > PERPLEXITY: ${ perplexityStatus } `
] ;
terminal . updatePanelText ( lines ) ;
}
function updateDevQueue ( issues ) {
const terminal = batcaveTerminals . find ( t => t . title === 'DEV QUEUE' ) ;
if ( ! terminal ) return ;
const lines = issues . slice ( 0 , 4 ) . map ( issue => ` > # ${ issue . number } : ${ issue . title . substring ( 0 , 15 ) } ... ` ) ;
while ( lines . length < 4 ) lines . push ( '> [EMPTY SLOT]' ) ;
terminal . updatePanelText ( lines ) ;
}
2026-04-05 21:05:20 +00:00
2026-04-05 22:56:15 +00:00
async function updateSovereignHealth ( ) {
2026-04-05 21:05:20 +00:00
const container = document . getElementById ( 'sovereign-health-content' ) ;
if ( ! container ) return ;
2026-04-12 19:27:51 -04:00
2026-04-05 22:56:15 +00:00
let metrics = { sovereignty _score : 100 , local _sessions : 0 , total _sessions : 0 } ;
2026-04-12 19:27:51 -04:00
let daemonReachable = false ;
2026-04-05 22:56:15 +00:00
try {
const res = await fetch ( 'http://localhost:8082/metrics' ) ;
if ( res . ok ) {
metrics = await res . json ( ) ;
2026-04-12 19:27:51 -04:00
daemonReachable = true ;
2026-04-05 22:56:15 +00:00
}
} catch ( e ) {
console . log ( 'Local health daemon not reachable, using static baseline.' ) ;
}
2026-04-05 21:05:20 +00:00
const services = [
2026-04-12 19:27:51 -04:00
{ name : 'LOCAL DAEMON' , status : daemonReachable ? 'ONLINE' : 'OFFLINE' } ,
2026-04-05 21:05:20 +00:00
{ name : 'FORGE / GITEA' , url : 'https://forge.alexanderwhitestone.com' , status : 'ONLINE' } ,
{ name : 'NEXUS CORE' , url : 'https://forge.alexanderwhitestone.com/Timmy_Foundation/the-nexus' , status : 'ONLINE' } ,
{ name : 'HERMES WS' , url : 'ws://143.198.27.163:8765' , status : wsConnected ? 'ONLINE' : 'OFFLINE' } ,
2026-04-05 22:56:15 +00:00
{ name : 'SOVEREIGNTY' , url : 'http://localhost:8082/metrics' , status : metrics . sovereignty _score + '%' }
2026-04-05 21:05:20 +00:00
] ;
container . innerHTML = '' ;
2026-04-12 19:27:51 -04:00
2026-04-05 22:56:15 +00:00
// Add Sovereignty Bar
const barDiv = document . createElement ( 'div' ) ;
barDiv . className = 'meta-stat' ;
barDiv . style . flexDirection = 'column' ;
barDiv . style . alignItems = 'flex-start' ;
barDiv . innerHTML = `
< div style = "display:flex; justify-content:space-between; width:100%; margin-bottom:4px;" >
< span > SOVEREIGNTY SCORE < / s p a n >
< span > $ { metrics . sovereignty _score } % < / s p a n >
< / d i v >
< div style = "width:100%; height:4px; background:rgba(255,255,255,0.1);" >
< div style = "width:${metrics.sovereignty_score}%; height:100%; background:var(--accent-color); box-shadow: 0 0 10px var(--accent-color);" > < / d i v >
< / d i v >
` ;
container . appendChild ( barDiv ) ;
2026-04-12 19:27:51 -04:00
// Session metrics (if daemon provides them)
if ( daemonReachable && ( metrics . local _sessions || metrics . total _sessions ) ) {
const sessDiv = document . createElement ( 'div' ) ;
sessDiv . className = 'meta-stat' ;
sessDiv . innerHTML = ` <span>SESSIONS</span><span> ${ metrics . local _sessions || 0 } local / ${ metrics . total _sessions || 0 } total</span> ` ;
container . appendChild ( sessDiv ) ;
}
2026-04-05 21:05:20 +00:00
services . forEach ( s => {
const div = document . createElement ( 'div' ) ;
div . className = 'meta-stat' ;
2026-04-05 22:56:15 +00:00
div . innerHTML = ` <span> ${ s . name } </span> <span class=" ${ s . status === 'OFFLINE' ? 'status-offline' : 'status-online' } "> ${ s . status } </span> ` ;
2026-04-05 21:05:20 +00:00
container . appendChild ( div ) ;
} ) ;
2026-04-12 19:27:51 -04:00
// Last updated timestamp
const tsDiv = document . createElement ( 'div' ) ;
tsDiv . className = 'meta-stat' ;
tsDiv . style . opacity = '0.5' ;
tsDiv . style . fontSize = '0.7em' ;
tsDiv . textContent = ` UPDATED ${ new Date ( ) . toLocaleTimeString ( ) } ` ;
container . appendChild ( tsDiv ) ;
2026-04-05 21:05:20 +00:00
}
2026-03-28 20:50:56 +00:00
function updateNexusCommand ( state ) {
const terminal = batcaveTerminals . find ( t => t . title === 'NEXUS COMMAND' ) ;
if ( ! terminal ) return ;
const lines = [
` > STATUS: ${ state . tower . status . toUpperCase ( ) } ` ,
` > ENERGY: ${ state . tower . energy } % ` ,
` > STABILITY: ${ ( state . matrix . stability * 100 ) . toFixed ( 1 ) } % ` ,
` > AGENTS: ${ state . matrix . active _agents . length } `
] ;
terminal . updatePanelText ( lines ) ;
}
// ═══ AGENT PRESENCE SYSTEM ═══
function createAgentPresences ( ) {
const agentData = [
{ id : 'timmy' , name : 'TIMMY' , color : NEXUS . colors . primary , pos : { x : - 4 , z : - 4 } , station : { x : - 4 , z : - 4 } } ,
{ id : 'kimi' , name : 'KIMI' , color : NEXUS . colors . secondary , pos : { x : 4 , z : - 4 } , station : { x : 4 , z : - 4 } } ,
{ id : 'claude' , name : 'CLAUDE' , color : NEXUS . colors . gold , pos : { x : 0 , z : - 6 } , station : { x : 0 , z : - 6 } } ,
{ id : 'perplexity' , name : 'PERPLEXITY' , color : 0x4488ff , pos : { x : - 6 , z : - 2 } , station : { x : - 6 , z : - 2 } } ,
] ;
agentData . forEach ( data => {
const group = new THREE . Group ( ) ;
group . position . set ( data . pos . x , 0 , data . pos . z ) ;
const color = new THREE . Color ( data . color ) ;
// Agent Orb
const orbGeo = new THREE . SphereGeometry ( 0.4 , 32 , 32 ) ;
const orbMat = new THREE . MeshPhysicalMaterial ( {
color : color ,
emissive : color ,
emissiveIntensity : 2 ,
roughness : 0 ,
metalness : 1 ,
transmission : 0.8 ,
thickness : 0.5 ,
} ) ;
const orb = new THREE . Mesh ( orbGeo , orbMat ) ;
orb . position . y = 3 ;
group . add ( orb ) ;
// Halo
const haloGeo = new THREE . TorusGeometry ( 0.6 , 0.02 , 16 , 64 ) ;
const haloMat = new THREE . MeshBasicMaterial ( { color : color , transparent : true , opacity : 0.4 } ) ;
const halo = new THREE . Mesh ( haloGeo , haloMat ) ;
halo . position . y = 3 ;
halo . rotation . x = Math . PI / 2 ;
group . add ( halo ) ;
// Label
const canvas = document . createElement ( 'canvas' ) ;
canvas . width = 256 ;
canvas . height = 64 ;
const ctx = canvas . getContext ( '2d' ) ;
ctx . font = 'bold 24px "Orbitron", sans-serif' ;
ctx . fillStyle = '#' + color . getHexString ( ) ;
ctx . textAlign = 'center' ;
ctx . fillText ( data . name , 128 , 40 ) ;
const tex = new THREE . CanvasTexture ( canvas ) ;
const mat = new THREE . MeshBasicMaterial ( { map : tex , transparent : true , side : THREE . DoubleSide } ) ;
const label = new THREE . Mesh ( new THREE . PlaneGeometry ( 2 , 0.5 ) , mat ) ;
label . position . y = 3.8 ;
group . add ( label ) ;
scene . add ( group ) ;
agents . push ( {
id : data . id ,
group ,
orb ,
halo ,
color ,
station : data . station ,
targetPos : new THREE . Vector3 ( data . pos . x , 0 , data . pos . z ) ,
wanderTimer : 0
} ) ;
} ) ;
}
function createThoughtStream ( ) {
const geo = new THREE . CylinderGeometry ( 8 , 8 , 12 , 32 , 1 , true ) ;
const mat = new THREE . ShaderMaterial ( {
transparent : true ,
side : THREE . BackSide ,
depthWrite : false ,
uniforms : {
uTime : { value : 0 } ,
uColor : { value : new THREE . Color ( NEXUS . colors . primary ) } ,
} ,
vertexShader : `
varying vec2 vUv ;
void main ( ) { vUv = uv ; gl _Position = projectionMatrix * modelViewMatrix * vec4 ( position , 1.0 ) ; }
` ,
fragmentShader : `
uniform float uTime ;
uniform vec3 uColor ;
varying vec2 vUv ;
float hash ( vec2 p ) { return fract ( sin ( dot ( p , vec2 ( 12.9898 , 78.233 ) ) ) * 43758.5453 ) ; }
void main ( ) {
float flow = fract ( vUv . y - uTime * 0.1 ) ;
float lines = step ( 0.98 , fract ( vUv . x * 20.0 + uTime * 0.05 ) ) ;
float dots = step ( 0.99 , hash ( vUv * 50.0 + floor ( uTime * 10.0 ) * 0.01 ) ) ;
float alpha = ( lines * 0.1 + dots * 0.5 ) * smoothstep ( 0.0 , 0.2 , vUv . y ) * smoothstep ( 1.0 , 0.8 , vUv . y ) ;
gl _FragColor = vec4 ( uColor , alpha * 0.3 ) ;
}
` ,
} ) ;
thoughtStreamMesh = new THREE . Mesh ( geo , mat ) ;
thoughtStreamMesh . position . y = 6 ;
scene . add ( thoughtStreamMesh ) ;
}
function createHarnessPulse ( ) {
const geo = new THREE . RingGeometry ( 0.1 , 0.2 , 64 ) ;
const mat = new THREE . MeshBasicMaterial ( {
color : NEXUS . colors . primary ,
transparent : true ,
opacity : 0 ,
side : THREE . DoubleSide ,
} ) ;
harnessPulseMesh = new THREE . Mesh ( geo , mat ) ;
harnessPulseMesh . rotation . x = - Math . PI / 2 ;
harnessPulseMesh . position . y = 0.1 ;
scene . add ( harnessPulseMesh ) ;
}
function createSessionPowerMeter ( ) {
const group = new THREE . Group ( ) ;
group . position . set ( 0 , 0 , 3 ) ;
const barCount = 12 ;
const barGeo = new THREE . BoxGeometry ( 0.2 , 0.1 , 0.1 ) ;
for ( let i = 0 ; i < barCount ; i ++ ) {
const mat = new THREE . MeshStandardMaterial ( {
color : NEXUS . colors . primary ,
emissive : NEXUS . colors . primary ,
emissiveIntensity : 0.2 ,
transparent : true ,
opacity : 0.6
} ) ;
const bar = new THREE . Mesh ( barGeo , mat ) ;
bar . position . y = 0.2 + i * 0.2 ;
group . add ( bar ) ;
powerMeterBars . push ( bar ) ;
}
const labelCanvas = document . createElement ( 'canvas' ) ;
labelCanvas . width = 256 ;
labelCanvas . height = 64 ;
const ctx = labelCanvas . getContext ( '2d' ) ;
ctx . font = 'bold 24px "Orbitron", sans-serif' ;
ctx . fillStyle = '#4af0c0' ;
ctx . textAlign = 'center' ;
ctx . fillText ( 'POWER LEVEL' , 128 , 40 ) ;
const tex = new THREE . CanvasTexture ( labelCanvas ) ;
const labelMat = new THREE . MeshBasicMaterial ( { map : tex , transparent : true , side : THREE . DoubleSide } ) ;
const label = new THREE . Mesh ( new THREE . PlaneGeometry ( 2 , 0.5 ) , labelMat ) ;
label . position . y = 3 ;
group . add ( label ) ;
scene . add ( group ) ;
}
// ═══ VISION SYSTEM ═══
function createVisionPoints ( data ) {
data . forEach ( config => {
const vp = createVisionPoint ( config ) ;
visionPoints . push ( vp ) ;
} ) ;
}
function createVisionPoint ( config ) {
const group = new THREE . Group ( ) ;
group . position . set ( config . position . x , config . position . y , config . position . z ) ;
const color = new THREE . Color ( config . color ) ;
// Floating Crystal
const crystalGeo = new THREE . OctahedronGeometry ( 0.6 , 0 ) ;
const crystalMat = new THREE . MeshPhysicalMaterial ( {
color : color ,
emissive : color ,
emissiveIntensity : 1 ,
roughness : 0 ,
metalness : 1 ,
transmission : 0.5 ,
thickness : 1 ,
} ) ;
const crystal = new THREE . Mesh ( crystalGeo , crystalMat ) ;
crystal . position . y = 2.5 ;
group . add ( crystal ) ;
// Glow Ring
const ringGeo = new THREE . TorusGeometry ( 0.8 , 0.02 , 16 , 64 ) ;
const ringMat = new THREE . MeshBasicMaterial ( { color : color , transparent : true , opacity : 0.5 } ) ;
const ring = new THREE . Mesh ( ringGeo , ringMat ) ;
ring . position . y = 2.5 ;
ring . rotation . x = Math . PI / 2 ;
group . add ( ring ) ;
// Light
const light = new THREE . PointLight ( color , 1 , 10 ) ;
light . position . set ( 0 , 2.5 , 0 ) ;
group . add ( light ) ;
scene . add ( group ) ;
return { config , group , crystal , ring , light } ;
}
// ═══ PORTAL SYSTEM ═══
function createPortals ( data ) {
data . forEach ( config => {
const portal = createPortal ( config ) ;
portals . push ( portal ) ;
} ) ;
}
function createPortal ( config ) {
const group = new THREE . Group ( ) ;
group . position . set ( config . position . x , config . position . y , config . position . z ) ;
if ( config . rotation ) {
group . rotation . y = config . rotation . y ;
}
const portalColor = new THREE . Color ( config . color ) ;
// Torus Ring
const torusGeo = new THREE . TorusGeometry ( 3 , 0.15 , 16 , 64 ) ;
const torusMat = new THREE . MeshStandardMaterial ( {
color : portalColor ,
emissive : portalColor ,
emissiveIntensity : 1.5 ,
roughness : 0.2 ,
metalness : 0.8 ,
} ) ;
const ring = new THREE . Mesh ( torusGeo , torusMat ) ;
ring . position . y = 3.5 ;
ring . name = ` portal_ring_ ${ config . id } ` ;
group . add ( ring ) ;
// Swirl Disc
const swirlGeo = new THREE . CircleGeometry ( 2.8 , 64 ) ;
const swirlMat = new THREE . ShaderMaterial ( {
transparent : true ,
side : THREE . DoubleSide ,
uniforms : {
uTime : { value : 0 } ,
uColor : { value : portalColor } ,
} ,
vertexShader : `
varying vec2 vUv ;
void main ( ) { vUv = uv ; gl _Position = projectionMatrix * modelViewMatrix * vec4 ( position , 1.0 ) ; }
` ,
fragmentShader : `
uniform float uTime ;
uniform vec3 uColor ;
varying vec2 vUv ;
void main ( ) {
vec2 c = vUv - 0.5 ;
float r = length ( c ) ;
float a = atan ( c . y , c . x ) ;
float swirl = sin ( a * 3.0 + r * 10.0 - uTime * 3.0 ) * 0.5 + 0.5 ;
float swirl2 = sin ( a * 5.0 - r * 8.0 + uTime * 2.0 ) * 0.5 + 0.5 ;
float mask = smoothstep ( 0.5 , 0.1 , r ) ;
vec3 col = mix ( uColor , vec3 ( 1.0 , 1.0 , 1.0 ) , swirl * 0.3 ) ;
col = mix ( col , vec3 ( 1.0 , 1.0 , 1.0 ) , swirl2 * 0.2 ) ;
float alpha = mask * ( 0.5 + 0.3 * swirl ) ;
gl _FragColor = vec4 ( col , alpha ) ;
}
` ,
} ) ;
const swirl = new THREE . Mesh ( swirlGeo , swirlMat ) ;
swirl . position . y = 3.5 ;
group . add ( swirl ) ;
// Orbital Particles
const pCount = 120 ;
const pGeo = new THREE . BufferGeometry ( ) ;
const pPos = new Float32Array ( pCount * 3 ) ;
const pSizes = new Float32Array ( pCount ) ;
for ( let i = 0 ; i < pCount ; i ++ ) {
const angle = Math . random ( ) * Math . PI * 2 ;
const r = 3.2 + Math . random ( ) * 0.5 ;
pPos [ i * 3 ] = Math . cos ( angle ) * r ;
pPos [ i * 3 + 1 ] = 3.5 + ( Math . random ( ) - 0.5 ) * 6 ;
pPos [ i * 3 + 2 ] = ( Math . random ( ) - 0.5 ) * 0.5 ;
pSizes [ i ] = 0.05 + Math . random ( ) * 0.1 ;
}
pGeo . setAttribute ( 'position' , new THREE . BufferAttribute ( pPos , 3 ) ) ;
pGeo . setAttribute ( 'size' , new THREE . BufferAttribute ( pSizes , 1 ) ) ;
const pMat = new THREE . PointsMaterial ( {
color : portalColor ,
size : 0.08 ,
transparent : true ,
opacity : 0.6 ,
blending : THREE . AdditiveBlending ,
depthWrite : false ,
} ) ;
const pSystem = new THREE . Points ( pGeo , pMat ) ;
group . add ( pSystem ) ;
// Pulsing Point Light
const light = new THREE . PointLight ( portalColor , 2 , 15 , 1.5 ) ;
light . position . set ( 0 , 3.5 , 1 ) ;
group . add ( light ) ;
// Label
const labelCanvas = document . createElement ( 'canvas' ) ;
labelCanvas . width = 512 ;
2026-04-12 19:26:25 -04:00
labelCanvas . height = 96 ;
2026-03-28 20:50:56 +00:00
const lctx = labelCanvas . getContext ( '2d' ) ;
lctx . font = 'bold 32px "Orbitron", sans-serif' ;
lctx . fillStyle = '#' + portalColor . getHexString ( ) ;
lctx . textAlign = 'center' ;
2026-04-12 19:26:25 -04:00
lctx . fillText ( ` ◈ ${ config . name . toUpperCase ( ) } ` , 256 , 36 ) ;
// Role tag (timmy/reflex/pilot) — defines portal ownership boundary
if ( config . role ) {
const roleColors = { timmy : '#4af0c0' , reflex : '#ff4466' , pilot : '#ffd700' } ;
lctx . font = 'bold 18px "Orbitron", sans-serif' ;
lctx . fillStyle = roleColors [ config . role ] || '#888888' ;
lctx . fillText ( config . role . toUpperCase ( ) , 256 , 68 ) ;
}
2026-03-28 20:50:56 +00:00
const labelTex = new THREE . CanvasTexture ( labelCanvas ) ;
const labelMat = new THREE . MeshBasicMaterial ( { map : labelTex , transparent : true , side : THREE . DoubleSide } ) ;
2026-04-12 19:26:25 -04:00
const labelMesh = new THREE . Mesh ( new THREE . PlaneGeometry ( 4 , 0.75 ) , labelMat ) ;
2026-03-28 20:50:56 +00:00
labelMesh . position . y = 7.5 ;
group . add ( labelMesh ) ;
// Base Pillars
for ( let side of [ - 1 , 1 ] ) {
const pillarGeo = new THREE . CylinderGeometry ( 0.2 , 0.3 , 7 , 8 ) ;
const pillarMat = new THREE . MeshStandardMaterial ( {
color : 0x1a1a2e ,
roughness : 0.5 ,
metalness : 0.7 ,
emissive : portalColor ,
emissiveIntensity : 0.1 ,
} ) ;
const pillar = new THREE . Mesh ( pillarGeo , pillarMat ) ;
pillar . position . set ( side * 3 , 3.5 , 0 ) ;
pillar . castShadow = true ;
group . add ( pillar ) ;
}
scene . add ( group ) ;
const portalObj = {
config ,
group ,
ring ,
swirl ,
pSystem ,
light ,
customElements : { }
} ;
// ═══ DISTINCT VISUAL IDENTITIES ═══
if ( config . id === 'archive' ) {
// Floating Data Cubes
const cubes = [ ] ;
for ( let i = 0 ; i < 6 ; i ++ ) {
const cubeGeo = new THREE . BoxGeometry ( 0.4 , 0.4 , 0.4 ) ;
const cubeMat = new THREE . MeshStandardMaterial ( {
color : portalColor ,
emissive : portalColor ,
emissiveIntensity : 1.5 ,
transparent : true ,
opacity : 0.8
} ) ;
const cube = new THREE . Mesh ( cubeGeo , cubeMat ) ;
group . add ( cube ) ;
cubes . push ( cube ) ;
}
portalObj . customElements . cubes = cubes ;
} else if ( config . id === 'chapel' ) {
// Glowing Core + Halo
const coreGeo = new THREE . SphereGeometry ( 1.2 , 32 , 32 ) ;
const coreMat = new THREE . MeshPhysicalMaterial ( {
color : 0xffffff ,
emissive : portalColor ,
emissiveIntensity : 2 ,
transparent : true ,
opacity : 0.4 ,
transmission : 0.9 ,
thickness : 2
} ) ;
const core = new THREE . Mesh ( coreGeo , coreMat ) ;
core . position . y = 3.5 ;
group . add ( core ) ;
portalObj . customElements . core = core ;
const haloGeo = new THREE . TorusGeometry ( 3.5 , 0.05 , 16 , 100 ) ;
const haloMat = new THREE . MeshBasicMaterial ( { color : portalColor , transparent : true , opacity : 0.3 } ) ;
const halo = new THREE . Mesh ( haloGeo , haloMat ) ;
halo . position . y = 3.5 ;
group . add ( halo ) ;
portalObj . customElements . halo = halo ;
} else if ( config . id === 'courtyard' ) {
// Double Rotating Rings
const outerRingGeo = new THREE . TorusGeometry ( 4.2 , 0.1 , 16 , 80 ) ;
const outerRingMat = new THREE . MeshStandardMaterial ( {
color : portalColor ,
emissive : portalColor ,
emissiveIntensity : 0.8 ,
transparent : true ,
opacity : 0.5
} ) ;
const outerRing = new THREE . Mesh ( outerRingGeo , outerRingMat ) ;
outerRing . position . y = 3.5 ;
group . add ( outerRing ) ;
portalObj . customElements . outerRing = outerRing ;
} else if ( config . id === 'gate' ) {
// Spiky Monoliths
const spikes = [ ] ;
for ( let i = 0 ; i < 8 ; i ++ ) {
const spikeGeo = new THREE . ConeGeometry ( 0.2 , 1.5 , 4 ) ;
const spikeMat = new THREE . MeshStandardMaterial ( { color : 0x111111 , emissive : portalColor , emissiveIntensity : 0.5 } ) ;
const spike = new THREE . Mesh ( spikeGeo , spikeMat ) ;
const angle = ( i / 8 ) * Math . PI * 2 ;
spike . position . set ( Math . cos ( angle ) * 3.5 , 3.5 + Math . sin ( angle ) * 3.5 , 0 ) ;
spike . rotation . z = angle + Math . PI / 2 ;
group . add ( spike ) ;
spikes . push ( spike ) ;
}
portalObj . customElements . spikes = spikes ;
// Darker Swirl
swirl . material . uniforms . uColor . value = new THREE . Color ( 0x220000 ) ;
}
return portalObj ;
}
// ═══ PARTICLES ═══
function createParticles ( ) {
const count = particleCount ( 1500 ) ;
const geo = new THREE . BufferGeometry ( ) ;
const positions = new Float32Array ( count * 3 ) ;
const colors = new Float32Array ( count * 3 ) ;
const sizes = new Float32Array ( count ) ;
const c1 = new THREE . Color ( NEXUS . colors . primary ) ;
const c2 = new THREE . Color ( NEXUS . colors . secondary ) ;
const c3 = new THREE . Color ( NEXUS . colors . gold ) ;
for ( let i = 0 ; i < count ; i ++ ) {
positions [ i * 3 ] = ( Math . random ( ) - 0.5 ) * 60 ;
positions [ i * 3 + 1 ] = Math . random ( ) * 20 ;
positions [ i * 3 + 2 ] = ( Math . random ( ) - 0.5 ) * 60 ;
const t = Math . random ( ) ;
const col = t < 0.5 ? c1 . clone ( ) . lerp ( c2 , t * 2 ) : c2 . clone ( ) . lerp ( c3 , ( t - 0.5 ) * 2 ) ;
colors [ i * 3 ] = col . r ;
colors [ i * 3 + 1 ] = col . g ;
colors [ i * 3 + 2 ] = col . b ;
sizes [ i ] = 0.02 + Math . random ( ) * 0.06 ;
}
geo . setAttribute ( 'position' , new THREE . BufferAttribute ( positions , 3 ) ) ;
geo . setAttribute ( 'color' , new THREE . BufferAttribute ( colors , 3 ) ) ;
geo . setAttribute ( 'size' , new THREE . BufferAttribute ( sizes , 1 ) ) ;
const mat = new THREE . ShaderMaterial ( {
uniforms : { uTime : { value : 0 } } ,
vertexShader : `
attribute float size ;
attribute vec3 color ;
varying vec3 vColor ;
uniform float uTime ;
void main ( ) {
vColor = color ;
vec3 pos = position ;
pos . y += sin ( uTime * 0.5 + position . x * 0.5 ) * 0.3 ;
pos . x += sin ( uTime * 0.3 + position . z * 0.4 ) * 0.2 ;
vec4 mv = modelViewMatrix * vec4 ( pos , 1.0 ) ;
gl _PointSize = size * 300.0 / - mv . z ;
gl _Position = projectionMatrix * mv ;
}
` ,
fragmentShader : `
varying vec3 vColor ;
void main ( ) {
float d = length ( gl _PointCoord - 0.5 ) ;
if ( d > 0.5 ) discard ;
float alpha = smoothstep ( 0.5 , 0.1 , d ) ;
gl _FragColor = vec4 ( vColor , alpha * 0.7 ) ;
}
` ,
transparent : true ,
depthWrite : false ,
blending : THREE . AdditiveBlending ,
} ) ;
particles = new THREE . Points ( geo , mat ) ;
scene . add ( particles ) ;
}
function createDustParticles ( ) {
const count = particleCount ( 500 ) ;
const geo = new THREE . BufferGeometry ( ) ;
const positions = new Float32Array ( count * 3 ) ;
for ( let i = 0 ; i < count ; i ++ ) {
positions [ i * 3 ] = ( Math . random ( ) - 0.5 ) * 40 ;
positions [ i * 3 + 1 ] = Math . random ( ) * 15 ;
positions [ i * 3 + 2 ] = ( Math . random ( ) - 0.5 ) * 40 ;
}
geo . setAttribute ( 'position' , new THREE . BufferAttribute ( positions , 3 ) ) ;
const mat = new THREE . PointsMaterial ( {
color : 0x8899bb ,
size : 0.03 ,
transparent : true ,
opacity : 0.3 ,
depthWrite : false ,
} ) ;
dustParticles = new THREE . Points ( geo , mat ) ;
scene . add ( dustParticles ) ;
}
// ═══ AMBIENT STRUCTURES ═══
function createAmbientStructures ( ) {
const crystalMat = new THREE . MeshPhysicalMaterial ( {
color : 0x3355aa ,
roughness : 0.1 ,
metalness : 0.2 ,
transmission : 0.6 ,
thickness : 2 ,
emissive : 0x1122aa ,
emissiveIntensity : 0.3 ,
} ) ;
const positions = [
{ x : - 18 , z : - 15 , s : 1.5 , ry : 0.3 } ,
{ x : - 20 , z : - 10 , s : 1 , ry : 0.8 } ,
{ x : - 15 , z : - 18 , s : 2 , ry : 1.2 } ,
{ x : 18 , z : - 15 , s : 1.8 , ry : 2.1 } ,
{ x : 20 , z : - 12 , s : 1.2 , ry : 0.5 } ,
{ x : - 12 , z : 18 , s : 1.3 , ry : 1.8 } ,
{ x : 14 , z : 16 , s : 1.6 , ry : 0.9 } ,
] ;
positions . forEach ( p => {
const geo = new THREE . ConeGeometry ( 0.4 * p . s , 2.5 * p . s , 5 ) ;
const crystal = new THREE . Mesh ( geo , crystalMat . clone ( ) ) ;
crystal . position . set ( p . x , 1.25 * p . s , p . z ) ;
crystal . rotation . y = p . ry ;
crystal . rotation . z = ( Math . random ( ) - 0.5 ) * 0.3 ;
crystal . castShadow = true ;
scene . add ( crystal ) ;
} ) ;
for ( let i = 0 ; i < 5 ; i ++ ) {
const angle = ( i / 5 ) * Math . PI * 2 ;
const r = 10 ;
const geo = new THREE . OctahedronGeometry ( 0.4 , 0 ) ;
const mat = new THREE . MeshStandardMaterial ( {
color : NEXUS . colors . primary ,
emissive : NEXUS . colors . primary ,
emissiveIntensity : 0.5 ,
} ) ;
const stone = new THREE . Mesh ( geo , mat ) ;
stone . position . set ( Math . cos ( angle ) * r , 5 + Math . sin ( i * 1.3 ) * 1.5 , Math . sin ( angle ) * r ) ;
stone . name = 'runestone_' + i ;
scene . add ( stone ) ;
}
const coreGeo = new THREE . IcosahedronGeometry ( 0.6 , 2 ) ;
const coreMat = new THREE . MeshPhysicalMaterial ( {
color : 0x4af0c0 ,
emissive : 0x4af0c0 ,
emissiveIntensity : 2 ,
roughness : 0 ,
metalness : 1 ,
transmission : 0.3 ,
thickness : 1 ,
} ) ;
const core = new THREE . Mesh ( coreGeo , coreMat ) ;
core . position . set ( 0 , 2.5 , 0 ) ;
core . name = 'nexus-core' ;
scene . add ( core ) ;
const pedGeo = new THREE . CylinderGeometry ( 0.8 , 1.2 , 1.5 , 8 ) ;
const pedMat = new THREE . MeshStandardMaterial ( {
color : 0x0a0f1a ,
roughness : 0.4 ,
metalness : 0.8 ,
emissive : 0x1a2a4a ,
emissiveIntensity : 0.3 ,
} ) ;
const pedestal = new THREE . Mesh ( pedGeo , pedMat ) ;
pedestal . position . set ( 0 , 0.75 , 0 ) ;
pedestal . castShadow = true ;
scene . add ( pedestal ) ;
}
// ═══ NAVIGATION MODE ═══
2026-04-12 11:52:13 -04:00
// ═══ VISITOR / OPERATOR MODE TOGGLE ═══
function toggleUIMode ( ) {
uiMode = uiMode === 'visitor' ? 'operator' : 'visitor' ;
document . body . classList . remove ( 'visitor-mode' , 'operator-mode' ) ;
document . body . classList . add ( uiMode + '-mode' ) ;
const label = document . getElementById ( 'mode-label' ) ;
const icon = document . querySelector ( '#mode-toggle-btn .hud-icon' ) ;
if ( label ) label . textContent = uiMode === 'visitor' ? 'VISITOR' : 'OPERATOR' ;
if ( icon ) icon . textContent = uiMode === 'visitor' ? '👁' : '⚙' ;
addChatMessage ( 'system' , ` Switched to ${ uiMode . toUpperCase ( ) } mode. ` ) ;
}
2026-03-28 20:50:56 +00:00
function cycleNavMode ( ) {
navModeIdx = ( navModeIdx + 1 ) % NAV _MODES . length ;
const mode = NAV _MODES [ navModeIdx ] ;
if ( mode === 'orbit' ) {
const dir = new THREE . Vector3 ( 0 , 0 , - 1 ) . applyEuler ( playerRot ) ;
orbitState . target . copy ( playerPos ) . addScaledVector ( dir , orbitState . radius ) ;
orbitState . target . y = Math . max ( 0 , orbitState . target . y ) ;
const toCamera = new THREE . Vector3 ( ) . subVectors ( playerPos , orbitState . target ) ;
orbitState . radius = toCamera . length ( ) ;
orbitState . theta = Math . atan2 ( toCamera . x , toCamera . z ) ;
orbitState . phi = Math . acos ( Math . max ( - 1 , Math . min ( 1 , toCamera . y / orbitState . radius ) ) ) ;
}
if ( mode === 'fly' ) flyY = playerPos . y ;
updateNavModeUI ( mode ) ;
}
function updateNavModeUI ( mode ) {
const el = document . getElementById ( 'nav-mode-label' ) ;
if ( el ) el . textContent = mode . toUpperCase ( ) ;
}
// ═══ CONTROLS ═══
function setupControls ( ) {
document . addEventListener ( 'keydown' , ( e ) => {
keys [ e . key . toLowerCase ( ) ] = true ;
if ( e . key === 'Enter' ) {
e . preventDefault ( ) ;
const input = document . getElementById ( 'chat-input' ) ;
if ( document . activeElement === input ) {
sendChatMessage ( ) ;
} else {
input . focus ( ) ;
}
}
if ( e . key . toLowerCase ( ) === 'm' && document . activeElement !== document . getElementById ( 'chat-input' ) ) {
openPortalAtlas ( ) ;
}
if ( e . key === 'Escape' ) {
document . getElementById ( 'chat-input' ) . blur ( ) ;
if ( portalOverlayActive ) closePortalOverlay ( ) ;
if ( visionOverlayActive ) closeVisionOverlay ( ) ;
if ( atlasOverlayActive ) closePortalAtlas ( ) ;
2026-04-11 03:29:05 +00:00
if ( _archiveDashboardOpen ) toggleArchiveHealthDashboard ( ) ;
2026-04-11 04:10:49 +00:00
if ( _memoryFilterOpen ) closeMemoryFilter ( ) ;
2026-03-28 20:50:56 +00:00
}
if ( e . key . toLowerCase ( ) === 'v' && document . activeElement !== document . getElementById ( 'chat-input' ) ) {
cycleNavMode ( ) ;
}
if ( e . key . toLowerCase ( ) === 'f' && activePortal && ! portalOverlayActive ) {
activatePortal ( activePortal ) ;
}
if ( e . key . toLowerCase ( ) === 'e' && activeVisionPoint && ! visionOverlayActive ) {
activateVisionPoint ( activeVisionPoint ) ;
}
2026-04-11 03:29:05 +00:00
if ( e . key . toLowerCase ( ) === 'h' && document . activeElement !== document . getElementById ( 'chat-input' ) ) {
toggleArchiveHealthDashboard ( ) ;
}
2026-04-11 04:10:49 +00:00
if ( e . key . toLowerCase ( ) === 'g' && document . activeElement !== document . getElementById ( 'chat-input' ) ) {
toggleMemoryFilter ( ) ;
}
2026-03-28 20:50:56 +00:00
} ) ;
document . addEventListener ( 'keyup' , ( e ) => {
keys [ e . key . toLowerCase ( ) ] = false ;
} ) ;
const canvas = document . getElementById ( 'nexus-canvas' ) ;
canvas . addEventListener ( 'mousedown' , ( e ) => {
if ( e . target === canvas ) {
mouseDown = true ;
orbitState . lastX = e . clientX ;
orbitState . lastY = e . clientY ;
2026-04-11 21:17:42 +00:00
_clickStartX = e . clientX ;
_clickStartY = e . clientY ;
2026-03-28 20:50:56 +00:00
2026-04-11 01:35:13 +00:00
// Raycasting for portals
2026-03-28 20:50:56 +00:00
if ( ! portalOverlayActive ) {
const mouse = new THREE . Vector2 (
( e . clientX / window . innerWidth ) * 2 - 1 ,
- ( e . clientY / window . innerHeight ) * 2 + 1
) ;
const raycaster = new THREE . Raycaster ( ) ;
raycaster . setFromCamera ( mouse , camera ) ;
2026-04-12 12:26:17 -04:00
// Raycast against both ring and swirl for a larger click target
const portalMeshes = portals . flatMap ( p => [ p . ring , p . swirl ] ) ;
const intersects = raycaster . intersectObjects ( portalMeshes ) ;
2026-04-11 01:35:13 +00:00
if ( intersects . length > 0 ) {
2026-04-12 12:26:17 -04:00
const hitObj = intersects [ 0 ] . object ;
const portal = portals . find ( p => p . ring === hitObj || p . swirl === hitObj ) ;
2026-04-11 01:35:13 +00:00
if ( portal ) activatePortal ( portal ) ;
2026-04-10 22:45:10 +00:00
}
2026-03-28 20:50:56 +00:00
}
}
} ) ;
2026-04-11 21:17:42 +00:00
document . addEventListener ( 'mouseup' , ( e ) => {
const wasDrag = Math . abs ( e . clientX - _clickStartX ) > 5 || Math . abs ( e . clientY - _clickStartY ) > 5 ;
mouseDown = false ;
if ( wasDrag || e . target !== canvas ) return ;
// Crystal click detection (Mnemosyne inspect panel, issue #1227)
if ( ! portalOverlayActive ) {
const mouse = new THREE . Vector2 (
( e . clientX / window . innerWidth ) * 2 - 1 ,
- ( e . clientY / window . innerHeight ) * 2 + 1
) ;
const raycaster = new THREE . Raycaster ( ) ;
raycaster . setFromCamera ( mouse , camera ) ;
const crystalMeshes = SpatialMemory . getCrystalMeshes ( ) ;
const hits = raycaster . intersectObjects ( crystalMeshes ) ;
if ( hits . length > 0 ) {
const entry = SpatialMemory . getMemoryFromMesh ( hits [ 0 ] . object ) ;
if ( entry ) {
SpatialMemory . highlightMemory ( entry . data . id ) ;
2026-04-12 06:45:25 +00:00
MemoryPulse . triggerPulse ( entry . data . id ) ;
2026-04-11 21:17:42 +00:00
const regionDef = SpatialMemory . REGIONS [ entry . region ] || SpatialMemory . REGIONS . working ;
MemoryInspect . show ( entry . data , regionDef ) ;
}
} else {
// Clicked empty space — close inspect panel and deselect crystal
if ( MemoryInspect . isOpen ( ) ) {
SpatialMemory . clearHighlight ( ) ;
MemoryInspect . hide ( ) ;
}
}
}
} ) ;
2026-03-28 20:50:56 +00:00
document . addEventListener ( 'mousemove' , ( e ) => {
if ( ! mouseDown ) return ;
if ( document . activeElement === document . getElementById ( 'chat-input' ) ) return ;
const mode = NAV _MODES [ navModeIdx ] ;
if ( mode === 'orbit' ) {
const dx = e . clientX - orbitState . lastX ;
const dy = e . clientY - orbitState . lastY ;
orbitState . lastX = e . clientX ;
orbitState . lastY = e . clientY ;
orbitState . theta -= dx * 0.005 ;
orbitState . phi = Math . max ( 0.05 , Math . min ( Math . PI * 0.85 , orbitState . phi + dy * 0.005 ) ) ;
} else {
playerRot . y -= e . movementX * 0.003 ;
playerRot . x -= e . movementY * 0.003 ;
playerRot . x = Math . max ( - Math . PI / 3 , Math . min ( Math . PI / 3 , playerRot . x ) ) ;
}
} ) ;
canvas . addEventListener ( 'wheel' , ( e ) => {
if ( NAV _MODES [ navModeIdx ] === 'orbit' ) {
orbitState . radius = Math . max ( orbitState . minR , Math . min ( orbitState . maxR , orbitState . radius + e . deltaY * 0.02 ) ) ;
}
} , { passive : true } ) ;
document . getElementById ( 'chat-toggle' ) . addEventListener ( 'click' , ( ) => {
chatOpen = ! chatOpen ;
document . getElementById ( 'chat-panel' ) . classList . toggle ( 'collapsed' , ! chatOpen ) ;
} ) ;
document . getElementById ( 'chat-send' ) . addEventListener ( 'click' , ( ) => sendChatMessage ( ) ) ;
2026-04-07 12:04:30 +00:00
// Add MemPalace mining button
document . querySelector ( '.chat-quick-actions' ) . innerHTML += `
2026-04-07 14:13:56 +00:00
< button class = "quick-action-btn" onclick = "mineMemPalaceContent()" > Mine Chat < / b u t t o n >
2026-04-07 12:38:48 +00:00
< div id = "mem-palace-stats" class = "mem-palace-stats" >
2026-04-07 14:04:56 +00:00
< div > Compression : < span id = "compression-ratio" > -- < / s p a n > x < / d i v >
< div > Docs : < span id = "docs-mined" > 0 < / s p a n > < / d i v >
< div > AAAK : < span id = "aaak-size" > 0 B < / s p a n > < / d i v >
2026-04-07 12:38:48 +00:00
< div > Compression : < span id = "compression-ratio" > -- < / s p a n > x < / d i v >
< div > Docs : < span id = "docs-mined" > 0 < / s p a n > < / d i v >
< div > AAAK : < span id = "aaak-size" > 0 B < / s p a n > < / d i v >
2026-04-07 12:54:34 +00:00
< div class = "mem-palace-logs" style = "margin-top:4px; font-size:10px; color:#4af0c0;" > Logs : < span id = "mem-logs" > 0 < / s p a n > < / d i v >
2026-04-07 12:38:48 +00:00
< / d i v >
2026-04-07 12:04:30 +00:00
` ;
2026-03-28 20:50:56 +00:00
// Chat quick actions
document . getElementById ( 'chat-quick-actions' ) . addEventListener ( 'click' , ( e ) => {
const btn = e . target . closest ( '.quick-action-btn' ) ;
if ( ! btn ) return ;
2026-04-11 01:35:13 +00:00
const action = btn . dataset . action ;
switch ( action ) {
case 'status' :
sendChatMessage ( "Timmy, what is the current system status?" ) ;
break ;
case 'agents' :
sendChatMessage ( "Timmy, check on all active agents." ) ;
break ;
case 'portals' :
openPortalAtlas ( ) ;
break ;
2026-04-12 12:47:19 -04:00
case 'soul' :
document . getElementById ( 'soul-overlay' ) . style . display = 'flex' ;
break ;
2026-04-11 01:35:13 +00:00
case 'help' :
sendChatMessage ( "Timmy, I need assistance with Nexus navigation." ) ;
break ;
2026-04-11 00:15:44 +00:00
}
2026-04-11 01:35:13 +00:00
} ) ;
2026-03-28 20:50:56 +00:00
document . getElementById ( 'portal-close-btn' ) . addEventListener ( 'click' , closePortalOverlay ) ;
document . getElementById ( 'vision-close-btn' ) . addEventListener ( 'click' , closeVisionOverlay ) ;
2026-04-12 11:52:13 -04:00
document . getElementById ( 'mode-toggle-btn' ) . addEventListener ( 'click' , toggleUIMode ) ;
2026-03-28 20:50:56 +00:00
document . getElementById ( 'atlas-toggle-btn' ) . addEventListener ( 'click' , openPortalAtlas ) ;
document . getElementById ( 'atlas-close-btn' ) . addEventListener ( 'click' , closePortalAtlas ) ;
2026-04-12 11:52:12 -04:00
initAtlasControls ( ) ;
2026-04-12 12:47:19 -04:00
// SOUL / Oath panel (issue #709)
document . getElementById ( 'soul-toggle-btn' ) . addEventListener ( 'click' , ( ) => {
document . getElementById ( 'soul-overlay' ) . style . display = 'flex' ;
} ) ;
document . getElementById ( 'soul-close-btn' ) . addEventListener ( 'click' , ( ) => {
document . getElementById ( 'soul-overlay' ) . style . display = 'none' ;
} ) ;
2026-03-28 20:50:56 +00:00
}
function sendChatMessage ( overrideText = null ) {
2026-04-07 14:04:56 +00:00
// Mine chat message to MemPalace
if ( overrideText ) {
window . electronAPI . execPython ( ` mempalace add_drawer " ${ this . wing } " "chat" " ${ overrideText } " ` ) ;
}
2026-03-28 20:50:56 +00:00
const input = document . getElementById ( 'chat-input' ) ;
const text = overrideText || input . value . trim ( ) ;
if ( ! text ) return ;
addChatMessage ( 'user' , text ) ;
if ( ! overrideText ) input . value = '' ;
setTimeout ( ( ) => {
const responses = [
'Processing your request through the harness...' ,
'I have noted this in my thought stream.' ,
'Acknowledged. Routing to appropriate agent loop.' ,
'The sovereign space recognizes your command.' ,
'Running analysis. Results will appear on the main terminal.' ,
'My crystal ball says... yes. Implementing.' ,
'Understood, Alexander. Adjusting priorities.' ,
] ;
const resp = responses [ Math . floor ( Math . random ( ) * responses . length ) ] ;
addChatMessage ( 'timmy' , resp ) ;
} , 500 + Math . random ( ) * 1000 ) ;
input . blur ( ) ;
}
// ═══ HERMES WEBSOCKET ═══
function connectHermes ( ) {
2026-04-07 11:46:16 +00:00
// Initialize MemPalace before Hermes connection
initializeMemPalace ( ) ;
// Existing Hermes connection code...
// Initialize MemPalace before Hermes connection
initializeMemPalace ( ) ;
2026-03-28 20:50:56 +00:00
if ( hermesWs ) return ;
2026-04-07 11:43:41 +00:00
// Initialize MemPalace storage
try {
console . log ( 'Initializing MemPalace memory system...' ) ;
// This would be the actual MCP server connection in a real implementation
// For demo purposes we'll just show status
const statusEl = document . getElementById ( 'mem-palace-status' ) ;
if ( statusEl ) {
statusEl . textContent = 'MEMPALACE INITIALIZING' ;
statusEl . style . color = '#4af0c0' ;
}
} catch ( err ) {
console . error ( 'Failed to initialize MemPalace:' , err ) ;
const statusEl = document . getElementById ( 'mem-palace-status' ) ;
if ( statusEl ) {
statusEl . textContent = 'MEMPALACE ERROR' ;
statusEl . style . color = '#ff4466' ;
}
}
2026-03-28 20:50:56 +00:00
const protocol = window . location . protocol === 'https:' ? 'wss:' : 'ws:' ;
const wsUrl = ` ${ protocol } // ${ window . location . host } /api/world/ws ` ;
console . log ( ` Connecting to Hermes at ${ wsUrl } ... ` ) ;
hermesWs = new WebSocket ( wsUrl ) ;
hermesWs . onopen = ( ) => {
console . log ( 'Hermes connected.' ) ;
wsConnected = true ;
addChatMessage ( 'system' , 'Hermes link established.' ) ;
updateWsHudStatus ( true ) ;
refreshWorkshopPanel ( ) ;
2026-04-11 01:35:13 +00:00
// Mnemosyne: request memory sync from Hermes
try {
hermesWs . send ( JSON . stringify ( { type : 'memory' , action : 'sync_request' } ) ) ;
console . info ( '[Mnemosyne] Sent sync_request to Hermes' ) ;
} catch ( e ) {
console . warn ( '[Mnemosyne] Failed to send sync_request:' , e ) ;
}
2026-03-28 20:50:56 +00:00
} ;
2026-04-07 11:32:55 +00:00
// Initialize MemPalace
connectMemPalace ( ) ;
2026-03-28 20:50:56 +00:00
hermesWs . onmessage = ( evt ) => {
try {
const data = JSON . parse ( evt . data ) ;
handleHermesMessage ( data ) ;
2026-04-07 11:43:41 +00:00
// Store in MemPalace
if ( data . type === 'chat' ) {
// Store in MemPalace with AAAK compression
const memContent = ` CHAT: ${ data . agent } ${ data . text } ` ;
// In a real implementation, we'd use mempalace.add_drawer()
console . log ( 'Storing in MemPalace:' , memContent ) ;
}
2026-03-28 20:50:56 +00:00
} catch ( e ) {
console . error ( 'Failed to parse Hermes message:' , e ) ;
}
} ;
hermesWs . onclose = ( ) => {
console . warn ( 'Hermes disconnected. Retrying in 5s...' ) ;
wsConnected = false ;
hermesWs = null ;
updateWsHudStatus ( false ) ;
refreshWorkshopPanel ( ) ;
if ( wsReconnectTimer ) clearTimeout ( wsReconnectTimer ) ;
wsReconnectTimer = setTimeout ( connectHermes , 5000 ) ;
} ;
hermesWs . onerror = ( err ) => {
console . error ( 'Hermes WS error:' , err ) ;
} ;
}
function handleHermesMessage ( data ) {
if ( data . type === 'chat' ) {
addChatMessage ( data . agent || 'timmy' , data . text ) ;
} else if ( data . type === 'tool_call' ) {
const content = ` Calling ${ data . tool } ( ${ JSON . stringify ( data . args ) } ) ` ;
recentToolOutputs . push ( { type : 'call' , agent : data . agent || 'SYSTEM' , content } ) ;
addToolMessage ( data . agent || 'SYSTEM' , 'call' , content ) ;
refreshWorkshopPanel ( ) ;
} else if ( data . type === 'tool_result' ) {
const content = ` Result: ${ JSON . stringify ( data . result ) } ` ;
recentToolOutputs . push ( { type : 'result' , agent : data . agent || 'SYSTEM' , content } ) ;
addToolMessage ( data . agent || 'SYSTEM' , 'result' , content ) ;
refreshWorkshopPanel ( ) ;
2026-04-11 01:35:13 +00:00
} else if ( data . type === 'memory' ) {
handleMemoryMessage ( data ) ;
2026-03-28 20:50:56 +00:00
} else if ( data . type === 'history' ) {
const container = document . getElementById ( 'chat-messages' ) ;
container . innerHTML = '' ;
data . messages . forEach ( msg => {
if ( msg . type === 'tool_call' ) addToolMessage ( msg . agent , 'call' , msg . content , false ) ;
else if ( msg . type === 'tool_result' ) addToolMessage ( msg . agent , 'result' , msg . content , false ) ;
else addChatMessage ( msg . agent , msg . text , false ) ;
} ) ;
}
2026-04-12 11:54:27 -04:00
} else if ( data . type && data . type . startsWith ( 'evennia.' ) ) {
handleEvenniaEvent ( data ) ;
2026-04-12 12:13:40 -04:00
// Evennia event bridge — process command/result/room fields if present
handleEvenniaEvent ( data ) ;
}
// ═══════════════════════════════════════════
// TIMMY ACTION STREAM — EVENNIA COMMAND FLOW
// ═══════════════════════════════════════════
const MAX _ACTION _STREAM = 8 ;
/ * *
* Add an entry to the action stream panel .
* @ param { 'cmd' | 'result' | 'room' } type
* @ param { string } text
* /
function addActionStreamEntry ( type , text ) {
const entry = { type , text , ts : Date . now ( ) } ;
actionStreamEntries . unshift ( entry ) ;
if ( actionStreamEntries . length > MAX _ACTION _STREAM ) actionStreamEntries . pop ( ) ;
renderActionStream ( ) ;
}
/ * *
* Update the current room display in the action stream .
* @ param { string } room
* /
function setActionStreamRoom ( room ) {
actionStreamRoom = room ;
const el = document . getElementById ( 'action-stream-room' ) ;
if ( el ) el . textContent = room ? ` ◈ ${ room } ` : '' ;
}
/ * *
* Render the action stream panel entries .
* /
function renderActionStream ( ) {
const el = document . getElementById ( 'action-stream-content' ) ;
if ( ! el ) return ;
el . innerHTML = actionStreamEntries . map ( e => {
const ts = new Date ( e . ts ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' , second : '2-digit' } ) ;
const cls = e . type === 'cmd' ? 'as-cmd' : e . type === 'result' ? 'as-result' : 'as-room' ;
const prefix = e . type === 'cmd' ? '>' : e . type === 'result' ? '←' : '◈' ;
return ` <div class="as-entry ${ cls } "><span class="as-prefix"> ${ prefix } </span> <span class="as-text"> ${ escHtml ( e . text ) } </span> <span class="as-ts"> ${ ts } </span></div> ` ;
} ) . join ( '' ) ;
}
/ * *
* Process Evennia - specific fields from Hermes WS messages .
* Called from handleHermesMessage for any message carrying evennia metadata .
* /
function handleEvenniaEvent ( data ) {
if ( data . evennia _command ) {
addActionStreamEntry ( 'cmd' , data . evennia _command ) ;
}
if ( data . evennia _result ) {
const excerpt = typeof data . evennia _result === 'string'
? data . evennia _result . substring ( 0 , 120 )
: JSON . stringify ( data . evennia _result ) . substring ( 0 , 120 ) ;
addActionStreamEntry ( 'result' , excerpt ) ;
}
if ( data . evennia _room ) {
setActionStreamRoom ( data . evennia _room ) ;
addActionStreamEntry ( 'room' , ` Moved to: ${ data . evennia _room } ` ) ;
}
2026-03-28 20:50:56 +00:00
}
2026-04-11 01:35:13 +00:00
// ═══════════════════════════════════════════
2026-04-12 11:54:27 -04:00
// ═══════════════════════════════════════════
// EVENNIA ROOM SNAPSHOT PANEL (Issue #728)
// ═══════════════════════════════════════════
function handleEvenniaEvent ( data ) {
const evtType = data . type ;
if ( evtType === 'evennia.room_snapshot' ) {
evenniaRoom = {
roomKey : data . room _key || data . room _id || '' ,
title : data . title || 'Unknown Room' ,
desc : data . desc || '' ,
exits : data . exits || [ ] ,
objects : data . objects || [ ] ,
occupants : data . occupants || [ ] ,
timestamp : data . timestamp || new Date ( ) . toISOString ( )
} ;
evenniaConnected = true ;
renderEvenniaRoomPanel ( ) ;
resetEvenniaStaleTimer ( ) ;
} else if ( evtType === 'evennia.player_move' ) {
// Movement may indicate current room changed; update location text
if ( data . to _room ) {
const locEl = document . getElementById ( 'hud-location-text' ) ;
if ( locEl ) locEl . textContent = data . to _room ;
}
} else if ( evtType === 'evennia.session_bound' ) {
evenniaConnected = true ;
renderEvenniaRoomPanel ( ) ;
} else if ( evtType === 'evennia.player_join' || evtType === 'evennia.player_leave' ) {
// Refresh occupant display if we have room data
if ( evenniaRoom ) renderEvenniaRoomPanel ( ) ;
}
}
function resetEvenniaStaleTimer ( ) {
if ( evenniaStaleTimer ) clearTimeout ( evenniaStaleTimer ) ;
const dot = document . getElementById ( 'erp-live-dot' ) ;
const status = document . getElementById ( 'erp-status' ) ;
if ( dot ) dot . className = 'erp-live-dot connected' ;
if ( status ) { status . textContent = 'LIVE' ; status . className = 'erp-status online' ; }
evenniaStaleTimer = setTimeout ( ( ) => {
if ( dot ) dot . className = 'erp-live-dot stale' ;
if ( status ) { status . textContent = 'STALE' ; status . className = 'erp-status stale' ; }
} , EVENNIA _STALE _MS ) ;
}
function renderEvenniaRoomPanel ( ) {
const panel = document . getElementById ( 'evennia-room-panel' ) ;
if ( ! panel ) return ;
panel . style . display = 'block' ;
const emptyEl = document . getElementById ( 'erp-empty' ) ;
const roomEl = document . getElementById ( 'erp-room' ) ;
if ( ! evenniaRoom ) {
if ( emptyEl ) emptyEl . style . display = 'flex' ;
if ( roomEl ) roomEl . style . display = 'none' ;
return ;
}
if ( emptyEl ) emptyEl . style . display = 'none' ;
if ( roomEl ) roomEl . style . display = 'block' ;
const titleEl = document . getElementById ( 'erp-room-title' ) ;
const descEl = document . getElementById ( 'erp-room-desc' ) ;
if ( titleEl ) titleEl . textContent = evenniaRoom . title ;
if ( descEl ) descEl . textContent = evenniaRoom . desc ;
renderEvenniaList ( 'erp-exits' , evenniaRoom . exits , ( item ) => {
const name = item . key || item . destination _id || item . name || '?' ;
const dest = item . destination _key || item . destination _id || '' ;
return { icon : '→' , label : name , extra : dest && dest !== name ? dest : '' } ;
} ) ;
renderEvenniaList ( 'erp-objects' , evenniaRoom . objects , ( item ) => {
const name = item . short _desc || item . key || item . id || item . name || '?' ;
return { icon : '◇' , label : name } ;
} ) ;
renderEvenniaList ( 'erp-occupants' , evenniaRoom . occupants , ( item ) => {
const name = item . character || item . name || item . account || '?' ;
return { icon : '◉' , label : name } ;
} ) ;
const tsEl = document . getElementById ( 'erp-footer-ts' ) ;
const roomKeyEl = document . getElementById ( 'erp-footer-room' ) ;
if ( tsEl ) {
try {
const d = new Date ( evenniaRoom . timestamp ) ;
tsEl . textContent = d . toISOString ( ) . replace ( 'T' , ' ' ) . substring ( 0 , 19 ) + ' UTC' ;
} catch ( e ) { tsEl . textContent = '—' ; }
}
if ( roomKeyEl ) roomKeyEl . textContent = evenniaRoom . roomKey ;
}
function renderEvenniaList ( containerId , items , mapFn ) {
const container = document . getElementById ( containerId ) ;
if ( ! container ) return ;
container . innerHTML = '' ;
if ( ! items || items . length === 0 ) {
const empty = document . createElement ( 'div' ) ;
empty . className = 'erp-section-empty' ;
empty . textContent = 'none' ;
container . appendChild ( empty ) ;
return ;
}
items . forEach ( item => {
const mapped = mapFn ( item ) ;
const row = document . createElement ( 'div' ) ;
row . className = 'erp-item' ;
row . innerHTML = ` <span class="erp-item-icon"> ${ mapped . icon } </span><span> ${ mapped . label } </span> ` ;
if ( mapped . extra ) {
row . innerHTML += ` <span class="erp-item-dest"> ${ mapped . extra } </span> ` ;
}
container . appendChild ( row ) ;
} ) ;
}
2026-04-11 01:35:13 +00:00
// MNEMOSYNE — LIVE MEMORY BRIDGE
// ═══════════════════════════════════════════
/ * *
* Handle incoming memory messages from Hermes WS .
* Actions : place , remove , update , sync _response
* /
/ * *
* Clear all entries from the memory feed .
* /
function clearMemoryFeed ( ) {
memoryFeedEntries = [ ] ;
renderMemoryFeed ( ) ;
console . info ( '[Mnemosyne] Memory feed cleared' ) ;
}
2026-04-11 21:17:42 +00:00
/ * *
* Navigate to a linked memory from the inspect panel .
* Highlights the target crystal and re - opens the panel with its data .
* @ param { string } memId
* /
function _navigateToMemory ( memId ) {
const all = SpatialMemory . getAllMemories ( ) ;
const data = all . find ( m => m . id === memId ) ;
if ( ! data ) {
console . warn ( '[MemoryInspect] Linked memory not found in scene:' , memId ) ;
return ;
}
SpatialMemory . highlightMemory ( memId ) ;
const regionDef = SpatialMemory . REGIONS [ data . category ] || SpatialMemory . REGIONS . working ;
MemoryInspect . show ( data , regionDef ) ;
}
2026-04-11 01:35:13 +00:00
function handleMemoryMessage ( data ) {
const action = data . action ;
const memory = data . memory ;
const memories = data . memories ;
if ( action === 'place' && memory ) {
const placed = SpatialMemory . placeMemory ( memory ) ;
if ( placed ) {
addMemoryFeedEntry ( 'place' , memory ) ;
console . info ( '[Mnemosyne] Memory placed via WS:' , memory . id ) ;
}
} else if ( action === 'remove' && memory ) {
SpatialMemory . removeMemory ( memory . id ) ;
addMemoryFeedEntry ( 'remove' , memory ) ;
console . info ( '[Mnemosyne] Memory removed via WS:' , memory . id ) ;
} else if ( action === 'update' && memory ) {
SpatialMemory . updateMemory ( memory . id , memory ) ;
addMemoryFeedEntry ( 'update' , memory ) ;
console . info ( '[Mnemosyne] Memory updated via WS:' , memory . id ) ;
} else if ( action === 'sync_response' && Array . isArray ( memories ) ) {
const count = SpatialMemory . importMemories ( memories ) ;
addMemoryFeedEntry ( 'sync' , { content : count + ' memories synced' , id : 'sync' } ) ;
console . info ( '[Mnemosyne] Synced' , count , 'memories from Hermes' ) ;
} else {
console . warn ( '[Mnemosyne] Unknown memory action:' , action ) ;
}
2026-04-11 03:29:05 +00:00
if ( _archiveDashboardOpen ) updateArchiveHealthDashboard ( ) ;
2026-04-11 01:35:13 +00:00
}
/ * *
* Add an entry to the memory activity feed panel .
* /
function addMemoryFeedEntry ( action , memory ) {
const entry = {
action ,
content : memory . content || memory . id || '(unknown)' ,
category : memory . category || 'working' ,
timestamp : new Date ( ) . toISOString ( )
} ;
memoryFeedEntries . unshift ( entry ) ;
if ( memoryFeedEntries . length > 5 ) memoryFeedEntries . pop ( ) ;
renderMemoryFeed ( ) ;
// Auto-dismiss entries older than 5 minutes
setTimeout ( ( ) => {
const idx = memoryFeedEntries . indexOf ( entry ) ;
if ( idx > - 1 ) {
memoryFeedEntries . splice ( idx , 1 ) ;
renderMemoryFeed ( ) ;
}
} , 300000 ) ;
}
/ * *
* Render the memory feed panel .
* /
function renderMemoryFeed ( ) {
const container = document . getElementById ( 'memory-feed-list' ) ;
if ( ! container ) return ;
container . innerHTML = '' ;
memoryFeedEntries . forEach ( entry => {
const el = document . createElement ( 'div' ) ;
el . className = 'memory-feed-entry memory-feed-' + entry . action ;
const regionDef = SpatialMemory . REGIONS [ entry . category ] || SpatialMemory . REGIONS . working ;
const dotColor = '#' + regionDef . color . toString ( 16 ) . padStart ( 6 , '0' ) ;
const time = new Date ( entry . timestamp ) . toLocaleTimeString ( ) ;
const truncated = entry . content . length > 40 ? entry . content . slice ( 0 , 40 ) + '\u2026' : entry . content ;
const actionIcon = { place : '\u2795' , remove : '\u2796' , update : '\u270F' , sync : '\u21C4' } [ entry . action ] || '\u2022' ;
el . innerHTML = '<span class="memory-feed-dot" style="background:' + dotColor + '"></span>' +
'<span class="memory-feed-action">' + actionIcon + '</span>' +
'<span class="memory-feed-content">' + truncated + '</span>' +
'<span class="memory-feed-time">' + time + '</span>' ;
container . appendChild ( el ) ;
} ) ;
// Show feed if there are entries
const panel = document . getElementById ( 'memory-feed' ) ;
if ( panel ) panel . style . display = memoryFeedEntries . length > 0 ? 'block' : 'none' ;
}
2026-04-11 03:29:05 +00:00
// ── Archive Health Dashboard (issue #1210) ────────────────────────────
let _archiveDashboardOpen = false ;
/ * *
* Toggle the archive health dashboard panel ( hotkey H ) .
* /
function toggleArchiveHealthDashboard ( ) {
_archiveDashboardOpen = ! _archiveDashboardOpen ;
const panel = document . getElementById ( 'archive-health-dashboard' ) ;
if ( ! panel ) return ;
if ( _archiveDashboardOpen ) {
updateArchiveHealthDashboard ( ) ;
panel . style . display = 'block' ;
} else {
panel . style . display = 'none' ;
}
}
/ * *
* Render current archive statistics into the dashboard panel .
* Reads live from SpatialMemory . getAllMemories ( ) — no backend needed .
* /
2026-04-11 04:10:49 +00:00
function toggleMemoryFilter ( ) {
_memoryFilterOpen = ! _memoryFilterOpen ;
if ( _memoryFilterOpen ) {
openMemoryFilter ( ) ;
} else {
closeMemoryFilter ( ) ;
}
}
2026-04-11 03:29:05 +00:00
function updateArchiveHealthDashboard ( ) {
const container = document . getElementById ( 'archive-health-content' ) ;
if ( ! container ) return ;
const memories = SpatialMemory . getAllMemories ( ) ;
const regions = SpatialMemory . REGIONS ;
const total = memories . length ;
// ── Category breakdown ────────────────────────────────────────────
const catCounts = { } ;
memories . forEach ( m => {
const cat = m . category || 'working' ;
catCounts [ cat ] = ( catCounts [ cat ] || 0 ) + 1 ;
} ) ;
// ── Trust distribution (using strength field as trust score) ──────
let trustHigh = 0 , trustMid = 0 , trustLow = 0 ;
memories . forEach ( m => {
const t = m . strength != null ? m . strength : 0.7 ;
if ( t > 0.8 ) trustHigh ++ ;
else if ( t >= 0.5 ) trustMid ++ ;
else trustLow ++ ;
} ) ;
// ── Timestamps ────────────────────────────────────────────────────
let newestMs = null , oldestMs = null ;
memories . forEach ( m => {
const ts = m . timestamp ? new Date ( m . timestamp ) . getTime ( ) : null ;
if ( ts && ! isNaN ( ts ) ) {
if ( newestMs === null || ts > newestMs ) newestMs = ts ;
if ( oldestMs === null || ts < oldestMs ) oldestMs = ts ;
}
} ) ;
const fmtDate = ms => ms ? new Date ( ms ) . toLocaleString ( undefined , { month : 'short' , day : 'numeric' , hour : '2-digit' , minute : '2-digit' } ) : '—' ;
// ── Entity connection count ───────────────────────────────────────
let entityConnCount = 0 ;
memories . forEach ( m => {
if ( m . connections && Array . isArray ( m . connections ) ) {
entityConnCount += m . connections . length ;
}
} ) ;
// Each connection is stored on both ends, divide by 2 for unique links
const uniqueLinks = Math . floor ( entityConnCount / 2 ) ;
// ── Build HTML ────────────────────────────────────────────────────
let html = '' ;
// Total count
html += ` <div>
< div class = "ah-section-label" > Total Memories < / d i v >
< div class = "ah-total" >
< span class = "ah-total-count" > $ { total } < / s p a n >
< span class = "ah-total-label" > crystals in archive < / s p a n >
< / d i v >
< / d i v > ` ;
// Category breakdown
const sortedCats = Object . entries ( catCounts ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) ;
if ( sortedCats . length > 0 ) {
html += ` <div><div class="ah-section-label">Categories</div> ` ;
sortedCats . forEach ( ( [ cat , count ] ) => {
const region = regions [ cat ] || regions . working ;
const color = '#' + region . color . toString ( 16 ) . padStart ( 6 , '0' ) ;
const pct = total > 0 ? Math . round ( ( count / total ) * 100 ) : 0 ;
html += ` <div class="ah-category-row">
< span class = "ah-cat-dot" style = "background:${color}" > < / s p a n >
< span class = "ah-cat-label" > $ { region . label || cat } < / s p a n >
< div class = "ah-cat-bar-wrap" >
< div class = "ah-cat-bar" style = "width:${pct}%;background:${color}" > < / d i v >
< / d i v >
< span class = "ah-cat-count" > $ { count } < / s p a n >
< / d i v > ` ;
} ) ;
html += ` </div> ` ;
}
// Trust distribution
html += ` <div>
< div class = "ah-section-label" > Trust Distribution < / d i v >
< div class = "ah-trust-row" >
< div class = "ah-trust-band ah-trust-high" >
< div class = "ah-trust-band-count" > $ { trustHigh } < / d i v >
< div class = "ah-trust-band-label" > High & gt ; 0.8 < / d i v >
< / d i v >
< div class = "ah-trust-band ah-trust-mid" >
< div class = "ah-trust-band-count" > $ { trustMid } < / d i v >
< div class = "ah-trust-band-label" > Mid 0.5 – 0.8 < / d i v >
< / d i v >
< div class = "ah-trust-band ah-trust-low" >
< div class = "ah-trust-band-count" > $ { trustLow } < / d i v >
< div class = "ah-trust-band-label" > Low & lt ; 0.5 < / d i v >
< / d i v >
< / d i v >
< / d i v > ` ;
// Timestamps
html += ` <div>
< div class = "ah-section-label" > Timeline < / d i v >
< div class = "ah-timestamps" >
< div class = "ah-ts-row" >
< span class = "ah-ts-label" > Newest < / s p a n >
< span class = "ah-ts-value" > $ { fmtDate ( newestMs ) } < / s p a n >
< / d i v >
< div class = "ah-ts-row" >
< span class = "ah-ts-label" > Oldest < / s p a n >
< span class = "ah-ts-value" > $ { fmtDate ( oldestMs ) } < / s p a n >
< / d i v >
< / d i v >
< / d i v > ` ;
// Entity connections
html += ` <div>
< div class = "ah-section-label" > Entity Connections < / d i v >
< span class = "ah-entity-count" > $ { uniqueLinks } < / s p a n >
< span class = "ah-entity-label" > unique links < / s p a n >
< / d i v > ` ;
// Hotkey hint
html += ` <div class="ah-hotkey-hint">PRESS H TO CLOSE</div> ` ;
container . innerHTML = html ;
}
2026-03-28 20:50:56 +00:00
function updateWsHudStatus ( connected ) {
2026-04-07 11:46:16 +00:00
// Update MemPalace status alongside regular WS status
updateMemPalaceStatus ( ) ;
// Existing WS status code...
// Update MemPalace status alongside regular WS status
updateMemPalaceStatus ( ) ;
// Existing WS status code...
2026-03-28 20:50:56 +00:00
const dot = document . querySelector ( '.chat-status-dot' ) ;
if ( dot ) {
dot . style . background = connected ? '#4af0c0' : '#ff4466' ;
dot . style . boxShadow = connected ? '0 0 10px #4af0c0' : '0 0 10px #ff4466' ;
}
2026-04-07 11:32:55 +00:00
// Update MemPalace status
const memStatus = document . getElementById ( 'mem-palace-status' ) ;
if ( memStatus ) {
memStatus . textContent = connected ? 'MEMPALACE ACTIVE' : 'MEMPALACE OFFLINE' ;
memStatus . style . color = connected ? '#4af0c0' : '#ff4466' ;
}
}
function connectMemPalace ( ) {
2026-04-13 18:53:08 -04:00
const statusEl = document . getElementById ( 'mem-palace-status' ) ;
const ratioEl = document . getElementById ( 'compression-ratio' ) ;
const docsEl = document . getElementById ( 'docs-mined' ) ;
const sizeEl = document . getElementById ( 'aaak-size' ) ;
// Show connecting state
if ( statusEl ) {
statusEl . textContent = 'MEMPALACE CONNECTING' ;
statusEl . style . color = '#ffd700' ;
statusEl . style . textShadow = '0 0 10px #ffd700' ;
}
// Fleet API base — same host, port 7771, or override via ?mempalace=host:port
const params = new URLSearchParams ( window . location . search ) ;
const override = params . get ( 'mempalace' ) ;
const apiBase = override
? ` http:// ${ override } `
: ` ${ window . location . protocol } // ${ window . location . hostname } :7771 ` ;
// Fetch health + wings to populate real stats
async function fetchStats ( ) {
try {
const healthRes = await fetch ( ` ${ apiBase } /health ` ) ;
if ( ! healthRes . ok ) throw new Error ( ` Health ${ healthRes . status } ` ) ;
const health = await healthRes . json ( ) ;
const wingsRes = await fetch ( ` ${ apiBase } /wings ` ) ;
const wings = wingsRes . ok ? await wingsRes . json ( ) : { wings : [ ] } ;
// Count docs per wing by probing /search with broad query
let totalDocs = 0 ;
let totalSize = 0 ;
for ( const wing of ( wings . wings || [ ] ) ) {
try {
const sr = await fetch ( ` ${ apiBase } /search?q=*&wing= ${ wing } &n=1 ` ) ;
if ( sr . ok ) {
const sd = await sr . json ( ) ;
totalDocs += sd . count || 0 ;
}
} catch ( _ ) { /* skip */ }
}
const compressionRatio = totalDocs > 0 ? Math . max ( 1 , Math . round ( totalDocs * 0.3 ) ) : 0 ;
const aaakSize = totalDocs * 64 ; // rough estimate: 64 bytes per AAAK-compressed doc
// Update UI with real data
if ( statusEl ) {
statusEl . textContent = 'MEMPALACE ACTIVE' ;
statusEl . style . color = '#4af0c0' ;
statusEl . style . textShadow = '0 0 10px #4af0c0' ;
}
if ( ratioEl ) ratioEl . textContent = ` ${ compressionRatio } x ` ;
if ( docsEl ) docsEl . textContent = String ( totalDocs ) ;
if ( sizeEl ) sizeEl . textContent = formatBytes ( aaakSize ) ;
console . log ( ` [MemPalace] Connected to ${ apiBase } — ${ totalDocs } docs across ${ wings . wings ? . length || 0 } wings ` ) ;
return true ;
} catch ( err ) {
console . warn ( '[MemPalace] Fleet API unavailable:' , err . message ) ;
if ( statusEl ) {
statusEl . textContent = 'MEMPALACE OFFLINE' ;
statusEl . style . color = '#ff4466' ;
statusEl . style . textShadow = '0 0 10px #ff4466' ;
}
if ( ratioEl ) ratioEl . textContent = '--x' ;
if ( docsEl ) docsEl . textContent = '0' ;
if ( sizeEl ) sizeEl . textContent = '0B' ;
return false ;
2026-04-07 14:08:42 +00:00
}
}
2026-04-13 18:53:08 -04:00
// Initial fetch + periodic refresh every 60s
fetchStats ( ) . then ( ok => {
if ( ok ) setInterval ( fetchStats , 60000 ) ;
} ) ;
}
function formatBytes ( bytes ) {
if ( bytes === 0 ) return '0B' ;
const k = 1024 ;
const sizes = [ 'B' , 'KB' , 'MB' , 'GB' ] ;
const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
return parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( 1 ) ) + sizes [ i ] ;
2026-04-07 14:08:42 +00:00
}
function mineMemPalaceContent ( ) {
const logs = document . getElementById ( 'mem-palace-logs' ) ;
const now = new Date ( ) . toLocaleTimeString ( ) ;
// Add mining progress indicator
logs . innerHTML = ` <div> ${ now } - Mining chat history...</div> ` + logs . innerHTML ;
2026-04-07 14:18:52 +00:00
// Get chat messages to mine
const messages = Array . from ( document . querySelectorAll ( '.chat-msg' ) ) . map ( m => m . innerText ) ;
if ( messages . length === 0 ) {
logs . innerHTML = ` <div style="color:#ff4466;"> ${ now } - No chat content to mine</div> ` + logs . innerHTML ;
return ;
}
// Update MemPalace stats
const ratio = parseInt ( document . getElementById ( 'compression-ratio' ) . textContent ) + 1 ;
const docs = parseInt ( document . getElementById ( 'docs-mined' ) . textContent ) + messages . length ;
const size = parseInt ( document . getElementById ( 'aaak-size' ) . textContent . replace ( 'B' , '' ) ) + ( messages . length * 30 ) ;
document . getElementById ( 'compression-ratio' ) . textContent = ` ${ ratio } x ` ;
document . getElementById ( 'docs-mined' ) . textContent = ` ${ docs } ` ;
document . getElementById ( 'aaak-size' ) . textContent = ` ${ size } B ` ;
// Add success message
logs . innerHTML = ` <div style="color:#4af0c0;"> ${ now } - Mined ${ messages . length } chat entries</div> ` + logs . innerHTML ;
2026-04-07 11:46:16 +00:00
// Actual MemPalace initialization would happen here
// For demo purposes we'll just show status
statusEl . textContent = 'Connected to local MemPalace' ;
statusEl . style . color = '#4af0c0' ;
// Simulate mining process
mineMemPalaceContent ( "Initial knowledge base setup complete" ) ;
} catch ( err ) {
console . error ( 'Failed to initialize MemPalace:' , err ) ;
document . getElementById ( 'mem-palace-status' ) . textContent = 'MemPalace ERROR' ;
document . getElementById ( 'mem-palace-status' ) . style . color = '#ff4466' ;
}
2026-04-07 11:32:55 +00:00
try {
// Initialize MemPalace MCP server
console . log ( 'Initializing MemPalace memory system...' ) ;
// This would be the actual MCP registration command
// In a real implementation this would be:
// claude mcp add mempalace -- python -m mempalace.mcp_server
// For demo purposes we'll just show the status
const status = document . getElementById ( 'mem-palace-status' ) ;
if ( status ) {
status . textContent = 'MEMPALACE INITIALIZING' ;
setTimeout ( ( ) => {
status . textContent = 'MEMPALACE ACTIVE' ;
status . style . color = '#4af0c0' ;
} , 1500 ) ;
}
} catch ( err ) {
console . error ( 'Failed to initialize MemPalace:' , err ) ;
const status = document . getElementById ( 'mem-palace-status' ) ;
if ( status ) {
status . textContent = 'MEMPALACE ERROR' ;
status . style . color = '#ff4466' ;
}
}
2026-03-28 20:50:56 +00:00
}
// ═══ SESSION PERSISTENCE ═══
function saveSession ( ) {
const msgs = Array . from ( document . querySelectorAll ( '.chat-msg' ) ) . slice ( - 60 ) . map ( el => ( {
html : el . innerHTML ,
className : el . className
} ) ) ;
2026-04-07 11:44:46 +00:00
// Store in MemPalace
if ( window . mempalace ) {
2026-04-07 14:13:56 +00:00
try {
mempalace . add _drawer ( 'chat_history' , {
content : JSON . stringify ( msgs ) ,
metadata : {
type : 'chat' ,
timestamp : Date . now ( )
}
} ) ;
} catch ( error ) {
console . error ( 'MemPalace save failed:' , error ) ;
}
2026-04-07 11:44:46 +00:00
}
2026-04-07 14:13:56 +00:00
// Fallback to localStorage
2026-03-28 20:50:56 +00:00
localStorage . setItem ( 'nexus_chat_history' , JSON . stringify ( msgs ) ) ;
}
function loadSession ( ) {
const saved = localStorage . getItem ( 'nexus_chat_history' ) ;
if ( saved ) {
const msgs = JSON . parse ( saved ) ;
const container = document . getElementById ( 'chat-messages' ) ;
container . innerHTML = '' ;
msgs . forEach ( m => {
const div = document . createElement ( 'div' ) ;
div . className = m . className ;
div . innerHTML = m . html ;
container . appendChild ( div ) ;
} ) ;
container . scrollTop = container . scrollHeight ;
}
}
function addChatMessage ( agent , text , shouldSave = true ) {
2026-04-07 11:46:16 +00:00
// Mine chat messages for MemPalace
mineMemPalaceContent ( text ) ;
// Mine chat messages for MemPalace
mineMemPalaceContent ( text ) ;
2026-03-28 20:50:56 +00:00
const container = document . getElementById ( 'chat-messages' ) ;
const div = document . createElement ( 'div' ) ;
div . className = ` chat-msg chat-msg- ${ agent } ` ;
2026-04-07 11:44:46 +00:00
// Store in MemPalace
if ( window . mempalace ) {
mempalace . add _drawer ( 'chat_history' , {
content : text ,
metadata : {
agent ,
timestamp : Date . now ( )
}
} ) ;
}
2026-04-07 11:43:41 +00:00
// Store in MemPalace
if ( agent !== 'system' ) {
// In a real implementation, we'd use mempalace.add_drawer()
console . log ( ` MemPalace storage: ${ agent } - ${ text } ` ) ;
}
2026-03-28 20:50:56 +00:00
const prefixes = {
user : '[ALEXANDER]' ,
timmy : '[TIMMY]' ,
system : '[NEXUS]' ,
error : '[ERROR]' ,
kimi : '[KIMI]' ,
claude : '[CLAUDE]' ,
perplexity : '[PERPLEXITY]'
} ;
const prefix = document . createElement ( 'span' ) ;
prefix . className = 'chat-msg-prefix' ;
prefix . textContent = ` ${ prefixes [ agent ] || '[' + agent . toUpperCase ( ) + ']' } ` ;
div . appendChild ( prefix ) ;
div . appendChild ( document . createTextNode ( text ) ) ;
container . appendChild ( div ) ;
container . scrollTop = container . scrollHeight ;
if ( shouldSave ) saveSession ( ) ;
}
function addToolMessage ( agent , type , content , shouldSave = true ) {
const container = document . getElementById ( 'chat-messages' ) ;
const div = document . createElement ( 'div' ) ;
div . className = ` chat-msg chat-msg-tool tool- ${ type } ` ;
const prefix = document . createElement ( 'div' ) ;
prefix . className = 'chat-msg-prefix' ;
prefix . textContent = ` [ ${ agent . toUpperCase ( ) } TOOL ${ type . toUpperCase ( ) } ] ` ;
const pre = document . createElement ( 'pre' ) ;
pre . className = 'tool-content' ;
pre . textContent = content ;
div . appendChild ( prefix ) ;
div . appendChild ( pre ) ;
container . appendChild ( div ) ;
container . scrollTop = container . scrollHeight ;
if ( shouldSave ) saveSession ( ) ;
}
// ═══ PORTAL INTERACTION ═══
function checkPortalProximity ( ) {
if ( portalOverlayActive ) return ;
let closest = null ;
let minDist = Infinity ;
portals . forEach ( portal => {
const dist = playerPos . distanceTo ( portal . group . position ) ;
if ( dist < 4.5 && dist < minDist ) {
minDist = dist ;
closest = portal ;
}
} ) ;
activePortal = closest ;
const hint = document . getElementById ( 'portal-hint' ) ;
if ( activePortal ) {
document . getElementById ( 'portal-hint-name' ) . textContent = activePortal . config . name ;
hint . style . display = 'flex' ;
} else {
hint . style . display = 'none' ;
}
}
function activatePortal ( portal ) {
portalOverlayActive = true ;
const overlay = document . getElementById ( 'portal-overlay' ) ;
const nameDisplay = document . getElementById ( 'portal-name-display' ) ;
const descDisplay = document . getElementById ( 'portal-desc-display' ) ;
const redirectBox = document . getElementById ( 'portal-redirect-box' ) ;
const errorBox = document . getElementById ( 'portal-error-box' ) ;
const timerDisplay = document . getElementById ( 'portal-timer' ) ;
const statusDot = document . getElementById ( 'portal-status-dot' ) ;
nameDisplay . textContent = portal . config . name . toUpperCase ( ) ;
descDisplay . textContent = portal . config . description ;
statusDot . style . background = portal . config . color ;
statusDot . style . boxShadow = ` 0 0 10px ${ portal . config . color } ` ;
overlay . style . display = 'flex' ;
if ( portal . config . destination && portal . config . destination . url ) {
redirectBox . style . display = 'block' ;
errorBox . style . display = 'none' ;
let count = 5 ;
timerDisplay . textContent = count ;
const interval = setInterval ( ( ) => {
count -- ;
timerDisplay . textContent = count ;
if ( count <= 0 ) {
clearInterval ( interval ) ;
if ( portalOverlayActive ) window . location . href = portal . config . destination . url ;
}
if ( ! portalOverlayActive ) clearInterval ( interval ) ;
} , 1000 ) ;
} else {
redirectBox . style . display = 'none' ;
errorBox . style . display = 'block' ;
}
}
function closePortalOverlay ( ) {
portalOverlayActive = false ;
document . getElementById ( 'portal-overlay' ) . style . display = 'none' ;
}
// ═══ VISION INTERACTION ═══
function checkVisionProximity ( ) {
if ( visionOverlayActive ) return ;
let closest = null ;
let minDist = Infinity ;
visionPoints . forEach ( vp => {
const dist = playerPos . distanceTo ( vp . group . position ) ;
if ( dist < 3.5 && dist < minDist ) {
minDist = dist ;
closest = vp ;
}
} ) ;
activeVisionPoint = closest ;
const hint = document . getElementById ( 'vision-hint' ) ;
if ( activeVisionPoint ) {
document . getElementById ( 'vision-hint-title' ) . textContent = activeVisionPoint . config . title ;
hint . style . display = 'flex' ;
} else {
hint . style . display = 'none' ;
}
}
function activateVisionPoint ( vp ) {
visionOverlayActive = true ;
const overlay = document . getElementById ( 'vision-overlay' ) ;
const titleDisplay = document . getElementById ( 'vision-title-display' ) ;
const contentDisplay = document . getElementById ( 'vision-content-display' ) ;
const statusDot = document . getElementById ( 'vision-status-dot' ) ;
titleDisplay . textContent = vp . config . title . toUpperCase ( ) ;
contentDisplay . textContent = vp . config . content ;
statusDot . style . background = vp . config . color ;
statusDot . style . boxShadow = ` 0 0 10px ${ vp . config . color } ` ;
overlay . style . display = 'flex' ;
}
function closeVisionOverlay ( ) {
visionOverlayActive = false ;
document . getElementById ( 'vision-overlay' ) . style . display = 'none' ;
}
2026-04-12 11:52:12 -04:00
// ═══ PORTAL ATLAS / WORLD DIRECTORY ═══
let atlasActiveFilter = 'all' ;
let atlasSearchQuery = '' ;
2026-03-28 20:50:56 +00:00
function openPortalAtlas ( ) {
atlasOverlayActive = true ;
document . getElementById ( 'atlas-overlay' ) . style . display = 'flex' ;
populateAtlas ( ) ;
2026-04-12 11:52:12 -04:00
// Focus search input
setTimeout ( ( ) => document . getElementById ( 'atlas-search' ) ? . focus ( ) , 100 ) ;
2026-03-28 20:50:56 +00:00
}
function closePortalAtlas ( ) {
atlasOverlayActive = false ;
document . getElementById ( 'atlas-overlay' ) . style . display = 'none' ;
2026-04-12 11:52:12 -04:00
atlasSearchQuery = '' ;
atlasActiveFilter = 'all' ;
}
function initAtlasControls ( ) {
const searchInput = document . getElementById ( 'atlas-search' ) ;
if ( searchInput ) {
searchInput . addEventListener ( 'input' , ( e ) => {
atlasSearchQuery = e . target . value . toLowerCase ( ) . trim ( ) ;
populateAtlas ( ) ;
} ) ;
}
const filterBtns = document . querySelectorAll ( '.atlas-filter-btn' ) ;
filterBtns . forEach ( btn => {
btn . addEventListener ( 'click' , ( ) => {
filterBtns . forEach ( b => b . classList . remove ( 'active' ) ) ;
btn . classList . add ( 'active' ) ;
atlasActiveFilter = btn . dataset . filter ;
populateAtlas ( ) ;
} ) ;
} ) ;
}
function matchesAtlasFilter ( config ) {
if ( atlasActiveFilter === 'all' ) return true ;
if ( atlasActiveFilter === 'harness' ) return ( config . portal _type || 'harness' ) === 'harness' || ! config . portal _type ;
if ( atlasActiveFilter === 'game-world' ) return config . portal _type === 'game-world' ;
return config . status === atlasActiveFilter ;
}
function matchesAtlasSearch ( config ) {
if ( ! atlasSearchQuery ) return true ;
const haystack = [ config . name , config . description , config . id ,
config . world _category , config . portal _type , config . destination ? . type ]
. filter ( Boolean ) . join ( ' ' ) . toLowerCase ( ) ;
return haystack . includes ( atlasSearchQuery ) ;
2026-03-28 20:50:56 +00:00
}
function populateAtlas ( ) {
const grid = document . getElementById ( 'atlas-grid' ) ;
grid . innerHTML = '' ;
2026-04-12 11:52:12 -04:00
2026-03-28 20:50:56 +00:00
let onlineCount = 0 ;
let standbyCount = 0 ;
2026-04-12 11:52:12 -04:00
let downloadedCount = 0 ;
let visibleCount = 0 ;
2026-04-12 12:32:31 -04:00
let readyCount = 0 ;
2026-03-28 20:50:56 +00:00
portals . forEach ( portal => {
const config = portal . config ;
if ( config . status === 'online' ) onlineCount ++ ;
if ( config . status === 'standby' ) standbyCount ++ ;
2026-04-12 11:52:12 -04:00
if ( config . status === 'downloaded' ) downloadedCount ++ ;
if ( ! matchesAtlasFilter ( config ) || ! matchesAtlasSearch ( config ) ) return ;
visibleCount ++ ;
2026-04-12 12:32:31 -04:00
if ( config . interaction _ready && config . status === 'online' ) readyCount ++ ;
2026-03-28 20:50:56 +00:00
const card = document . createElement ( 'div' ) ;
card . className = 'atlas-card' ;
card . style . setProperty ( '--portal-color' , config . color ) ;
2026-04-12 11:52:12 -04:00
2026-03-28 20:50:56 +00:00
const statusClass = ` status- ${ config . status || 'online' } ` ;
2026-04-12 11:52:12 -04:00
const statusLabel = ( config . status || 'ONLINE' ) . toUpperCase ( ) ;
const portalType = config . portal _type || 'harness' ;
const categoryLabel = config . world _category
? config . world _category . replace ( /-/g , ' ' ) . toUpperCase ( )
: portalType . replace ( /-/g , ' ' ) . toUpperCase ( ) ;
// Readiness bar for game-worlds
let readinessHTML = '' ;
if ( config . readiness _steps ) {
const steps = Object . values ( config . readiness _steps ) ;
readinessHTML = ` <div class="atlas-card-readiness" title="Readiness: ${ steps . filter ( s => s . done ) . length } / ${ steps . length } "> ` ;
steps . forEach ( step => {
readinessHTML += ` <div class="readiness-step ${ step . done ? 'done' : '' } " title=" ${ step . label } ${ step . done ? ' ✓' : '' } "></div> ` ;
} ) ;
readinessHTML += '</div>' ;
}
// Action label
const actionLabel = config . destination ? . action _label
|| ( config . status === 'online' ? 'ENTER' : config . status === 'downloaded' ? 'LAUNCH' : 'VIEW' ) ;
2026-04-12 12:32:31 -04:00
const agents = config . agents _present || [ ] ;
const ready = config . interaction _ready && config . status === 'online' ;
const presenceLabel = agents . length > 0
? agents . map ( a => a . toUpperCase ( ) ) . join ( ', ' )
: 'No agents present' ;
const readyLabel = ready ? 'INTERACTION READY' : 'UNAVAILABLE' ;
const readyClass = ready ? 'status-online' : 'status-offline' ;
2026-04-12 11:52:12 -04:00
2026-03-28 20:50:56 +00:00
card . innerHTML = `
< div class = "atlas-card-header" >
2026-04-12 11:52:12 -04:00
< div >
< span class = "atlas-card-name" > $ { config . name } < / s p a n >
< span class = "atlas-card-category" > $ { categoryLabel } < / s p a n >
< / d i v >
< div class = "atlas-card-status ${statusClass}" > $ { statusLabel } < / d i v >
2026-03-28 20:50:56 +00:00
< / d i v >
< div class = "atlas-card-desc" > $ { config . description } < / d i v >
2026-04-12 11:52:12 -04:00
$ { readinessHTML }
2026-04-12 12:32:31 -04:00
< div class = "atlas-card-presence" >
< div class = "atlas-card-agents" > $ { agents . length > 0 ? 'Agents: ' + presenceLabel : presenceLabel } < / d i v >
< div class = "atlas-card-ready ${readyClass}" > $ { readyLabel } < / d i v >
< / d i v >
2026-03-28 20:50:56 +00:00
< div class = "atlas-card-footer" >
< div class = "atlas-card-coord" > X : $ { config . position . x } Z : $ { config . position . z } < / d i v >
2026-04-12 11:52:12 -04:00
< div class = "atlas-card-action" > $ { actionLabel } → < / d i v >
2026-04-12 19:26:25 -04:00
$ { config . role ? ` <div class="atlas-card-role role- ${ config . role } "> ${ config . role . toUpperCase ( ) } </div> ` : '' }
< div class = "atlas-card-type" > $ { config . destination ? . type ? . toUpperCase ( ) || 'UNKNOWN' } < / d i v >
2026-03-28 20:50:56 +00:00
< / d i v >
` ;
2026-04-12 11:52:12 -04:00
2026-03-28 20:50:56 +00:00
card . addEventListener ( 'click' , ( ) => {
focusPortal ( portal ) ;
closePortalAtlas ( ) ;
} ) ;
2026-04-12 11:52:12 -04:00
2026-03-28 20:50:56 +00:00
grid . appendChild ( card ) ;
} ) ;
2026-04-12 11:52:12 -04:00
// Show empty state
if ( visibleCount === 0 ) {
const empty = document . createElement ( 'div' ) ;
empty . className = 'atlas-empty' ;
empty . textContent = atlasSearchQuery
? ` No worlds match " ${ atlasSearchQuery } " `
: 'No worlds in this category' ;
grid . appendChild ( empty ) ;
}
2026-03-28 20:50:56 +00:00
document . getElementById ( 'atlas-online-count' ) . textContent = onlineCount ;
document . getElementById ( 'atlas-standby-count' ) . textContent = standbyCount ;
2026-04-12 11:52:12 -04:00
document . getElementById ( 'atlas-downloaded-count' ) . textContent = downloadedCount ;
document . getElementById ( 'atlas-total-count' ) . textContent = portals . length ;
2026-04-12 12:32:31 -04:00
document . getElementById ( 'atlas-ready-count' ) . textContent = readyCount ;
2026-03-28 20:50:56 +00:00
2026-04-11 01:35:13 +00:00
// Update Bannerlord HUD status
2026-03-28 20:50:56 +00:00
const bannerlord = portals . find ( p => p . config . id === 'bannerlord' ) ;
if ( bannerlord ) {
const statusEl = document . getElementById ( 'bannerlord-status' ) ;
2026-04-11 01:35:13 +00:00
statusEl . className = 'hud-status-item ' + ( bannerlord . config . status || 'offline' ) ;
2026-03-28 20:50:56 +00:00
}
}
function focusPortal ( portal ) {
// Teleport player to a position in front of the portal
const offset = new THREE . Vector3 ( 0 , 0 , 6 ) . applyEuler ( new THREE . Euler ( 0 , portal . config . rotation ? . y || 0 , 0 ) ) ;
playerPos . copy ( portal . group . position ) . add ( offset ) ;
playerPos . y = 2 ; // Keep at eye level
// Rotate player to face the portal
playerRot . y = ( portal . config . rotation ? . y || 0 ) + Math . PI ;
playerRot . x = 0 ;
addChatMessage ( 'system' , ` Navigation focus: ${ portal . config . name } ` ) ;
// If in orbit mode, reset target
if ( NAV _MODES [ navModeIdx ] === 'orbit' ) {
orbitState . target . copy ( portal . group . position ) ;
orbitState . target . y = 3.5 ;
}
}
// ═══ GAME LOOP ═══
let lastThoughtTime = 0 ;
let pulseTimer = 0 ;
function gameLoop ( ) {
requestAnimationFrame ( gameLoop ) ;
const delta = Math . min ( clock . getDelta ( ) , 0.1 ) ;
const elapsed = clock . elapsedTime ;
// Agent Thought Simulation
if ( elapsed - lastThoughtTime > 4 ) {
lastThoughtTime = elapsed ;
simulateAgentThought ( ) ;
}
// Harness Pulse
pulseTimer += delta ;
if ( pulseTimer > 8 ) {
pulseTimer = 0 ;
triggerHarnessPulse ( ) ;
}
if ( harnessPulseMesh ) {
harnessPulseMesh . scale . addScalar ( delta * 15 ) ;
harnessPulseMesh . material . opacity = Math . max ( 0 , harnessPulseMesh . material . opacity - delta * 0.5 ) ;
}
updateAshStorm ( delta , elapsed ) ;
2026-04-10 02:13:31 +00:00
// Project Mnemosyne - Memory Orb Animation
if ( typeof animateMemoryOrbs === 'function' ) {
2026-04-10 07:41:13 +00:00
SpatialMemory . update ( delta ) ;
2026-04-12 12:52:39 -04:00
SpatialAudio . update ( delta ) ;
2026-04-11 19:48:46 +00:00
MemoryBirth . update ( delta ) ;
2026-04-12 06:45:25 +00:00
MemoryPulse . update ( ) ;
2026-04-10 02:13:31 +00:00
animateMemoryOrbs ( delta ) ;
}
2026-03-28 20:50:56 +00:00
const mode = NAV _MODES [ navModeIdx ] ;
const chatActive = document . activeElement === document . getElementById ( 'chat-input' ) ;
if ( mode === 'walk' ) {
if ( ! chatActive && ! portalOverlayActive ) {
const speed = 6 * delta ;
const dir = new THREE . Vector3 ( ) ;
if ( keys [ 'w' ] ) dir . z -= 1 ;
if ( keys [ 's' ] ) dir . z += 1 ;
if ( keys [ 'a' ] ) dir . x -= 1 ;
if ( keys [ 'd' ] ) dir . x += 1 ;
if ( dir . length ( ) > 0 ) {
dir . normalize ( ) . multiplyScalar ( speed ) ;
dir . applyAxisAngle ( new THREE . Vector3 ( 0 , 1 , 0 ) , playerRot . y ) ;
playerPos . add ( dir ) ;
const maxR = 24 ;
const dist = Math . sqrt ( playerPos . x * playerPos . x + playerPos . z * playerPos . z ) ;
if ( dist > maxR ) { playerPos . x *= maxR / dist ; playerPos . z *= maxR / dist ; }
}
}
playerPos . y = 2 ;
camera . position . copy ( playerPos ) ;
camera . rotation . set ( playerRot . x , playerRot . y , 0 , 'YXZ' ) ;
} else if ( mode === 'orbit' ) {
if ( ! chatActive && ! portalOverlayActive ) {
const speed = 8 * delta ;
const pan = new THREE . Vector3 ( ) ;
if ( keys [ 'w' ] ) pan . z -= 1 ;
if ( keys [ 's' ] ) pan . z += 1 ;
if ( keys [ 'a' ] ) pan . x -= 1 ;
if ( keys [ 'd' ] ) pan . x += 1 ;
if ( pan . length ( ) > 0 ) {
pan . normalize ( ) . multiplyScalar ( speed ) ;
pan . applyAxisAngle ( new THREE . Vector3 ( 0 , 1 , 0 ) , orbitState . theta ) ;
orbitState . target . add ( pan ) ;
orbitState . target . y = Math . max ( 0 , Math . min ( 20 , orbitState . target . y ) ) ;
}
}
const r = orbitState . radius ;
camera . position . set (
orbitState . target . x + r * Math . sin ( orbitState . phi ) * Math . sin ( orbitState . theta ) ,
orbitState . target . y + r * Math . cos ( orbitState . phi ) ,
orbitState . target . z + r * Math . sin ( orbitState . phi ) * Math . cos ( orbitState . theta )
) ;
camera . lookAt ( orbitState . target ) ;
playerPos . copy ( camera . position ) ;
playerRot . y = orbitState . theta ;
} else if ( mode === 'fly' ) {
if ( ! chatActive && ! portalOverlayActive ) {
const speed = 8 * delta ;
const forward = new THREE . Vector3 ( - Math . sin ( playerRot . y ) , 0 , - Math . cos ( playerRot . y ) ) ;
const right = new THREE . Vector3 ( Math . cos ( playerRot . y ) , 0 , - Math . sin ( playerRot . y ) ) ;
if ( keys [ 'w' ] ) playerPos . addScaledVector ( forward , speed ) ;
if ( keys [ 's' ] ) playerPos . addScaledVector ( forward , - speed ) ;
if ( keys [ 'a' ] ) playerPos . addScaledVector ( right , - speed ) ;
if ( keys [ 'd' ] ) playerPos . addScaledVector ( right , speed ) ;
if ( keys [ 'q' ] || keys [ ' ' ] ) flyY += speed ;
if ( keys [ 'e' ] || keys [ 'shift' ] ) flyY -= speed ;
flyY = Math . max ( 0.5 , Math . min ( 30 , flyY ) ) ;
playerPos . y = flyY ;
}
camera . position . copy ( playerPos ) ;
camera . rotation . set ( playerRot . x , playerRot . y , 0 , 'YXZ' ) ;
}
// Proximity check
checkPortalProximity ( ) ;
checkVisionProximity ( ) ;
const sky = scene . getObjectByName ( 'skybox' ) ;
if ( sky ) sky . material . uniforms . uTime . value = elapsed ;
batcaveTerminals . forEach ( t => {
if ( t . scanMat ? . uniforms ) t . scanMat . uniforms . uTime . value = elapsed ;
} ) ;
// Animate Portals
portals . forEach ( portal => {
portal . ring . rotation . z = elapsed * 0.3 ;
portal . ring . rotation . x = Math . sin ( elapsed * 0.5 ) * 0.1 ;
if ( portal . swirl . material . uniforms ) {
portal . swirl . material . uniforms . uTime . value = elapsed ;
}
// Pulse light
portal . light . intensity = 1.5 + Math . sin ( elapsed * 2 ) * 0.5 ;
// Custom animations for distinct identities
if ( portal . config . id === 'archive' && portal . customElements . cubes ) {
portal . customElements . cubes . forEach ( ( cube , i ) => {
cube . rotation . x += delta * ( 0.5 + i * 0.1 ) ;
cube . rotation . y += delta * ( 0.3 + i * 0.1 ) ;
const orbitSpeed = 0.5 + i * 0.2 ;
const orbitRadius = 4 + Math . sin ( elapsed * 0.5 + i ) * 0.5 ;
cube . position . x = Math . cos ( elapsed * orbitSpeed + i ) * orbitRadius ;
cube . position . z = Math . sin ( elapsed * orbitSpeed + i ) * orbitRadius ;
cube . position . y = 3.5 + Math . sin ( elapsed * 1.2 + i ) * 1.5 ;
} ) ;
}
if ( portal . config . id === 'chapel' && portal . customElements . halo ) {
portal . customElements . halo . rotation . z -= delta * 0.2 ;
portal . customElements . halo . scale . setScalar ( 1 + Math . sin ( elapsed * 0.8 ) * 0.05 ) ;
portal . customElements . core . material . emissiveIntensity = 2 + Math . sin ( elapsed * 3 ) * 1 ;
}
if ( portal . config . id === 'courtyard' && portal . customElements . outerRing ) {
portal . customElements . outerRing . rotation . z -= delta * 0.5 ;
portal . customElements . outerRing . rotation . y = Math . cos ( elapsed * 0.4 ) * 0.2 ;
}
if ( portal . config . id === 'gate' && portal . customElements . spikes ) {
portal . customElements . spikes . forEach ( ( spike , i ) => {
const s = 1 + Math . sin ( elapsed * 2 + i ) * 0.2 ;
spike . scale . set ( s , s , s ) ;
} ) ;
}
// Animate particles
const positions = portal . pSystem . geometry . attributes . position . array ;
for ( let i = 0 ; i < positions . length / 3 ; i ++ ) {
positions [ i * 3 + 1 ] += Math . sin ( elapsed + i ) * 0.002 ;
}
portal . pSystem . geometry . attributes . position . needsUpdate = true ;
} ) ;
// Animate Vision Points
visionPoints . forEach ( vp => {
vp . crystal . rotation . y = elapsed * 0.8 ;
vp . crystal . rotation . x = Math . sin ( elapsed * 0.5 ) * 0.2 ;
vp . crystal . position . y = 2.5 + Math . sin ( elapsed * 1.5 ) * 0.2 ;
vp . ring . rotation . z = elapsed * 0.5 ;
vp . ring . scale . setScalar ( 1 + Math . sin ( elapsed * 2 ) * 0.05 ) ;
vp . light . intensity = 1 + Math . sin ( elapsed * 3 ) * 0.3 ;
} ) ;
// Animate Agents
agents . forEach ( ( agent , i ) => {
// Wander logic
agent . wanderTimer -= delta ;
if ( agent . wanderTimer <= 0 ) {
agent . wanderTimer = 3 + Math . random ( ) * 5 ;
agent . targetPos . set (
agent . station . x + ( Math . random ( ) - 0.5 ) * 4 ,
0 ,
agent . station . z + ( Math . random ( ) - 0.5 ) * 4
) ;
}
agent . group . position . lerp ( agent . targetPos , delta * 0.5 ) ;
agent . orb . position . y = 3 + Math . sin ( elapsed * 2 + i ) * 0.15 ;
agent . halo . rotation . z = elapsed * 0.5 ;
agent . halo . scale . setScalar ( 1 + Math . sin ( elapsed * 3 + i ) * 0.1 ) ;
agent . orb . material . emissiveIntensity = 2 + Math . sin ( elapsed * 4 + i ) * 1 ;
} ) ;
// Animate Power Meter
powerMeterBars . forEach ( ( bar , i ) => {
const level = ( Math . sin ( elapsed * 2 + i * 0.5 ) * 0.5 + 0.5 ) ;
const active = level > ( i / powerMeterBars . length ) ;
bar . material . emissiveIntensity = active ? 2 : 0.2 ;
bar . material . opacity = active ? 0.9 : 0.3 ;
bar . scale . x = active ? 1.2 : 1.0 ;
} ) ;
if ( thoughtStreamMesh ) {
thoughtStreamMesh . material . uniforms . uTime . value = elapsed ;
thoughtStreamMesh . rotation . y = elapsed * 0.05 ;
}
if ( particles ? . material ? . uniforms ) {
particles . material . uniforms . uTime . value = elapsed ;
}
if ( dustParticles ) {
dustParticles . rotation . y = elapsed * 0.01 ;
}
for ( let i = 0 ; i < 5 ; i ++ ) {
const stone = scene . getObjectByName ( 'runestone_' + i ) ;
if ( stone ) {
stone . position . y = 5 + Math . sin ( elapsed * 0.8 + i * 1.3 ) * 0.8 ;
stone . rotation . y = elapsed * 0.5 + i ;
stone . rotation . x = elapsed * 0.3 + i * 0.7 ;
}
}
const core = scene . getObjectByName ( 'nexus-core' ) ;
if ( core ) {
core . position . y = 2.5 + Math . sin ( elapsed * 1.2 ) * 0.3 ;
core . rotation . y = elapsed * 0.4 ;
core . rotation . x = elapsed * 0.2 ;
core . material . emissiveIntensity = 1.5 + Math . sin ( elapsed * 2 ) * 0.5 ;
}
2026-04-12 11:51:40 -04:00
if ( composer ) { composer . render ( ) ; } else { renderer . render ( scene , camera ) ; }
2026-03-28 20:50:56 +00:00
updateAshStorm ( delta , elapsed ) ;
2026-04-10 02:13:31 +00:00
// Project Mnemosyne - Memory Orb Animation
if ( typeof animateMemoryOrbs === 'function' ) {
animateMemoryOrbs ( delta ) ;
}
2026-03-28 20:50:56 +00:00
updatePortalTunnel ( delta , elapsed ) ;
if ( workshopScanMat ) workshopScanMat . uniforms . uTime . value = clock . getElapsedTime ( ) ;
if ( activePortal !== lastFocusedPortal ) {
lastFocusedPortal = activePortal ;
refreshWorkshopPanel ( ) ;
}
frameCount ++ ;
const now = performance . now ( ) ;
if ( now - lastFPSTime >= 1000 ) {
fps = frameCount ;
frameCount = 0 ;
lastFPSTime = now ;
}
if ( debugOverlay ) {
const info = renderer . info ;
debugOverlay . textContent =
` FPS: ${ fps } Draw: ${ info . render ? . calls } Tri: ${ info . render ? . triangles } [ ${ performanceTier } ] \n ` +
` Pos: ${ playerPos . x . toFixed ( 1 ) } , ${ playerPos . y . toFixed ( 1 ) } , ${ playerPos . z . toFixed ( 1 ) } NAV: ${ NAV _MODES [ navModeIdx ] } ` ;
}
renderer . info . reset ( ) ;
}
function onResize ( ) {
const w = window . innerWidth ;
const h = window . innerHeight ;
camera . aspect = w / h ;
camera . updateProjectionMatrix ( ) ;
renderer . setSize ( w , h ) ;
2026-04-12 11:51:40 -04:00
if ( composer ) composer . setSize ( w , h ) ;
2026-03-28 20:50:56 +00:00
}
// ═══ AGENT SIMULATION ═══
function simulateAgentThought ( ) {
const agentIds = [ 'timmy' , 'kimi' , 'claude' , 'perplexity' ] ;
const agentId = agentIds [ Math . floor ( Math . random ( ) * agentIds . length ) ] ;
const thoughts = {
timmy : [
'Analyzing portal stability...' ,
'Sovereign nodes synchronized.' ,
'Memory stream optimization complete.' ,
'Scanning for external interference...' ,
'The harness is humming beautifully.' ,
] ,
kimi : [
'Processing linguistic patterns...' ,
'Context window expanded.' ,
'Synthesizing creative output...' ,
'Awaiting user prompt sequence.' ,
'Neural weights adjusted.' ,
] ,
claude : [
'Reasoning through complex logic...' ,
'Ethical guardrails verified.' ,
'Refining thought architecture...' ,
'Connecting disparate data points.' ,
'Deep analysis in progress.' ,
] ,
perplexity : [
'Searching global knowledge graph...' ,
'Verifying source citations...' ,
'Synthesizing real-time data...' ,
'Mapping information topology...' ,
'Fact-checking active streams.' ,
]
} ;
const thought = thoughts [ agentId ] [ Math . floor ( Math . random ( ) * thoughts [ agentId ] . length ) ] ;
addAgentLog ( agentId , thought ) ;
}
function addAgentLog ( agentId , text ) {
const container = document . getElementById ( 'agent-log-content' ) ;
if ( ! container ) return ;
const entry = document . createElement ( 'div' ) ;
entry . className = 'agent-log-entry' ;
entry . innerHTML = ` <span class="agent-log-tag tag- ${ agentId } ">[ ${ agentId . toUpperCase ( ) } ]</span><span class="agent-log-text"> ${ text } </span> ` ;
container . prepend ( entry ) ;
if ( container . children . length > 6 ) {
container . lastElementChild . remove ( ) ;
}
}
function triggerHarnessPulse ( ) {
if ( ! harnessPulseMesh ) return ;
harnessPulseMesh . scale . setScalar ( 0.1 ) ;
harnessPulseMesh . material . opacity = 0.8 ;
// Flash the core
const core = scene . getObjectByName ( 'nexus-core' ) ;
if ( core ) {
core . material . emissiveIntensity = 10 ;
setTimeout ( ( ) => { if ( core ) core . material . emissiveIntensity = 2 ; } , 200 ) ;
}
}
// ═══ ASH STORM (MORROWIND) ═══
let ashStormParticles ;
function createAshStorm ( ) {
const count = 1000 ;
const geo = new THREE . BufferGeometry ( ) ;
const pos = new Float32Array ( count * 3 ) ;
const vel = new Float32Array ( count * 3 ) ;
for ( let i = 0 ; i < count ; i ++ ) {
pos [ i * 3 ] = ( Math . random ( ) - 0.5 ) * 20 ;
pos [ i * 3 + 1 ] = Math . random ( ) * 10 ;
pos [ i * 3 + 2 ] = ( Math . random ( ) - 0.5 ) * 20 ;
vel [ i * 3 ] = - 0.05 - Math . random ( ) * 0.1 ;
vel [ i * 3 + 1 ] = - 0.02 - Math . random ( ) * 0.05 ;
vel [ i * 3 + 2 ] = ( Math . random ( ) - 0.5 ) * 0.05 ;
}
geo . setAttribute ( 'position' , new THREE . BufferAttribute ( pos , 3 ) ) ;
geo . setAttribute ( 'velocity' , new THREE . BufferAttribute ( vel , 3 ) ) ;
const mat = new THREE . PointsMaterial ( {
color : 0x886644 ,
size : 0.05 ,
transparent : true ,
opacity : 0 ,
depthWrite : false ,
blending : THREE . AdditiveBlending
} ) ;
ashStormParticles = new THREE . Points ( geo , mat ) ;
ashStormParticles . position . set ( 15 , 0 , - 10 ) ; // Center on Morrowind portal
scene . add ( ashStormParticles ) ;
}
function updateAshStorm ( delta , elapsed ) {
if ( ! ashStormParticles ) return ;
const morrowindPortalPos = new THREE . Vector3 ( 15 , 0 , - 10 ) ;
const dist = playerPos . distanceTo ( morrowindPortalPos ) ;
const intensity = Math . max ( 0 , 1 - ( dist / 12 ) ) ;
ashStormParticles . material . opacity = intensity * 0.4 ;
if ( intensity > 0 ) {
const pos = ashStormParticles . geometry . attributes . position . array ;
const vel = ashStormParticles . geometry . attributes . velocity . array ;
for ( let i = 0 ; i < pos . length / 3 ; i ++ ) {
pos [ i * 3 ] += vel [ i * 3 ] ;
pos [ i * 3 + 1 ] += vel [ i * 3 + 1 ] ;
pos [ i * 3 + 2 ] += vel [ i * 3 + 2 ] ;
if ( pos [ i * 3 + 1 ] < 0 || Math . abs ( pos [ i * 3 ] ) > 10 || Math . abs ( pos [ i * 3 + 2 ] ) > 10 ) {
pos [ i * 3 ] = ( Math . random ( ) - 0.5 ) * 20 ;
pos [ i * 3 + 1 ] = 10 ;
pos [ i * 3 + 2 ] = ( Math . random ( ) - 0.5 ) * 20 ;
}
}
ashStormParticles . geometry . attributes . position . needsUpdate = true ;
}
}
2026-04-10 02:13:31 +00:00
// ═══════════════════════════════════════════
// PROJECT MNEMOSYNE — HOLOGRAPHIC MEMORY ORBS
// ═══════════════════════════════════════════
// Memory orbs registry for animation loop
const memoryOrbs = [ ] ;
/ * *
* Spawn a glowing memory orb at the given position .
* Used to visualize RAG retrievals and memory recalls in the Nexus .
*
* @ param { THREE . Vector3 } position - World position for the orb
* @ param { number } color - Hex color ( default : 0x4af0c0 - cyan )
* @ param { number } size - Radius of the orb ( default : 0.5 )
* @ param { object } metadata - Optional metadata for the memory ( source , timestamp , etc . )
* @ returns { THREE . Mesh } The created orb mesh
* /
function spawnMemoryOrb ( position , color = 0x4af0c0 , size = 0.5 , metadata = { } ) {
if ( typeof THREE === 'undefined' || typeof scene === 'undefined' ) {
console . warn ( '[Mnemosyne] THREE/scene not available for orb spawn' ) ;
return null ;
}
const geometry = new THREE . SphereGeometry ( size , 32 , 32 ) ;
const material = new THREE . MeshStandardMaterial ( {
color : color ,
emissive : color ,
emissiveIntensity : 2.5 ,
metalness : 0.3 ,
roughness : 0.2 ,
transparent : true ,
opacity : 0.85 ,
envMapIntensity : 1.5
} ) ;
const orb = new THREE . Mesh ( geometry , material ) ;
orb . position . copy ( position ) ;
orb . castShadow = true ;
orb . receiveShadow = true ;
orb . userData = {
type : 'memory_orb' ,
pulse : Math . random ( ) * Math . PI * 2 , // Random phase offset
pulseSpeed : 0.002 + Math . random ( ) * 0.001 ,
originalScale : size ,
metadata : metadata ,
createdAt : Date . now ( )
} ;
// Point light for local illumination
const light = new THREE . PointLight ( color , 1.5 , 8 ) ;
orb . add ( light ) ;
scene . add ( orb ) ;
memoryOrbs . push ( orb ) ;
console . info ( '[Mnemosyne] Memory orb spawned:' , metadata . source || 'unknown' ) ;
return orb ;
}
/ * *
* Remove a memory orb from the scene and dispose resources .
* @ param { THREE . Mesh } orb - The orb to remove
* /
function removeMemoryOrb ( orb ) {
if ( ! orb ) return ;
if ( orb . parent ) orb . parent . remove ( orb ) ;
if ( orb . geometry ) orb . geometry . dispose ( ) ;
if ( orb . material ) orb . material . dispose ( ) ;
const idx = memoryOrbs . indexOf ( orb ) ;
if ( idx > - 1 ) memoryOrbs . splice ( idx , 1 ) ;
}
/ * *
* Animate all memory orbs — pulse , rotate , and fade .
* Called from gameLoop ( ) every frame .
* @ param { number } delta - Time since last frame
* /
function animateMemoryOrbs ( delta ) {
for ( let i = memoryOrbs . length - 1 ; i >= 0 ; i -- ) {
const orb = memoryOrbs [ i ] ;
if ( ! orb || ! orb . userData ) continue ;
// Pulse animation
orb . userData . pulse += orb . userData . pulseSpeed * delta * 1000 ;
const pulseFactor = 1 + Math . sin ( orb . userData . pulse ) * 0.1 ;
orb . scale . setScalar ( pulseFactor * orb . userData . originalScale ) ;
// Gentle rotation
orb . rotation . y += delta * 0.5 ;
// Fade after 30 seconds
const age = ( Date . now ( ) - orb . userData . createdAt ) / 1000 ;
if ( age > 30 ) {
const fadeDuration = 10 ;
const fadeProgress = Math . min ( 1 , ( age - 30 ) / fadeDuration ) ;
orb . material . opacity = 0.85 * ( 1 - fadeProgress ) ;
if ( fadeProgress >= 1 ) {
removeMemoryOrb ( orb ) ;
i -- ; // Adjust index after removal
}
}
}
}
/ * *
* Spawn memory orbs arranged in a spiral for RAG retrieval results .
* @ param { Array } results - Array of { content , score , source }
* @ param { THREE . Vector3 } center - Center position ( default : above avatar )
* /
function spawnRetrievalOrbs ( results , center ) {
if ( ! results || ! Array . isArray ( results ) || results . length === 0 ) return ;
if ( ! center ) {
center = new THREE . Vector3 ( 0 , 2 , 0 ) ;
}
const colors = [ 0x4af0c0 , 0x7b5cff , 0xffd700 , 0xff4466 , 0x00ff88 ] ;
const radius = 3 ;
results . forEach ( ( result , i ) => {
const angle = ( i / results . length ) * Math . PI * 2 ;
const height = ( i / results . length ) * 2 - 1 ;
const position = new THREE . Vector3 (
center . x + Math . cos ( angle ) * radius ,
center . y + height ,
center . z + Math . sin ( angle ) * radius
) ;
const colorIdx = Math . min ( colors . length - 1 , Math . floor ( ( result . score || 0.5 ) * colors . length ) ) ;
const size = 0.3 + ( result . score || 0.5 ) * 0.4 ;
spawnMemoryOrb ( position , colors [ colorIdx ] , size , {
source : result . source || 'unknown' ,
score : result . score || 0 ,
contentPreview : ( result . content || '' ) . substring ( 0 , 100 )
} ) ;
} ) ;
}
2026-03-28 20:50:56 +00:00
init ( ) . then ( ( ) => {
createAshStorm ( ) ;
createPortalTunnel ( ) ;
2026-04-10 07:41:13 +00:00
// Project Mnemosyne — seed demo spatial memories
const demoMemories = [
{ id : 'mem_nexus_birth' , content : 'The Nexus came online — first render of the 3D world' , category : 'knowledge' , strength : 0.95 , connections : [ 'mem_mnemosyne_start' ] } ,
{ id : 'mem_first_portal' , content : 'First portal deployed — connection to external service' , category : 'engineering' , strength : 0.85 , connections : [ 'mem_nexus_birth' ] } ,
{ id : 'mem_hermes_chat' , content : 'First conversation through the Hermes gateway' , category : 'social' , strength : 0.7 , connections : [ ] } ,
{ id : 'mem_mnemosyne_start' , content : 'Project Mnemosyne began — the living archive awakens' , category : 'projects' , strength : 0.9 , connections : [ 'mem_nexus_birth' , 'mem_spatial_schema' ] } ,
{ id : 'mem_spatial_schema' , content : 'Spatial Memory Schema defined — memories gain permanent homes' , category : 'engineering' , strength : 0.8 , connections : [ 'mem_mnemosyne_start' ] } ,
] ;
demoMemories . forEach ( m => SpatialMemory . placeMemory ( m ) ) ;
2026-03-28 20:50:56 +00:00
fetchGiteaData ( ) ;
setInterval ( fetchGiteaData , 30000 ) ;
2026-04-07 14:29:26 +00:00
runWeeklyAudit ( ) ;
setInterval ( runWeeklyAudit , 604800000 ) ; // 7 days interval
2026-04-07 10:00:07 +00:00
// Register service worker for PWA
if ( 'serviceWorker' in navigator ) {
navigator . serviceWorker . register ( '/service-worker.js' ) ;
}
2026-04-07 11:39:26 +00:00
// Initialize MemPalace memory system
function connectMemPalace ( ) {
try {
// Initialize MemPalace MCP server
console . log ( 'Initializing MemPalace memory system...' ) ;
2026-04-07 11:49:35 +00:00
// Actual MCP server connection
2026-04-07 11:39:26 +00:00
const statusEl = document . getElementById ( 'mem-palace-status' ) ;
if ( statusEl ) {
2026-04-07 11:49:35 +00:00
statusEl . textContent = 'MemPalace ACTIVE' ;
2026-04-07 11:39:26 +00:00
statusEl . style . color = '#4af0c0' ;
statusEl . style . textShadow = '0 0 10px #4af0c0' ;
}
2026-04-07 11:49:35 +00:00
// Initialize MCP server connection
if ( window . Claude && window . Claude . mcp ) {
window . Claude . mcp . add ( 'mempalace' , {
init : ( ) => {
return { status : 'active' , version : '3.0.0' } ;
} ,
search : ( query ) => {
2026-04-07 12:43:45 +00:00
return new Promise ( ( query ) => {
2026-04-07 11:49:35 +00:00
setTimeout ( ( ) => {
resolve ( [
{
id : '1' ,
content : 'MemPalace: Palace architecture, AAAK compression, knowledge graph' ,
score : 0.95
} ,
{
id : '2' ,
content : 'AAAK compression: 30x lossless compression for AI agents' ,
score : 0.88
}
] ) ;
} , 500 ) ;
} ) ;
}
} ) ;
}
2026-04-07 12:43:45 +00:00
// Initialize memory stats tracking
document . getElementById ( 'compression-ratio' ) . textContent = '0x' ;
document . getElementById ( 'docs-mined' ) . textContent = '0' ;
document . getElementById ( 'aaak-size' ) . textContent = '0B' ;
2026-04-07 11:39:26 +00:00
} catch ( err ) {
console . error ( 'Failed to initialize MemPalace:' , err ) ;
const statusEl = document . getElementById ( 'mem-palace-status' ) ;
if ( statusEl ) {
2026-04-07 11:49:35 +00:00
statusEl . textContent = 'MemPalace ERROR' ;
2026-04-07 11:39:26 +00:00
statusEl . style . color = '#ff4466' ;
statusEl . style . textShadow = '0 0 10px #ff4466' ;
}
}
}
2026-04-07 11:50:38 +00:00
// Initialize MemPalace
const mempalace = {
status : { compression : 0 , docs : 0 , aak : '0B' } ,
mineChat : ( ) => {
2026-04-07 12:27:15 +00:00
try {
const messages = Array . from ( document . querySelectorAll ( '.chat-msg' ) ) . map ( m => m . innerText ) ;
if ( messages . length > 0 ) {
2026-04-07 14:02:31 +00:00
// Actual MemPalace mining
const wing = 'nexus_chat' ;
const room = 'conversation_history' ;
messages . forEach ( ( msg , idx ) => {
// Store in MemPalace
window . mempalace . add _drawer ( {
wing ,
room ,
content : msg ,
metadata : {
type : 'chat' ,
timestamp : Date . now ( ) - ( messages . length - idx ) * 1000
}
} ) ;
} ) ;
// Update stats
2026-04-07 12:27:15 +00:00
mempalace . status . docs += messages . length ;
mempalace . status . compression = Math . min ( 100 , mempalace . status . compression + ( messages . length / 10 ) ) ;
mempalace . status . aak = ` ${ Math . floor ( parseInt ( mempalace . status . aak . replace ( 'B' , '' ) ) + messages . length * 30 ) } B ` ;
2026-04-07 14:02:31 +00:00
updateMemPalaceStatus ( ) ;
2026-04-07 12:27:15 +00:00
}
} catch ( error ) {
2026-04-07 14:02:31 +00:00
console . error ( 'MemPalace mine failed:' , error ) ;
document . getElementById ( 'mem-palace-status' ) . textContent = 'Mining Error' ;
2026-04-07 12:27:15 +00:00
document . getElementById ( 'mem-palace-status' ) . style . color = '#ff4466' ;
2026-04-07 11:50:38 +00:00
}
}
} ;
2026-04-07 13:08:29 +00:00
// Mine chat history to MemPalace with AAAK compression
2026-04-07 12:46:01 +00:00
function mineChatToMemPalace ( ) {
2026-04-07 12:02:03 +00:00
const messages = Array . from ( document . querySelectorAll ( '.chat-msg' ) ) . map ( m => m . innerText ) ;
if ( messages . length > 0 ) {
2026-04-07 12:46:01 +00:00
try {
2026-04-07 13:08:29 +00:00
// Convert to AAAK format
const aaakContent = messages . map ( msg => {
const lines = msg . split ( '\n' ) ;
return lines . map ( line => {
// Simple AAAK compression pattern
return line . replace ( /(\w+): (.+)/g , '$1: $2' )
. replace ( /(\d{4}-\d{2}-\d{2})/ , 'DT:$1' )
. replace ( /(\d+ years?)/ , 'T:$1' ) ;
} ) . join ( '\n' ) ;
} ) . join ( '\n---\n' ) ;
2026-04-07 12:46:01 +00:00
mempalace . add ( {
2026-04-07 13:08:29 +00:00
content : aaakContent ,
2026-04-07 12:46:01 +00:00
wing : 'nexus_chat' ,
room : 'conversation_history' ,
tags : [ 'chat' , 'conversation' , 'user_interaction' ]
2026-04-07 12:43:45 +00:00
} ) ;
2026-04-07 12:46:01 +00:00
2026-04-07 13:08:29 +00:00
updateMemPalaceStatus ( ) ;
2026-04-07 12:46:01 +00:00
} catch ( error ) {
console . error ( 'MemPalace mining failed:' , error ) ;
2026-04-07 13:08:29 +00:00
document . getElementById ( 'mem-palace-status' ) . textContent = 'Mining Error' ;
2026-04-07 12:43:45 +00:00
}
2026-04-07 12:02:03 +00:00
}
2026-04-07 11:50:38 +00:00
}
2026-04-07 13:07:13 +00:00
2026-04-07 13:08:29 +00:00
function updateMemPalaceStatus ( ) {
try {
const stats = mempalace . status ( ) ;
document . getElementById ( 'compression-ratio' ) . textContent =
stats . compression _ratio . toFixed ( 1 ) + 'x' ;
document . getElementById ( 'docs-mined' ) . textContent = stats . total _docs ;
document . getElementById ( 'aaak-size' ) . textContent = stats . aaak _size + 'B' ;
document . getElementById ( 'mem-palace-status' ) . textContent = 'Mining Active' ;
} catch ( error ) {
document . getElementById ( 'mem-palace-status' ) . textContent = 'Connection Lost' ;
}
}
2026-04-07 13:07:13 +00:00
// Mine chat on send
document . getElementById ( 'chat-send-btn' ) . addEventListener ( 'click' , ( ) => {
mineChatToMemPalace ( ) ;
} ) ;
// Auto-mine chat every 30s
setInterval ( mineChatToMemPalace , 30000 ) ;
2026-04-07 11:50:38 +00:00
// Update UI status
function updateMemPalaceStatus ( ) {
2026-04-07 14:08:42 +00:00
try {
const status = mempalace . status ( ) ;
document . getElementById ( 'compression-ratio' ) . textContent = status . compression _ratio . toFixed ( 1 ) + 'x' ;
document . getElementById ( 'docs-mined' ) . textContent = status . total _docs ;
document . getElementById ( 'aaak-size' ) . textContent = status . aaak _size + 'b' ;
} catch ( error ) {
document . getElementById ( 'mem-palace-status' ) . textContent = 'Connection Lost' ;
}
}
// Add mining event listener
document . getElementById ( 'mem-palace-btn' ) . addEventListener ( 'click' , ( ) => {
mineMemPalaceContent ( ) ;
} ) ;
// Auto-mine chat every 30s
setInterval ( mineMemPalaceContent , 30000 ) ;
2026-04-07 12:46:01 +00:00
try {
const status = mempalace . status ( ) ;
document . getElementById ( 'compression-ratio' ) . textContent = status . compression _ratio . toFixed ( 1 ) + 'x' ;
document . getElementById ( 'docs-mined' ) . textContent = status . total _docs ;
document . getElementById ( 'aaak-size' ) . textContent = status . aaak _size + 'B' ;
} catch ( error ) {
console . error ( 'Failed to update MemPalace status:' , error ) ;
}
2026-04-07 11:49:35 +00:00
}
2026-04-07 11:50:38 +00:00
// Auto-mine chat history every 30s
setInterval ( mineMemPalaceContent , 30000 ) ;
2026-04-07 11:39:26 +00:00
// Call MemPalace initialization
connectMemPalace ( ) ;
2026-04-07 11:49:35 +00:00
mineMemPalaceContent ( ) ;
2026-03-28 20:50:56 +00:00
} ) ;
2026-04-12 12:17:45 +00:00
// Memory optimization loop
setInterval ( ( ) => { console . log ( 'Running optimization...' ) ; } , 60000 ) ;