kiosapi 0.1.21 → 0.1.22

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 CHANGED
@@ -251,11 +251,15 @@ async function runTool(call, otomatis, model) {
251
251
  const str = (k) => (typeof args[k] === 'string' ? args[k] : '');
252
252
  try {
253
253
  switch (call.function.name) {
254
- case 'baca_file':
255
- console.log(dim(` baca ${str('path')}`));
256
- return { output: bacaFile(str('path')) };
254
+ case 'baca_file': {
255
+ const mulai = typeof args.mulai === 'number' ? Math.floor(args.mulai) : undefined;
256
+ const sampai = typeof args.sampai === 'number' ? Math.floor(args.sampai) : undefined;
257
+ const rangeStr = mulai != null ? ` baris ${mulai}${sampai != null ? `–${sampai}` : '+'}` : '';
258
+ console.log(dim(` baca ${str('path')}${rangeStr}`));
259
+ return { output: bacaFile(str('path'), mulai, sampai) };
260
+ }
257
261
  case 'daftar_file': {
258
- const ked = typeof args['kedalaman'] === 'number' ? args['kedalaman'] : undefined;
262
+ const ked = typeof args.kedalaman === 'number' ? args.kedalaman : undefined;
259
263
  const dPath = str('path') || '.';
260
264
  console.log(dim(` daftar ${dPath}${ked != null ? ` (kedalaman ${ked})` : ''}`));
261
265
  return { output: daftarFile(dPath, ked) };
@@ -406,7 +410,9 @@ export async function runTurn(s, userText) {
406
410
  try {
407
411
  snippets.push(`<root-directory>\n${daftarFile('.', 2)}\n</root-directory>`);
408
412
  }
409
- catch { /* ignore */ }
413
+ catch {
414
+ /* ignore */
415
+ }
410
416
  for (const f of ['package.json', 'pyproject.toml', 'Cargo.toml', 'go.mod']) {
411
417
  const abs = join(process.cwd(), f);
412
418
  if (existsSync(abs)) {
@@ -415,7 +421,9 @@ export async function runTurn(s, userText) {
415
421
  // 1500 chars: enough for name/scripts/deps, not the full lockfile prose
416
422
  snippets.push(`<file path="${f}">\n${content.slice(0, 1_500)}\n</file>`);
417
423
  }
418
- catch { /* unreadable — skip */ }
424
+ catch {
425
+ /* unreadable — skip */
426
+ }
419
427
  }
420
428
  }
421
429
  if (snippets.length > 0) {
@@ -523,9 +531,7 @@ export async function runTurn(s, userText) {
523
531
  const total = callCounts.get(loopSig) ?? 0;
524
532
  console.log(red(`\n⚠ Loop terdeteksi: "${loopName}" dipanggil ${total}× — agen terjebak.`));
525
533
  console.log(yellow('Berikan instruksi baru, atau ketik /bersih untuk mulai ulang.'));
526
- const loopMsg = `⚠ LOOP: "${loopName}" sudah dipanggil ${total}× dengan argumen yang sama. ` +
527
- `JANGAN panggil lagi. Gunakan tool "selesai" dan jelaskan kendalanya, ` +
528
- `atau minta klarifikasi dari pengguna.`;
534
+ const loopMsg = `⚠ LOOP: "${loopName}" sudah dipanggil ${total}× dengan argumen yang sama. JANGAN panggil lagi. Gunakan tool "selesai" dan jelaskan kendalanya, atau minta klarifikasi dari pengguna.`;
529
535
  for (const call of calls) {
530
536
  s.messages.push({ role: 'tool', content: loopMsg, tool_call_id: call.id });
531
537
  }
@@ -545,11 +551,8 @@ export async function runTurn(s, userText) {
545
551
  const toolName = call.function.name;
546
552
  const cachedOut = toolCache.get(sig) ?? '';
547
553
  const warn = count >= COUNT_LIMIT - 1
548
- ? `⚠ STOP: "${toolName}" sudah dipanggil ${count}× dengan argumen yang sama — ` +
549
- `hasilnya tidak berubah. WAJIB gunakan tool "selesai" atau eksplorasi path BERBEDA.`
550
- : `[Cache — identik dengan panggilan sebelumnya]\n${cachedOut}\n\n` +
551
- `⚠ Kamu sudah memanggil "${toolName}" dengan argumen yang sama ${count}×. ` +
552
- `Jangan ulangi — masuk ke subfolder spesifik atau gunakan "selesai".`;
554
+ ? `⚠ STOP: "${toolName}" sudah dipanggil ${count}× dengan argumen yang sama — hasilnya tidak berubah. WAJIB gunakan tool "selesai" atau eksplorasi path BERBEDA.`
555
+ : `[Cache identik dengan panggilan sebelumnya]\n${cachedOut}\n\n⚠ Kamu sudah memanggil "${toolName}" dengan argumen yang sama ${count}×. Jangan ulangi — masuk ke subfolder spesifik atau gunakan "selesai".`;
553
556
  console.log(dim(` ↩ ${toolName} (cache ke-${count})`));
554
557
  s.messages.push({ role: 'tool', content: warn, tool_call_id: call.id });
555
558
  continue;
@@ -559,9 +562,9 @@ export async function runTurn(s, userText) {
559
562
  // listing or 100 KB of file content, which quickly overflows small-context-window models
560
563
  // (Workers AI llama has ~8K token limit). The full result is already returned by runTool;
561
564
  // truncating only the *stored* copy keeps the API payload manageable without losing info.
562
- const MAX_STORED_RESULT = 5_000;
565
+ const MAX_STORED_RESULT = 10_000;
563
566
  const stored = result.output.length > MAX_STORED_RESULT
564
- ? `${result.output.slice(0, MAX_STORED_RESULT)}\n…[dipotong — gunakan path/range spesifik jika perlu lebih]`
567
+ ? `${result.output.slice(0, MAX_STORED_RESULT)}\n…[dipotong — gunakan baca_file(path, baris_lanjutan) untuk membaca sisa file]`
565
568
  : result.output;
566
569
  s.messages.push({ role: 'tool', content: stored, tool_call_id: call.id });
567
570
  if (result.modifiedPath)
@@ -4,10 +4,14 @@ const TOOLS = {
4
4
  type: 'function',
5
5
  function: {
6
6
  name: 'baca_file',
7
- description: 'Baca isi sebuah file (path relatif ke direktori kerja).',
7
+ description: 'Baca isi file. Untuk file besar yang terpotong, gunakan mulai (nomor baris 1-based) untuk membaca bagian selanjutnya.',
8
8
  parameters: {
9
9
  type: 'object',
10
- properties: { path: { type: 'string', description: 'Path file' } },
10
+ properties: {
11
+ path: { type: 'string', description: 'Path file' },
12
+ mulai: { type: 'number', description: 'Baris awal, 1-based (default: 1)' },
13
+ sampai: { type: 'number', description: 'Baris akhir (default: akhir file)' },
14
+ },
11
15
  required: ['path'],
12
16
  },
13
17
  },
@@ -211,9 +215,9 @@ OS: ${osName} · ${shellNote}
211
215
  Aturan:
212
216
  - Bekerja langkah demi langkah: pakai tool untuk membaca sebelum mengubah.
213
217
  - Strategi eksplorasi: struktur root tersedia di konteks awal — identifikasi subfolder relevan LANGSUNG, lalu baca dengan baca_file. Jangan ulangi daftar_file pada path yang sama.
214
- - baca_file menampilkan "[path · N baris · M char]" di baris pertama TIDAK perlu menghitung baris dengan shell.
218
+ - baca_file menampilkan "[path · N baris · M char]" jika ada "TERPOTONG", panggil baca_file(path, baris_lanjutan) BUKAN dengan argumen identik. Gunakan mulai/sampai untuk baca bagian tertentu.
215
219
  - Kedalaman daftar_file: gunakan kedalaman=2 (default) untuk subfolder besar. Kedalaman=3 hanya jika kamu sudah tahu folder itu kecil. JANGAN gunakan kedalaman=4+ kecuali diminta eksplisit.
216
- - JANGAN memanggil tool APAPUN dengan argumen identik lebih dari 1× dalam satu sesi. Jika tool mengembalikan peringatan cache "⚠", langsung ganti ke path atau argumen BERBEDA — atau gunakan cari untuk mencari bagian spesifik.
220
+ - JANGAN memanggil tool APAPUN dengan argumen identik lebih dari 1× dalam satu sesi. Jika tool mengembalikan peringatan cache "⚠", langsung ganti ke path atau argumen BERBEDA — untuk baca_file: gunakan mulai= berbeda atau gunakan cari.
217
221
  - Path selalu relatif ke direktori kerja; akses ke luar ditolak.
218
222
  - Gunakan hapus_file/pindah_file untuk menghapus/memindahkan file (lebih aman dari jalankan del/rm).
219
223
  - Buat perubahan kecil dan jelas. Setelah tugas beres, panggil tool "selesai" dengan ringkasan.
@@ -42,17 +42,30 @@ export function safePath(p) {
42
42
  }
43
43
  return { abs, rel: rel || '.' };
44
44
  }
45
- /** baca_file — read a text file. */
46
- export function bacaFile(path) {
45
+ /** baca_file — read a text file, optionally a specific line range (1-based). */
46
+ export function bacaFile(path, mulai, sampai) {
47
47
  const { abs } = safePath(path);
48
48
  if (!existsSync(abs))
49
49
  return `Error: file tidak ada: ${path}`;
50
50
  const text = readFileSync(abs, 'utf8');
51
- const lineCount = text.split('\n').length;
51
+ const allLines = text.split('\n');
52
+ const total = allLines.length;
53
+ if (mulai != null || sampai != null) {
54
+ const from = Math.max(1, Math.floor(mulai ?? 1));
55
+ const to = Math.min(total, Math.floor(sampai ?? total));
56
+ const content = allLines.slice(from - 1, to).join('\n');
57
+ const hint = to < total
58
+ ? `\n\n…[baris ${to + 1}–${total} belum dibaca — panggil baca_file("${path}", ${to + 1}) untuk lanjut]`
59
+ : '';
60
+ return `[${path} · baris ${from}–${to} dari ${total}]\n${content}${hint}`;
61
+ }
52
62
  if (text.length > MAX_READ) {
53
- return `[${path} · ${lineCount} baris · ${text.length} char — terpotong di ${MAX_READ} char]\n${text.slice(0, MAX_READ)}\n…[dipotong — gunakan cari untuk mencari teks spesifik di sisa file]`;
63
+ const sliced = text.slice(0, MAX_READ);
64
+ const linesShown = sliced.split('\n').length;
65
+ return (`[${path} · ${total} baris · ${text.length} char — menampilkan baris 1–${linesShown}]\n` +
66
+ `${sliced}\n\n…[TERPOTONG — panggil baca_file("${path}", ${linesShown + 1}) untuk melanjutkan]`);
54
67
  }
55
- return `[${path} · ${lineCount} baris · ${text.length} char]\n${text}`;
68
+ return `[${path} · ${total} baris · ${text.length} char]\n${text}`;
56
69
  }
57
70
  /** daftar_file — list a directory (ignored entries hidden).
58
71
  * kedalaman controls tree depth (1–5). Defaults: 3 for root ".", 1 for subdirs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiosapi",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
6
  "keywords": [