aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-world-model.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-05 18:01:01 +0200
committerAgando <roduccomm@gmail.com>2026-06-05 18:01:01 +0200
commitb538577cd0a4292e4cd4ca1c9f9829fc7e133caa (patch)
tree04d76bf4f0d680cdb26b1a205481bc8628cd77b4 /modules/core/module-world-model.js
parent5092f9a0ce02691dc476d7b64fa731a1518dd834 (diff)
auto: 2026-06-05_18:01 changes
Diffstat (limited to 'modules/core/module-world-model.js')
-rw-r--r--modules/core/module-world-model.js381
1 files changed, 381 insertions, 0 deletions
diff --git a/modules/core/module-world-model.js b/modules/core/module-world-model.js
new file mode 100644
index 0000000..b7bb28f
--- /dev/null
+++ b/modules/core/module-world-model.js
@@ -0,0 +1,381 @@
+// module-world-model.js — Global World Model: system self-awareness synthesis engine
+// Aggregates from memory graph, neural VFS, skills index, telemetry
+// Commands: world-model, world-model-summary, world-model-ask, world-model-refresh, world-model-config
+class WorldModelModule {
+ constructor() {
+ this.commands = {};
+ this.terminal = null;
+ this._model = null;
+ this._lastRefresh = null;
+ this._REFRESH_INTERVAL = 300000; // 5 minutes
+ this._MAX_PROJECTS = 10;
+ this._refreshTimer = null;
+ }
+
+ getHelpInfo() {
+ return {
+ 'world-model': 'world-model — Show full world model as structured output',
+ 'world-model-summary': 'world-model-summary — One-line system self-awareness statement',
+ 'world-model-ask': 'world-model-ask <question> — Query the model (e.g. "what changed since yesterday")',
+ 'world-model-refresh': 'world-model-refresh — Force re-synthesis from all subsystems',
+ 'world-model-config': 'world-model-config <key> <value> — Tune: refresh-interval, max-projects',
+ };
+ }
+
+ registerCommands() {
+ this.commands['world-model'] = () => this.showModel();
+ this.commands['world-model-summary'] = () => this.showSummary();
+ this.commands['world-model-ask'] = (args) => this.ask(args.join(' '));
+ this.commands['world-model-refresh'] = () => this.refresh();
+ this.commands['world-model-config'] = (args) => this.setConfig(args);
+ }
+
+ init(terminal) {
+ this.terminal = terminal;
+ this._loadFromVFS();
+ // Auto-refresh on a timer
+ this._refreshTimer = setInterval(() => {
+ const age = Date.now() - (this._lastRefresh ? new Date(this._lastRefresh).getTime() : 0);
+ if (age > this._REFRESH_INTERVAL) this._synthesize();
+ }, this._REFRESH_INTERVAL);
+ // Initial synthesis
+ this._synthesize();
+ this.terminal.print('🌐 World Model loaded — system self-awareness active', 'info');
+ }
+
+ // ─── Synthesis Engine ───
+
+ _synthesize() {
+ try {
+ const fs = this.terminal.fs;
+ if (!fs) return;
+
+ const model = {
+ version: 'v4.4',
+ synthesized: new Date().toISOString(),
+ system: this._gatherSystemInfo(fs),
+ modules: this._gatherModuleInfo(),
+ files: this._gatherFileInfo(fs),
+ skills: this._gatherSkillInfo(fs),
+ memory: this._gatherMemoryInfo(),
+ agents: this._gatherAgentInfo(),
+ telemetry: this._gatherTelemetry(fs),
+ projects: this._gatherProjects(fs),
+ recent: this._gatherRecent(fs)
+ };
+
+ this._model = model;
+ this._lastRefresh = model.synthesized;
+ this._saveToVFS(fs);
+
+ // Log synthesis summary
+ const mCount = model.modules?.loaded || '?';
+ const sCount = model.skills?.count || '?';
+ const fCount = model.files?.total || '?';
+ console.log(`[WorldModel] Synthesis complete: ${mCount} modules, ${sCount} skills, ${fCount} files`);
+ } catch (e) {
+ console.error('[WorldModel] Synthesis error:', e.message);
+ }
+ }
+
+ _gatherSystemInfo(fs) {
+ const healthData = fs.fsReadJSON('/system/metrics/health.json');
+ return {
+ version: 'newOS4 v4.4',
+ modules: Object.keys(this.terminal?.moduleManager?.modules || {}).length || 0,
+ vfsSize: JSON.stringify(fs?.root || {}).length,
+ online: typeof navigator !== 'undefined' ? navigator.onLine : true,
+ memory: typeof performance !== 'undefined' && performance.memory
+ ? `${(performance.memory.usedJSHeapSize / 1048576).toFixed(1)}MB`
+ : 'N/A',
+ health: healthData?.status || 'OK',
+ lastHealthCheck: healthData?.timestamp || null
+ };
+ }
+
+ _gatherModuleInfo() {
+ const mm = this.terminal?.moduleManager;
+ if (!mm) return { loaded: 0, modified: [] };
+ const modules = Object.keys(mm.modules || {}).map(name => {
+ const mod = mm.modules[name];
+ const clsName = mod?.constructor?.name || name;
+ return { name: clsName, hasCommands: !!mod?.commands };
+ });
+ // Check for local modifications
+ const modsDir = this.terminal.fs?.readDir('/_mods/');
+ const modified = modsDir
+ ? modsDir.filter(e => e.type === 'file' && e.name.endsWith('.js') && !e.name.endsWith('.orig.js'))
+ .map(e => e.name.replace('.js', ''))
+ : [];
+ return {
+ loaded: modules.length,
+ total: 49,
+ modules,
+ modified,
+ modifiedCount: modified.length
+ };
+ }
+
+ _gatherFileInfo(fs) {
+ if (!fs) return { total: 0, size: '0KB' };
+ let fileCount = 0, dirCount = 0;
+ const walk = (path) => {
+ const entries = fs.ls(path);
+ if (!entries) return;
+ for (const e of entries) {
+ if (e.type === 'file') fileCount++;
+ else { dirCount++; walk(path === '/' ? '/' + e.name : path + '/' + e.name); }
+ }
+ };
+ walk('/');
+ return {
+ total: fileCount,
+ directories: dirCount,
+ size: `${(JSON.stringify(fs.root).length / 1024).toFixed(1)}KB`
+ };
+ }
+
+ _gatherSkillInfo(fs) {
+ try {
+ const idx = fs.fsReadJSON('/skills/index.json');
+ const count = idx ? Object.keys(idx).length : 0;
+ return { count, skills: idx ? Object.keys(idx) : [] };
+ } catch (e) {
+ return { count: 0, skills: [] };
+ }
+ }
+
+ _gatherMemoryInfo() {
+ const mg = this.terminal?.moduleManager?.modules?.['MemoryGraphModule'];
+ if (mg && mg._nodes) {
+ const nodes = Object.keys(mg._nodes).length;
+ const edges = (mg._edges || []).length;
+ return { nodes, edges, active: true };
+ }
+ // Fallback: count memory files
+ try {
+ const memIndex = this.terminal.fs?.fsReadJSON('/memory/index.json');
+ return { nodes: memIndex ? Object.keys(memIndex).length : 0, edges: 0, active: false };
+ } catch (e) {
+ return { nodes: 0, edges: 0, active: false };
+ }
+ }
+
+ _gatherAgentInfo() {
+ const orch = this.terminal?.moduleManager?.modules?.['OrchestratorModule'];
+ if (!orch || !orch._agents) return { agents: 0, active: 0 };
+ const agents = Object.keys(orch._agents).length;
+ const active = Object.values(orch._agents).filter(a => a.status === 'busy').length;
+ return { agents, active, totalLeases: Object.keys(orch._a2aMessages || {}).length };
+ }
+
+ _gatherTelemetry(fs) {
+ try {
+ const latency = fs.fsReadJSON('/system/metrics/tool-latency.json');
+ const errors = fs.fsReadJSON('/system/metrics/error-log.json');
+ return {
+ toolLatencyDataPoints: latency ? Object.keys(latency).length : 0,
+ recentErrors: errors ? (Array.isArray(errors) ? errors.length : Object.keys(errors).length) : 0,
+ };
+ } catch (e) {
+ return { toolLatencyDataPoints: 0, recentErrors: 0 };
+ }
+ }
+
+ _gatherProjects(fs) {
+ try {
+ const mg = this.terminal?.moduleManager?.modules?.['MemoryGraphModule'];
+ if (mg && mg._nodes) {
+ // Find task clusters as "projects"
+ const tasks = Object.values(mg._nodes).filter(n => n.type === 'task');
+ const recentFiles = Object.values(mg._nodes)
+ .filter(n => n.type === 'file')
+ .sort((a, b) => b.access_count - a.access_count)
+ .slice(0, this._MAX_PROJECTS);
+ return {
+ totalTasks: tasks.length,
+ activeProjects: recentFiles.slice(0, 5).map(f => ({
+ name: f.label,
+ accesses: f.access_count,
+ lastModified: f.updated
+ }))
+ };
+ }
+ return { totalTasks: 0, activeProjects: [] };
+ } catch (e) {
+ return { totalTasks: 0, activeProjects: [] };
+ }
+ }
+
+ _gatherRecent(fs) {
+ const recent = [];
+ try {
+ // Check modified modules
+ const modsDir = fs.readDir('/_mods/');
+ if (modsDir) {
+ for (const e of modsDir) {
+ if (e.type === 'file' && e.name.endsWith('.js') && !e.name.endsWith('.orig.js') && !e.name.endsWith('.meta.json')) {
+ recent.push(`Modified module: ${e.name.replace('.js', '')}`);
+ }
+ }
+ }
+ // Check skills activity
+ const skills = fs.ls('/skills/');
+ if (skills && skills.length > 0) {
+ recent.push(`Skills directory: ${skills.filter(s => s.type === 'file').length} files`);
+ }
+ } catch (e) {}
+ return recent;
+ }
+
+ // ─── Commands ───
+
+ showModel() {
+ if (!this._model) return this.terminal.print('No model yet — run world-model-refresh', 'warning');
+ const m = this._model;
+
+ this.terminal.print('🌐 World Model', 'header');
+ this.terminal.print(` Version: ${m.version} (last synthsized: ${new Date(m.synthesized).toLocaleTimeString()})`, 'info');
+
+ this.terminal.print(` ⚙️ System — ${m.system.modules} modules loaded, ${m.system.vfsSize} VFS, ${m.system.memory}`, 'info');
+ this.terminal.print(` 🧩 Modules — ${m.modules.loaded} loaded (${m.modules.modifiedCount} modified)`, 'info');
+ this.terminal.print(` 📁 Files — ${m.files.total} files, ${m.files.directories} dirs, ${m.files.size}`, 'info');
+ this.terminal.print(` 📚 Skills — ${m.skills.count} installed`, 'info');
+ this.terminal.print(` 🧠 Memory Graph — ${m.memory.nodes} nodes, ${m.memory.edges} edges${m.memory.active ? ' ✅' : ''}`, 'info');
+ this.terminal.print(` 🤖 Agents — ${m.agents.agents} registered (${m.agents.active} active)`, 'info');
+ this.terminal.print(` 📊 Telemetry — ${m.telemetry.toolLatencyDataPoints} tool latency points, ${m.telemetry.recentErrors} recent errors`, 'info');
+
+ if (m.projects.activeProjects.length > 0) {
+ this.terminal.print(' Active context:', 'header');
+ for (const p of m.projects.activeProjects.slice(0, 3)) {
+ this.terminal.print(` 📌 ${p.name} (${p.accesses}x accesses)`, 'info');
+ }
+ }
+
+ if (m.recent.length > 0) {
+ this.terminal.print(' Recent changes:', 'header');
+ for (const r of m.recent.slice(0, 3)) {
+ this.terminal.print(` ${r}`, 'info');
+ }
+ }
+ }
+
+ showSummary() {
+ if (!this._model) return this.terminal.print('No model yet', 'warning');
+ const m = this._model;
+ const summary = `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'}`;
+ this.terminal.print(summary, 'info');
+ return summary;
+ }
+
+ ask(question) {
+ 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 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'})`;
+ } else if (q.includes('skill')) {
+ answer = `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}`;
+ } else if (q.includes('agent') || q.includes('orchestrat')) {
+ answer = `${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'}`;
+ } else if (q.includes('memory') || q.includes('graph')) {
+ answer = `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';
+ } else if (q.includes('project') || q.includes('work')) {
+ const projects = m.projects.activeProjects;
+ answer = 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`;
+ } else if (q.includes('summary') || q.includes('overview') || q.includes('all')) {
+ answer = this.showSummary();
+ return;
+ } else {
+ answer = `I'm not sure how to answer "${question}". Try: modules, skills, files, agents, health, memory, changes, projects, telemetry, or summary.`;
+ }
+
+ this.terminal.print(`❓ ${question}`, 'info');
+ this.terminal.print(`💡 ${answer}`, 'success');
+ }
+
+ refresh() {
+ this._synthesize();
+ this.terminal.print(`✅ World Model refreshed at ${new Date().toLocaleTimeString()}`, 'success');
+ }
+
+ setConfig(args) {
+ if (args.length < 2) return this.terminal.print('Usage: world-model-config <key> <value>', 'error');
+ const key = args[0];
+ const val = args[1];
+
+ const configMap = {
+ 'refresh-interval': () => {
+ const n = parseInt(val) * 1000;
+ if (isNaN(n) || n < 10000) return 'Must be >= 10 seconds';
+ this._REFRESH_INTERVAL = n;
+ if (this._refreshTimer) clearInterval(this._refreshTimer);
+ this._refreshTimer = setInterval(() => this._synthesize(), n);
+ return `refresh-interval set to ${val}s`;
+ },
+ 'max-projects': () => {
+ const n = parseInt(val);
+ if (isNaN(n) || n < 1) return 'Must be >= 1';
+ this._MAX_PROJECTS = n;
+ return `max-projects set to ${n}`;
+ },
+ };
+
+ if (!configMap[key]) return this.terminal.print(`Unknown: ${key}. Valid: ${Object.keys(configMap).join(', ')}`, 'error');
+ const msg = configMap[key]();
+ this.terminal.print(`✅ ${msg}`, 'success');
+ }
+
+ // ─── Context Injection ───
+
+ getContextForPrompt() {
+ if (!this._model) return '';
+ const m = this._model;
+ return `[World Model Context]\\nSystem: newOS4 ${m.version} · ${m.modules.loaded} modules · ${m.skills.count} skills · ${m.files.total} files · ${m.agents.agents} agents${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 ───
+
+ _loadFromVFS() {
+ try {
+ const fs = this.terminal.fs;
+ if (!fs) return;
+ const data = fs.fsReadJSON('/system/world-model.json');
+ if (data) {
+ this._model = data;
+ this._lastRefresh = data.synthesized;
+ }
+ } catch (e) {
+ console.warn('[WorldModel] Load error (fresh start):', e.message);
+ }
+ }
+
+ _saveToVFS(fs) {
+ try {
+ if (!fs.readDir('/system')) fs.mkdir('system');
+ fs.fsWriteJSON('/system/world-model.json', this._model);
+ } catch (e) {
+ console.error('[WorldModel] Save error:', e.message);
+ }
+ }
+}
+
+// Required for module system
+if (typeof window !== 'undefined') {
+ window.WorldModelModule = WorldModelModule;
+} \ No newline at end of file