kiosapi 0.1.25 → 0.1.27
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 +18 -7
- package/dist/agent/schemas.js +2 -1
- package/dist/agent/team.js +119 -36
- package/package.json +1 -1
package/dist/agent/run.js
CHANGED
|
@@ -436,13 +436,23 @@ export async function runTurn(s, userText) {
|
|
|
436
436
|
catch {
|
|
437
437
|
/* ignore */
|
|
438
438
|
}
|
|
439
|
-
|
|
440
|
-
|
|
439
|
+
// Project context files: read upfront and embed in system message so the model
|
|
440
|
+
// never needs to tool-call for them. blueprint.md / kiosapi.json are orientation
|
|
441
|
+
// anchors that models otherwise read repeatedly when they lose track.
|
|
442
|
+
const CONTEXT_FILES = [
|
|
443
|
+
{ name: 'blueprint.md', cap: 3_000 },
|
|
444
|
+
{ name: 'kiosapi.json', cap: 2_000 },
|
|
445
|
+
{ name: 'package.json', cap: 1_500 },
|
|
446
|
+
{ name: 'pyproject.toml', cap: 1_500 },
|
|
447
|
+
{ name: 'Cargo.toml', cap: 1_500 },
|
|
448
|
+
{ name: 'go.mod', cap: 1_500 },
|
|
449
|
+
];
|
|
450
|
+
for (const { name, cap } of CONTEXT_FILES) {
|
|
451
|
+
const abs = join(process.cwd(), name);
|
|
441
452
|
if (existsSync(abs)) {
|
|
442
453
|
try {
|
|
443
454
|
const content = readFileSync(abs, 'utf8');
|
|
444
|
-
|
|
445
|
-
snippets.push(`<file path="${f}">\n${content.slice(0, 1_500)}\n</file>`);
|
|
455
|
+
snippets.push(`<file path="${name}">\n${content.slice(0, cap)}\n</file>`);
|
|
446
456
|
}
|
|
447
457
|
catch {
|
|
448
458
|
/* unreadable — skip */
|
|
@@ -452,11 +462,12 @@ export async function runTurn(s, userText) {
|
|
|
452
462
|
if (snippets.length > 0) {
|
|
453
463
|
const sysMsg = s.messages[0];
|
|
454
464
|
if (sysMsg?.role === 'system') {
|
|
455
|
-
// Hard cap:
|
|
465
|
+
// Hard cap: 10KB keeps things manageable for most models. blueprint.md alone can
|
|
466
|
+
// be large; cap individual files above and total here.
|
|
456
467
|
const joined = snippets.join('\n\n');
|
|
457
|
-
const capped = joined.length >
|
|
468
|
+
const capped = joined.length > 10_000 ? `${joined.slice(0, 10_000)}\n…` : joined;
|
|
458
469
|
sysMsg.content +=
|
|
459
|
-
`\n\n## Konteks Proyek\n${capped}`;
|
|
470
|
+
`\n\n## Konteks Proyek (sudah tersedia — JANGAN baca ulang dengan baca_file)\n${capped}`;
|
|
460
471
|
}
|
|
461
472
|
}
|
|
462
473
|
}
|
package/dist/agent/schemas.js
CHANGED
|
@@ -214,7 +214,8 @@ Direktori kerja: ${process.cwd()}
|
|
|
214
214
|
OS: ${osName} · ${shellNote}
|
|
215
215
|
Aturan:
|
|
216
216
|
- Bekerja langkah demi langkah: pakai tool untuk membaca sebelum mengubah.
|
|
217
|
-
-
|
|
217
|
+
- Konteks proyek (blueprint.md, kiosapi.json, struktur root) sudah di-inject ke system message — JANGAN baca ulang dengan baca_file. Langsung kerjakan tugas.
|
|
218
|
+
- Strategi eksplorasi: identifikasi subfolder relevan dari konteks awal, lalu baca spesifik dengan baca_file. JANGAN ulangi daftar_file pada path yang sama.
|
|
218
219
|
- 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.
|
|
219
220
|
- 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.
|
|
220
221
|
- 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.
|
package/dist/agent/team.js
CHANGED
|
@@ -1,49 +1,128 @@
|
|
|
1
1
|
import { modelSupportsTools } from '../api.js';
|
|
2
2
|
import { bold, dim, green, yellow } from '../ui.js';
|
|
3
3
|
import { newSession, runTurn } from './run.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Try to extract a StructuredPlan from the planner's selesai output.
|
|
6
|
+
* Handles: raw JSON, ```json block, or JSON embedded in prose.
|
|
7
|
+
*/
|
|
8
|
+
function tryParsePlan(output) {
|
|
9
|
+
const candidates = [output.trim()];
|
|
10
|
+
const codeBlock = output.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
11
|
+
if (codeBlock?.[1])
|
|
12
|
+
candidates.push(codeBlock[1].trim());
|
|
13
|
+
const embedded = output.match(/\{[\s\S]*?"langkah"[\s\S]*\}/);
|
|
14
|
+
if (embedded?.[0])
|
|
15
|
+
candidates.push(embedded[0]);
|
|
16
|
+
for (const c of candidates) {
|
|
17
|
+
try {
|
|
18
|
+
const p = JSON.parse(c);
|
|
19
|
+
if (p !== null &&
|
|
20
|
+
typeof p === 'object' &&
|
|
21
|
+
Array.isArray(p.langkah) &&
|
|
22
|
+
p.langkah.length > 0 &&
|
|
23
|
+
typeof p.langkah[0]?.tugas === 'string') {
|
|
24
|
+
return p;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
/* try next */
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
29
32
|
}
|
|
30
|
-
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Built-in team: planner-executor
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
const PLANNER_BRIEF = `Peranmu: PERENCANA. Telusuri kode seperlunya lalu buat rencana langkah kecil & terstruktur.
|
|
37
|
+
|
|
38
|
+
WAJIB: panggil tool "selesai" dengan parameter ringkasan berisi JSON dalam format TEPAT ini (tidak ada teks lain sebelum/sesudah JSON):
|
|
39
|
+
{"ringkasan":"<ringkasan singkat>","langkah":[{"tugas":"<deskripsi spesifik & actionable>","file":["path/file1.ts"],"catatan":"<konteks tambahan, opsional>"},...]}
|
|
40
|
+
|
|
41
|
+
Aturan rencana:
|
|
42
|
+
- Masing-masing langkah: 1–3 file, satu perubahan jelas, bisa selesai dalam ~15 tool calls.
|
|
43
|
+
- Maksimal 10 langkah — pecah yang kompleks, gabung yang trivial.
|
|
44
|
+
- Sebutkan path file LENGKAP dan AKURAT (gunakan daftar_file untuk memastikan).
|
|
45
|
+
- JANGAN menulis kode — hanya rencanakan.`;
|
|
46
|
+
const REVIEWER_BRIEF = 'Peranmu: PENINJAU. Baca file yang relevan dan tinjau hasil implementasi: sebutkan masalah/risiko & saran perbaikan singkat.';
|
|
31
47
|
export async function runTeam(task, opts) {
|
|
32
|
-
console.log(bold('👥 Tim agen
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
console.log(bold('👥 Tim agen — mode perencana-eksekutor'));
|
|
49
|
+
// ① Planner
|
|
50
|
+
console.log(`\n${bold('① Perencana')} ${dim(`(${opts.models.perencana})`)}`);
|
|
51
|
+
const plannerSession = newSession(opts.models.perencana, 'rencana', opts.otomatis);
|
|
52
|
+
plannerSession.messages.push({ role: 'system', content: PLANNER_BRIEF });
|
|
53
|
+
const planOutput = await runTurn(plannerSession, task);
|
|
54
|
+
const plan = tryParsePlan(planOutput);
|
|
55
|
+
if (!plan) {
|
|
56
|
+
console.log(yellow('⚠ Perencana tidak menghasilkan rencana terstruktur — jalankan sebagai sesi pengkode tunggal.'));
|
|
57
|
+
const s = newSession(opts.models.pengkode, 'buat', opts.otomatis);
|
|
58
|
+
s.messages.push({
|
|
59
|
+
role: 'system',
|
|
60
|
+
content: 'Peranmu: PENGKODE. Implementasikan tugas mengikuti rencana. Buat perubahan kecil & jelas, lalu panggil selesai.',
|
|
61
|
+
});
|
|
62
|
+
await runTurn(s, `Tugas: ${task}\n\nRencana:\n${planOutput || '(tidak ada rencana eksplisit)'}`);
|
|
63
|
+
console.log(`\n${bold('③ Peninjau')} ${dim(`(${opts.models.peninjau})`)}`);
|
|
64
|
+
const rev = newSession(opts.models.peninjau, 'rencana', opts.otomatis);
|
|
65
|
+
rev.messages.push({ role: 'system', content: REVIEWER_BRIEF });
|
|
66
|
+
await runTurn(rev, `Tinjau hasil implementasi untuk tugas: ${task}`);
|
|
67
|
+
console.log(green('\n✓ Tim selesai.'));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// Show plan
|
|
71
|
+
console.log(bold(`\n📋 ${plan.ringkasan}`));
|
|
72
|
+
for (const [i, step] of plan.langkah.entries()) {
|
|
73
|
+
console.log(` ${dim(`${i + 1}.`)} ${step.tugas}`);
|
|
74
|
+
console.log(dim(` 📄 ${step.file.join(', ')}`));
|
|
75
|
+
if (step.catatan)
|
|
76
|
+
console.log(dim(` 💬 ${step.catatan}`));
|
|
77
|
+
}
|
|
78
|
+
console.log('');
|
|
79
|
+
// ② Execute each step in a fresh focused session
|
|
80
|
+
for (const [i, step] of plan.langkah.entries()) {
|
|
81
|
+
const label = `${i + 1}/${plan.langkah.length}`;
|
|
82
|
+
console.log(bold(`\n② Pengkode — Langkah ${label}`) + dim(`: ${step.tugas}`));
|
|
83
|
+
console.log(dim(` 📄 ${step.file.join(', ')}`));
|
|
84
|
+
const s = newSession(opts.models.pengkode, 'buat', opts.otomatis);
|
|
85
|
+
s.messages.push({
|
|
86
|
+
role: 'system',
|
|
87
|
+
content: [
|
|
88
|
+
`Peranmu: PENGKODE — langkah ${label} dari rencana keseluruhan.`,
|
|
89
|
+
`TUGAS: ${step.tugas}`,
|
|
90
|
+
`File relevan: ${step.file.join(', ')}`,
|
|
91
|
+
step.catatan ? `Catatan: ${step.catatan}` : '',
|
|
92
|
+
'Fokus HANYA pada tugas ini. Baca file relevan, buat perubahan, panggil selesai.',
|
|
93
|
+
'Jangan baca file lain kecuali diperlukan langsung untuk memahami konteks.',
|
|
94
|
+
]
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.join('\n'),
|
|
97
|
+
});
|
|
98
|
+
const stepPrompt = [
|
|
99
|
+
step.tugas,
|
|
100
|
+
`\nFile: ${step.file.join(', ')}`,
|
|
101
|
+
step.catatan ? `\nCatatan: ${step.catatan}` : '',
|
|
102
|
+
]
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.join('');
|
|
105
|
+
await runTurn(s, stepPrompt);
|
|
106
|
+
}
|
|
107
|
+
// ③ Reviewer — pass the plan so it knows which files were touched
|
|
108
|
+
console.log(`\n${bold('③ Peninjau')} ${dim(`(${opts.models.peninjau})`)}`);
|
|
109
|
+
const stepSummary = plan.langkah
|
|
110
|
+
.map((s, i) => ` ${i + 1}. ${s.tugas} [${s.file.join(', ')}]`)
|
|
111
|
+
.join('\n');
|
|
112
|
+
const rev = newSession(opts.models.peninjau, 'rencana', opts.otomatis);
|
|
113
|
+
rev.messages.push({ role: 'system', content: REVIEWER_BRIEF });
|
|
114
|
+
await runTurn(rev, `Tinjau hasil implementasi untuk tugas: ${task}\n\nLangkah yang sudah dikerjakan:\n${stepSummary}`);
|
|
36
115
|
console.log(green('\n✓ Tim selesai.'));
|
|
37
116
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Custom team (unchanged)
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Cap output passed between roles to prevent context inflation across a long pipeline.
|
|
121
|
+
const MAX_ROLE_OUTPUT = 3_000;
|
|
42
122
|
export async function runCustomTeam(task, opts) {
|
|
43
123
|
const { config, otomatis, modelOverrides = {} } = opts;
|
|
44
124
|
const alur = config.alur.filter((r) => r in config.peran);
|
|
45
125
|
console.log(bold(`👥 Tim "${config.nama}": ${alur.join(' → ')}`));
|
|
46
|
-
// Warn about models that may lack tool support (best-effort, silent on network error)
|
|
47
126
|
const uniqueModels = [
|
|
48
127
|
...new Set(alur.map((r) => modelOverrides[r] ?? config.peran[r]?.model).filter(Boolean)),
|
|
49
128
|
];
|
|
@@ -67,7 +146,11 @@ export async function runCustomTeam(task, opts) {
|
|
|
67
146
|
if (roleCfg.brief)
|
|
68
147
|
s.messages.push({ role: 'system', content: roleCfg.brief });
|
|
69
148
|
const result = await runTurn(s, context);
|
|
70
|
-
|
|
149
|
+
const trimmed = (result ?? '').trim();
|
|
150
|
+
const capped = trimmed.length > MAX_ROLE_OUTPUT
|
|
151
|
+
? `${trimmed.slice(0, MAX_ROLE_OUTPUT)}\n…[dipotong — ${trimmed.length - MAX_ROLE_OUTPUT} char lebih]`
|
|
152
|
+
: trimmed;
|
|
153
|
+
context = `Tugas asal: ${task}\n\nOutput dari ${roleName}:\n${capped || '(tidak ada output eksplisit)'}`;
|
|
71
154
|
}
|
|
72
155
|
console.log(green(`\n✓ Tim "${config.nama}" selesai.`));
|
|
73
156
|
}
|