aboutsummaryrefslogtreecommitdiff
path: root/modules/core/core-bundle.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-09-29 13:30:20 +0200
committerAgando <roduccomm@gmail.com>2026-09-29 13:30:20 +0200
commit588339dd1cca9d9f7874196b7635baba61454b12 (patch)
tree911a414fc4fc8a48b3a29b9686d05eac7372e32d /modules/core/core-bundle.js
parentff9fb8909af62fe57f9732f18f280657270ab5d0 (diff)
newOS4 v4.6.13: typo suggestion bar (one-tap fix), consistent unknown-cmd messages, AI loop guard
Diffstat (limited to 'modules/core/core-bundle.js')
-rw-r--r--modules/core/core-bundle.js59
1 files changed, 45 insertions, 14 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js
index cfdfe11..29c6cc6 100644
--- a/modules/core/core-bundle.js
+++ b/modules/core/core-bundle.js
@@ -2611,23 +2611,27 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
}
this.blockedCount++;
const sug = this.suggest(cmd);
- if (sug) {
- this.terminal.print(`Security: Unknown command "${cmd}". Did you mean: "${sug.cmd}"?`, 'warning');
- } else {
- this.terminal.print(`Security: Unknown command "${cmd}" — blocked. Use 'security-level 0' to disable.`, 'warning');
- }
- // At level 1: suggest/fix but still route to AI (terminal is also a chat interface)
// At level 2+: block entirely (paranoid)
if (this.level >= 2) {
- this.terminal.print(`Security: Unknown command "${cmd}" blocked (level ${this.level}).`, 'error');
+ this.terminal.print(`Security: Unknown command "${cmd}" blocked (level ${this.level}).${sug ? ` Did you mean: "${sug.cmd}"?` : ''}`, 'error');
return;
}
- if (this.level >= 1) {
- if (sug) {
- this.terminal.print(`Security: Did you mean "${sug.cmd}"? Routing to AI...`, 'warning');
- } else {
- this.terminal.print(`Security: Routing to AI...`, 'info');
- }
+ // 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) {
+ 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) {
+ this.terminal.print(`Command not found: ${cmd}${sug ? `. Did you mean: ${sug.cmd}?` : ''}`, 'error');
+ return;
+ }
+ // Level 1 fallback: route to AI (terminal is also a chat interface)
+ if (sug) {
+ this.terminal.print(`Security: Unknown command "${cmd}" — routing to AI (did you mean "${sug.cmd}"?)...`, 'warning');
+ } else {
+ this.terminal.print(`Security: Unknown command "${cmd}" — routing to AI...`, 'info');
}
return this._tryAIRoute(rawInput);
}
@@ -3177,6 +3181,30 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
this.outputContainer.appendChild(line);
this.scrollToBottom();
}
+ showSuggestionBar(cmd, args) {
+ // One-tap typo fix: renders clickable "✅ Run <sug> <args> / ❌ Dismiss" bar
+ const area = document.getElementById('ai-confirm-area');
+ const runCmd = cmd + ((args && args.length) ? ' ' + args.join(' ') : '');
+ if (!area) {
+ // Fallback for hosts without the confirm UI: print clickable command
+ this.print(`Did you mean: ${runCmd} — type it to run.`, 'info');
+ return;
+ }
+ area.innerHTML = `
+ <span class="confirm-desc">💡 Did you mean: <code>${runCmd}</code>?</span>
+ <button class="confirm-btn" id="cmd-confirm-allow" onclick="(function(){var a=document.getElementById('cmd-confirm-allow');if(a.__allow) a.__allow();}).call(this)">✅ Run it</button>
+ <button class="confirm-btn deny" id="cmd-confirm-deny" onclick="(function(){var d=document.getElementById('cmd-confirm-deny');if(d.__deny) d.__deny();}).call(this)">❌ Dismiss</button>
+ `;
+ area.classList.add('visible');
+ const allowBtn = document.getElementById('cmd-confirm-allow');
+ const denyBtn = document.getElementById('cmd-confirm-deny');
+ const onAllow = () => { area.classList.remove('visible'); area.innerHTML = ''; this.executeCommand(runCmd); };
+ const onDeny = () => { area.classList.remove('visible'); area.innerHTML = ''; this.print('Dismissed.', 'text-muted'); };
+ allowBtn.addEventListener('click', onAllow);
+ denyBtn.addEventListener('click', onDeny);
+ allowBtn.__allow = onAllow;
+ denyBtn.__deny = onDeny;
+ }
showConfirmationBar(desc) {
const area = document.getElementById('ai-confirm-area');
if (!area) {
@@ -10221,7 +10249,10 @@ buildSystemPrompt() {
const originalPrint = this.terminal.print.bind(this.terminal);
let output = '';
this.terminal.print = (text, type) => { output += text + '\n'; };
- try { await this.terminal.security.checkAndRun(cmd); } finally { this.terminal.print = originalPrint; }
+ // 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; }
// 📸 Diff and append shadow report
const shadowDiff = this._diffShadowVFS();
if (shadowDiff && shadowDiff.length > 0) {