diff options
| author | Agando <roduccomm@gmail.com> | 2026-07-09 06:00:32 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-07-09 06:00:32 +0200 |
| commit | 2538c91876455fe96febf441fc8dde20f2606023 (patch) | |
| tree | b74c949cdf4296d10103dd57ef8178ba427a5b39 /modules/core/module-world-model.js | |
| parent | ea4d09fb5ccba12ed774fcf7c913edfa1e6a851a (diff) | |
auto: 2026-07-09_06:00 changes
Diffstat (limited to 'modules/core/module-world-model.js')
| -rw-r--r-- | modules/core/module-world-model.js | 96 |
1 files changed, 67 insertions, 29 deletions
diff --git a/modules/core/module-world-model.js b/modules/core/module-world-model.js index 6f0c4e5..61eecfc 100644 --- a/modules/core/module-world-model.js +++ b/modules/core/module-world-model.js @@ -7,9 +7,14 @@ class WorldModelModule { this.terminal = null; this._model = null; this._lastRefresh = null; - this._REFRESH_INTERVAL = 300000; // 5 minutes + this._REFRESH_INTERVAL = 300000; // 5 minutes (fallback) this._MAX_PROJECTS = 10; this._refreshTimer = null; + // v4.5.89: Event-driven sync state + this._syncDebounce = null; + this._syncing = false; + this._lastEventSync = 0; + this._EVENT_DEBOUNCE_MS = 500; } getHelpInfo() { @@ -51,6 +56,25 @@ class WorldModelModule { this.terminal.print('🌐 World Model loaded — system self-awareness active', 'info'); } + // v4.5.89: Event-driven sync — debounced, recursion-guarded + _scheduleSync() { + if (this._syncing) return; // Prevent recursion during active synthesis + if (this._syncDebounce) clearTimeout(this._syncDebounce); + const now = Date.now(); + // Skip if we synced < 2s ago (rate limit beyond debounce) + if (now - this._lastEventSync < 2000) return; + this._syncDebounce = setTimeout(() => { + this._syncDebounce = null; + this._syncing = true; + try { + this._synthesize(); + this._lastEventSync = Date.now(); + } finally { + this._syncing = false; + } + }, this._EVENT_DEBOUNCE_MS); + } + // ─── Synthesis Engine ─── _synthesize() { @@ -119,9 +143,10 @@ class WorldModelModule { try { const mm = this.terminal?.moduleManager; if (!mm) return 0; - // Count registry entries (all known modules) vs loaded - const registry = mm.moduleRegistry || []; - return registry.length || Object.keys(mm.modules || {}).length; + // Use max of registry vs loaded so the ratio is never >1 (e.g. 43/7 → 43/43) + const loadedCount = Object.keys(mm.modules || {}).length; + const registryCount = (mm.moduleRegistry || []).length; + return Math.max(loadedCount, registryCount); } catch (e) { console.warn('[WorldModel] Total modules gather error:', e.message); return Object.keys(this.terminal?.moduleManager?.modules || {}).length || 0; @@ -129,10 +154,10 @@ class WorldModelModule { } _gatherSystemInfo(fs, moduleCount, version) { - // Get real health data from ServerMonitorModule's _latencyLog and AI assistant's _errorLog + // Get real health data from ServerMonitorModule's _latencyLog and DebugSupervisorModule const sm = this.terminal?.moduleManager?.modules?.['ServerMonitorModule']; - const ai = this.terminal?.moduleManager?.modules?.['WebX11AIAssistantModule']; - const errorCount = ai?._errorLog?.length || 0; + const ds = this.terminal?.moduleManager?.modules?.['DebugSupervisorModule']; + const errorCount = ds ? Object.keys(ds._errorDB?.errors || {}).length : 0; const latencyErrors = sm?._latencyLog ? Object.values(sm._latencyLog).filter(e => e.status === 'error').length : 0; const totalErrors = errorCount + latencyErrors; return { @@ -228,11 +253,11 @@ class WorldModelModule { _gatherTelemetry(fs) { try { - // Get real latency data from ServerMonitorModule and errors from AI assistant + // Get real latency data from ServerMonitorModule and errors from DebugSupervisorModule const sm = this.terminal?.moduleManager?.modules?.['ServerMonitorModule']; - const ai = this.terminal?.moduleManager?.modules?.['WebX11AIAssistantModule']; + const ds = this.terminal?.moduleManager?.modules?.['DebugSupervisorModule']; const latencyDataPoints = sm?._latencyLog ? Object.keys(sm._latencyLog).length : 0; - const recentErrors = ai?._errorLog?.length || 0; + const recentErrors = ds ? Object.keys(ds._errorDB?.errors || {}).length : 0; return { toolLatencyDataPoints: latencyDataPoints, recentErrors: recentErrors, @@ -352,41 +377,44 @@ class WorldModelModule { if (!question) return this.terminal.print('Usage: world-model-ask <question>', 'error'); if (!this._model) return this.terminal.print('No model — run world-model-refresh first', 'warning'); - const q = question.toLowerCase(); + const answer = this._answerQuery(question); + this.terminal.print(`❓ ${question}`, 'info'); + this.terminal.print(`💡 ${answer}`, 'success'); + } + + // Shared query routing — used by both ask() (terminal) and world_model_query (AI tool) + _answerQuery(question) { + if (!this._model) return 'World model not synthesized yet — run world-model-refresh'; + const q = (question || '').toLowerCase(); const m = this._model; - let answer = ''; if (q.includes('module') || q.includes('loaded')) { - answer = `Loaded ${m.modules.loaded} modules (${m.modules.modifiedCount} modified: ${m.modules.modified.join(', ') || 'none'})`; + return `Loaded ${m.modules.loaded} modules (${m.modules.modifiedCount} modified: ${m.modules.modified.join(', ') || 'none'})`; } else if (q.includes('skill')) { - answer = `Installed ${m.skills.count} skills: ${m.skills.skills.join(', ') || 'none'}`; + return `Installed ${m.skills.count} skills: ${m.skills.skills.join(', ') || 'none'}`; } else if (q.includes('file') || q.includes('vfs')) { - answer = `${m.files.total} files in ${m.files.directories} directories, total ${m.files.size}`; + return `${m.files.total} files in ${m.files.directories} directories, total ${m.files.size}`; } else if (q.includes('agent') || q.includes('orchestrat')) { - answer = `${m.agents.agents} agents (${m.agents.active} active)`; + return `${m.agents.agents} agents (${m.agents.active} active)`; } else if (q.includes('health') || q.includes('status')) { - answer = `Health: ${m.system.health} · Memory: ${m.system.memory} · ${m.telemetry.recentErrors > 0 ? `${m.telemetry.recentErrors} recent errors` : 'No errors'}`; + return `Health: ${m.system.health} · Memory: ${m.system.memory} · ${m.telemetry.recentErrors > 0 ? `${m.telemetry.recentErrors} recent errors` : 'No errors'}`; } else if (q.includes('memory') || q.includes('graph')) { - answer = `Memory Graph: ${m.memory.nodes} nodes, ${m.memory.edges} edges (${m.memory.active ? 'active' : 'inactive'})`; + return `Memory Graph: ${m.memory.nodes} nodes, ${m.memory.edges} edges (${m.memory.active ? 'active' : 'inactive'})`; } else if (q.includes('change') || q.includes('yesterday') || q.includes('recent')) { const recent = m.recent; - answer = recent.length > 0 ? `Recent changes: ${recent.join('; ')}` : 'No recent changes detected'; + return recent.length > 0 ? `Recent changes: ${recent.join('; ')}` : 'No recent changes detected'; } else if (q.includes('project') || q.includes('work')) { const projects = m.projects.activeProjects; - answer = projects.length > 0 + return projects.length > 0 ? `Active projects: ${projects.map(p => `${p.name} (${p.accesses}x)`).join(', ')}` : 'No active projects detected'; } else if (q.includes('telemetry') || q.includes('latency')) { - answer = `${m.telemetry.toolLatencyDataPoints} tool latency data points tracked`; + return `${m.telemetry.toolLatencyDataPoints} tool latency data points tracked, ${m.telemetry.recentErrors} recent errors`; } else if (q.includes('summary') || q.includes('overview') || q.includes('all')) { - answer = this.showSummary(); - return; + return `newOS4 ${m.version} · ${m.modules.loaded}/${m.modules.total} modules · ${m.skills.count} skills · ${m.files.total} files · ${m.agents.agents} agents · ${m.memory.nodes} graph nodes · ${m.telemetry.recentErrors > 0 ? `⚠️ ${m.telemetry.recentErrors} errors` : '✅ OK'}`; } else { - answer = `I'm not sure how to answer "${question}". Try: modules, skills, files, agents, health, memory, changes, projects, telemetry, or summary.`; + return `Unknown query. Try: modules, skills, files, agents, health, memory, changes, projects, telemetry, or summary.`; } - - this.terminal.print(`❓ ${question}`, 'info'); - this.terminal.print(`💡 ${answer}`, 'success'); } refresh() { @@ -429,7 +457,7 @@ class WorldModelModule { const agentStr = m.agents.error ? 'agents: N/A' : `${m.agents.agents} agents`; - return `[World Model Context]\\nSystem: newOS4 ${m.version} · ${m.modules.loaded} modules · ${m.skills.count} skills · ${m.files.total} files · ${agentStr}${m.telemetry.recentErrors > 0 ? ` · ⚠️ ${m.telemetry.recentErrors} errors` : ' · ✅ OK'}${m.modules.modifiedCount > 0 ? `\\nModified modules: ${m.modules.modified.slice(0, 3).join(', ')}${m.modules.modifiedCount > 3 ? ` (+${m.modules.modifiedCount - 3} more)` : ''}` : ''}`; + return `[World Model Context]\nSystem: newOS4 ${m.version} · ${m.modules.loaded} modules · ${m.skills.count} skills · ${m.files.total} files · ${agentStr}${m.telemetry.recentErrors > 0 ? ` · ⚠️ ${m.telemetry.recentErrors} errors` : ' · ✅ OK'}${m.modules.modifiedCount > 0 ? `\nModified modules: ${m.modules.modified.slice(0, 3).join(', ')}${m.modules.modifiedCount > 3 ? ` (+${m.modules.modifiedCount - 3} more)` : ''}` : ''}`; } // ─── Persistence ─── @@ -448,10 +476,20 @@ class WorldModelModule { } } + // v4.5.89: Atomic write — temp file + rename to prevent corruption _saveToVFS(fs) { try { if (!fs.readDir('/system')) fs.mkdir('system'); - fs.fsWriteJSON('/system/world-model.json', this._model); + // v4.5.89b fix: Clean stale temp from previous crash (P1 from review) + try { fs.deleteFile('/system/world-model.tmp.json'); } catch (_) {} + fs.fsWriteJSON('/system/world-model.tmp.json', this._model); + const tmp = fs.fsReadJSON('/system/world-model.tmp.json'); + if (tmp) { + fs.fsWriteJSON('/system/world-model.json', tmp); + fs.deleteFile('/system/world-model.tmp.json'); + } else { + console.error('[WorldModel] Save failed: temp file corrupt after write'); + } } catch (e) { console.error('[WorldModel] Save error:', e.message); } |