kiosapi 0.1.17 → 0.1.19
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 +46 -10
- package/dist/agent/schemas.js +2 -1
- package/dist/agent/tools.js +5 -1
- package/package.json +1 -1
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
|
|
377
|
-
// message so the model starts oriented without
|
|
378
|
-
//
|
|
379
|
-
//
|
|
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'
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
570
|
+
toolCache.set(sig, stored);
|
|
535
571
|
if (result.done) {
|
|
536
572
|
if (stepModified.size > 0)
|
|
537
573
|
console.log(dim(` ✎ ${[...stepModified].join(' · ')}`));
|
package/dist/agent/schemas.js
CHANGED
|
@@ -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:
|
|
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/dist/agent/tools.js
CHANGED
|
@@ -306,7 +306,11 @@ export async function jalankan(perintah) {
|
|
|
306
306
|
let output = '';
|
|
307
307
|
let limitHit = false;
|
|
308
308
|
process.stdout.write('\n');
|
|
309
|
-
|
|
309
|
+
// On Windows, explicitly use cmd.exe so Windows commands (findstr, dir, etc.) work correctly
|
|
310
|
+
// even when the CLI is launched from a Git Bash or similar POSIX-shell terminal.
|
|
311
|
+
const proc = process.platform === 'win32'
|
|
312
|
+
? spawn('cmd.exe', ['/c', perintah], { cwd: ROOT })
|
|
313
|
+
: spawn(perintah, { cwd: ROOT, shell: true });
|
|
310
314
|
const onData = (chunk) => {
|
|
311
315
|
const text = chunk.toString();
|
|
312
316
|
output += text;
|