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.
Files changed (2) hide show
  1. package/dist/agent/run.js +35 -24
  2. 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
- // Sliding window of recent tool-call signatures (name + serialised args) across all steps.
372
- // Used to detect the model repeating the same call in a stuck loop (e.g. daftar . × 20).
373
- const recentSigs = [];
374
- const LOOP_THRESHOLD = 3;
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: push new signatures, then check if last N are identical ---
416
+ // --- Loop detection before running any tools this step ---
417
+ let loopSig = null;
414
418
  for (const call of calls) {
415
- recentSigs.push(`${call.function.name}:${call.function.arguments}`);
416
- }
417
- if (recentSigs.length > LOOP_THRESHOLD * 3) {
418
- recentSigs.splice(0, recentSigs.length - LOOP_THRESHOLD * 3);
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 (recentSigs.length >= LOOP_THRESHOLD) {
421
- const tail = recentSigs.slice(-LOOP_THRESHOLD);
422
- if (tail.every((sig) => sig === tail[0])) {
423
- const loopName = calls[0]?.function.name ?? 'unknown';
424
- console.log(red(`\n⚠ Loop terdeteksi: "${loopName}" dipanggil ${LOOP_THRESHOLD}× berturut-turut dengan argumen sama.`));
425
- console.log(yellow('Berikan instruksi baru, atau ketik /bersih untuk mulai ulang.'));
426
- // Push error tool results for all calls in this step so history stays balanced.
427
- const loopMsg = `⚠ LOOP TERDETEKSI: "${loopName}" sudah dipanggil ${LOOP_THRESHOLD}× dengan argumen yang sama. BERHENTI memanggil tool ini. Gunakan tool "selesai" dengan penjelasan bahwa tugas tidak dapat diselesaikan, atau tunggu instruksi baru dari pengguna.`;
428
- for (const call of calls) {
429
- s.messages.push({ role: 'tool', content: loopMsg, tool_call_id: call.id });
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiosapi",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
6
  "keywords": [