aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-aiassist.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-09-29 15:56:27 +0200
committerAgando <roduccomm@gmail.com>2026-09-29 15:56:27 +0200
commit7b9dd97689db400f2408ed50b04857d7d73d83e5 (patch)
treeb1325c677c908f908fec695aafa3a696fd9e69e8 /modules/core/module-aiassist.js
parent25adb2f8bb96ac1e534645083e02557875af8b67 (diff)
newOS4 v4.6.15: reentrant print-capture stack, AI tail -f block, read-only sed in safeRead, security test harness (21 checks)
Diffstat (limited to 'modules/core/module-aiassist.js')
-rw-r--r--modules/core/module-aiassist.js33
1 files changed, 16 insertions, 17 deletions
diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js
index 3d04df7..9c090ce 100644
--- a/modules/core/module-aiassist.js
+++ b/modules/core/module-aiassist.js
@@ -945,24 +945,22 @@ buildSystemPrompt() {
}
// ─── Self-Improvement Helpers ───
async _captureCommandOutput(cmd) {
- 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++;
+ this.terminal._pushPrintCapture(buf => { output += buf; });
try { await this.terminal.security.checkAndRun(cmd); }
- finally { this.terminal.security._aiToolDepth--; this.terminal.print = originalPrint; }
+ finally { this.terminal._popPrintCapture(); this.terminal.security._aiToolDepth--; }
return output.trim();
}
async _captureShellCommand(cmd) {
// Routes through executeCommand (shell parser) for >, |, &&, export, etc.
- 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++;
+ this.terminal._pushPrintCapture(buf => { output += buf; });
try { await this.terminal.executeCommand(cmd); }
- finally { this.terminal.security._aiToolDepth--; this.terminal.print = originalPrint; }
+ finally { this.terminal._popPrintCapture(); this.terminal.security._aiToolDepth--; }
return output.trim();
}
async _captureConsoleErrors(durationMs) {
@@ -2011,11 +2009,10 @@ buildSystemPrompt() {
this._snapshotShadowVFS();
const cmdName = cmd.split(' ')[0].toLowerCase();
const cmdArgs = cmd.split(' ').slice(1);
- const originalPrint = this.terminal.print.bind(this.terminal);
let output = '';
- this.terminal.print = (text, type) => { output += text + '\n'; };
+ this.terminal._pushPrintCapture(buf => { output += buf; });
this.terminal.dispatchCommand(cmdName, cmdArgs).then(() => {
- this.terminal.print = originalPrint;
+ this.terminal._popPrintCapture();
const diff = this._diffShadowVFS(false);
if (!diff || diff.length === 0) {
this.terminal.print('📦 Shadow: No filesystem changes detected.', 'success');
@@ -2032,7 +2029,7 @@ buildSystemPrompt() {
this.terminal.print('🔒 VFS modified — type "shadow-commit" to accept or "shadow-rollback" to undo.', 'warning');
}
}).catch(e => {
- this.terminal.print = originalPrint;
+ this.terminal._popPrintCapture();
this.terminal.print('⚠️ Shadow command error: ' + e.message, 'error');
});
}
@@ -2060,8 +2057,11 @@ buildSystemPrompt() {
if (name === 'run_command') {
const cmdStr = typeof args === 'object' ? (args?.command || '') : String(args);
const cmd = cmdStr.split(' ')[0].toLowerCase();
+ // sed: read-only UNLESS -i (in-place write) or it starts a live tail -f timer
+ if (cmd === 'sed' && /(^|\s)-i(\s|$)/.test(cmdStr)) return true;
+ if (cmd === 'tail' && /(^|\s)-f\b|(^|\s)--follow\b/.test(cmdStr)) return false; // tail -f self-downgrades for AI (module-fs guard)
const safeRead = new Set(['ls','cat','pwd','echo','date','env','whoami','which','hostname',
- 'cal','tree','find','grep','head','tail','sort','wc','du','diff','basename','dirname',
+ 'cal','tree','find','grep','head','tail','sed','sort','wc','du','diff','basename','dirname',
'yes','seq','cut','uniq','expand','printf','base64','help','clear','set','export',
'unalias','history','ll','h','mem','status','df','free','top','ps','health-check',
'system-health','modules','listmods','modules-list','get-help-info','alias',
@@ -2247,10 +2247,9 @@ buildSystemPrompt() {
if (!kw) return JSON.stringify({ error: 'No keyword' });
const mod = this.terminal.moduleManager.modules['WpmModule'];
if (mod) {
- const originalPrint = this.terminal.print.bind(this.terminal);
let output = '';
- this.terminal.print = (text, type) => { output += text + '\n'; };
- try { await mod.search(kw); } finally { this.terminal.print = originalPrint; }
+ this.terminal._pushPrintCapture(buf => { output += buf; });
+ try { await mod.search(kw); } finally { this.terminal._popPrintCapture(); }
return output.trim() || 'Search completed';
}
return JSON.stringify({ error: 'WPM module not available' });
@@ -2260,13 +2259,13 @@ buildSystemPrompt() {
if (!cmd) return JSON.stringify({ error: 'No command provided' });
// 📸 Shadow VFS auto-snapshot before run_command
this._snapshotShadowVFS();
- const originalPrint = this.terminal.print.bind(this.terminal);
let output = '';
- this.terminal.print = (text, type) => { output += text + '\n'; };
// 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; }
+ this.terminal._pushPrintCapture(buf => { output += buf; });
+ try { await this.terminal.security.checkAndRun(cmd); }
+ finally { this.terminal._popPrintCapture(); this.terminal.security._aiToolDepth--; }
// Parse per-call captured output (race-free) — not shared security state
const nfMatch = output.match(/Command not found: ([^\s.]+)(?:\. Did you mean: ([^\s.?]+)\?)?/);
if (nfMatch) {