hanoman 0.9.32 → 0.9.34

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 (26) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/cli.js +244 -130
  3. package/dist/server.js +21992 -20454
  4. package/package.json +1 -1
  5. package/prisma/migrations/20261020120000_lead_session_inbox/migration.sql +42 -0
  6. package/prisma/schema.prisma +46 -0
  7. package/web/assets/{DalangHanomanScreen-DdsEmtBl.js → DalangHanomanScreen-BwBpu2_P.js} +1 -1
  8. package/web/assets/{DocPreviewModal-BbOqdfaH.js → DocPreviewModal-z4u5bA7e.js} +1 -1
  9. package/web/assets/{DocsWorkspace-Cr7AXz9z.js → DocsWorkspace-CtD3uuCo.js} +1 -1
  10. package/web/assets/{IdeScreen-hdbQW-ui.js → IdeScreen-D-jOfQ5p.js} +1 -1
  11. package/web/assets/LeadScreen-BzauaH1t.js +1 -0
  12. package/web/assets/{ReviewScreen-BIkpEspG.js → ReviewScreen-CMhrSnaH.js} +1 -1
  13. package/web/assets/{SchedulerScreen-3KVE-d_d.js → SchedulerScreen-h9C8CMSA.js} +1 -1
  14. package/web/assets/{SettingsScreen-B0-W2TeR.js → SettingsScreen-DrV1eN2q.js} +3 -3
  15. package/web/assets/{TeamScreen-DNdt2P1g.js → TeamScreen-DQ7DBfil.js} +1 -1
  16. package/web/assets/{TerminalScreen-BjQF48XI.js → TerminalScreen-BecrzSkO.js} +1 -1
  17. package/web/assets/{TriageScreen-CzM99CrG.js → TriageScreen-jtTZsaK5.js} +1 -1
  18. package/web/assets/{VpsScreen-SGB_BpbS.js → VpsScreen-v5UmXGHF.js} +1 -1
  19. package/web/assets/{fields-of8G961J.js → fields-B0XXQmkb.js} +1 -1
  20. package/web/assets/index-kLq09yz-.js +1012 -0
  21. package/web/assets/{live-DQen7wLm.js → live-wjfa6DIz.js} +1 -1
  22. package/web/assets/{sched-lead-shared-C34TvLVv.js → sched-lead-shared-BfF2ZYfo.js} +1 -1
  23. package/web/assets/{settings-rebase-DHIWk4mV.js → settings-rebase-BMeQSp64.js} +1 -1
  24. package/web/index.html +1 -1
  25. package/web/assets/LeadScreen-BWE4mfJI.js +0 -1
  26. package/web/assets/index-C5P4ykKO.js +0 -1012
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.9.32",
3
- "sha": "5946ca19",
4
- "builtAt": "2026-10-19T07:56:38.058Z"
2
+ "version": "0.9.34",
3
+ "sha": "b5eb6b97",
4
+ "builtAt": "2026-10-20T14:03:19.363Z"
5
5
  }
package/dist/cli.js CHANGED
@@ -4491,6 +4491,127 @@ var init_auto_merge = __esm({
4491
4491
  }
4492
4492
  });
4493
4493
 
4494
+ // ../shared/src/lead.ts
4495
+ var zLeadGate, zLeadKind, zLeadConfidence, zLeadStatus, zLeadFlowStatus, LEAD_ACTIONS, zLeadAction, zLeadChoice, zLeadSelect, zLeadVerdict, zLeadAsk, zLeadOverride, LEAD_AUTHORITY_MODES, zLeadAuthorityMode, zLeadProject, zLeadAct;
4496
+ var init_lead = __esm({
4497
+ "../shared/src/lead.ts"() {
4498
+ "use strict";
4499
+ init_zod();
4500
+ zLeadGate = external_exports.enum(["contract", "detected", "pulse"]);
4501
+ zLeadKind = external_exports.enum(["answer", "order", "collision", "quality", "refusal"]);
4502
+ zLeadConfidence = external_exports.enum(["tinggi", "sedang", "ragu"]);
4503
+ zLeadStatus = external_exports.enum(["berlaku", "ditimpa", "dibatalkan", "gagal"]);
4504
+ zLeadFlowStatus = external_exports.enum(["menunggu", "sebagian", "selesai", "dibatalkan"]);
4505
+ LEAD_ACTIONS = [
4506
+ "none",
4507
+ // keputusan murni jawaban; tak ada tindakan menyusul
4508
+ "answer-session",
4509
+ // ketik jawaban ke pane sesi (pintu deteksi otomatis)
4510
+ "start-session",
4511
+ // mulai sesi untuk sebuah backlog (lewat antrean scheduler)
4512
+ "stop-session",
4513
+ // hentikan sesi — worktree DIBIARKAN utuh (AC-32a)
4514
+ "resume-session",
4515
+ // lanjutkan sesi terputus (jalur ADR-0084)
4516
+ "restart-session",
4517
+ // ulangi pekerjaan dari awal
4518
+ "order-queue",
4519
+ // tata urutan antrean yang sudah ada (ADR-0072) — bukan antrean kedua
4520
+ "hold-work",
4521
+ // tunda satu pekerjaan karena tabrakan area kerja
4522
+ "push-branch",
4523
+ // dorong perubahan ke branch
4524
+ "integrate-main",
4525
+ // integrasikan ke branch utama (risiko yang diterima sadar, PRD §Risiko)
4526
+ "run-migration"
4527
+ // jalankan migration pada basis data LOKAL operator
4528
+ ];
4529
+ zLeadAction = external_exports.enum(LEAD_ACTIONS);
4530
+ zLeadChoice = external_exports.object({ index: external_exports.number().int().positive(), option: external_exports.string() });
4531
+ zLeadSelect = external_exports.object({
4532
+ mode: external_exports.enum(["single", "multi"]).default("single"),
4533
+ min: external_exports.number().int().min(0).max(20).default(0),
4534
+ max: external_exports.number().int().min(1).max(20).nullable().default(null)
4535
+ });
4536
+ zLeadVerdict = external_exports.object({
4537
+ decision: external_exports.string().min(1),
4538
+ reason: external_exports.string().min(1),
4539
+ refs: external_exports.array(external_exports.string()).default([]),
4540
+ confidence: zLeadConfidence.default("sedang"),
4541
+ // Sengaja `string`, BUKAN `zLeadAction`: tindakan di luar allowlist harus bisa MASUK supaya
4542
+ // server menolaknya secara sadar — mencatat penolakan & menotifikasi operator (AC-33). Kalau
4543
+ // enum yang menyaring di sini, permintaan "deploy ke produksi" hanya akan tampak sebagai
4544
+ // keluaran rusak, dan justru peristiwa paling layak dilaporkan itulah yang hilang dari jejak.
4545
+ action: external_exports.string().default("none"),
4546
+ /**
4547
+ * SPEC-480 · opsi yang dipilih — nomor ATAU label. Sengaja `string`, bukan enum/number: pilihan
4548
+ * di luar daftar harus BISA MASUK supaya server menolaknya secara sadar & mencatatnya, alasan
4549
+ * yang sama persis dengan `action` di atas.
4550
+ */
4551
+ choice: external_exports.string().default(""),
4552
+ /**
4553
+ * SPEC-485 · pilihan JAMAK. `string[]` dengan alasan yang sama persis seperti `choice` & `action`
4554
+ * di atas: pilihan di luar daftar harus BISA MASUK supaya server menolaknya secara sadar dan
4555
+ * mencatatnya. Kosong + `choice` terisi dibaca sebagai satu pilihan — keluaran agen berbentuk
4556
+ * ADR-0098 harus tetap terpakai, dan menuntut field baru berarti setiap agen lama mendadak
4557
+ * "tak memilih apa pun".
4558
+ */
4559
+ choices: external_exports.array(external_exports.string().max(2e3)).max(20).default([]),
4560
+ /**
4561
+ * SPEC-480 · apa yang KURANG bila konteksnya memang tak cukup untuk memutuskan. Bukan pengganti
4562
+ * `confidence: "ragu"` (bukti tipis, jawabannya tetap ada) melainkan untuk fakta konkret yang
4563
+ * tak ada di repo maupun konteks. Terisi ⇒ server memaksa `ragu` ⇒ operator dinotifikasi.
4564
+ */
4565
+ missing: external_exports.array(external_exports.string().max(200)).max(10).default([]),
4566
+ /** Teks yang benar-benar diketik ke pane sesi (pintu deteksi otomatis). */
4567
+ reply: external_exports.string().default("")
4568
+ });
4569
+ zLeadAsk = external_exports.object({
4570
+ projectId: external_exports.string().min(1),
4571
+ specId: external_exports.string().nullish(),
4572
+ sessionId: external_exports.string().nullish(),
4573
+ question: external_exports.string().min(1).max(8e3),
4574
+ options: external_exports.array(external_exports.string().max(2e3)).max(20).default([]),
4575
+ context: external_exports.string().max(2e4).default(""),
4576
+ /** SPEC-485 · bentuk pilihan yang diminta peminta. Default = perilaku hari ini (single). */
4577
+ select: zLeadSelect.default({ mode: "single", min: 0, max: null }),
4578
+ /** `true` = peminta akan mengajukan pertanyaan lanjutan; alurnya dibiarkan terbuka sampai submit. */
4579
+ chain: external_exports.boolean().default(false),
4580
+ /** Lanjutkan rantai yang sudah ada. Alur tertutup ditolak 409 — bukan dibuatkan diam-diam. */
4581
+ flowId: external_exports.string().min(1).nullish().default(null)
4582
+ });
4583
+ zLeadOverride = external_exports.object({
4584
+ answer: external_exports.string().min(1).max(8e3),
4585
+ reason: external_exports.string().max(8e3).default(""),
4586
+ /**
4587
+ * SPEC-485 · centang operator sebagai DATA, bukan prosa. Dipetakan ke opsi baris yang ditimpa,
4588
+ * lalu ikut diketikkan ke pane — jadi manusia mencentang di dashboard dan kotaknya benar-benar
4589
+ * tercentang di dialog sesi.
4590
+ */
4591
+ choices: external_exports.array(external_exports.string().max(2e3)).max(20).default([])
4592
+ });
4593
+ LEAD_AUTHORITY_MODES = ["auto", "ask", "off"];
4594
+ zLeadAuthorityMode = external_exports.enum(LEAD_AUTHORITY_MODES);
4595
+ zLeadProject = external_exports.object({
4596
+ // zod 3: record berkunci enum diinfer `Partial<Record<…>>` — tindakan yang tak disebut memakai default.
4597
+ authority: external_exports.record(zLeadAction, zLeadAuthorityMode).default({}),
4598
+ targetBranch: external_exports.string().min(1).default("main"),
4599
+ /** Kosong = integrasi lead SELALU minta izin (server tak bisa membuktikan test hijau). */
4600
+ verifyCommand: external_exports.string().default(""),
4601
+ quotaStopPct: external_exports.number().int().min(1).max(100).default(85),
4602
+ rampStep: external_exports.number().int().min(1).max(16).default(1),
4603
+ rampProbeMin: external_exports.number().int().min(1).max(240).default(10),
4604
+ failStreak: external_exports.number().int().min(1).max(20).default(2),
4605
+ answerSlaSec: external_exports.number().int().min(30).max(3600).default(300)
4606
+ });
4607
+ zLeadAct = external_exports.object({
4608
+ eventId: external_exports.string().min(1),
4609
+ verdict: zLeadVerdict,
4610
+ evidence: external_exports.array(external_exports.string().max(500)).max(50).default([])
4611
+ });
4612
+ }
4613
+ });
4614
+
4494
4615
  // ../shared/src/relay.ts
4495
4616
  function utf8Bytes(s2) {
4496
4617
  let n2 = 0;
@@ -4887,6 +5008,7 @@ var init_entities = __esm({
4887
5008
  init_telegram();
4888
5009
  init_agent_engine();
4889
5010
  init_auto_merge();
5011
+ init_lead();
4890
5012
  init_method_catalog();
4891
5013
  init_relay();
4892
5014
  init_logs();
@@ -5064,26 +5186,12 @@ var init_entities = __esm({
5064
5186
  // rem per project (AC-15/US-4)
5065
5187
  everyMin: external_exports.number().int().min(1).max(1440).default(5),
5066
5188
  // denyut proaktif (OQ-2)
5067
- // SPEC-432 · batas waktu satu putusan (AC-4/35). Default 120 dtk MELAWAN prompt lead sendiri
5068
- // ("kumpulkan bukti dulu: SoT, ADR, plan, kode, riwayat git") dan kalah: satu keputusan `order`
5069
- // nyata terukur 306 dtk pada claude-opus-5 · xhigh, jadi 7/7 baris jejak operator berstatus
5070
- // `gagal`. 600 dtk memberi kelonggaran di atas ongkos terukur itu; yang memangkas ongkosnya
5071
- // sendiri adalah paragraf anggaran waktu di `leadPrompt` (306 dtk → 101 dtk).
5072
- timeoutSec: external_exports.number().int().min(10).max(900).default(600),
5189
+ // ADR-0228 · `timeoutSec` (batas satu proses brain), `maxConcurrent` & `queueWaitSec` (gerbang
5190
+ // konkurensi brain, SPEC-479) DICABUT bersama brain sekali-panggil. Tenggatnya kini milik inbox:
5191
+ // `projects[<id>].answerSlaSec` untuk pertanyaan, `deliverTimeoutSec` untuk pengantaran. Baris
5192
+ // Setting lama yang masih membawa kunci itu tetap ter-parse — zod membuang kunci tak dikenal.
5073
5193
  // AC-11 / OQ-10 · berapa jawaban otomatis berturut-turut untuk SATU sesi sebelum lead berhenti.
5074
5194
  maxAutoAnswers: external_exports.number().int().min(1).max(20).default(3),
5075
- // SPEC-479 (QA) · berapa putusan boleh DISUSUN SEKALIGUS, untuk KETIGA pintu bersama. Sebelum
5076
- // knob ini jawabannya tak pernah dinyatakan, jadi ia jatuh ke bentuk kode tiap pintu: 1 di pintu
5077
- // deteksi (`for`+`await`, terukur `maxInFlight = 1` dengan tangga tunggu linier) dan tak hingga
5078
- // di pintu kontrak (Fastify konkuren, terukur 12 permintaan → 12 proses agen). Default 2 diambil
5079
- // dari mesin tempat keluhan lahir — 8 GB / 8 core yang sudah menanggung sesi pekerja di tmux,
5080
- // sementara satu `claude -p --effort xhigh` adalah runtime Node penuh, bukan panggilan HTTP tipis.
5081
- maxConcurrent: external_exports.number().int().min(1).max(16).default(2),
5082
- // Deadline penerimaan: menunggu slot lebih lama dari ini → penolakan EKSPLISIT yang bisa dicoba
5083
- // ulang (503 + Retry-After di pintu kontrak), bukan gantung. Wajib ada karena Fastify menyetel
5084
- // `requestTimeout: 0` (terukur dari `buildApp()`) — tak ada pihak lain yang akan memutus peminta.
5085
- // 0 = tanpa antrean sama sekali: penuh berarti langsung ditolak.
5086
- queueWaitSec: external_exports.number().int().min(0).max(900).default(120),
5087
5195
  // SPEC-485 · ADR-0102 · umur maksimum satu RANTAI keputusan yang dibiarkan terbuka. Peminta bisa
5088
5196
  // mati di tengah rantai (sesi ditutup, agen crash); tanpa batas ini alurnya `sebagian` selamanya
5089
5197
  // dan tak ada yang tahu apakah ia masih ditunggu. Penyapunya menumpang tick lead yang sudah ada —
@@ -5092,6 +5200,14 @@ var init_entities = __esm({
5092
5200
  // OQ-3 · syarat objektif sebelum lead boleh mengintegrasikan ke branch utama. Default MENYALA:
5093
5201
  // risiko "kode masuk main tanpa mata manusia" diterima sadar, tapi syaratnya tetap terukur.
5094
5202
  requireGreenBeforeIntegrate: external_exports.boolean().default(true),
5203
+ // ADR-0228 · sesi lead. Knob GLOBAL: tenggat antar-jemput event, umur izin, dan kapan pane
5204
+ // lead dirotasi supaya konteksnya tak menumpuk tanpa batas.
5205
+ deliverTimeoutSec: external_exports.number().int().min(60).max(3600).default(600),
5206
+ approvalTtlMin: external_exports.number().int().min(5).max(10080).default(1440),
5207
+ rotateAfterEvents: external_exports.number().int().min(5).max(500).default(40),
5208
+ // Setelan PER PROYEK (wewenang, branch target, perintah verifikasi, jatah). Proyek yang tak
5209
+ // disebut memakai default `zLeadProject` — penggabungnya `lead/project-config.ts`.
5210
+ projects: external_exports.record(external_exports.string(), zLeadProject.partial()).default({}),
5095
5211
  engine: zLeadEngine.default({})
5096
5212
  });
5097
5213
  LEAD_DEFAULTS = zLead.parse({});
@@ -5289,6 +5405,10 @@ var init_agent = __esm({
5289
5405
  // endpoint tulis menumpang prefix status yang dipetakan tanpa melihat method (AC-5).
5290
5406
  "lead:read",
5291
5407
  "lead:write",
5408
+ // ADR-0228 · menyetujui tindakan lead yang menunggu izin (bisa MERGE ke branch target). Akses
5409
+ // `danger`, tak diimplikasikan `lead:write`: pane lead & agen peminta putusan memegang write,
5410
+ // dan mereka tak boleh menyetujui tindakannya sendiri (ADR-0192).
5411
+ "lead:approve",
5292
5412
  // SPEC-450 · ADR-0094 · custom agent. Domain TERSENDIRI, dipetakan MENURUT METHOD: menulis
5293
5413
  // definisi agen mengubah apa yang dilihat SETIAP sesi baru di seluruh workspace, jadi izin
5294
5414
  // baca tak pernah cukup untuk itu (kelas bug SPEC-405).
@@ -6556,108 +6676,6 @@ var init_github = __esm({
6556
6676
  }
6557
6677
  });
6558
6678
 
6559
- // ../shared/src/lead.ts
6560
- var zLeadGate, zLeadKind, zLeadConfidence, zLeadStatus, zLeadFlowStatus, LEAD_ACTIONS, zLeadAction, zLeadChoice, zLeadSelect, zLeadVerdict, zLeadAsk, zLeadOverride;
6561
- var init_lead = __esm({
6562
- "../shared/src/lead.ts"() {
6563
- "use strict";
6564
- init_zod();
6565
- zLeadGate = external_exports.enum(["contract", "detected", "pulse"]);
6566
- zLeadKind = external_exports.enum(["answer", "order", "collision", "quality", "refusal"]);
6567
- zLeadConfidence = external_exports.enum(["tinggi", "sedang", "ragu"]);
6568
- zLeadStatus = external_exports.enum(["berlaku", "ditimpa", "dibatalkan", "gagal"]);
6569
- zLeadFlowStatus = external_exports.enum(["menunggu", "sebagian", "selesai", "dibatalkan"]);
6570
- LEAD_ACTIONS = [
6571
- "none",
6572
- // keputusan murni jawaban; tak ada tindakan menyusul
6573
- "answer-session",
6574
- // ketik jawaban ke pane sesi (pintu deteksi otomatis)
6575
- "start-session",
6576
- // mulai sesi untuk sebuah backlog (lewat antrean scheduler)
6577
- "stop-session",
6578
- // hentikan sesi — worktree DIBIARKAN utuh (AC-32a)
6579
- "resume-session",
6580
- // lanjutkan sesi terputus (jalur ADR-0084)
6581
- "restart-session",
6582
- // ulangi pekerjaan dari awal
6583
- "order-queue",
6584
- // tata urutan antrean yang sudah ada (ADR-0072) — bukan antrean kedua
6585
- "hold-work",
6586
- // tunda satu pekerjaan karena tabrakan area kerja
6587
- "push-branch",
6588
- // dorong perubahan ke branch
6589
- "integrate-main",
6590
- // integrasikan ke branch utama (risiko yang diterima sadar, PRD §Risiko)
6591
- "run-migration"
6592
- // jalankan migration pada basis data LOKAL operator
6593
- ];
6594
- zLeadAction = external_exports.enum(LEAD_ACTIONS);
6595
- zLeadChoice = external_exports.object({ index: external_exports.number().int().positive(), option: external_exports.string() });
6596
- zLeadSelect = external_exports.object({
6597
- mode: external_exports.enum(["single", "multi"]).default("single"),
6598
- min: external_exports.number().int().min(0).max(20).default(0),
6599
- max: external_exports.number().int().min(1).max(20).nullable().default(null)
6600
- });
6601
- zLeadVerdict = external_exports.object({
6602
- decision: external_exports.string().min(1),
6603
- reason: external_exports.string().min(1),
6604
- refs: external_exports.array(external_exports.string()).default([]),
6605
- confidence: zLeadConfidence.default("sedang"),
6606
- // Sengaja `string`, BUKAN `zLeadAction`: tindakan di luar allowlist harus bisa MASUK supaya
6607
- // server menolaknya secara sadar — mencatat penolakan & menotifikasi operator (AC-33). Kalau
6608
- // enum yang menyaring di sini, permintaan "deploy ke produksi" hanya akan tampak sebagai
6609
- // keluaran rusak, dan justru peristiwa paling layak dilaporkan itulah yang hilang dari jejak.
6610
- action: external_exports.string().default("none"),
6611
- /**
6612
- * SPEC-480 · opsi yang dipilih — nomor ATAU label. Sengaja `string`, bukan enum/number: pilihan
6613
- * di luar daftar harus BISA MASUK supaya server menolaknya secara sadar & mencatatnya, alasan
6614
- * yang sama persis dengan `action` di atas.
6615
- */
6616
- choice: external_exports.string().default(""),
6617
- /**
6618
- * SPEC-485 · pilihan JAMAK. `string[]` dengan alasan yang sama persis seperti `choice` & `action`
6619
- * di atas: pilihan di luar daftar harus BISA MASUK supaya server menolaknya secara sadar dan
6620
- * mencatatnya. Kosong + `choice` terisi dibaca sebagai satu pilihan — keluaran agen berbentuk
6621
- * ADR-0098 harus tetap terpakai, dan menuntut field baru berarti setiap agen lama mendadak
6622
- * "tak memilih apa pun".
6623
- */
6624
- choices: external_exports.array(external_exports.string().max(2e3)).max(20).default([]),
6625
- /**
6626
- * SPEC-480 · apa yang KURANG bila konteksnya memang tak cukup untuk memutuskan. Bukan pengganti
6627
- * `confidence: "ragu"` (bukti tipis, jawabannya tetap ada) melainkan untuk fakta konkret yang
6628
- * tak ada di repo maupun konteks. Terisi ⇒ server memaksa `ragu` ⇒ operator dinotifikasi.
6629
- */
6630
- missing: external_exports.array(external_exports.string().max(200)).max(10).default([]),
6631
- /** Teks yang benar-benar diketik ke pane sesi (pintu deteksi otomatis). */
6632
- reply: external_exports.string().default("")
6633
- });
6634
- zLeadAsk = external_exports.object({
6635
- projectId: external_exports.string().min(1),
6636
- specId: external_exports.string().nullish(),
6637
- sessionId: external_exports.string().nullish(),
6638
- question: external_exports.string().min(1).max(8e3),
6639
- options: external_exports.array(external_exports.string().max(2e3)).max(20).default([]),
6640
- context: external_exports.string().max(2e4).default(""),
6641
- /** SPEC-485 · bentuk pilihan yang diminta peminta. Default = perilaku hari ini (single). */
6642
- select: zLeadSelect.default({ mode: "single", min: 0, max: null }),
6643
- /** `true` = peminta akan mengajukan pertanyaan lanjutan; alurnya dibiarkan terbuka sampai submit. */
6644
- chain: external_exports.boolean().default(false),
6645
- /** Lanjutkan rantai yang sudah ada. Alur tertutup ditolak 409 — bukan dibuatkan diam-diam. */
6646
- flowId: external_exports.string().min(1).nullish().default(null)
6647
- });
6648
- zLeadOverride = external_exports.object({
6649
- answer: external_exports.string().min(1).max(8e3),
6650
- reason: external_exports.string().max(8e3).default(""),
6651
- /**
6652
- * SPEC-485 · centang operator sebagai DATA, bukan prosa. Dipetakan ke opsi baris yang ditimpa,
6653
- * lalu ikut diketikkan ke pane — jadi manusia mencentang di dashboard dan kotaknya benar-benar
6654
- * tercentang di dialog sesi.
6655
- */
6656
- choices: external_exports.array(external_exports.string().max(2e3)).max(20).default([])
6657
- });
6658
- }
6659
- });
6660
-
6661
6679
  // ../shared/src/handled-by.ts
6662
6680
  var zHandledByEntry, HANDLED_BY_MAX, zHandledBy, zHandledByView;
6663
6681
  var init_handled_by = __esm({
@@ -6890,6 +6908,8 @@ var init_dto = __esm({
6890
6908
  priority: zPriority.optional(),
6891
6909
  payload: external_exports.union([zBriefPayload, zQaPayload, zGoalPayload]).optional(),
6892
6910
  // SPEC-407 · +goal
6911
+ // Pindah project: konten-sejenis, jadi ikut gerbang `editingContent` (hanya selagi belum dimulai).
6912
+ projectId: external_exports.string().min(1).optional(),
6893
6913
  // SPEC-447 · ADR-0093 · SENGAJA di luar gerbang `editingContent` (SPEC-186): gerbang itu
6894
6914
  // melindungi konten yang sudah jadi dasar kerja sesi berjalan, sedangkan dependsOn hanya
6895
6915
  // menggerbangi peluncuran BERIKUTNYA. `[]` = kosongkan.
@@ -7086,7 +7106,12 @@ var init_dto = __esm({
7086
7106
  inFlight: external_exports.number(),
7087
7107
  queued: external_exports.number(),
7088
7108
  capacity: external_exports.number()
7089
- }).default({ inFlight: 0, queued: 0, capacity: 1 })
7109
+ }).default({ inFlight: 0, queued: 0, capacity: 1 }),
7110
+ // ADR-0228 · sesi lead: inbox terbuka/terlambat, izin yang menunggu, dan pane lead per proyek.
7111
+ // `.default([])` supaya klien lama tetap mem-parse respons ini.
7112
+ inbox: external_exports.array(external_exports.custom()).default([]),
7113
+ approvals: external_exports.array(external_exports.custom()).default([]),
7114
+ sessions: external_exports.array(external_exports.custom()).default([])
7090
7115
  });
7091
7116
  zFlow = external_exports.enum(["feature", "qa", "scaffold", "reverse", "prd", "audit", "breakdown", "goal", "no_effort"]);
7092
7117
  zPrdBrief = external_exports.object({
@@ -7740,6 +7765,10 @@ var init_api = __esm({
7740
7765
  leadFlows: `${API}/lead/flows`,
7741
7766
  leadFlowSubmit: (id) => `${API}/lead/flows/${encodeURIComponent(id)}/submit`,
7742
7767
  leadFlowCancel: (id) => `${API}/lead/flows/${encodeURIComponent(id)}/cancel`,
7768
+ // ADR-0228 · sesi lead: inbox, antrean izin, dan pane lead per proyek.
7769
+ leadInboxTake: (id) => `${API}/lead/inbox/${encodeURIComponent(id)}/take`,
7770
+ leadApprovalDecide: (id) => `${API}/lead/approvals/${encodeURIComponent(id)}/decide`,
7771
+ leadSession: (projectId, op) => `${API}/lead/sessions/${encodeURIComponent(projectId)}/${op}`,
7743
7772
  // SPEC-450 · ADR-0094 · katalog custom agent. `?projectId=` → himpunan EFEKTIF (global+project).
7744
7773
  customAgents: `${API}/custom-agents`,
7745
7774
  customAgentMetrics: `${API}/custom-agents/metrics`,
@@ -7934,7 +7963,10 @@ var init_session_kind = __esm({
7934
7963
  "shell",
7935
7964
  "worktree",
7936
7965
  "terminal",
7937
- "telegram"
7966
+ "telegram",
7967
+ // ADR-0228 · pane hanoman-lead per proyek (`lead-<proyek>`). Tak restartable: ia dilahirkan
7968
+ // ulang oleh server sendiri, bukan oleh tombol "Mulai lagi".
7969
+ "lead"
7938
7970
  ];
7939
7971
  zSessionKind = external_exports.enum(SESSION_KINDS);
7940
7972
  }
@@ -9442,6 +9474,7 @@ var init_backlog = __esm({
9442
9474
  title: str("Judul baru."),
9443
9475
  priority: PRIORITY,
9444
9476
  payload: SPEC_PAYLOAD_ONEOF,
9477
+ projectId: str("Pindahkan backlog ke project lain (id project). Hanya selagi belum dimulai; branch asal & dependency dikosongkan, dan ditolak 409 bila item lain bergantung padanya."),
9445
9478
  dependsOn: strArray("Ganti seluruh daftar dependency. `[]` mengosongkan. Ini SATU-SATUNYA field di sini yang masih boleh diubah setelah item dimulai.")
9446
9479
  },
9447
9480
  required: ["spec"]
@@ -9457,6 +9490,7 @@ var init_backlog = __esm({
9457
9490
  ...s(a.title) ? { title: a.title } : {},
9458
9491
  ...s(a.priority) ? { priority: a.priority } : {},
9459
9492
  ...a.payload !== void 0 ? { payload: a.payload } : {},
9493
+ ...s(a.projectId) ? { projectId: a.projectId } : {},
9460
9494
  ...Array.isArray(a.dependsOn) ? { dependsOn: a.dependsOn } : {}
9461
9495
  }
9462
9496
  }),
@@ -12010,7 +12044,7 @@ var init_support = __esm({
12010
12044
  });
12011
12045
 
12012
12046
  // ../shared/src/mcp-catalog/lead.ts
12013
- var CORE6, MORE5, LEAD_TOOLS;
12047
+ var CORE6, MORE5, listParams, listQuery, SESSION, LEAD_TOOLS;
12014
12048
  var init_lead2 = __esm({
12015
12049
  "../shared/src/mcp-catalog/lead.ts"() {
12016
12050
  "use strict";
@@ -12048,7 +12082,7 @@ var init_lead2 = __esm({
12048
12082
  {
12049
12083
  name: "hanoman_lead_ask",
12050
12084
  title: "Minta putusan hanoman-lead",
12051
- description: "Minta putusan ke hanoman-lead saat menemui persimpangan yang biasanya butuh manusia. Jawabannya terbaca mesin (`decision`, `reason`, `refs`, `confidence`, `action`, `choices`) dan `refs` hanya memuat rujukan yang benar-benar ada di repo. Bila opsinya TIDAK saling eksklusif, set `multi: true` \u2014 balasannya memuat `choices` (daftar), bukan hanya `choice`. Panggilan ini melahirkan jejak permanen dan putusannya bisa menggerakkan sesi \u2014 pakai hanya saat memang buntu. 409 = lead tak aktif atau proyek belum opt-in: kembali ke perilaku biasa, berhenti dan tunggu manusia; 400 = bentuk `multi`/`minChoices`/`maxChoices` mustahil dipenuhi oleh daftar opsi yang kamu kirim.",
12085
+ description: "Minta putusan ke hanoman-lead saat menemui persimpangan yang biasanya butuh manusia. Pertanyaanmu masuk inbox sesi lead proyek itu dan dijawab pane lead setelah ia memeriksa; panggilan ini MENUNGGU sampai dijawab atau tenggat `answerSlaSec` proyek (default 300 dtk). Jawabannya terbaca mesin (`decision`, `reason`, `refs`, `confidence`, `action`, `choices`) dan `refs` hanya memuat rujukan yang benar-benar ada di repo. Bila opsinya TIDAK saling eksklusif, set `multi: true` \u2014 balasannya memuat `choices` (daftar), bukan hanya `choice`. Panggilan ini melahirkan jejak permanen dan putusannya bisa menggerakkan sesi \u2014 pakai hanya saat memang buntu. 409 = lead tak aktif atau proyek belum opt-in: berhenti dan tunggu manusia; 504 = sesi lead tak menjawab dalam tenggat (diserahkan ke manusia); 400 = bentuk `multi`/`minChoices`/`maxChoices` mustahil dipenuhi oleh daftar opsi yang kamu kirim.",
12052
12086
  inputSchema: obj({
12053
12087
  properties: {
12054
12088
  project: str("Id proyek."),
@@ -12213,7 +12247,87 @@ var init_lead2 = __esm({
12213
12247
  shape: (raw) => raw
12214
12248
  }
12215
12249
  ];
12216
- LEAD_TOOLS = [...CORE6, ...MORE5];
12250
+ listParams = {
12251
+ project: str("Saring menurut id proyek."),
12252
+ status: str("Saring menurut status."),
12253
+ page: int("Halaman, mulai 1.", { minimum: 1 }),
12254
+ limit: int("Item per halaman.", { minimum: 1, maximum: 100 })
12255
+ };
12256
+ listQuery = (a) => query({ projectId: s(a.project), status: s(a.status), page: n(a.page)?.toString(), limit: n(a.limit)?.toString() });
12257
+ SESSION = [
12258
+ {
12259
+ name: "hanoman_lead_inbox",
12260
+ title: "Inbox sesi lead",
12261
+ description: "Event yang diantarkan ke sesi hanoman-lead, terbaru dulu: pertanyaan sesi pekerja (`ask`), sesi selesai/gagal (`session-ended`), pilihan backlog (`pulse`), penolakan manusia (`rejected`), integrasi gagal (`integrate-failed`). `status`: `open` (menunggu diantar), `delivered` (sudah di pane lead), `done`, `late` (lewat tenggat \u2192 manusia), `human` (diambil manusia).",
12262
+ inputSchema: obj({ properties: listParams }),
12263
+ mode: "read",
12264
+ capability: "lead:read",
12265
+ samplePath: "/lead/inbox",
12266
+ sampleMethod: "GET",
12267
+ build: (a) => ({ method: "GET", path: "/lead/inbox", query: listQuery(a) }),
12268
+ shape: (raw) => raw
12269
+ },
12270
+ {
12271
+ name: "hanoman_lead_inbox_take",
12272
+ title: "Ambil alih event lead",
12273
+ description: "Mengambil satu event inbox dari sesi lead supaya MANUSIA yang memutuskannya. Jawaban lead yang datang sesudahnya ditolak server. Event yang sudah ditutup menjawab 409.",
12274
+ inputSchema: obj({ properties: { event: str("Id event, dari hanoman_lead_inbox.") }, required: ["event"] }),
12275
+ mode: "write",
12276
+ capability: "lead:write",
12277
+ samplePath: "/lead/inbox/e1/take",
12278
+ sampleMethod: "POST",
12279
+ build: (a) => ({ method: "POST", path: `/lead/inbox/${enc(String(a.event))}/take` }),
12280
+ shape: (raw) => raw
12281
+ },
12282
+ {
12283
+ name: "hanoman_lead_approvals_list",
12284
+ title: "Tindakan lead yang menunggu izin",
12285
+ description: "Tindakan hanoman-lead yang ditahan gerbang server menunggu izin manusia (mis. merge ke branch target, jawaban yang menyentuh produksi/kredensial). Tiap kartu membawa `reason` lead, `evidence` yang ia periksa, dan `gateReport` dari server. `status`: `pending`, `approved`, `rejected`, `expired` (kedaluwarsa = ditolak).",
12286
+ inputSchema: obj({ properties: listParams }),
12287
+ mode: "read",
12288
+ capability: "lead:read",
12289
+ samplePath: "/lead/approvals",
12290
+ sampleMethod: "GET",
12291
+ build: (a) => ({ method: "GET", path: "/lead/approvals", query: listQuery(a) }),
12292
+ shape: (raw) => raw
12293
+ },
12294
+ {
12295
+ name: "hanoman_lead_approval_decide",
12296
+ title: "Setujui/tolak tindakan lead (BERBAHAYA)",
12297
+ description: "BERBAHAYA \u2014 menyetujui atau menolak tindakan hanoman-lead yang menunggu izin. Menyetujui MENJALANKAN tindakan itu apa adanya, termasuk merge branch sesi ke branch target (server tetap menjalankan verifikasinya lebih dulu). Baca dulu kartunya lewat hanoman_lead_approvals_list. Penolakan dikirim balik ke lead beserta `note`. Izin yang sudah diputuskan menjawab 409. Butuh capability `lead:approve`. Hanya muncul saat tingkat `--danger` menyala.",
12298
+ inputSchema: obj({
12299
+ properties: {
12300
+ approval: str("Id izin, dari hanoman_lead_approvals_list."),
12301
+ approve: bool("`true` = setujui, `false` = tolak."),
12302
+ note: str("Opsional. Catatan untuk lead, maksimum 2000 karakter.")
12303
+ },
12304
+ required: ["approval", "approve"]
12305
+ }),
12306
+ mode: "danger",
12307
+ capability: "lead:approve",
12308
+ samplePath: "/lead/approvals/a1/decide",
12309
+ sampleMethod: "POST",
12310
+ build: (a) => ({
12311
+ method: "POST",
12312
+ path: `/lead/approvals/${enc(String(a.approval))}/decide`,
12313
+ body: { approve: a.approve === true, ...s(a.note) ? { note: a.note } : {} }
12314
+ }),
12315
+ shape: (raw) => raw
12316
+ },
12317
+ ...["start", "stop", "rotate"].map((op) => ({
12318
+ name: `hanoman_lead_session_${op}`,
12319
+ title: op === "start" ? "Mulai sesi lead (BERBAHAYA)" : op === "stop" ? "Hentikan sesi lead (BERBAHAYA)" : "Rotasi sesi lead (BERBAHAYA)",
12320
+ description: op === "start" ? "BERBAHAYA \u2014 melahirkan pane `lead-<proyek>` (agen hidup yang menjawab pertanyaan sesi pekerja, memilih backlog, dan memutuskan nasib sesi yang selesai). 409 = lead tak aktif/proyek belum opt-in atau belum di-bind, atau pane sedang backoff sesudah gagal lahir. Hanya muncul saat tingkat `--danger` menyala." : op === "stop" ? "BERBAHAYA \u2014 menghentikan pane lead proyek. Pertanyaan sesi pekerja jatuh ke manusia sampai pane lahir lagi (tick lead melahirkannya kembali bila inbox berisi dan lead tak dijeda \u2014 jeda proyeknya untuk benar-benar menghentikan). Hanya muncul saat tingkat `--danger` menyala." : "BERBAHAYA \u2014 menghentikan pane lead lalu melahirkannya lagi dengan konteks bersih (state ada di DB). Event yang sedang diproses dikembalikan ke inbox oleh sapuan. Hanya muncul saat tingkat `--danger` menyala.",
12321
+ inputSchema: obj({ properties: { project: str("Id proyek.") }, required: ["project"] }),
12322
+ mode: "danger",
12323
+ capability: "lead:write",
12324
+ samplePath: `/lead/sessions/p1/${op}`,
12325
+ sampleMethod: "POST",
12326
+ build: (a) => ({ method: "POST", path: `/lead/sessions/${enc(String(a.project))}/${op}` }),
12327
+ shape: (raw) => raw
12328
+ }))
12329
+ ];
12330
+ LEAD_TOOLS = [...CORE6, ...MORE5, ...SESSION];
12217
12331
  }
12218
12332
  });
12219
12333
 
@@ -13470,13 +13584,13 @@ var init_transfer = __esm({
13470
13584
  });
13471
13585
 
13472
13586
  // ../shared/src/mcp-catalog/gates.ts
13473
- var SESSION, CRON, intArray, DECIDE_NOTE, DIALOG, SPAWN_NOTE, CRON_FIELDS, CRONS, GATES_TOOLS;
13587
+ var SESSION2, CRON, intArray, DECIDE_NOTE, DIALOG, SPAWN_NOTE, CRON_FIELDS, CRONS, GATES_TOOLS;
13474
13588
  var init_gates = __esm({
13475
13589
  "../shared/src/mcp-catalog/gates.ts"() {
13476
13590
  "use strict";
13477
13591
  init_mcp_schema();
13478
13592
  init_helpers();
13479
- SESSION = str("Id sesi hidup, dari hanoman_sessions_list.");
13593
+ SESSION2 = str("Id sesi hidup, dari hanoman_sessions_list.");
13480
13594
  CRON = str("Id cron, dari hanoman_scheduler_crons_list.");
13481
13595
  intArray = (description) => ({ type: "array", description, items: { type: "integer", minimum: 1 }, maxItems: 32 });
13482
13596
  DECIDE_NOTE = "Menuntut capability `sessions:decide` (BUKAN `sessions:write`). Server menolak 403 bila token ini yang meluncurkan sesi tersebut, atau bila panggilan datang dari dalam sebuah sesi hanoman \u2014 agen tak boleh memutuskan pertanyaan sesinya sendiri.";
@@ -13485,7 +13599,7 @@ var init_gates = __esm({
13485
13599
  name: "hanoman_session_dialog_get",
13486
13600
  title: "Baca dialog pertanyaan sesi (BERBAHAYA)",
13487
13601
  description: `BERBAHAYA \u2014 membaca dialog AskUserQuestion yang sedang tampil di pane sebuah sesi: judul, opsi bernomor, dan \`screenHash\` yang wajib dikirim balik saat menjawab. Hasil kosong (\`dialog: null\`) berarti layar bukan dialog yang bisa dijawab (atau sesinya sudah berakhir). Dialog trust & prompt izin sengaja TIDAK pernah muncul di sini. ${DECIDE_NOTE} Hanya muncul saat tingkat \`--danger\` menyala.`,
13488
- inputSchema: obj({ properties: { session: SESSION }, required: ["session"] }),
13602
+ inputSchema: obj({ properties: { session: SESSION2 }, required: ["session"] }),
13489
13603
  mode: "danger",
13490
13604
  capability: "sessions:decide",
13491
13605
  samplePath: "/terminal/sessions/s1/dialog",
@@ -13499,7 +13613,7 @@ var init_gates = __esm({
13499
13613
  description: `BERBAHAYA \u2014 mengetikkan jawaban ke dialog AskUserQuestion sebuah sesi; sesi langsung bergerak dan tak ada undo. Ambil \`screenHash\` dari hanoman_session_dialog_get tepat sebelumnya: 409 \`stale\` berarti layar sudah berganti \u2014 baca ulang, jangan menebak. 409 \`deciding\` berarti hanoman-lead sedang menyusun keputusan untuk sesi itu (jangan dipaksa; rebut dulu lewat hanoman_session_dialog_takeover bila memang perlu). 409 \`answering\` berarti jawaban lain sedang dikirim. Isi TEPAT SATU bentuk: \`choice\` (single-select), \`choices\` (multi-select; [] = kosongkan semua lalu submit), atau \`text\` (prosa untuk kolom bebas/catatan; boleh menyertai \`choices\`). ${DECIDE_NOTE} Ini gerbang "manusia yang memutuskan": jawablah hanya atas instruksi manusia. Hanya muncul saat tingkat \`--danger\` menyala.`,
13500
13614
  inputSchema: obj({
13501
13615
  properties: {
13502
- session: SESSION,
13616
+ session: SESSION2,
13503
13617
  screenHash: str("Nilai `screenHash` dari hanoman_session_dialog_get untuk layar yang sedang dijawab."),
13504
13618
  choice: int("Nomor baris opsi (`options[].n`) untuk dialog single-select.", { minimum: 1 }),
13505
13619
  choices: intArray("Nomor baris yang dicentang untuk dialog multi-select; [] mengosongkan semuanya. Tak boleh bersama `choice`."),
@@ -13524,7 +13638,7 @@ var init_gates = __esm({
13524
13638
  name: "hanoman_session_dialog_takeover",
13525
13639
  title: "Rebut sesi dari hanoman-lead (BERBAHAYA)",
13526
13640
  description: `BERBAHAYA \u2014 merebut sesi yang pertanyaannya sedang ditangani hanoman-lead: lead dibatalkan dan hanya pemegang \`sessions:decide\` yang bisa menjawab sesudahnya. 404 berarti tak ada pertanyaan hidup untuk sesi itu; 409 \`answering\` berarti lead sudah terlanjur mengetik jawabannya ke pane (rebutan kalah). ${DECIDE_NOTE} Hanya muncul saat tingkat \`--danger\` menyala.`,
13527
- inputSchema: obj({ properties: { session: SESSION }, required: ["session"] }),
13641
+ inputSchema: obj({ properties: { session: SESSION2 }, required: ["session"] }),
13528
13642
  mode: "danger",
13529
13643
  capability: "sessions:decide",
13530
13644
  samplePath: "/terminal/sessions/s1/dialog/takeover",