kiosapi 0.1.17 → 0.1.18

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
@@ -89,8 +89,27 @@ export function loadCheckpoint() {
89
89
  return null;
90
90
  try {
91
91
  const data = JSON.parse(readFileSync(CHECKPOINT_PATH, 'utf8'));
92
+ // Sanitize messages from older checkpoint formats:
93
+ // - Strip extra system messages (index > 0): pre-v0.1.17 auto-inject pushed a second
94
+ // {role:'system'} that some providers reject when they appear mid-conversation.
95
+ // - Null out content when tool_calls are present: strict OpenAI-compat providers (DeepSeek
96
+ // etc.) require content:null on assistant tool-call turns; old checkpoints stored the
97
+ // prefacing text alongside tool_calls which caused HTTP 400 on the next API call.
98
+ const [sys0, ...rest] = data.messages ?? [];
99
+ const sanitized = [
100
+ ...(sys0 ? [sys0] : []),
101
+ ...rest
102
+ .filter((m) => m.role !== 'system')
103
+ .map((m) => {
104
+ if (m.role === 'assistant' && m.tool_calls?.length) {
105
+ return { ...m, content: null };
106
+ }
107
+ return m;
108
+ }),
109
+ ];
92
110
  return {
93
111
  ...data,
112
+ messages: sanitized,
94
113
  teamModels: data.teamModels ?? {},
95
114
  totalTokens: data.totalTokens ?? 0,
96
115
  };
@@ -373,22 +392,28 @@ export function undoLastTurn(s) {
373
392
  * agent's final text (last answer or the `selesai` summary) — useful for chaining agents in a team.
374
393
  */
375
394
  export async function runTurn(s, userText) {
376
- // On the very first turn of a fresh session, append project metadata to the existing system
377
- // message so the model starts oriented without needing daftar_file(".") for basic orientation.
378
- // We APPEND (not push a new system message) because some providers reject requests that have
379
- // more than one system-role message a second {role:'system'} at index 1 causes HTTP 400.
395
+ // On the very first turn of a fresh session, append a compact project snapshot to the
396
+ // existing system message so the model starts oriented without calling daftar_file(".").
397
+ // DESIGN CONSTRAINTS:
398
+ // - APPEND, never push a second system message (some providers reject role:'system' at idx>0)
399
+ // - Keep total injected content small: free-tier models (Workers AI llama etc.) have 8K token
400
+ // context windows; a fat system message plus a few tool results fills it up fast, causing the
401
+ // upstream to return HTTP 400. Depth-2 tree (~1-2KB) is enough for high-level orientation.
380
402
  if (s.messages.filter((m) => m.role === 'user').length === 0) {
381
403
  const snippets = [];
404
+ // Depth 2 (not 3): shows top-level dirs + their immediate contents — enough to orient without
405
+ // producing 300 lines that consume half a small model's context window.
382
406
  try {
383
- snippets.push(`<root-directory>\n${daftarFile('.')}\n</root-directory>`);
407
+ snippets.push(`<root-directory>\n${daftarFile('.', 2)}\n</root-directory>`);
384
408
  }
385
409
  catch { /* ignore */ }
386
- for (const f of ['package.json', 'pyproject.toml', 'Cargo.toml', 'go.mod', 'README.md']) {
410
+ for (const f of ['package.json', 'pyproject.toml', 'Cargo.toml', 'go.mod']) {
387
411
  const abs = join(process.cwd(), f);
388
412
  if (existsSync(abs)) {
389
413
  try {
390
414
  const content = readFileSync(abs, 'utf8');
391
- snippets.push(`<file path="${f}">\n${content.slice(0, 4000)}\n</file>`);
415
+ // 1500 chars: enough for name/scripts/deps, not the full lockfile prose
416
+ snippets.push(`<file path="${f}">\n${content.slice(0, 1_500)}\n</file>`);
392
417
  }
393
418
  catch { /* unreadable — skip */ }
394
419
  }
@@ -396,8 +421,11 @@ export async function runTurn(s, userText) {
396
421
  if (snippets.length > 0) {
397
422
  const sysMsg = s.messages[0];
398
423
  if (sysMsg?.role === 'system') {
424
+ // Hard cap: keep total injected context under 5KB so the system message stays manageable.
425
+ const joined = snippets.join('\n\n');
426
+ const capped = joined.length > 5_000 ? `${joined.slice(0, 5_000)}\n…` : joined;
399
427
  sysMsg.content +=
400
- `\n\n## Konteks Proyek (auto-injected — jangan panggil daftar_file(".") lagi)\n${snippets.join('\n\n')}`;
428
+ `\n\n## Konteks Proyek\n${capped}`;
401
429
  }
402
430
  }
403
431
  }
@@ -527,11 +555,19 @@ export async function runTurn(s, userText) {
527
555
  continue;
528
556
  }
529
557
  const result = await runTool(call, s.otomatis, s.model);
530
- s.messages.push({ role: 'tool', content: result.output, tool_call_id: call.id });
558
+ // Cap what goes into the conversation history: full output can be 300 lines of directory
559
+ // listing or 100 KB of file content, which quickly overflows small-context-window models
560
+ // (Workers AI llama has ~8K token limit). The full result is already returned by runTool;
561
+ // truncating only the *stored* copy keeps the API payload manageable without losing info.
562
+ const MAX_STORED_RESULT = 5_000;
563
+ 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]`
565
+ : result.output;
566
+ s.messages.push({ role: 'tool', content: stored, tool_call_id: call.id });
531
567
  if (result.modifiedPath)
532
568
  stepModified.add(result.modifiedPath);
533
569
  if (READ_ONLY_TOOLS.has(call.function.name))
534
- toolCache.set(sig, result.output);
570
+ toolCache.set(sig, stored);
535
571
  if (result.done) {
536
572
  if (stepModified.size > 0)
537
573
  console.log(dim(` ✎ ${[...stepModified].join(' · ')}`));
@@ -198,7 +198,8 @@ Direktori kerja: ${process.cwd()}
198
198
  OS: ${osName} · ${shellNote}
199
199
  Aturan:
200
200
  - Bekerja langkah demi langkah: pakai tool untuk membaca sebelum mengubah.
201
- - Strategi eksplorasi: daftar_file(".") sudah tersedia di konteks awal — identifikasi file yang relevan LANGSUNG, lalu baca dengan baca_file. Jangan ulangi daftar_file pada path yang sama.
201
+ - 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.
202
+ - 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.
202
203
  - 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.
203
204
  - Path selalu relatif ke direktori kerja; akses ke luar ditolak.
204
205
  - Gunakan hapus_file/pindah_file untuk menghapus/memindahkan file (lebih aman dari jalankan del/rm).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiosapi",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
6
  "keywords": [