aboutsummaryrefslogtreecommitdiff
path: root/modules/core/core-bundle.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-09-29 13:48:35 +0200
committerAgando <roduccomm@gmail.com>2026-09-29 13:48:35 +0200
commitdd50ae715f3a648aeb42fb524fe1b106a0c084c7 (patch)
treea199c1a6dab4d30d3e54a745e5eb05b9ccd575a1 /modules/core/core-bundle.js
parent78059a20c9c6fef43da99e81ff847928a33b989c (diff)
newOS4 v4.6.14: race-safe AI loop guard (depth counter), cover _capture* entry points, structured JSON tool error
Diffstat (limited to 'modules/core/core-bundle.js')
-rw-r--r--modules/core/core-bundle.js30
1 files changed, 22 insertions, 8 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js
index 29c6cc6..942f836 100644
--- a/modules/core/core-bundle.js
+++ b/modules/core/core-bundle.js
@@ -2441,6 +2441,9 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
this.systemCmds = new Set(this._baseSystem);
this.suspiciousFlags = new Set(this._baseSuspiciousFlags);
this.whitelist = new Set(this._baseWhitelist);
+ // AI loop-guard: depth counter (reentrant, race-safe vs parallel tool calls)
+ this._aiToolDepth = 0;
+ this._lastUnknownCmd = null;
// Load persisted user deltas (add/remove) so hardcoded defaults can be demoted.
this._loadSecurityDeltas();
this._registerBuiltIn();
@@ -2594,6 +2597,7 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
return { cmd, args, known, destructive, system, unknown, hasSuspicious };
}
async checkAndRun(rawInput) {
+ this._lastUnknownCmd = null;
const { cmd, args, known, destructive, system, unknown, hasSuspicious } = this._classify(rawInput);
// Level 3 Audit: just log and allow
if (this.level === 3) {
@@ -2617,13 +2621,14 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
return;
}
// Level 1: typo of a safe whitelisted command → one-tap clickable fix, no AI call burned
- if (sug && sug.dist <= 1 && this.whitelist.has(sug.cmd) && this.terminal.showSuggestionBar && !this._aiToolCall) {
+ if (sug && sug.dist <= 1 && this.whitelist.has(sug.cmd) && this.terminal.showSuggestionBar && !(this._aiToolDepth > 0)) {
this.terminal.print(`Unknown command "${cmd}" — did you mean:`, 'warning');
this.terminal.showSuggestionBar(sug.cmd, args);
return;
}
// AI-invoked commands must not route back to AI (infinite loop) — return structured error
- if (this._aiToolCall) {
+ if (this._aiToolDepth > 0) {
+ this._lastUnknownCmd = cmd;
this.terminal.print(`Command not found: ${cmd}${sug ? `. Did you mean: ${sug.cmd}?` : ''}`, 'error');
return;
}
@@ -8938,8 +8943,10 @@ buildSystemPrompt() {
const originalPrint = this.terminal.print.bind(this.terminal);
let output = '';
this.terminal.print = (text, type) => { output += text + '\n'; };
+ // Loop guard: AI-initiated commands must not route back to AI on unknown command
+ this.terminal.security._aiToolDepth++;
try { await this.terminal.security.checkAndRun(cmd); }
- finally { this.terminal.print = originalPrint; }
+ finally { this.terminal.security._aiToolDepth--; this.terminal.print = originalPrint; }
return output.trim();
}
async _captureShellCommand(cmd) {
@@ -8947,8 +8954,10 @@ buildSystemPrompt() {
const originalPrint = this.terminal.print.bind(this.terminal);
let output = '';
this.terminal.print = (text, type) => { output += text + '\n'; };
+ // Loop guard: AI-initiated commands must not route back to AI on unknown command
+ this.terminal.security._aiToolDepth++;
try { await this.terminal.executeCommand(cmd); }
- finally { this.terminal.print = originalPrint; }
+ finally { this.terminal.security._aiToolDepth--; this.terminal.print = originalPrint; }
return output.trim();
}
async _captureConsoleErrors(durationMs) {
@@ -10249,10 +10258,15 @@ buildSystemPrompt() {
const originalPrint = this.terminal.print.bind(this.terminal);
let output = '';
this.terminal.print = (text, type) => { output += text + '\n'; };
- // Flag security so unknown commands return an error instead of routing back to AI (loop guard)
- const prevAiFlag = this.terminal.security._aiToolCall;
- this.terminal.security._aiToolCall = true;
- try { await this.terminal.security.checkAndRun(cmd); } finally { this.terminal.security._aiToolCall = prevAiFlag; this.terminal.print = originalPrint; }
+ // Loop guard (depth counter — race-safe under Promise.all tool fan-out):
+ // unknown commands return an error instead of routing back to AI
+ this.terminal.security._aiToolDepth++;
+ try { await this.terminal.security.checkAndRun(cmd); } finally { this.terminal.security._aiToolDepth--; this.terminal.print = originalPrint; }
+ const unknownCmd = this.terminal.security._lastUnknownCmd;
+ if (unknownCmd) {
+ const sug = this.terminal.security.suggest(unknownCmd);
+ return JSON.stringify({ error: `Command not found: ${unknownCmd}`, suggestion: sug ? sug.cmd : null });
+ }
// 📸 Diff and append shadow report
const shadowDiff = this._diffShadowVFS();
if (shadowDiff && shadowDiff.length > 0) {