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.js17
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);