aboutsummaryrefslogtreecommitdiff
path: root/modules/core/core-bundle.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-23 18:15:05 +0200
committerAgando <roduccomm@gmail.com>2026-10-23 18:15:05 +0200
commit5a7230121afc75ad96f1ed32ac90791a98fabce2 (patch)
tree4a4420f7e65425b3bf39a9696494e836ba1ab6a2 /modules/core/core-bundle.js
parent2eec7a5c7ab9b724a718ece8b1ffc434677068ac (diff)
fix(newos4): one error counter for errors/dashboard/world-model (v4.6.61)
Packet 2 (three surfaces, three answers). Measured first: `errors` scanned /logs/current.log for [ERROR] lines, but that file only ever receives explicit terminal.log('ERROR', ...) calls -- exactly one call site in the whole app -- so it read 0. The dashboard added DebugSupervisor._errorDB plus failed tool calls (18), and the world model read the debug DB alone (11). - terminal.countErrors() / countLogErrors(): the single counter, exposed on the one object all three surfaces already hold - `errors` prints the canonical total with its breakdown, the entry list (hash, count, severity, message), and reports the log scan separately as a mirror - dashboard health.errors and health.errorBreakdown derive from it - world model telemetry.recentErrors derives from it, plus errorsBySource - terminal.log('ERROR', ...) now also records into DebugSupervisor, so the log is a human-readable mirror of one store instead of a second store almost nobody wrote Verified live on v4.6.61: with a seeded debug entry + failed tool call, all three report 2; a single log('ERROR') call moves debug 1->2 and the total by exactly 1 (the mirror line is counted once, in the log, and reported separately).
Diffstat (limited to 'modules/core/core-bundle.js')
-rw-r--r--modules/core/core-bundle.js77
1 files changed, 64 insertions, 13 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js
index 941707e..e265b68 100644
--- a/modules/core/core-bundle.js
+++ b/modules/core/core-bundle.js
@@ -5406,6 +5406,16 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
}
}
log(level, source, message) {
+ // v4.6.60: an ERROR also goes to the DebugSupervisor DB, so the log is a
+ // human-readable MIRROR of one store instead of a second store that only one
+ // code path ever wrote to (packet §2: 0 / 18 / 11 in the same second).
+ if (String(level).toUpperCase() === 'ERROR') {
+ try {
+ const ds = this.moduleManager && this.moduleManager.modules
+ ? this.moduleManager.modules['DebugSupervisorModule'] : null;
+ if (ds && typeof ds._recordError === 'function') ds._recordError(String(message), String(source || 'terminal'));
+ } catch (e) { /* never let logging throw */ }
+ }
const d = new Date();
const ts = d.toISOString().split('T')[1].split('.')[0];
const entry = `[${ts}][${level}][${source}] ${message}`;
@@ -5424,6 +5434,28 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
}
} catch (e) {}
}
+ // v4.6.59: ONE error counter. Three surfaces used to disagree because each
+ // tallied a different sink: `errors` scanned /logs/current.log (which only ever
+ // receives explicit terminal.log('ERROR') calls — one call site in the whole app),
+ // the dashboard added debug-DB + failed tool calls, and the world model read the
+ // debug DB alone. Measured: 0 / 18 / 11 in the same second (packet §2).
+ countLogErrors() {
+ try {
+ const c = this.fs.readFile('/logs/current.log') || '';
+ return c.split('\n').filter(l => l.includes('[ERROR]')).length;
+ } catch (e) { return 0; }
+ }
+ countErrors() {
+ const mm = this.moduleManager;
+ const ds = mm && mm.modules ? mm.modules['DebugSupervisorModule'] : null;
+ const sm = mm && mm.modules ? mm.modules['ServerMonitorModule'] : null;
+ const debug = (ds && ds._errorDB && ds._errorDB.errors)
+ ? Object.keys(ds._errorDB.errors).length : 0;
+ const latency = (sm && Array.isArray(sm._latencyLog))
+ ? sm._latencyLog.filter(e => e && e.error).length : 0;
+ const log = this.countLogErrors();
+ return { debug: debug, latency: latency, log: log, total: debug + latency };
+ }
setAiBusy(busy) {
this._aiBusy = !!busy;
if (this._aiBusyInterval) {
@@ -5678,13 +5710,25 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
// v4.5.43: errors [n] — alias for logs [n] ERROR
this.commands['errors'] = (args) => {
const n = parseInt(args[0]) || 20;
- const content = this.terminal.fs.readFile('/logs/current.log');
- if (!content) { this.terminal.print('No logs yet', 'info'); return; }
- const lines = content.split('\n').filter(l => l && l.includes('[ERROR]'));
- const tail = lines.slice(-n);
- if (tail.length === 0) { this.terminal.print('✅ No errors found in logs', 'success'); return; }
- this.terminal.print(`--- Errors (last ${tail.length}) ---`, 'error');
- tail.forEach(l => this.terminal.print(l, 'error'));
+ // v4.6.59: read the canonical counter (DebugSupervisor DB + failed tool
+ // calls) — the same numbers the dashboard and world model publish. The VFS
+ // log is reported separately because it only receives explicit
+ // terminal.log('ERROR') calls, so it cannot be the source of truth.
+ const e = this.terminal.countErrors();
+ if (e.total === 0) {
+ this.terminal.print(`✅ 0 errors — debug DB ${e.debug} · failed tool calls ${e.latency} · log ${e.log}`, 'success');
+ } else {
+ this.terminal.print(`--- ${e.total} ${e.total === 1 ? 'error' : 'errors'} — debug DB ${e.debug} · failed tool calls ${e.latency} ---`, 'error');
+ const ds = this.terminal.moduleManager.modules['DebugSupervisorModule'];
+ const entries = Object.entries((ds && ds._errorDB && ds._errorDB.errors) || {})
+ .map(([hash, v]) => Object.assign({ hash: hash }, v))
+ .sort((a, b) => (b.lastSeen || 0) - (a.lastSeen || 0))
+ .slice(0, n);
+ entries.forEach(x => this.terminal.print(
+ (x.hash || '').slice(0, 8) + ' ×' + (x.count || 1) + ' ' +
+ ((x.severity || '') + ' ' + (x.message || '')).trim(), 'error'));
+ }
+ this.terminal.print(` scope: this browser session · log scan ${e.log} [ERROR] line(s) in /logs/current.log`, 'info');
};
// v4.5.43: doctor — run all health checks, rank issues, show top 3
@@ -12914,12 +12958,13 @@ buildSystemPrompt() {
}
// System health
- var totalErrors = 0;
+ var errCounts = { total: 0, debug: 0, latency: 0, log: 0 };
try {
- var ds = mm ? mm.modules['DebugSupervisorModule'] : null;
- if (ds && ds._errorDB) totalErrors += Object.keys(ds._errorDB.errors).length;
- if (sm && sm._latencyLog) totalErrors += sm._latencyLog.filter(function(e) { return e.error; }).length;
+ if (this.terminal && typeof this.terminal.countErrors === 'function') {
+ errCounts = this.terminal.countErrors();
+ }
} catch (e) {}
+ var totalErrors = errCounts.total;
var predictor = mm ? mm.modules['PredictorModule'] : null;
var predictorAcc = 'n/a (no paired runs)';
if (predictor && predictor._accuracy && predictor._accuracy.total > 0) {
@@ -12931,6 +12976,7 @@ buildSystemPrompt() {
vfsSize: data.vfs ? data.vfs.size : '?',
online: typeof navigator !== 'undefined' ? navigator.onLine : true,
errors: totalErrors,
+ errorBreakdown: errCounts,
predictorAccuracy: predictorAcc
};
@@ -18469,10 +18515,15 @@ class WorldModelModule {
const sm = this.terminal?.moduleManager?.modules?.['ServerMonitorModule'];
const ds = this.terminal?.moduleManager?.modules?.['DebugSupervisorModule'];
const latencyDataPoints = sm?._latencyLog ? Object.keys(sm._latencyLog).length : 0;
- const recentErrors = ds ? Object.keys(ds._errorDB?.errors || {}).length : 0;
+ // v4.6.59: same canonical counter as the `errors` command and the dashboard.
+ // Reading the debug DB alone made the world model disagree with both (packet §2).
+ const errInfo = (this.terminal && typeof this.terminal.countErrors === 'function')
+ ? this.terminal.countErrors()
+ : { total: ds ? Object.keys(ds._errorDB?.errors || {}).length : 0, debug: 0, latency: 0, log: 0 };
return {
toolLatencyDataPoints: latencyDataPoints,
- recentErrors: recentErrors,
+ recentErrors: errInfo.total,
+ errorsBySource: { debug: errInfo.debug, toolCalls: errInfo.latency, log: errInfo.log },
};
} catch (e) {
console.warn('[WorldModel] Telemetry gather error:', e.message);