diff options
Diffstat (limited to 'modules/core')
| -rw-r--r-- | modules/core/core-bundle.js | 167 | ||||
| -rw-r--r-- | modules/core/module-aiassist.js | 21 | ||||
| -rw-r--r-- | modules/core/terminal.js | 2 |
3 files changed, 176 insertions, 14 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index 175df87..13330ab 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -3473,6 +3473,7 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm this._safeRegister('PredictorModule', 'predictor'); this._safeRegister('WorldModelModule', 'world model'); this._safeRegister('ServerMonitorModule', 'server monitor'); + this._safeRegister('SmartContextModule', 'smart context'); this._safeRegister('SimpleGUIModule', 'simple GUI builder'); // v4.5.47: Critical ext modules promoted to boot path this._safeRegister('EditorModule', 'editor'); @@ -3520,6 +3521,7 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm FileManagerModule: typeof FileManagerModule !== 'undefined' ? FileManagerModule : undefined, DiffModule: typeof DiffModule !== 'undefined' ? DiffModule : undefined, BusyBoxModule: typeof BusyBoxModule !== 'undefined' ? BusyBoxModule : undefined, + SmartContextModule: typeof SmartContextModule !== 'undefined' ? SmartContextModule : undefined, }; const cls = registry[className]; if (typeof cls === 'undefined') { @@ -8197,6 +8199,7 @@ buildSystemPrompt() { async agenticLoop(messages) { let toolTurns = 0; const _seenCalls = new Map(); // v4.5.66: loop detection — track repeated tool calls + const _loopWindow = 30000; // 30s window for loop detection while (toolTurns < this.MAX_TOOL_TURNS) { if (this._cancelled) { this.terminal.print('AI loop cancelled', 'warning'); return { finalAnswer: null, toolTurns }; } toolTurns++; @@ -8223,16 +8226,22 @@ buildSystemPrompt() { return { finalAnswer: text, toolTurns }; } if (res.tool_calls && res.tool_calls.length > 0) { - // v4.5.66: Loop detection — break if AI repeats same tool+args 3+ times - // Reset per-turn so legitimate retries across turns don't false-positive - _seenCalls.clear(); + // v4.5.66: Loop detection — break if AI repeats same tool+args 6+ times in 30s + // Accumulates across turns (no clear) so real loops are caught, + // but time-windowed so legitimate retries across separate queries don't false-positive + const now = Date.now(); let looping = false; for (const tc of res.tool_calls) { const sig = tc.function.name + ':' + (tc.function.arguments || ''); - const count = (_seenCalls.get(sig) || 0) + 1; - _seenCalls.set(sig, count); - if (count >= 3) { - this.terminal.print(`🔄 Loop detected: ${tc.function.name} called ${count}x with same args — breaking loop`, 'warning'); + let entry = _seenCalls.get(sig); + if (!entry || (now - entry.ts) > _loopWindow) { + entry = { count: 0, ts: now }; + } + entry.count++; + entry.ts = now; + _seenCalls.set(sig, entry); + if (entry.count >= 6) { + this.terminal.print(`🔄 Loop detected: ${tc.function.name} called ${entry.count}x with same args in 30s — breaking loop`, 'warning'); looping = true; break; } @@ -16271,4 +16280,148 @@ if (typeof window !== 'undefined') { window.NeuralVFSModule = NeuralVFSModule; } +/* === module-smartctx.js === */ +// Extracted from newOS4 index.html — SmartContextModule + class SmartContextModule { + constructor() { + this.commands = {}; this.terminal = null; + this.contextProfiles = this.loadProfiles(); this.activeProfile = this.loadActiveProfile(); + } + getHelpInfo() { + return { + 'context': 'context [name] — Set or view current context profile', + 'contexts': 'contexts — List all available context profiles', + 'context-new': 'context-new <name> <displayName> <systemPrompt> — Create a new context profile', + 'context-delete': 'context-delete <name> — Delete a context profile', + 'smart-ask': 'smart-ask <question> — Ask a question using the active context' + }; + } + loadProfiles() { + try { + const saved = localStorage.getItem('webx11_context_profiles'); + return saved ? JSON.parse(saved) : this.getDefaultProfiles(); + } catch (e) { return this.getDefaultProfiles(); } + } + saveProfiles() { + localStorage.setItem('webx11_context_profiles', JSON.stringify(this.contextProfiles)); + } + loadActiveProfile() { + try { + const saved = localStorage.getItem('webx11_active_context_profile'); + if (saved && this.contextProfiles[saved]) return saved; + } catch (e) {} + return null; + } + saveActiveProfile() { + try { + if (this.activeProfile) { + localStorage.setItem('webx11_active_context_profile', this.activeProfile); + } else { + localStorage.removeItem('webx11_active_context_profile'); + } + } catch (e) {} + } + getDefaultProfiles() { + return { + 'coding': { name: 'Coding Assistant', systemPrompt: "You are an expert programmer.", temperature: 0.7 }, + 'debug': { name: 'Debug Helper', systemPrompt: "You are a debugging expert.", temperature: 0.5 }, + 'creative': { name: 'Creative Writer', systemPrompt: "You are a creative writer.", temperature: 0.9 }, + 'technical': { name: 'Technical Docs', systemPrompt: "You are a technical writer.", temperature: 0.3 }, + 'chat': { name: 'Casual Chat', systemPrompt: "You are a friendly assistant.", temperature: 0.8 } + }; + } + registerCommands() { + this.commands['context'] = (args) => this.setContext(args); + this.commands['contexts'] = () => this.listContexts(); + this.commands['context-new'] = (args) => this.createContext(args); + this.commands['context-delete'] = (args) => this.deleteContext(args); + this.commands['smart-ask'] = (args) => this.smartAsk(args); + } + setContext(args) { + if (!args[0]) { + this.terminal.print(`Current context: ${this.activeProfile || 'none'}`, "info"); return; + } + if (this.contextProfiles[args[0]]) { + this.activeProfile = args[0]; + this.saveActiveProfile(); + this.terminal.print(`Context set to: ${this.contextProfiles[args[0]].name}`, "success"); + } else { + this.terminal.print(`Profile '${args[0]}' not found`, "error"); + } + } + listContexts() { + this.terminal.print("=== Context Profiles ===", "info"); + Object.entries(this.contextProfiles).forEach(([key, profile]) => { + const active = key === this.activeProfile ? ' ✅' : ''; + this.terminal.print(`${key}: ${profile.name}${active}`); + }); + } + createContext(args) { + if (args.length < 3) { + this.terminal.print("Usage: context-new <name> <displayName> <systemPrompt>", "warning"); return; + } + const name = args[0], displayName = args[1], systemPrompt = args.slice(2).join(' '); + this.contextProfiles[name] = { name: displayName, systemPrompt, temperature: 0.7 }; + this.saveProfiles(); + this.terminal.print(`Context profile '${name}' created`, "success"); + } + deleteContext(args) { + if (!args[0]) { + this.terminal.print("Usage: context-delete <name>", "warning"); return; + } + if (this.contextProfiles[args[0]]) { + delete this.contextProfiles[args[0]]; + this.saveProfiles(); + if (this.activeProfile === args[0]) { this.activeProfile = null; } + this.terminal.print(`Context profile '${args[0]}' deleted`, "success"); + } else { + this.terminal.print(`Profile '${args[0]}' not found`, "error"); + } + } + async smartAsk(args) { + if (!args[0]) { + this.terminal.print("Usage: smart-ask <your-question>", "warning"); return; + } + let systemPrompt = "You are a helpful assistant."; + let temperature = 0.7; + if (this.activeProfile && this.contextProfiles[this.activeProfile]) { + const profile = this.contextProfiles[this.activeProfile]; + systemPrompt = profile.systemPrompt; + temperature = profile.temperature || 0.7; + this.terminal.print(`Using context: ${profile.name} (temp: ${temperature})`, "info"); + } + // Use agentic loop via the AI assistant module for tool-enabled responses + const aiModule = this.terminal?.moduleManager?.modules?.WebX11AIAssistantModule; + if (aiModule) { + // Set temperature on ollamaManager modelParams temporarily + const om = this.terminal.ollamaManager; + const savedTemp = om.modelParams ? om.modelParams.temperature : undefined; + if (om.modelParams) { + om.modelParams.temperature = temperature; + } + try { + const messages = [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: args.join(' ') } + ]; + const result = await om.chatWithTools(messages, aiModule.getToolDefinitions()); + if (result && result.message) { + this.terminal.printAIResponse(result.message.content || '(no response)'); + } else if (result && result.error) { + this.terminal.print(`⚠️ AI error: ${result.error}`, 'error'); + } + } finally { + if (om.modelParams && savedTemp !== undefined) { + om.modelParams.temperature = savedTemp; + } + } + } else { + // Fallback: simple chat with temperature + const om = this.terminal.ollamaManager; + const fullPrompt = `${systemPrompt}\n\nUser: ${args.join(' ')}`; + await om.chat(fullPrompt); + } + } + } + /* === end core module bundle === */ diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js index 9c9b1ca..caf6d99 100644 --- a/modules/core/module-aiassist.js +++ b/modules/core/module-aiassist.js @@ -765,6 +765,7 @@ buildSystemPrompt() { async agenticLoop(messages) { let toolTurns = 0; const _seenCalls = new Map(); // v4.5.66: loop detection — track repeated tool calls + const _loopWindow = 30000; // 30s window for loop detection while (toolTurns < this.MAX_TOOL_TURNS) { if (this._cancelled) { this.terminal.print('AI loop cancelled', 'warning'); return { finalAnswer: null, toolTurns }; } toolTurns++; @@ -791,16 +792,22 @@ buildSystemPrompt() { return { finalAnswer: text, toolTurns }; } if (res.tool_calls && res.tool_calls.length > 0) { - // v4.5.66: Loop detection — break if AI repeats same tool+args 3+ times - // Reset per-turn so legitimate retries across turns don't false-positive - _seenCalls.clear(); + // v4.5.66: Loop detection — break if AI repeats same tool+args 6+ times in 30s + // Accumulates across turns (no clear) so real loops are caught, + // but time-windowed so legitimate retries across separate queries don't false-positive + const now = Date.now(); let looping = false; for (const tc of res.tool_calls) { const sig = tc.function.name + ':' + (tc.function.arguments || ''); - const count = (_seenCalls.get(sig) || 0) + 1; - _seenCalls.set(sig, count); - if (count >= 3) { - this.terminal.print(`🔄 Loop detected: ${tc.function.name} called ${count}x with same args — breaking loop`, 'warning'); + let entry = _seenCalls.get(sig); + if (!entry || (now - entry.ts) > _loopWindow) { + entry = { count: 0, ts: now }; + } + entry.count++; + entry.ts = now; + _seenCalls.set(sig, entry); + if (entry.count >= 6) { + this.terminal.print(`🔄 Loop detected: ${tc.function.name} called ${entry.count}x with same args in 30s — breaking loop`, 'warning'); looping = true; break; } diff --git a/modules/core/terminal.js b/modules/core/terminal.js index b5f50a6..2b41fd3 100644 --- a/modules/core/terminal.js +++ b/modules/core/terminal.js @@ -1193,6 +1193,7 @@ this._safeRegister('PredictorModule', 'predictor'); this._safeRegister('WorldModelModule', 'world model'); this._safeRegister('ServerMonitorModule', 'server monitor'); + this._safeRegister('SmartContextModule', 'smart context'); this._safeRegister('SimpleGUIModule', 'simple GUI builder'); // v4.5.47: Critical ext modules promoted to boot path this._safeRegister('EditorModule', 'editor'); @@ -1240,6 +1241,7 @@ FileManagerModule: typeof FileManagerModule !== 'undefined' ? FileManagerModule : undefined, DiffModule: typeof DiffModule !== 'undefined' ? DiffModule : undefined, BusyBoxModule: typeof BusyBoxModule !== 'undefined' ? BusyBoxModule : undefined, + SmartContextModule: typeof SmartContextModule !== 'undefined' ? SmartContextModule : undefined, }; const cls = registry[className]; if (typeof cls === 'undefined') { |