diff options
Diffstat (limited to 'modules/core/module-aiassist.js')
| -rw-r--r-- | modules/core/module-aiassist.js | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js index f9e1e98..e983e6a 100644 --- a/modules/core/module-aiassist.js +++ b/modules/core/module-aiassist.js @@ -538,6 +538,22 @@ RULES: } return null; } + _trackToolFailure(toolName, error) { + try { + const key = 'webx11_tool_failures'; + const raw = localStorage.getItem(key) || '{}'; + const map = JSON.parse(raw); + if (!map[toolName]) map[toolName] = { count: 0, last: '', errors: [] }; + map[toolName].count++; + map[toolName].last = new Date().toISOString(); + map[toolName].errors.push(error); + if (map[toolName].errors.length > 5) map[toolName].errors.shift(); + localStorage.setItem(key, JSON.stringify(map)); + if (map[toolName].count >= 3) { + this.terminal.print(`⚠️ Tool '${toolName}' has failed ${map[toolName].count} times. Consider creating a workaround skill.`, 'warning'); + } + } catch (e) { /* ignore */ } + } async agenticLoop(messages) { let toolTurns = 0; while (toolTurns < this.MAX_TOOL_TURNS) { @@ -579,8 +595,11 @@ RULES: const obs = this._summarizeObservation(toolResult, 150); const err = this._isToolError(toolResult); if (err) { + 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 { + this.terminal.log('INFO', 'AI', `Tool ${fn.name} ok (${t1}s)`); this.terminal.print(`📊 Result (${t1}s): ${obs}`, 'info'); } messages.push({ role: 'tool', content: toolResult }); @@ -598,8 +617,11 @@ RULES: const dObs = this._summarizeObservation(toolResult, 150); const dErr = this._isToolError(toolResult); if (dErr) { + this.terminal.log('WARN', 'AI', `Tool ${fn.name} failed: ${dErr} (${t1}s)`); + this._trackToolFailure(fn.name, dErr); this.terminal.print(`📊 Result (${t1}s): ⚠️ Error: ${dErr}`, 'warning'); } else { + this.terminal.log('INFO', 'AI', `Tool ${fn.name} ok (${t1}s)`); this.terminal.print(`📊 Result (${t1}s): ${dObs}`, 'info'); } messages.push({ role: 'tool', content: toolResult }); @@ -803,8 +825,9 @@ RULES: return name === 'write_file' || name === 'delete_file' || name === 'run_command' || name === 'make_directory' || name === 'edit_module'; } async runTool(name, argsStr) { + let a = {}; try { - const a = typeof argsStr === 'object' ? argsStr : (argsStr ? JSON.parse(argsStr) : {}); + a = typeof argsStr === 'object' ? argsStr : (argsStr ? JSON.parse(argsStr) : {}); switch (name) { case 'list_files': { const originalPath = a.path || ''; @@ -1058,7 +1081,7 @@ RULES: for (const [name, meta] of Object.entries(index)) { const score = (name.includes(query) ? 3 : 0) + (meta.description.toLowerCase().includes(query) ? 2 : 0) + ((meta.tags || []).some(t => t.toLowerCase().includes(query)) ? 2 : 0); if (score > 0) { - const content = fs.fsRead(`/skills/${name}.md`) || ''; + const content = fs.readFile(`/skills/${name}.md`) || ''; matches.push({ name, description: meta.description, category: meta.category, score, content: content.substring(0, 2000) }); } } @@ -1078,7 +1101,7 @@ RULES: } const fs = this.terminal.fs; fs.mkdirRecursive('/skills'); - fs.fsWrite(`/skills/${name}.md`, content); + fs.writeFile(`/skills/${name}.md`, content); const index = fs.fsReadJSON('/skills/index.json') || {}; index[name] = { name, description, tags, category, created: index[name]?.created || new Date().toISOString(), updated: new Date().toISOString() }; fs.fsWriteJSON('/skills/index.json', index); @@ -1093,7 +1116,7 @@ RULES: const fs = this.terminal.fs; const index = fs.fsReadJSON('/skills/index.json') || {}; if (!index[pname]) return JSON.stringify({ error: `Skill "${pname}" not found` }); - fs.fsWrite(`/skills/${pname}.md`, pcontent); + fs.writeFile(`/skills/${pname}.md`, pcontent); index[pname].updated = new Date().toISOString(); // Re-parse frontmatter for metadata updates const metaMatch = pcontent.match(/^---\n([\s\S]*?)\n---/); @@ -1134,8 +1157,10 @@ RULES: } } async runModuleTool(name, argsStr) { - const a = typeof argsStr === 'object' ? argsStr : (argsStr ? JSON.parse(argsStr) : {}); - switch (name) { + let a = {}; + try { + a = typeof argsStr === 'object' ? argsStr : (argsStr ? JSON.parse(argsStr) : {}); + switch (name) { case 'view_module': { const modName = a.name || ''; const entry = this.terminal.moduleManager.moduleRegistry.find(m => m.name === modName); @@ -1211,6 +1236,10 @@ RULES: return JSON.stringify({ modules, total: modules.length, hasLocalEdits: hasMods }); } default: return JSON.stringify({ error: `Unknown module tool: ${name}` }); + } + } catch (e) { + this._errorLog.push({ tool: name, args: a, error: e.message, timestamp: Date.now() }); + return JSON.stringify({ error: e.message, stack: e.stack?.substring(0, 200) }); } } clearContext() { @@ -1358,11 +1387,11 @@ category: productivity - User says "map out", "diagram", "brainstorm", "organize thoughts" - Visualizing project structure or task decomposition`; - fs.fsWrite('/skills/mindmap-user-guide.md', mindmapSkill); + fs.writeFile('/skills/mindmap-user-guide.md', mindmapSkill); const existingIndex = fs.fsReadJSON('/skills/index.json') || {}; existingIndex['mindmap-user-guide'] = { name: 'mindmap-user-guide', description: 'How to create, view, and manage brainstorming mindmaps using the mindmap terminal module', tags: ['mindmap', 'brainstorming', 'organization', 'planning'], category: 'productivity', created: existingIndex['mindmap-user-guide']?.created || new Date().toISOString(), updated: new Date().toISOString() }; fs.fsWriteJSON('/skills/index.json', existingIndex); - fs.fsWrite('/skills/_seeded', '1'); + fs.writeFile('/skills/_seeded', '1'); } _trackPattern(userMessage) { const lower = userMessage.toLowerCase(); @@ -1381,7 +1410,7 @@ category: productivity for (const [tool, errors] of Object.entries(grouped)) { if (errors.length >= 3) { const skillName = 'tool-' + tool + '-workaround'; - const existing = this.terminal.fs.fsRead('/skills/' + skillName + '.md'); + const existing = this.terminal.fs.readFile('/skills/' + skillName + '.md'); if (existing) continue; const steps = errors.map((e, i) => (i + 1) + '. When calling ' + tool + ', avoid: "' + JSON.stringify(e.args) + '" which caused: ' + e.error).join('\n'); const content = '---\nname: ' + skillName + '\ndescription: Workarounds for ' + tool + ' failures\ntags: [' + tool + ', error, workaround]\ncategory: system\n---\n## Steps\n' + steps; @@ -1397,7 +1426,7 @@ category: productivity const now = Date.now(); const issues = []; for (const [name, meta] of Object.entries(index)) { - const content = fs.fsRead('/skills/' + name + '.md'); + const content = fs.readFile('/skills/' + name + '.md'); if (!content || content.length < 50) { issues.push({ name, issue: 'empty or too short' }); continue; |