kiosapi 0.1.26 → 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.
Files changed (2) hide show
  1. package/dist/agent/team.js +119 -36
  2. package/package.json +1 -1
@@ -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
- const ROLES = {
5
- perencana: {
6
- title: '① Perencana',
7
- mode: 'rencana',
8
- brief: 'Peranmu: PERENCANA. Telusuri kode seperlunya, lalu susun rencana langkah yang jelas & ringkas. JANGAN menulis kode.',
9
- },
10
- pengkode: {
11
- title: '② Pengkode',
12
- mode: 'buat',
13
- brief: 'Peranmu: PENGKODE. Implementasikan tugas mengikuti rencana. Buat perubahan kecil & jelas, lalu panggil selesai.',
14
- },
15
- peninjau: {
16
- title: '③ Peninjau',
17
- mode: 'rencana',
18
- brief: 'Peranmu: PENINJAU. Baca file yang relevan dan tinjau hasil implementasi: sebutkan masalah/risiko & saran perbaikan singkat.',
19
- },
20
- };
21
- /** Run one role as a sub-agent (with its own model) and return its final text. */
22
- async function runRole(name, task, opts) {
23
- const role = ROLES[name];
24
- const model = opts.models[name];
25
- console.log(`\n${bold(role.title)} ${dim(`(${model})`)}`);
26
- const s = newSession(model, role.mode, opts.otomatis);
27
- s.messages.push({ role: 'system', content: role.brief });
28
- return runTurn(s, task);
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
- /** Orchestrate the perencana → pengkode → peninjau pipeline for a task. */
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: perencana pengkode → peninjau'));
33
- const plan = await runRole('perencana', task, opts);
34
- await runRole('pengkode', `Tugas: ${task}\n\nRencana:\n${plan || '(tidak ada rencana eksplisit)'}`, opts);
35
- await runRole('peninjau', `Tinjau hasil implementasi untuk tugas: ${task}`, opts);
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
- * Run a custom team defined by a TeamConfig. Each role runs sequentially; the output of one role
40
- * is passed as context to the next.
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
- context = `Tugas asal: ${task}\n\nOutput dari ${roleName}:\n${result || '(tidak ada output eksplisit)'}`;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiosapi",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "type": "module",
5
5
  "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
6
  "keywords": [