aboutsummaryrefslogtreecommitdiff
path: root/modules/test/test-world-model-ask.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-22 00:46:05 +0200
committerAgando <roduccomm@gmail.com>2026-10-22 00:46:05 +0200
commit70b9a2f8cc2b249362ca4893fdbb6703c0ea7b8a (patch)
treeb87027e9790b2bb0945f4192a5d08419fa58b478 /modules/test/test-world-model-ask.js
parent14c3cb36a3b037111a9fbdf8ff6ea32ee62b3be1 (diff)
feat(newos4): world-model-ask answers with the LLM, grounded in the model (v4.6.50)
`world-model-ask` was a 10-keyword lookup: any real question returned "Unknown query. Try: modules, skills, ...". The documented example ("what changed since yesterday") compared nothing — it restated the current snapshot. - _modelDigest(): whitelisted snapshot (measured 942-965 chars on fixtures, ~3.3 KB live) + recent error sample + changesSinceLastSnapshot - _buildAskPrompt(): "answer only from this JSON, never invent numbers, name the command that would produce missing data" - ask(): connects Ollama on demand, answers via chat(), falls back to the facts path with a visible warning; --facts keeps the instant lookup - _diffSincePrev(): keeps one previous snapshot in /system/world-model.prev.json so "what changed" is a real diff (files 136 -> 140) - world_model_query tool returns the snapshot (digest or detail:full) instead of ten canned strings; no nested LLM call inside a tool call - help text now says what it does; node test modules/test/test-world-model-ask.js (11 assertions, runs the real class) Verified live v4.6.50: facts "Loaded 46 modules" | LLM "The snapshot does not contain information regarding the cause of memory growth ... use world-model-refresh" | diff "files 136 -> 140" | tool snapshot 3,334 bytes.
Diffstat (limited to 'modules/test/test-world-model-ask.js')
-rw-r--r--modules/test/test-world-model-ask.js47
1 files changed, 47 insertions, 0 deletions
diff --git a/modules/test/test-world-model-ask.js b/modules/test/test-world-model-ask.js
new file mode 100644
index 0000000..4a0c21a
--- /dev/null
+++ b/modules/test/test-world-model-ask.js
@@ -0,0 +1,47 @@
+// node modules/test/test-world-model-ask.js
+// Executes the real class from module-world-model.js (no imports, no DOM at class level).
+const fs = require('fs');
+const path = require('path');
+const src = fs.readFileSync(path.join(__dirname, '../core/module-world-model.js'), 'utf8');
+const WorldModelModule = new Function(src + '\nreturn WorldModelModule;')();
+
+const wm = new WorldModelModule();
+const failures = [];
+const check = (name, cond, detail) => {
+ if (!cond) failures.push(name + (detail ? ' — ' + detail : ''));
+ console.log((cond ? 'PASS' : 'FAIL'), name, detail || '');
+};
+
+// no model yet
+check('empty model digest is empty', wm._modelDigest() === '');
+
+wm._model = {
+ version: 'v4.6.47', synthesized: '2026-10-21T20:00:00.000Z',
+ system: { health: 'ok', memory: '3 MB' },
+ modules: { loaded: 30, total: 30, modifiedCount: 0, modified: [] },
+ files: { total: 42, directories: 8, size: '12 KB' },
+ skills: { count: 23, skills: Array.from({ length: 60 }, (_, i) => 'skill-' + i) },
+ memory: { nodes: 120, edges: 200, active: true },
+ agents: { agents: 4, active: 1 },
+ telemetry: { toolLatencyDataPoints: 9, recentErrors: 0 },
+ projects: { totalTasks: 3, activeProjects: [] },
+ recent: ['Skills directory: 23 files']
+};
+
+const digest = wm._modelDigest();
+check('digest is valid JSON', (() => { try { JSON.parse(digest); return true; } catch (e) { return false; } })());
+check('digest is capped at 4000', digest.length <= 4000, digest.length + ' chars');
+check('digest keeps counts', /"count":23/.test(digest) && /"loaded":30/.test(digest));
+check('digest trims the skill list to 40', (JSON.parse(digest).skills.skills || []).length === 40,
+ String((JSON.parse(digest).skills.skills || []).length));
+
+const prompt = wm._buildAskPrompt(' why did the build break? ');
+check('prompt carries the question', /QUESTION: why did the build break\?$/.test(prompt), prompt.slice(-60));
+check('prompt carries the snapshot', prompt.indexOf('SNAPSHOT: {') > 0);
+check('prompt forbids invention', /Never invent numbers/.test(prompt));
+
+check('facts path still answers a keyword', /30 modules/.test(wm._answerQuery('how many modules')));
+check('facts path still refuses nonsense', /Unknown query/.test(wm._answerQuery('why did it break')));
+
+console.log(failures.length ? '\n' + failures.length + ' FAILED' : '\nall PASS');
+process.exit(failures.length ? 1 : 0);