aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-aiassist.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/core/module-aiassist.js')
-rw-r--r--modules/core/module-aiassist.js78
1 files changed, 77 insertions, 1 deletions
diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js
index f77ec61..f7be209 100644
--- a/modules/core/module-aiassist.js
+++ b/modules/core/module-aiassist.js
@@ -16,7 +16,10 @@
this._sessionSaved = false;
this._thinkingTimer = null;
this._thinkingStartTime = 0;
+ this._validatorSuppressed = false;
this._toolDefs = null;
+ this._toolCallLog = [];
+ this._debugPanelEnabled = false;
try { const s = localStorage.getItem('webx11_ai_patterns'); if (s) this._commandFreq = JSON.parse(s); } catch (e) {}
try { const p = localStorage.getItem('webx11_user_profile'); if (p) this._userProfile = JSON.parse(p); } catch (e) {}
// Restore AI conversation context from localStorage
@@ -392,6 +395,7 @@ RULES:
'ai-context': 'ai-context — Show current conversation context',
'ai-allow': 'ai-allow — Confirm pending AI action',
'ai-deny': 'ai-deny — Deny pending AI action',
+ 'ai-ignore': 'ai-ignore — Suppress validator warnings for this session',
'ai-stop': 'ai-stop — Cancel current AI operation',
'ai-model': 'ai-model [model] — Show or set AI model'
};
@@ -404,9 +408,11 @@ RULES:
this.commands['ai-context'] = () => this.showContext();
this.commands['ai-allow'] = () => this.confirmPending();
this.commands['ai-deny'] = () => this.denyPending();
+ this.commands['ai-ignore'] = () => this.ignoreValidator();
this.commands['ai-stop'] = () => this.cancelAi();
this.commands['ai-model'] = (args) => this.showModel(args);
this.commands['ai-compress'] = () => this._manualCompress();
+ this.commands['debug'] = () => this.toggleDebug();
}
async chat(args) {
const userMessage = args.join(' ');
@@ -542,6 +548,28 @@ RULES:
}
return result.substring(0, maxLen) + '...(truncated from ' + result.length + ' chars)';
}
+ _escapeHtml(str) {
+ return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
+ }
+ _updateDebugPanel() {
+ if (!this._debugPanelEnabled) return;
+ const panel = document.getElementById('debug-panel');
+ if (!panel) return;
+ const recent = this._toolCallLog.slice(-20);
+ let html = '';
+ for (const entry of recent) {
+ const statusClass = entry.error ? 'debug-entry-error' : 'debug-entry-ok';
+ const argsStr = typeof entry.args === 'string' ? entry.args.substring(0, 60) : JSON.stringify(entry.args).substring(0, 60);
+ html += '<div class="debug-entry ' + statusClass + '">' +
+ '<span class="debug-turn">#' + entry.turn + '</span> ' +
+ '<span class="debug-name">' + this._escapeHtml(entry.name) + '</span> ' +
+ '<span class="debug-args">' + this._escapeHtml(argsStr) + '</span> ' +
+ '<span class="debug-time">' + entry.duration + 's</span>' +
+ (entry.error ? ' <span class="debug-err">⚠️</span>' : '') +
+ '</div>';
+ }
+ panel.innerHTML = '<div class="debug-header">🔧 Tool Log <span class="debug-count">' + this._toolCallLog.length + ' calls</span></div>' + html;
+ }
_isToolError(result) {
if (!result || typeof result !== 'string') return null;
const trimmed = result.trim();
@@ -618,6 +646,15 @@ RULES:
const t0 = Date.now();
const toolResult = await this.runTool(fn.name, fn.arguments);
const t1 = Math.round((Date.now() - t0) / 1000);
+ this._toolCallLog.push({
+ turn: toolTurns,
+ name: fn.name,
+ args: fn.arguments,
+ result: toolResult,
+ duration: t1,
+ error: this._isToolError(toolResult) ? true : false
+ });
+ if (this._debugPanelEnabled) this._updateDebugPanel();
return { fn, toolResult, t1 };
}));
for (const { fn, toolResult, t1 } of safeResults) {
@@ -628,6 +665,10 @@ RULES:
this.terminal.log('WARN', 'AI', `Tool ${fn.name} failed: ${err} (${t1}s)`);
this._trackToolFailure(fn.name, err);
this.terminal.print(`📊 Result (${t1}s): ⚠️ Error: ${err}`, 'warning');
+ } else if (toolResult && toolResult.length > 150) {
+ this.terminal.log('INFO', 'AI', `Tool ${fn.name} ok (${t1}s)`);
+ const safeId = 'toolresult-' + Date.now() + '-' + Math.random().toString(36).slice(2, 6);
+ this.terminal.printHTML(`📊 Result (${t1}s): <span id="${safeId}-short">${this._escapeHtml(obs)} <a href="#" onclick="var e=document.getElementById('${safeId}-full');var s=document.getElementById('${safeId}-short');if(e){e.style.display='inline';s.style.display='none'}return false" style="color:var(--info-color);cursor:pointer">[+] show full (${toolResult.length} chars)</a></span><span id="${safeId}-full" style="display:none">${this._escapeHtml(toolResult)} <a href="#" onclick="var e=document.getElementById('${safeId}-full');var s=document.getElementById('${safeId}-short');if(e&&s){e.style.display='none';s.style.display='inline'}return false" style="color:var(--warning-color);cursor:pointer">[-] collapse</a></span>`);
} else {
this.terminal.log('INFO', 'AI', `Tool ${fn.name} ok (${t1}s)`);
this.terminal.print(`📊 Result (${t1}s): ${obs}`, 'info');
@@ -644,6 +685,15 @@ RULES:
this.terminal.print(`🔧 Tool: ${fn.name}(${typeof fn.arguments === 'string' ? fn.arguments : JSON.stringify(fn.arguments)})`, 'info');
const toolResult = await this.runTool(fn.name, fn.arguments);
const t1 = Math.round((Date.now() - t0) / 1000);
+ this._toolCallLog.push({
+ turn: toolTurns,
+ name: fn.name,
+ args: fn.arguments,
+ result: toolResult,
+ duration: t1,
+ error: this._isToolError(toolResult) ? true : false
+ });
+ if (this._debugPanelEnabled) this._updateDebugPanel();
const dObs = this._summarizeObservation(toolResult, 150);
const dErr = this._isToolError(toolResult);
if (dErr) {
@@ -664,7 +714,7 @@ RULES:
}
}
// Validator: check safe tool results for consistency
- if (safeResults.length > 0 && pendingText) {
+ if (safeResults.length > 0 && pendingText && !this._validatorSuppressed) {
const om = this.terminal.ollamaManager;
if (om && om.validatorEnabled && om.validatorModel) {
let invalidTools = [];
@@ -856,6 +906,10 @@ RULES:
denyPending() {
this._resolveConfirm(false);
}
+ ignoreValidator() {
+ this._validatorSuppressed = true;
+ this.terminal.print('🔍 Validator suppressed for this session', 'info');
+ }
cancelAi() {
this._cancelled = true;
this.terminal.print('⏹ Cancelling AI...', 'warning');
@@ -870,6 +924,28 @@ RULES:
// Immediately reset UI so user knows it stopped
this.terminal.setAiBusy(false);
}
+ toggleDebug() {
+ this._debugPanelEnabled = !this._debugPanelEnabled;
+ let panel = document.getElementById('debug-panel');
+ if (!panel) {
+ // Create the debug panel
+ panel = document.createElement('div');
+ panel.id = 'debug-panel';
+ panel.style.cssText = 'position:fixed;right:10px;bottom:50px;width:380px;max-height:400px;overflow-y:auto;background:#0a0a0a;border:1px solid #333;border-radius:6px;z-index:9999;font-family:monospace;font-size:0.78em;display:none;';
+ document.body.appendChild(panel);
+ // Add close button handler
+ panel.addEventListener('click', function() {
+ // Toggle collapsed/expanded state
+ });
+ }
+ panel.style.display = this._debugPanelEnabled ? 'block' : 'none';
+ if (this._debugPanelEnabled) {
+ this._updateDebugPanel();
+ this.terminal.print('🔧 Debug panel enabled — shows tool call log in bottom-right corner. Close with: debug', 'info');
+ } else {
+ this.terminal.print('🔧 Debug panel disabled', 'info');
+ }
+ }
isDestructive(name) {
return name === 'write_file' || name === 'delete_file' || name === 'run_command' || name === 'make_directory' || name === 'edit_module';
}