kiosapi 0.1.27 → 0.1.28

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 +32 -21
  2. package/package.json +1 -1
package/dist/agent/run.js CHANGED
@@ -56,32 +56,36 @@ function trimContext(messages) {
56
56
  const dropped = clean.length - tail.length;
57
57
  if (dropped <= 0)
58
58
  return [system, ...clean];
59
- // Collect baca_file paths from the dropped messages so the model knows which files it
60
- // already has context for — prevents it from re-orienting by re-reading config/schema files.
59
+ // Collect baca_file and daftar_file calls from the dropped messages so the model knows which
60
+ // files/directories it already has context for — prevents re-orientation loops after trimming.
61
61
  const droppedHead = clean.slice(0, clean.length - tail.length + skip);
62
62
  const droppedPaths = [];
63
+ const droppedDirs = [];
63
64
  for (const msg of droppedHead) {
64
65
  if (msg.role === 'assistant' && msg.tool_calls) {
65
66
  for (const tc of msg.tool_calls) {
66
- if (tc.function?.name === 'baca_file') {
67
- try {
68
- const a = JSON.parse(tc.function.arguments ?? '{}');
69
- if (a.path)
70
- droppedPaths.push(a.path);
71
- }
72
- catch {
73
- /* ignore */
74
- }
67
+ try {
68
+ const a = JSON.parse(tc.function.arguments ?? '{}');
69
+ if (tc.function?.name === 'baca_file' && a.path)
70
+ droppedPaths.push(a.path);
71
+ if (tc.function?.name === 'daftar_file')
72
+ droppedDirs.push(a.path || '.');
73
+ }
74
+ catch {
75
+ /* ignore */
75
76
  }
76
77
  }
77
78
  }
78
79
  }
79
80
  const filesNote = droppedPaths.length > 0
80
- ? ` File sudah dibaca (tak lagi di window): ${[...new Set(droppedPaths)].join(', ')}. JANGAN baca ulang — gunakan cari untuk mencari teks spesifik.`
81
+ ? ` File sudah dibaca: ${[...new Set(droppedPaths)].join(', ')}. JANGAN baca ulang — gunakan cari untuk teks spesifik.`
82
+ : '';
83
+ const dirsNote = droppedDirs.length > 0
84
+ ? ` Direktori sudah terdaftar: ${[...new Set(droppedDirs)].join(', ')}. JANGAN list ulang — gunakan baca_file untuk file spesifik.`
81
85
  : '';
82
86
  const note = {
83
87
  role: 'system',
84
- content: `[Kiosapi: ${dropped} pesan awal dihapus dari konteks.${filesNote}]`,
88
+ content: `[Kiosapi: ${dropped} pesan awal dihapus dari konteks.${filesNote}${dirsNote}]`,
85
89
  };
86
90
  return [system, note, ...tail];
87
91
  }
@@ -482,12 +486,15 @@ export async function runTurn(s, userText) {
482
486
  }
483
487
  };
484
488
  // Loop detection: count total calls per signature (tool+args) across the whole runTurn.
485
- // Consecutive-3 catches obvious tight loops; count-4 catches spread-out repetition where
486
- // other calls (daftar src, baca ...) appear between the repeating call.
489
+ // Consecutive-3 catches obvious tight loops; count-N catches spread-out repetition.
490
+ // daftar_file has a lower limit (3) because re-listing the same directory is almost never
491
+ // useful; baca_file legitimately needs more chances (range reads + post-edit re-reads).
487
492
  const callCounts = new Map();
488
493
  const lastSigs = []; // last 3 sigs for consecutive detection
489
- const COUNT_LIMIT = 6; // same tool+args called 6× total → loop (higher because cache now returns content)
494
+ const COUNT_LIMITS = { daftar_file: 3, cari: 4 };
495
+ const DEFAULT_COUNT_LIMIT = 6;
490
496
  const CONSEC_LIMIT = 3; // same sig 3× in a row → loop
497
+ const countLimitFor = (toolName) => COUNT_LIMITS[toolName] ?? DEFAULT_COUNT_LIMIT;
491
498
  // Read-only tool cache: on 2nd+ identical call, return the previous result with a warning
492
499
  // instead of re-running. This gives the model early feedback so it can self-correct before
493
500
  // COUNT_LIMIT is reached, preventing the common "list root, list root, list root" pattern.
@@ -553,9 +560,9 @@ export async function runTurn(s, userText) {
553
560
  lastSigs.push(sig);
554
561
  if (lastSigs.length > CONSEC_LIMIT)
555
562
  lastSigs.shift();
556
- // Total count exceeded, or 3 in a row
563
+ // Total count exceeded (per-tool limit), or 3 in a row
557
564
  const consecutive = lastSigs.length === CONSEC_LIMIT && lastSigs.every((s) => s === sig);
558
- if (count >= COUNT_LIMIT || consecutive) {
565
+ if (count >= countLimitFor(call.function.name) || consecutive) {
559
566
  loopSig = sig;
560
567
  break;
561
568
  }
@@ -588,9 +595,13 @@ export async function runTurn(s, userText) {
588
595
  // trimContext drops old messages from its window. The escalating warning text
589
596
  // discourages repetition; withholding the content at count=3+ only causes the model
590
597
  // to keep retrying — counter-productive to stopping the loop.
591
- const stopNote = count >= COUNT_LIMIT - 1
592
- ? `\n\n⚠ STOP (ke-${count}): JANGAN panggil "${toolName}" lagi dengan argumen yang sama. Gunakan tool "selesai" dan jelaskan kendalanya, atau gunakan cari/baca_file dengan path/range BERBEDA.`
593
- : `\n\n⚠ Cache ke-${count}: "${toolName}" sudah dipanggil ${count}× dengan argumen sama. Gunakan cari atau baca_file(path, mulai=N) untuk bagian berbeda.`;
598
+ const toolLimit = countLimitFor(toolName);
599
+ const isLastChance = count >= toolLimit - 1;
600
+ const stopNote = isLastChance
601
+ ? `\n\n⚠ STOP (ke-${count}/${toolLimit}): JANGAN panggil "${toolName}" lagi dengan argumen yang sama. Gunakan tool "selesai" dan jelaskan kendalanya, atau gunakan cari/baca_file dengan path/range BERBEDA.`
602
+ : toolName === 'daftar_file'
603
+ ? `\n\n⚠ Cache ke-${count}: direktori ini SUDAH terdaftar. JANGAN list ulang — gunakan baca_file(path) untuk file spesifik di dalamnya.`
604
+ : `\n\n⚠ Cache ke-${count}: "${toolName}" sudah dipanggil ${count}× dengan argumen sama. Gunakan cari atau baca_file(path, mulai=N) untuk bagian berbeda.`;
594
605
  const warn = `[Cache ke-${count}]\n${cachedOut}${stopNote}`;
595
606
  const cachePathHint = (() => {
596
607
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiosapi",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
6
  "keywords": [