2026-03-28 20:50:56 +00:00
import * as THREE from 'three' ;
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-10 22:45:10 +00:00
import { SessionRooms } from './nexus/components/session-rooms.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 ;
let loadProgress = 0 ;
let performanceTier = 'high' ;
// ═══ HERMES WS STATE ═══
let hermesWs = null ;
let wsReconnectTimer = null ;
let wsConnected = false ;
let recentToolOutputs = [ ] ;
let workshopPanelCtx = null ;
let workshopPanelTexture = null ;
let workshopPanelCanvas = null ;
let workshopScanMat = null ;
let workshopPanelRefreshTimer = 0 ;
let lastFocusedPortal = null ;
// ═══ 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
// ═══ 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 ) ;
}
}
addRule ( condition , action , description ) {
this . rules . push ( { condition , action , description } ) ;
}
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 ) {
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 ) ;
this . pendingRequests = new Map ( ) ;
}
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-03-30 03:04:27 +00:00
let metaLayer , neuroBridge , cbr , symbolicPlanner , knowledgeGraph , blackboard , symbolicEngine , calibrator ;
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-03-30 03:04:27 +00:00
calibrator = new AdaptiveCalibrator ( 'nexus-v1' , { base _rate : 0.05 } ) ;
// 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 ) ;
// Setup Planner
symbolicPlanner . addAction ( 'Stabilize Matrix' , { energy : 50 } , { stability : 1.0 } ) ;
}
function updateGOFAI ( delta , elapsed ) {
const startTime = performance . now ( ) ;
// Simulate perception
neuroBridge . perceive ( { stability : 0.3 , energy : 80 , activePortals : 1 } ) ;
// Run reasoning
if ( Math . floor ( elapsed * 2 ) > Math . floor ( ( elapsed - delta ) * 2 ) ) {
symbolicEngine . reason ( ) ;
2026-03-30 03:10:15 +00:00
pseLayer . offloadReasoning ( Array . from ( symbolicEngine . facts . entries ( ) ) , symbolicEngine . rules . map ( r => ( { description : r . description } ) ) ) ;
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-03-30 03:04:27 +00:00
setupGOFAI ( ) ;
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 ) ;
createAmbientStructures ( ) ;
createAgentPresences ( ) ;
createThoughtStream ( ) ;
createHarnessPulse ( ) ;
createSessionPowerMeter ( ) ;
createWorkshopTerminal ( ) ;
createAshStorm ( ) ;
2026-04-10 07:41:13 +00:00
SpatialMemory . init ( scene ) ;
2026-04-10 22:45:10 +00:00
SessionRooms . init ( scene , camera , null ) ;
2026-03-28 20:50:56 +00:00
updateLoad ( 90 ) ;
loadSession ( ) ;
connectHermes ( ) ;
fetchGiteaData ( ) ;
setInterval ( fetchGiteaData , 30000 ) ; // Refresh every 30s
composer = new EffectComposer ( renderer ) ;
composer . addPass ( new RenderPass ( scene , camera ) ) ;
const bloom = new UnrealBloomPass (
new THREE . Vector2 ( window . innerWidth , window . innerHeight ) ,
0.6 , 0.4 , 0.85
) ;
composer . addPass ( bloom ) ;
composer . addPass ( new SMAAPass ( window . innerWidth , window . innerHeight ) ) ;
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' ) ;
document . getElementById ( 'hud' ) . style . display = 'block' ;
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-07 11:09:31 +00: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-05 22:56:15 +00:00
let metrics = { sovereignty _score : 100 , local _sessions : 0 , total _sessions : 0 } ;
try {
const res = await fetch ( 'http://localhost:8082/metrics' ) ;
if ( res . ok ) {
metrics = await res . json ( ) ;
}
} catch ( e ) {
// Fallback to static if local daemon not running
console . log ( 'Local health daemon not reachable, using static baseline.' ) ;
}
2026-04-05 21:05:20 +00:00
const services = [
{ 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-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-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-05 22:56:15 +00:00
} ) ;
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 ;
labelCanvas . height = 64 ;
const lctx = labelCanvas . getContext ( '2d' ) ;
lctx . font = 'bold 32px "Orbitron", sans-serif' ;
lctx . fillStyle = '#' + portalColor . getHexString ( ) ;
lctx . textAlign = 'center' ;
lctx . fillText ( ` ◈ ${ config . name . toUpperCase ( ) } ` , 256 , 42 ) ;
const labelTex = new THREE . CanvasTexture ( labelCanvas ) ;
const labelMat = new THREE . MeshBasicMaterial ( { map : labelTex , transparent : true , side : THREE . DoubleSide } ) ;
const labelMesh = new THREE . Mesh ( new THREE . PlaneGeometry ( 4 , 0.5 ) , labelMat ) ;
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 ═══
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 ( ) ;
}
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 ) ;
}
} ) ;
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-10 19:38:54 +00:00
// Raycasting for portals and memory crystals
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-10 19:38:54 +00:00
// Priority 1: Portals
const portalHits = raycaster . intersectObjects ( portals . map ( p => p . ring ) ) ;
if ( portalHits . length > 0 ) {
const clickedRing = portalHits [ 0 ] . object ;
2026-03-28 20:50:56 +00:00
const portal = portals . find ( p => p . ring === clickedRing ) ;
2026-04-10 19:38:54 +00:00
if ( portal ) { activatePortal ( portal ) ; return ; }
2026-03-28 20:50:56 +00:00
}
2026-04-10 19:38:54 +00:00
// Priority 2: Memory crystals (Mnemosyne)
const crystalMeshes = SpatialMemory . getCrystalMeshes ( ) ;
if ( crystalMeshes . length > 0 ) {
const crystalHits = raycaster . intersectObjects ( crystalMeshes , false ) ;
if ( crystalHits . length > 0 ) {
const hitMesh = crystalHits [ 0 ] . object ;
const memInfo = SpatialMemory . getMemoryFromMesh ( hitMesh ) ;
if ( memInfo ) {
SpatialMemory . highlightMemory ( memInfo . data . id ) ;
2026-04-10 22:29:17 +00:00
showMemoryPanel ( memInfo , e . clientX , e . clientY ) ;
2026-04-10 19:38:54 +00:00
return ;
}
}
}
2026-04-10 22:45:10 +00:00
// Priority 3: Session rooms (Mnemosyne #1171)
const roomMeshes = SessionRooms . getClickableMeshes ( ) ;
if ( roomMeshes . length > 0 ) {
const roomHits = raycaster . intersectObjects ( roomMeshes , false ) ;
if ( roomHits . length > 0 ) {
const session = SessionRooms . handleRoomClick ( roomHits [ 0 ] . object ) ;
if ( session ) { _showSessionRoomPanel ( session ) ; return ; }
}
}
2026-04-10 19:38:54 +00:00
// Clicked empty space — dismiss panel
dismissMemoryPanel ( ) ;
2026-04-10 22:45:10 +00:00
_dismissSessionRoomPanel ( ) ;
2026-03-28 20:50:56 +00:00
}
}
} ) ;
document . addEventListener ( 'mouseup' , ( ) => { mouseDown = false ; } ) ;
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 ;
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 ;
case 'help' :
sendChatMessage ( "Timmy, I need assistance with Nexus navigation." ) ;
break ;
}
} ) ;
document . getElementById ( 'portal-close-btn' ) . addEventListener ( 'click' , closePortalOverlay ) ;
document . getElementById ( 'vision-close-btn' ) . addEventListener ( 'click' , closeVisionOverlay ) ;
document . getElementById ( 'atlas-toggle-btn' ) . addEventListener ( 'click' , openPortalAtlas ) ;
document . getElementById ( 'atlas-close-btn' ) . addEventListener ( 'click' , closePortalAtlas ) ;
2026-04-10 20:10:45 -04:00
// Mnemosyne export/import (#1174)
document . getElementById ( 'mnemosyne-export-btn' ) . addEventListener ( 'click' , ( ) => {
const result = SpatialMemory . exportToFile ( ) ;
if ( result ) {
addChatMessage ( 'system' , 'Mnemosyne: Exported ' + result . count + ' memories to ' + result . filename ) ;
}
} ) ;
document . getElementById ( 'mnemosyne-import-btn' ) . addEventListener ( 'click' , ( ) => {
document . getElementById ( 'mnemosyne-import-file' ) . click ( ) ;
} ) ;
document . getElementById ( 'mnemosyne-import-file' ) . addEventListener ( 'change' , async ( e ) => {
const file = e . target . files [ 0 ] ;
if ( ! file ) return ;
try {
const result = await SpatialMemory . importFromFile ( file ) ;
addChatMessage ( 'system' , 'Mnemosyne: Imported ' + result . count + ' of ' + result . total + ' memories' ) ;
} catch ( err ) {
addChatMessage ( 'system' , 'Mnemosyne: Import failed — ' + err . message ) ;
}
e . target . value = '' ;
} ) ;
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-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 ( ) ;
} 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 ) ;
} ) ;
}
}
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-07 14:08:42 +00:00
try {
// Initialize MemPalace MCP server
console . log ( 'Initializing MemPalace memory system...' ) ;
// Actual MCP server connection
const statusEl = document . getElementById ( 'mem-palace-status' ) ;
if ( statusEl ) {
statusEl . textContent = 'MemPalace ACTIVE' ;
statusEl . style . color = '#4af0c0' ;
statusEl . style . textShadow = '0 0 10px #4af0c0' ;
}
// 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 ) => {
return new Promise ( ( resolve ) => {
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 ) ;
} ) ;
}
} ) ;
}
// Initialize memory stats tracking
document . getElementById ( 'compression-ratio' ) . textContent = '0x' ;
document . getElementById ( 'docs-mined' ) . textContent = '0' ;
document . getElementById ( 'aaak-size' ) . textContent = '0B' ;
} 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' ;
statusEl . style . textShadow = '0 0 10px #ff4466' ;
}
}
}
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' ;
}
// ═══ PORTAL ATLAS ═══
function openPortalAtlas ( ) {
atlasOverlayActive = true ;
document . getElementById ( 'atlas-overlay' ) . style . display = 'flex' ;
populateAtlas ( ) ;
}
function closePortalAtlas ( ) {
atlasOverlayActive = false ;
document . getElementById ( 'atlas-overlay' ) . style . display = 'none' ;
}
function populateAtlas ( ) {
const grid = document . getElementById ( 'atlas-grid' ) ;
grid . innerHTML = '' ;
let onlineCount = 0 ;
let standbyCount = 0 ;
portals . forEach ( portal => {
const config = portal . config ;
if ( config . status === 'online' ) onlineCount ++ ;
if ( config . status === 'standby' ) standbyCount ++ ;
const card = document . createElement ( 'div' ) ;
card . className = 'atlas-card' ;
card . style . setProperty ( '--portal-color' , config . color ) ;
const statusClass = ` status- ${ config . status || 'online' } ` ;
card . innerHTML = `
< div class = "atlas-card-header" >
< div class = "atlas-card-name" > $ { config . name } < / d i v >
< div class = "atlas-card-status ${statusClass}" > $ { config . status || 'ONLINE' } < / d i v >
< / d i v >
< div class = "atlas-card-desc" > $ { config . description } < / d i v >
< div class = "atlas-card-footer" >
< div class = "atlas-card-coord" > X : $ { config . position . x } Z : $ { config . position . z } < / d i v >
< div class = "atlas-card-type" > $ { config . destination ? . type ? . toUpperCase ( ) || 'UNKNOWN' } < / d i v >
< / d i v >
` ;
card . addEventListener ( 'click' , ( ) => {
focusPortal ( portal ) ;
closePortalAtlas ( ) ;
} ) ;
grid . appendChild ( card ) ;
} ) ;
document . getElementById ( 'atlas-online-count' ) . textContent = onlineCount ;
document . getElementById ( 'atlas-standby-count' ) . textContent = standbyCount ;
// Update Bannerlord HUD status
const bannerlord = portals . find ( p => p . config . id === 'bannerlord' ) ;
if ( bannerlord ) {
const statusEl = document . getElementById ( 'bannerlord-status' ) ;
statusEl . className = 'hud-status-item ' + ( bannerlord . config . status || 'offline' ) ;
}
}
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 ;
2026-04-10 19:38:54 +00:00
// ═══════════════════════════════════════════
// MNEMOSYNE — MEMORY CRYSTAL INSPECTION
// ═══════════════════════════════════════════
2026-04-10 22:29:17 +00:00
// ── pin state for memory panel ──
let _memPanelPinned = false ;
/** Convert a packed hex color integer to "r,g,b" string for CSS rgba(). */
function _hexToRgb ( hex ) {
return ( ( hex >> 16 ) & 255 ) + ',' + ( ( hex >> 8 ) & 255 ) + ',' + ( hex & 255 ) ;
}
2026-04-10 19:38:54 +00:00
/ * *
2026-04-10 22:29:17 +00:00
* Position the panel near the screen click coordinates , keeping it on - screen .
2026-04-10 19:38:54 +00:00
* /
2026-04-10 22:29:17 +00:00
function _positionPanel ( panel , clickX , clickY ) {
const W = window . innerWidth ;
const H = window . innerHeight ;
const panelW = 356 ; // matches CSS width + padding
const panelH = 420 ; // generous estimate
const margin = 12 ;
let left = clickX + 24 ;
if ( left + panelW > W - margin ) left = clickX - panelW - 24 ;
left = Math . max ( margin , Math . min ( W - panelW - margin , left ) ) ;
let top = clickY - 80 ;
top = Math . max ( margin , Math . min ( H - panelH - margin , top ) ) ;
panel . style . right = 'auto' ;
panel . style . top = top + 'px' ;
panel . style . left = left + 'px' ;
panel . style . transform = 'none' ;
}
/ * *
* Navigate to ( highlight + show panel for ) a memory crystal by id .
* /
function _navigateToMemory ( memId ) {
SpatialMemory . highlightMemory ( memId ) ;
addChatMessage ( 'system' , ` Focus: ${ memId . replace ( /_/g , ' ' ) } ` ) ;
const meshes = SpatialMemory . getCrystalMeshes ( ) ;
for ( const mesh of meshes ) {
if ( mesh . userData && mesh . userData . memId === memId ) {
const memInfo = SpatialMemory . getMemoryFromMesh ( mesh ) ;
if ( memInfo ) { showMemoryPanel ( memInfo ) ; break ; }
}
}
}
/ * *
* Show the holographic detail panel for a clicked crystal .
* @ param { object } memInfo — { data , region } from SpatialMemory . getMemoryFromMesh ( )
* @ param { number } [ clickX ] — screen X of the click ( for panel positioning )
* @ param { number } [ clickY ] — screen Y of the click
* /
function showMemoryPanel ( memInfo , clickX , clickY ) {
2026-04-10 19:38:54 +00:00
const panel = document . getElementById ( 'memory-panel' ) ;
if ( ! panel ) return ;
const { data , region } = memInfo ;
const regionDef = SpatialMemory . REGIONS [ region ] || SpatialMemory . REGIONS . working ;
2026-04-10 22:29:17 +00:00
const colorHex = regionDef . color . toString ( 16 ) . padStart ( 6 , '0' ) ;
const colorRgb = _hexToRgb ( regionDef . color ) ;
2026-04-10 19:38:54 +00:00
2026-04-10 22:29:17 +00:00
// Header — region dot + label
2026-04-10 19:38:54 +00:00
document . getElementById ( 'memory-panel-region' ) . textContent = regionDef . label ;
2026-04-10 22:29:17 +00:00
document . getElementById ( 'memory-panel-region-dot' ) . style . background = '#' + colorHex ;
// Category badge
const badge = document . getElementById ( 'memory-panel-category-badge' ) ;
if ( badge ) {
badge . textContent = ( data . category || region || 'memory' ) . toUpperCase ( ) ;
badge . style . background = 'rgba(' + colorRgb + ',0.16)' ;
badge . style . color = '#' + colorHex ;
badge . style . borderColor = 'rgba(' + colorRgb + ',0.4)' ;
}
// Entity name (humanised id)
const entityEl = document . getElementById ( 'memory-panel-entity-name' ) ;
if ( entityEl ) entityEl . textContent = ( data . id || '\u2014' ) . replace ( /_/g , ' ' ) ;
// Fact content
2026-04-10 19:38:54 +00:00
document . getElementById ( 'memory-panel-content' ) . textContent = data . content || '(empty)' ;
2026-04-10 22:29:17 +00:00
// Trust score bar
const strength = data . strength != null ? data . strength : 0.7 ;
const trustFill = document . getElementById ( 'memory-panel-trust-fill' ) ;
const trustVal = document . getElementById ( 'memory-panel-trust-value' ) ;
if ( trustFill ) {
trustFill . style . width = ( strength * 100 ) . toFixed ( 0 ) + '%' ;
trustFill . style . background = '#' + colorHex ;
}
if ( trustVal ) trustVal . textContent = ( strength * 100 ) . toFixed ( 0 ) + '%' ;
// Meta rows
2026-04-10 19:38:54 +00:00
document . getElementById ( 'memory-panel-id' ) . textContent = data . id || '\u2014' ;
document . getElementById ( 'memory-panel-source' ) . textContent = data . source || 'unknown' ;
document . getElementById ( 'memory-panel-time' ) . textContent = data . timestamp ? new Date ( data . timestamp ) . toLocaleString ( ) : '\u2014' ;
2026-04-10 22:29:17 +00:00
// Related entities — clickable links
2026-04-10 19:38:54 +00:00
const connEl = document . getElementById ( 'memory-panel-connections' ) ;
connEl . innerHTML = '' ;
if ( data . connections && data . connections . length > 0 ) {
data . connections . forEach ( cid => {
2026-04-10 22:29:17 +00:00
const btn = document . createElement ( 'button' ) ;
btn . className = 'memory-conn-tag memory-conn-link' ;
btn . textContent = cid . replace ( /_/g , ' ' ) ;
btn . title = 'Go to: ' + cid ;
btn . addEventListener ( 'click' , ( ev ) => { ev . stopPropagation ( ) ; _navigateToMemory ( cid ) ; } ) ;
connEl . appendChild ( btn ) ;
2026-04-10 19:38:54 +00:00
} ) ;
} else {
connEl . innerHTML = '<span style="color:var(--color-text-muted)">None</span>' ;
}
2026-04-10 22:29:17 +00:00
// Pin button — reset on fresh open
_memPanelPinned = false ;
const pinBtn = document . getElementById ( 'memory-panel-pin' ) ;
if ( pinBtn ) {
pinBtn . classList . remove ( 'pinned' ) ;
pinBtn . title = 'Pin panel' ;
pinBtn . onclick = ( ) => {
_memPanelPinned = ! _memPanelPinned ;
pinBtn . classList . toggle ( 'pinned' , _memPanelPinned ) ;
pinBtn . title = _memPanelPinned ? 'Unpin panel' : 'Pin panel' ;
} ;
}
// Positioning — near click if coords provided
if ( clickX != null && clickY != null ) {
_positionPanel ( panel , clickX , clickY ) ;
}
// Fade in
panel . classList . remove ( 'memory-panel-fade-out' ) ;
2026-04-10 19:38:54 +00:00
panel . style . display = 'flex' ;
}
/ * *
2026-04-10 22:29:17 +00:00
* Dismiss the panel ( respects pin ) . Called on empty - space click .
2026-04-10 19:38:54 +00:00
* /
function dismissMemoryPanel ( ) {
2026-04-10 22:29:17 +00:00
if ( _memPanelPinned ) return ;
_dismissMemoryPanelForce ( ) ;
}
/ * *
* Force - dismiss the panel regardless of pin state . Used by the close button .
* /
function _dismissMemoryPanelForce ( ) {
_memPanelPinned = false ;
2026-04-10 19:38:54 +00:00
SpatialMemory . clearHighlight ( ) ;
const panel = document . getElementById ( 'memory-panel' ) ;
2026-04-10 22:29:17 +00:00
if ( ! panel || panel . style . display === 'none' ) return ;
panel . classList . add ( 'memory-panel-fade-out' ) ;
setTimeout ( ( ) => {
panel . style . display = 'none' ;
panel . classList . remove ( 'memory-panel-fade-out' ) ;
} , 200 ) ;
2026-04-10 19:38:54 +00:00
}
2026-04-10 22:45:10 +00:00
/ * *
* Show the session room HUD panel when a chamber is entered .
* @ param { object } session — { id , timestamp , facts [ ] }
* /
function _showSessionRoomPanel ( session ) {
const panel = document . getElementById ( 'session-room-panel' ) ;
if ( ! panel ) return ;
const dt = session . timestamp ? new Date ( session . timestamp ) : new Date ( ) ;
const tsEl = document . getElementById ( 'session-room-timestamp' ) ;
if ( tsEl ) tsEl . textContent = isNaN ( dt . getTime ( ) ) ? session . id : dt . toLocaleString ( ) ;
const countEl = document . getElementById ( 'session-room-fact-count' ) ;
const facts = session . facts || [ ] ;
if ( countEl ) countEl . textContent = facts . length + ( facts . length === 1 ? ' fact' : ' facts' ) + ' in this chamber' ;
const listEl = document . getElementById ( 'session-room-facts' ) ;
if ( listEl ) {
listEl . innerHTML = '' ;
facts . slice ( 0 , 8 ) . forEach ( f => {
const item = document . createElement ( 'div' ) ;
item . className = 'session-room-fact-item' ;
item . textContent = f . content || f . id || '(unknown)' ;
item . title = f . content || '' ;
listEl . appendChild ( item ) ;
} ) ;
if ( facts . length > 8 ) {
const more = document . createElement ( 'div' ) ;
more . className = 'session-room-fact-item' ;
more . style . color = 'rgba(200,180,255,0.4)' ;
more . textContent = '\u2026 ' + ( facts . length - 8 ) + ' more' ;
listEl . appendChild ( more ) ;
}
}
// Close button
const closeBtn = document . getElementById ( 'session-room-close' ) ;
if ( closeBtn ) closeBtn . onclick = ( ) => _dismissSessionRoomPanel ( ) ;
panel . classList . remove ( 'session-panel-fade-out' ) ;
panel . style . display = 'block' ;
}
/ * *
* Dismiss the session room panel .
* /
function _dismissSessionRoomPanel ( ) {
const panel = document . getElementById ( 'session-room-panel' ) ;
if ( ! panel || panel . style . display === 'none' ) return ;
panel . classList . add ( 'session-panel-fade-out' ) ;
setTimeout ( ( ) => {
panel . style . display = 'none' ;
panel . classList . remove ( 'session-panel-fade-out' ) ;
} , 200 ) ;
}
2026-04-10 19:38:54 +00:00
2026-03-28 20:50:56 +00:00
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-10 02:13:31 +00:00
animateMemoryOrbs ( delta ) ;
}
2026-04-10 22:45:10 +00:00
// Project Mnemosyne - Session Rooms (#1171)
SessionRooms . update ( delta ) ;
2026-04-10 02:13:31 +00:00
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 ;
}
composer . render ( ) ;
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 ) ;
composer . setSize ( w , h ) ;
}
// ═══ 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' ] } ,
2026-04-10 22:45:04 +00:00
// MemPalace category zone demos — issue #1168
{ id : 'mem_pref_dark_mode' , content : 'User prefers dark mode and monospace fonts' , category : 'user_pref' , strength : 0.9 , connections : [ ] } ,
{ id : 'mem_pref_verbose_logs' , content : 'User prefers verbose logging during debug sessions' , category : 'user_pref' , strength : 0.7 , connections : [ ] } ,
{ id : 'mem_proj_nexus_goal' , content : 'The Nexus goal: local-first 3D training ground for Timmy' , category : 'project' , strength : 0.95 , connections : [ 'mem_proj_mnemosyne' ] } ,
{ id : 'mem_proj_mnemosyne' , content : 'Project Mnemosyne: holographic living archive of facts' , category : 'project' , strength : 0.85 , connections : [ 'mem_proj_nexus_goal' ] } ,
{ id : 'mem_tool_three_js' , content : 'Three.js — 3D rendering library used for the Nexus world' , category : 'tool' , strength : 0.8 , connections : [ ] } ,
{ id : 'mem_tool_gitea' , content : 'Gitea API at forge.alexanderwhitestone.com for issue tracking' , category : 'tool' , strength : 0.75 , connections : [ ] } ,
{ id : 'mem_gen_websocket' , content : 'WebSocket bridge (server.py) connects Timmy cognition to the browser' , category : 'general' , strength : 0.7 , connections : [ ] } ,
{ id : 'mem_gen_hermes' , content : 'Hermes harness: telemetry and durable truth pipeline' , category : 'general' , strength : 0.65 , connections : [ ] } ,
2026-04-10 07:41:13 +00:00
] ;
demoMemories . forEach ( m => SpatialMemory . placeMemory ( m ) ) ;
2026-04-10 22:18:46 +00:00
// Gravity well clustering — attract related crystals, bake positions (issue #1175)
SpatialMemory . runGravityLayout ( ) ;
2026-04-10 22:45:10 +00:00
// Project Mnemosyne — seed demo session rooms (#1171)
// Sessions group facts by conversation/work session with a timestamp.
const demoSessions = [
{
id : 'session_2026_03_01' ,
timestamp : '2026-03-01T10:00:00.000Z' ,
facts : [
{ id : 'mem_nexus_birth' , content : 'The Nexus came online — first render of the 3D world' , category : 'knowledge' , strength : 0.95 } ,
{ id : 'mem_mnemosyne_start' , content : 'Project Mnemosyne began — the living archive awakens' , category : 'projects' , strength : 0.9 } ,
]
} ,
{
id : 'session_2026_03_15' ,
timestamp : '2026-03-15T14:30:00.000Z' ,
facts : [
{ id : 'mem_first_portal' , content : 'First portal deployed — connection to external service' , category : 'engineering' , strength : 0.85 } ,
{ id : 'mem_hermes_chat' , content : 'First conversation through the Hermes gateway' , category : 'social' , strength : 0.7 } ,
{ id : 'mem_spatial_schema' , content : 'Spatial Memory Schema defined — memories gain homes' , category : 'engineering' , strength : 0.8 } ,
]
} ,
{
id : 'session_2026_04_10' ,
timestamp : '2026-04-10T09:00:00.000Z' ,
facts : [
{ id : 'mem_session_rooms' , content : 'Session rooms introduced — holographic chambers per session' , category : 'projects' , strength : 0.88 } ,
{ id : 'mem_gravity_wells' , content : 'Gravity-well clustering bakes crystal positions on load' , category : 'engineering' , strength : 0.75 } ,
]
}
] ;
SessionRooms . updateSessions ( demoSessions ) ;
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
} ) ;