kiosapi 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/run.js +35 -24
- package/package.json +1 -1
package/dist/agent/run.js
CHANGED
|
@@ -368,10 +368,13 @@ export async function runTurn(s, userText) {
|
|
|
368
368
|
console.log(dim(` ↳ ${idn(totalIn + totalOut)} token (${idn(totalIn)} in · ${idn(totalOut)} out)`));
|
|
369
369
|
}
|
|
370
370
|
};
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
|
|
374
|
-
const
|
|
371
|
+
// Loop detection: count total calls per signature (tool+args) across the whole runTurn.
|
|
372
|
+
// Consecutive-3 catches obvious tight loops; count-4 catches spread-out repetition where
|
|
373
|
+
// other calls (daftar src, baca ...) appear between the repeating call.
|
|
374
|
+
const callCounts = new Map();
|
|
375
|
+
const lastSigs = []; // last 3 sigs for consecutive detection
|
|
376
|
+
const COUNT_LIMIT = 4; // same tool+args called 4× total → loop
|
|
377
|
+
const CONSEC_LIMIT = 3; // same sig 3× in a row → loop
|
|
375
378
|
const stepLimit = s.maxSteps ?? MAX_STEPS;
|
|
376
379
|
for (let step = 0; step < stepLimit; step++) {
|
|
377
380
|
const stop = thinking();
|
|
@@ -410,29 +413,37 @@ export async function runTurn(s, userText) {
|
|
|
410
413
|
s.totalTokens += totalIn + totalOut;
|
|
411
414
|
return lastText;
|
|
412
415
|
}
|
|
413
|
-
// --- Loop detection
|
|
416
|
+
// --- Loop detection before running any tools this step ---
|
|
417
|
+
let loopSig = null;
|
|
414
418
|
for (const call of calls) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
+
const sig = `${call.function.name}:${call.function.arguments}`;
|
|
420
|
+
const count = (callCounts.get(sig) ?? 0) + 1;
|
|
421
|
+
callCounts.set(sig, count);
|
|
422
|
+
lastSigs.push(sig);
|
|
423
|
+
if (lastSigs.length > CONSEC_LIMIT)
|
|
424
|
+
lastSigs.shift();
|
|
425
|
+
// Total count exceeded, or 3 in a row
|
|
426
|
+
const consecutive = lastSigs.length === CONSEC_LIMIT && lastSigs.every((s) => s === sig);
|
|
427
|
+
if (count >= COUNT_LIMIT || consecutive) {
|
|
428
|
+
loopSig = sig;
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
419
431
|
}
|
|
420
|
-
if (
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
saveCheckpoint(s);
|
|
432
|
-
showUsage();
|
|
433
|
-
s.totalTokens += totalIn + totalOut;
|
|
434
|
-
return lastText;
|
|
432
|
+
if (loopSig !== null) {
|
|
433
|
+
const loopName = loopSig.split(':')[0] ?? 'unknown';
|
|
434
|
+
const total = callCounts.get(loopSig) ?? 0;
|
|
435
|
+
console.log(red(`\n⚠ Loop terdeteksi: "${loopName}" dipanggil ${total}× — agen terjebak.`));
|
|
436
|
+
console.log(yellow('Berikan instruksi baru, atau ketik /bersih untuk mulai ulang.'));
|
|
437
|
+
const loopMsg = `⚠ LOOP: "${loopName}" sudah dipanggil ${total}× dengan argumen yang sama. ` +
|
|
438
|
+
`JANGAN panggil lagi. Gunakan tool "selesai" dan jelaskan kendalanya, ` +
|
|
439
|
+
`atau minta klarifikasi dari pengguna.`;
|
|
440
|
+
for (const call of calls) {
|
|
441
|
+
s.messages.push({ role: 'tool', content: loopMsg, tool_call_id: call.id });
|
|
435
442
|
}
|
|
443
|
+
saveCheckpoint(s);
|
|
444
|
+
showUsage();
|
|
445
|
+
s.totalTokens += totalIn + totalOut;
|
|
446
|
+
return lastText;
|
|
436
447
|
}
|
|
437
448
|
const stepModified = new Set();
|
|
438
449
|
for (const call of calls) {
|