diff options
Diffstat (limited to 'modules/core/module-aiassist.js')
| -rw-r--r-- | modules/core/module-aiassist.js | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js index 8d1fc9d..f69494e 100644 --- a/modules/core/module-aiassist.js +++ b/modules/core/module-aiassist.js @@ -122,7 +122,7 @@ buildSystemPrompt() { } catch (e) {} base += '\n\n[File reads, tools, verification]\n' + '- read_file returns 8000 chars by default (limit up to 16000). If the reply says truncated=true, call read_file again with offset=<next_offset> before concluding anything about the file.\n' + - '- Prefer targeted reads over whole files: run_command with grep -n "<pattern>" <file>, sed -n \'10,40p\' <file>, head -40 <file>, tail -40 <file>, wc -l <file>.\n' + + '- Prefer targeted reads over whole files: run_command with grep "<pattern>" <file> (prints line: text), sed -n \'10,40p\' <file>, head -40 <file>, tail -n 40 <file>, wc -l <file>. Flags are tolerated, but plain `cat <file>` and read_file offset/limit always work.\n' + '- sed without -i is read-only and allowed; sed -i, rm, mv and writes need user confirmation.\n' + '- After ANY write or edit: re-read the changed lines (cat or sed -n) and run a check that exists in THIS terminal — `test` for the suite, `grep -n` to confirm the new line, `wc -l` for size. There is no shell, no node and no curl here.\n' + '- If a tool fails twice, change approach (different tool, smaller scope) instead of retrying the same call.'; @@ -1540,7 +1540,14 @@ buildSystemPrompt() { const now = Date.now(); const blocked = []; for (const tc of res.tool_calls) { - const sig = tc.function.name + ':' + (tc.function.arguments || ''); + // 2026-10-21: arguments can be an object (already parsed upstream). + // `+ object` produced "name:[object Object]", collapsing every call to + // the same tool into one key → false "called 3x" loop warnings when the + // model legitimately batched distinct commands. + const _argKey = (typeof tc.function.arguments === 'string') + ? tc.function.arguments + : JSON.stringify(tc.function.arguments || {}); + const sig = tc.function.name + ':' + _argKey; let entry = _seenCalls.get(sig); if (!entry || (now - entry.ts) > _loopWindow) { entry = { count: 0, ts: now, resultHash: null, blocked: false, lastResult: null, epoch: this._toolEpoch }; @@ -1555,7 +1562,7 @@ buildSystemPrompt() { entry.ts = now; _seenCalls.set(sig, entry); - const argPreview = String(tc.function.arguments || '') + const argPreview = String(_argKey || '') .replace(/(token|key|secret|password|bearer)[^,}]*/ig, '$1:[redacted]') .slice(0, 60); if (entry.blocked || entry.count >= _blockAt) { @@ -1668,7 +1675,7 @@ buildSystemPrompt() { this.terminal.print(`📊 Result (${t1}s): ${obs}`, 'info'); } try { - const _sigR = fn.name + ':' + (fn.arguments || ''); + const _sigR = fn.name + ':' + ((typeof fn.arguments === 'string') ? fn.arguments : JSON.stringify(fn.arguments || {})); const _eR = _seenCalls.get(_sigR); if (_eR) { const _rh = _sigHash(toolResult); @@ -1743,7 +1750,7 @@ buildSystemPrompt() { this.terminal.print(`📊 Result (${t1}s): ${dObs}`, 'info'); } try { - const _sigD = fn.name + ':' + (fn.arguments || ''); + const _sigD = fn.name + ':' + ((typeof fn.arguments === 'string') ? fn.arguments : JSON.stringify(fn.arguments || {})); const _eD = _seenCalls.get(_sigD); if (_eD) { const _rhD = _sigHash(toolResult); |