hanoman 0.1.12 → 0.1.14

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.12",
3
- "sha": "f88186c",
4
- "builtAt": "2026-08-01T11:46:59.162Z"
2
+ "version": "0.1.14",
3
+ "sha": "70cc48f",
4
+ "builtAt": "2026-08-01T16:01:40.254Z"
5
5
  }
package/dist/cli.js CHANGED
@@ -4380,6 +4380,25 @@ var init_telegram = __esm({
4380
4380
  }
4381
4381
  });
4382
4382
 
4383
+ // ../shared/src/auto-merge.ts
4384
+ var AUTO_MERGE_MODES, zAutoMerge;
4385
+ var init_auto_merge = __esm({
4386
+ "../shared/src/auto-merge.ts"() {
4387
+ "use strict";
4388
+ init_zod();
4389
+ AUTO_MERGE_MODES = ["off", "default-branch", "branch"];
4390
+ zAutoMerge = external_exports.object({
4391
+ mode: external_exports.enum(AUTO_MERGE_MODES).default("off"),
4392
+ // Tujuan: `local` = perbarui ref/fast-forward di checkout ini; `origin` = push.
4393
+ dest: external_exports.enum(["local", "origin"]).default("local"),
4394
+ // Hanya bermakna saat mode = "branch". Untuk "default-branch" ia diresolve SAAT EKSEKUSI.
4395
+ branch: external_exports.string().min(1).nullable().default(null),
4396
+ // Hapus `hanoman/<spec>` (lokal + origin) — HANYA sesudah merge terbukti bersih.
4397
+ deleteBranch: external_exports.boolean().default(false)
4398
+ });
4399
+ }
4400
+ });
4401
+
4383
4402
  // ../shared/src/entities.ts
4384
4403
  var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSpec, NOTIFY_SOUNDS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
4385
4404
  var init_entities = __esm({
@@ -4388,6 +4407,7 @@ var init_entities = __esm({
4388
4407
  init_zod();
4389
4408
  init_enums();
4390
4409
  init_telegram();
4410
+ init_auto_merge();
4391
4411
  zProject = external_exports.object({
4392
4412
  id: external_exports.string(),
4393
4413
  name: external_exports.string(),
@@ -4453,7 +4473,10 @@ var init_entities = __esm({
4453
4473
  dependsOn: external_exports.array(external_exports.string()).default([]),
4454
4474
  // Turunan (bukan kolom): dihitung `liveSpecs` dari stage dependency + git. Klien tak pernah
4455
4475
  // mengirimkannya — `.default([])` supaya bentuk lama tetap parse.
4456
- blockedBy: external_exports.array(zSpecBlocker).default([])
4476
+ blockedBy: external_exports.array(zSpecBlocker).default([]),
4477
+ // SPEC-486 · ADR-0103 · override kebijakan auto-merge item ini; null = warisi project.
4478
+ // `.nullable().default(null)` menjaga respons/klien versi lama tetap parse.
4479
+ autoMerge: zAutoMerge.nullable().default(null)
4457
4480
  });
4458
4481
  NOTIFY_SOUNDS = [
4459
4482
  "off",
@@ -4540,6 +4563,11 @@ var init_entities = __esm({
4540
4563
  // `requestTimeout: 0` (terukur dari `buildApp()`) — tak ada pihak lain yang akan memutus peminta.
4541
4564
  // 0 = tanpa antrean sama sekali: penuh berarti langsung ditolak.
4542
4565
  queueWaitSec: external_exports.number().int().min(0).max(900).default(120),
4566
+ // SPEC-485 · ADR-0102 · umur maksimum satu RANTAI keputusan yang dibiarkan terbuka. Peminta bisa
4567
+ // mati di tengah rantai (sesi ditutup, agen crash); tanpa batas ini alurnya `sebagian` selamanya
4568
+ // dan tak ada yang tahu apakah ia masih ditunggu. Penyapunya menumpang tick lead yang sudah ada —
4569
+ // ADR-0024 melarang timer/scheduler baru.
4570
+ flowTtlMin: external_exports.number().int().min(1).max(1440).default(60),
4543
4571
  // OQ-3 · syarat objektif sebelum lead boleh mengintegrasikan ke branch utama. Default MENYALA:
4544
4572
  // risiko "kode masuk main tanpa mata manusia" diterima sadar, tapi syaratnya tetap terukur.
4545
4573
  requireGreenBeforeIntegrate: external_exports.boolean().default(true),
@@ -4685,12 +4713,24 @@ var init_agent = __esm({
4685
4713
  });
4686
4714
 
4687
4715
  // ../shared/src/custom-agent.ts
4688
- var AGENT_NAME_RE, zCustomAgent, zCreateCustomAgent, zUpdateCustomAgent;
4716
+ var AGENT_RUNTIMES, zAgentRuntime, AGENT_NAME_RE, DEFAULT_AGENT_TOOLS, zCustomAgent, zCreateCustomAgent, zUpdateCustomAgent;
4689
4717
  var init_custom_agent = __esm({
4690
4718
  "../shared/src/custom-agent.ts"() {
4691
4719
  "use strict";
4692
4720
  init_zod();
4721
+ AGENT_RUNTIMES = ["claude", "codex"];
4722
+ zAgentRuntime = external_exports.enum(AGENT_RUNTIMES);
4693
4723
  AGENT_NAME_RE = /^[a-z][a-z0-9-]{1,39}$/;
4724
+ DEFAULT_AGENT_TOOLS = [
4725
+ "Read",
4726
+ "Write",
4727
+ "Edit",
4728
+ "Bash",
4729
+ "Glob",
4730
+ "Grep",
4731
+ "WebFetch",
4732
+ "WebSearch"
4733
+ ];
4694
4734
  zCustomAgent = external_exports.object({
4695
4735
  id: external_exports.string(),
4696
4736
  projectId: external_exports.string().nullable(),
@@ -4700,6 +4740,7 @@ var init_custom_agent = __esm({
4700
4740
  tools: external_exports.array(external_exports.string()).nullable(),
4701
4741
  model: external_exports.string().nullable(),
4702
4742
  mentions: external_exports.array(external_exports.string()).nullable(),
4743
+ runtime: external_exports.enum(AGENT_RUNTIMES).nullable(),
4703
4744
  enabled: external_exports.boolean(),
4704
4745
  createdAt: external_exports.string(),
4705
4746
  updatedAt: external_exports.string()
@@ -4712,12 +4753,29 @@ var init_custom_agent = __esm({
4712
4753
  tools: external_exports.array(external_exports.string()).nullable().optional(),
4713
4754
  model: external_exports.string().nullable().optional(),
4714
4755
  mentions: external_exports.array(external_exports.string()).nullable().optional(),
4756
+ // SPEC-484 · ADR-0101 · PENYARING mesin sesi, bukan pemilih proses. null/absen = ikut sesi induk.
4757
+ runtime: external_exports.enum(AGENT_RUNTIMES).nullable().optional(),
4715
4758
  enabled: external_exports.boolean().optional()
4716
4759
  });
4717
4760
  zUpdateCustomAgent = zCreateCustomAgent.omit({ name: true, projectId: true }).partial();
4718
4761
  }
4719
4762
  });
4720
4763
 
4764
+ // ../shared/src/agent-catalog.ts
4765
+ var BUILTIN_AGENT_TOOLS;
4766
+ var init_agent_catalog = __esm({
4767
+ "../shared/src/agent-catalog.ts"() {
4768
+ "use strict";
4769
+ init_custom_agent();
4770
+ init_entities();
4771
+ BUILTIN_AGENT_TOOLS = DEFAULT_AGENT_TOOLS.map((id) => ({
4772
+ id,
4773
+ label: id,
4774
+ group: "builtin"
4775
+ }));
4776
+ }
4777
+ });
4778
+
4721
4779
  // ../shared/src/github.ts
4722
4780
  var zGithubIssueStatus, zNormalIssue, zGithubIssueView;
4723
4781
  var init_github = __esm({
@@ -4749,7 +4807,7 @@ var init_github = __esm({
4749
4807
  });
4750
4808
 
4751
4809
  // ../shared/src/lead.ts
4752
- var zLeadGate, zLeadKind, zLeadConfidence, zLeadStatus, LEAD_ACTIONS, zLeadAction, zLeadChoice, zLeadVerdict, zLeadAsk, zLeadOverride;
4810
+ var zLeadGate, zLeadKind, zLeadConfidence, zLeadStatus, zLeadFlowStatus, LEAD_ACTIONS, zLeadAction, zLeadChoice, zLeadSelect, zLeadVerdict, zLeadAsk, zLeadOverride;
4753
4811
  var init_lead = __esm({
4754
4812
  "../shared/src/lead.ts"() {
4755
4813
  "use strict";
@@ -4758,6 +4816,7 @@ var init_lead = __esm({
4758
4816
  zLeadKind = external_exports.enum(["answer", "order", "collision", "quality", "refusal"]);
4759
4817
  zLeadConfidence = external_exports.enum(["tinggi", "sedang", "ragu"]);
4760
4818
  zLeadStatus = external_exports.enum(["berlaku", "ditimpa", "dibatalkan", "gagal"]);
4819
+ zLeadFlowStatus = external_exports.enum(["menunggu", "sebagian", "selesai", "dibatalkan"]);
4761
4820
  LEAD_ACTIONS = [
4762
4821
  "none",
4763
4822
  // keputusan murni jawaban; tak ada tindakan menyusul
@@ -4784,6 +4843,11 @@ var init_lead = __esm({
4784
4843
  ];
4785
4844
  zLeadAction = external_exports.enum(LEAD_ACTIONS);
4786
4845
  zLeadChoice = external_exports.object({ index: external_exports.number().int().positive(), option: external_exports.string() });
4846
+ zLeadSelect = external_exports.object({
4847
+ mode: external_exports.enum(["single", "multi"]).default("single"),
4848
+ min: external_exports.number().int().min(0).max(20).default(0),
4849
+ max: external_exports.number().int().min(1).max(20).nullable().default(null)
4850
+ });
4787
4851
  zLeadVerdict = external_exports.object({
4788
4852
  decision: external_exports.string().min(1),
4789
4853
  reason: external_exports.string().min(1),
@@ -4800,6 +4864,14 @@ var init_lead = __esm({
4800
4864
  * yang sama persis dengan `action` di atas.
4801
4865
  */
4802
4866
  choice: external_exports.string().default(""),
4867
+ /**
4868
+ * SPEC-485 · pilihan JAMAK. `string[]` dengan alasan yang sama persis seperti `choice` & `action`
4869
+ * di atas: pilihan di luar daftar harus BISA MASUK supaya server menolaknya secara sadar dan
4870
+ * mencatatnya. Kosong + `choice` terisi dibaca sebagai satu pilihan — keluaran agen berbentuk
4871
+ * ADR-0098 harus tetap terpakai, dan menuntut field baru berarti setiap agen lama mendadak
4872
+ * "tak memilih apa pun".
4873
+ */
4874
+ choices: external_exports.array(external_exports.string().max(2e3)).max(20).default([]),
4803
4875
  /**
4804
4876
  * SPEC-480 · apa yang KURANG bila konteksnya memang tak cukup untuk memutuskan. Bukan pengganti
4805
4877
  * `confidence: "ragu"` (bukti tipis, jawabannya tetap ada) melainkan untuk fakta konkret yang
@@ -4815,23 +4887,36 @@ var init_lead = __esm({
4815
4887
  sessionId: external_exports.string().nullish(),
4816
4888
  question: external_exports.string().min(1).max(8e3),
4817
4889
  options: external_exports.array(external_exports.string().max(2e3)).max(20).default([]),
4818
- context: external_exports.string().max(2e4).default("")
4890
+ context: external_exports.string().max(2e4).default(""),
4891
+ /** SPEC-485 · bentuk pilihan yang diminta peminta. Default = perilaku hari ini (single). */
4892
+ select: zLeadSelect.default({ mode: "single", min: 0, max: null }),
4893
+ /** `true` = peminta akan mengajukan pertanyaan lanjutan; alurnya dibiarkan terbuka sampai submit. */
4894
+ chain: external_exports.boolean().default(false),
4895
+ /** Lanjutkan rantai yang sudah ada. Alur tertutup ditolak 409 — bukan dibuatkan diam-diam. */
4896
+ flowId: external_exports.string().min(1).nullish().default(null)
4819
4897
  });
4820
4898
  zLeadOverride = external_exports.object({
4821
4899
  answer: external_exports.string().min(1).max(8e3),
4822
- reason: external_exports.string().max(8e3).default("")
4900
+ reason: external_exports.string().max(8e3).default(""),
4901
+ /**
4902
+ * SPEC-485 · centang operator sebagai DATA, bukan prosa. Dipetakan ke opsi baris yang ditimpa,
4903
+ * lalu ikut diketikkan ke pane — jadi manusia mencentang di dashboard dan kotaknya benar-benar
4904
+ * tercentang di dialog sesi.
4905
+ */
4906
+ choices: external_exports.array(external_exports.string().max(2e3)).max(20).default([])
4823
4907
  });
4824
4908
  }
4825
4909
  });
4826
4910
 
4827
4911
  // ../shared/src/dto.ts
4828
- var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerState, zLeadDecisionView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, zLogin, zSignup, zChangePassword, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, UPDATE_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus;
4912
+ var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, zLogin, zSignup, zChangePassword, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, UPDATE_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus;
4829
4913
  var init_dto = __esm({
4830
4914
  "../shared/src/dto.ts"() {
4831
4915
  "use strict";
4832
4916
  init_zod();
4833
4917
  init_entities();
4834
4918
  init_lead();
4919
+ init_auto_merge();
4835
4920
  init_enums();
4836
4921
  zIssueDeviceToken = external_exports.object({ name: external_exports.string().min(1) });
4837
4922
  zSessionResult = external_exports.object({
@@ -4884,8 +4969,11 @@ var init_dto = __esm({
4884
4969
  // SPEC-217 · path default/server editable (null = kosongkan)
4885
4970
  schedulerOptIn: external_exports.boolean().optional(),
4886
4971
  // SPEC-294 · opt-in scheduler otonom (lokal, tak disync)
4887
- leadOptIn: external_exports.boolean().optional()
4972
+ leadOptIn: external_exports.boolean().optional(),
4888
4973
  // SPEC-409 · ADR-0091 · opt-in hanoman-lead (lokal, tak disync)
4974
+ // SPEC-486 · ADR-0103 · kebijakan auto-merge project (null = kosongkan → tanpa auto-merge).
4975
+ // Divalidasi server terhadap repo (checkAutoMerge): repoDir wajib ada, branch wajib nyata.
4976
+ autoMerge: zAutoMerge.nullable().optional()
4889
4977
  });
4890
4978
  zCreateSpec = external_exports.object({
4891
4979
  project: external_exports.string(),
@@ -4914,7 +5002,11 @@ var init_dto = __esm({
4914
5002
  // SPEC-447 · ADR-0093 · SENGAJA di luar gerbang `editingContent` (SPEC-186): gerbang itu
4915
5003
  // melindungi konten yang sudah jadi dasar kerja sesi berjalan, sedangkan dependsOn hanya
4916
5004
  // menggerbangi peluncuran BERIKUTNYA. `[]` = kosongkan.
4917
- dependsOn: external_exports.array(external_exports.string()).optional()
5005
+ dependsOn: external_exports.array(external_exports.string()).optional(),
5006
+ // SPEC-486 · ADR-0103 · override per item; `null` mengembalikannya ke warisan project.
5007
+ // SENGAJA di luar gerbang `editingContent` (SPEC-186), sama seperti dependsOn: ia menggerbangi
5008
+ // apa yang terjadi SESUDAH kerja, bukan konten yang sedang dikerjakan sesi hidup.
5009
+ autoMerge: zAutoMerge.nullable().optional()
4918
5010
  });
4919
5011
  zIntegrate = external_exports.object({
4920
5012
  op: external_exports.enum(["merge", "rebase"]),
@@ -4937,7 +5029,9 @@ var init_dto = __esm({
4937
5029
  // SPEC-253 · Help Center publik aktif
4938
5030
  schedulerOptIn: external_exports.boolean().default(false),
4939
5031
  // SPEC-294 · opt-in scheduler otonom
4940
- leadOptIn: external_exports.boolean().default(false)
5032
+ leadOptIn: external_exports.boolean().default(false),
5033
+ // SPEC-409 · ADR-0091 · opt-in hanoman-lead
5034
+ autoMerge: zAutoMerge.nullable().default(null)
4941
5035
  });
4942
5036
  zSchedulerQueueItem = external_exports.object({
4943
5037
  id: external_exports.string(),
@@ -4994,11 +5088,32 @@ var init_dto = __esm({
4994
5088
  choiceIndex: external_exports.number().nullable().default(null),
4995
5089
  options: external_exports.array(external_exports.string()).default([]),
4996
5090
  missing: external_exports.array(external_exports.string()).default([]),
5091
+ // SPEC-485 · ADR-0102 · jawaban SELALU daftar di permukaan baca. Baris pra-migrasi tak punya
5092
+ // kolomnya, jadi `toDecisionView` MENURUNKANNYA dari `choice`/`choiceIndex` — itulah yang
5093
+ // membuat riwayat lama tetap terbaca sesudah perubahan skema.
5094
+ choices: external_exports.array(zLeadChoice).default([]),
5095
+ select: zLeadSelect.nullable().default(null),
5096
+ flowId: external_exports.string().nullable().default(null),
5097
+ step: external_exports.number().nullable().default(null),
4997
5098
  status: zLeadStatus,
4998
5099
  weighty: external_exports.boolean(),
4999
5100
  supersededById: external_exports.string().nullable(),
5000
5101
  createdAt: external_exports.string()
5001
5102
  });
5103
+ zLeadFlowView = external_exports.object({
5104
+ id: external_exports.string(),
5105
+ projectId: external_exports.string(),
5106
+ specId: external_exports.string().nullable(),
5107
+ sessionId: external_exports.string().nullable(),
5108
+ gate: zLeadGate,
5109
+ status: zLeadFlowStatus,
5110
+ title: external_exports.string(),
5111
+ steps: external_exports.number(),
5112
+ closeReason: external_exports.string().nullable(),
5113
+ openedAt: external_exports.string(),
5114
+ closedAt: external_exports.string().nullable(),
5115
+ expiresAt: external_exports.string()
5116
+ });
5002
5117
  zLeadAnswer = external_exports.object({
5003
5118
  id: external_exports.string(),
5004
5119
  decision: external_exports.string(),
@@ -5010,7 +5125,12 @@ var init_dto = __esm({
5010
5125
  // dipilih sebagai field, bukan sesuatu yang harus ditebak dari `decision`. `decision`/`reason`
5011
5126
  // di sini TERPANGKAS (jejak penuh ada di `GET /lead/decisions`).
5012
5127
  choice: zLeadChoice.nullable().default(null),
5013
- missing: external_exports.array(external_exports.string()).default([])
5128
+ missing: external_exports.array(external_exports.string()).default([]),
5129
+ // SPEC-485 · ADR-0102 · `choices` adalah bentuk yang berlaku; `choice` di atas tinggal
5130
+ // `choices[0]`. `flowId`/`flowStatus` memberi peminta pegangan untuk melanjutkan rantainya.
5131
+ choices: external_exports.array(zLeadChoice).default([]),
5132
+ flowId: external_exports.string().nullable().default(null),
5133
+ flowStatus: zLeadFlowStatus.nullable().default(null)
5014
5134
  });
5015
5135
  zLeadProjectStatus = external_exports.object({
5016
5136
  projectId: external_exports.string(),
@@ -5264,6 +5384,9 @@ var init_api = __esm({
5264
5384
  limits: `${API}/limits`,
5265
5385
  // SPEC-339 · versi codex CLI (peringatan lunak model GPT-5.6).
5266
5386
  codexVersion: `${API}/codex/version`,
5387
+ // SPEC-489 · panduan AI agent — markdown MENTAH & PUBLIC (tanpa auth). Satu definisi URL untuk
5388
+ // klien web dan untuk tautan yang disalin operator ke agennya.
5389
+ agentDoc: `${API}/agent-integration.md`,
5267
5390
  docs: (id) => `${API}/projects/${id}/docs`,
5268
5391
  docFile: (id, path) => `${API}/projects/${id}/docs/${path}`,
5269
5392
  // SPEC-210 · dokumen PRD project (freshest-wins: worktree sesi prd hidup > repoDir)
@@ -5370,8 +5493,15 @@ var init_api = __esm({
5370
5493
  leadDecisions: `${API}/lead/decisions`,
5371
5494
  leadDecisionOverride: (id) => `${API}/lead/decisions/${encodeURIComponent(id)}/override`,
5372
5495
  leadDecisionCancel: (id) => `${API}/lead/decisions/${encodeURIComponent(id)}/cancel`,
5496
+ // SPEC-485 · ADR-0102 · rantai keputusan. Di bawah prefix `lead` supaya capability-nya turunan
5497
+ // method (lead:read/lead:write) tanpa peta baru — kelas bug SPEC-405 sudah ditutup di sana.
5498
+ leadFlows: `${API}/lead/flows`,
5499
+ leadFlowSubmit: (id) => `${API}/lead/flows/${encodeURIComponent(id)}/submit`,
5500
+ leadFlowCancel: (id) => `${API}/lead/flows/${encodeURIComponent(id)}/cancel`,
5373
5501
  // SPEC-450 · ADR-0094 · katalog custom agent. `?projectId=` → himpunan EFEKTIF (global+project).
5374
5502
  customAgents: `${API}/custom-agents`,
5503
+ // SPEC-484 · ADR-0101 · sumber daftar tools/model/runtime untuk form (mention dari `customAgents`).
5504
+ customAgentCatalog: `${API}/custom-agents/catalog`,
5375
5505
  // SPEC-481 · ADR-0100 · webhook keluar (cookie-only)
5376
5506
  webhooks: `${API}/webhooks`,
5377
5507
  webhook: (id) => `${API}/webhooks/${encodeURIComponent(id)}`,
@@ -5535,6 +5665,7 @@ var init_config_registry = __esm({
5535
5665
  kind: "url",
5536
5666
  apply: "live",
5537
5667
  category: "knob",
5668
+ example: "https://hanoman.nafanesia.id",
5538
5669
  help: "Base URL hub tujuan sync (REST + WS). Kosong = instance ini murni HUB."
5539
5670
  },
5540
5671
  {
@@ -5544,6 +5675,7 @@ var init_config_registry = __esm({
5544
5675
  kind: "secret",
5545
5676
  apply: "live",
5546
5677
  category: "credential",
5678
+ example: "43 karakter base64url dari tab Perangkat hub",
5547
5679
  help: "Token yang diterbitkan hub (tab Perangkat di hub). Dikirim sebagai Bearer."
5548
5680
  },
5549
5681
  {
@@ -5553,6 +5685,7 @@ var init_config_registry = __esm({
5553
5685
  kind: "int",
5554
5686
  apply: "live",
5555
5687
  category: "knob",
5688
+ example: "15000",
5556
5689
  default: "15000",
5557
5690
  min: 1e3
5558
5691
  },
@@ -5564,13 +5697,14 @@ var init_config_registry = __esm({
5564
5697
  kind: "secret",
5565
5698
  apply: "new-session",
5566
5699
  category: "credential",
5700
+ example: "sk-ant-oat01-\u2026",
5567
5701
  inheritEnv: true,
5568
5702
  help: "Token `claude setup-token`. Diwarisi proses claude yang di-spawn."
5569
5703
  },
5570
- { key: "ANTHROPIC_API_KEY", group: "claude", label: "Anthropic API key", kind: "secret", apply: "new-session", category: "credential", inheritEnv: true },
5571
- { key: "HANOMAN_CLAUDE_BIN", group: "claude", label: "Biner claude", kind: "path", apply: "new-session", category: "knob", default: "claude" },
5704
+ { key: "ANTHROPIC_API_KEY", group: "claude", label: "Anthropic API key", kind: "secret", apply: "new-session", category: "credential", example: "sk-ant-api03-\u2026", inheritEnv: true },
5705
+ { key: "HANOMAN_CLAUDE_BIN", group: "claude", label: "Biner claude", kind: "path", apply: "new-session", category: "knob", example: "/opt/homebrew/bin/claude", default: "claude" },
5572
5706
  // codex (SPEC-338 · ADR-0074)
5573
- { key: "HANOMAN_CODEX_BIN", group: "claude", label: "Biner codex", kind: "path", apply: "new-session", category: "knob", default: "codex" },
5707
+ { key: "HANOMAN_CODEX_BIN", group: "claude", label: "Biner codex", kind: "path", apply: "new-session", category: "knob", example: "/opt/homebrew/bin/codex", default: "codex" },
5574
5708
  {
5575
5709
  key: "CLAUDE_CONFIG_DIR",
5576
5710
  group: "claude",
@@ -5578,11 +5712,12 @@ var init_config_registry = __esm({
5578
5712
  kind: "path",
5579
5713
  apply: "new-session",
5580
5714
  category: "knob",
5715
+ example: "~/.claude",
5581
5716
  help: "Default ~/.claude. Sumber .credentials.json untuk panel usage/limit."
5582
5717
  },
5583
5718
  // vps
5584
- { key: "HANOMAN_SSH_KEY_DIR", group: "vps", label: "Dir key SSH", kind: "path", apply: "new-session", category: "knob", help: "Default ~/.hanoman." },
5585
- { key: "HANOMAN_SSH_BIN", group: "vps", label: "Biner ssh", kind: "path", apply: "new-session", category: "knob", default: "ssh" },
5719
+ { key: "HANOMAN_SSH_KEY_DIR", group: "vps", label: "Dir key SSH", kind: "path", apply: "new-session", category: "knob", example: "~/.hanoman", help: "Default ~/.hanoman." },
5720
+ { key: "HANOMAN_SSH_BIN", group: "vps", label: "Biner ssh", kind: "path", apply: "new-session", category: "knob", example: "/usr/bin/ssh", default: "ssh" },
5586
5721
  // github (SPEC-471 · ADR-0095 · tarik issue → backlog). Dua mode auth, satu jalur kode:
5587
5722
  // `gh` memakai keyring mesin; bila GITHUB_TOKEN diisi ia diteruskan sebagai GH_TOKEN ke
5588
5723
  // proses gh DAN dipakai jalur REST (hub VPS yang tak punya keyring).
@@ -5593,6 +5728,7 @@ var init_config_registry = __esm({
5593
5728
  kind: "secret",
5594
5729
  apply: "live",
5595
5730
  category: "credential",
5731
+ example: "ghp_\u2026",
5596
5732
  help: "PAT scope `repo` (atau `public_repo`). Kosong = andalkan `gh auth login` di mesin ini."
5597
5733
  },
5598
5734
  {
@@ -5602,6 +5738,7 @@ var init_config_registry = __esm({
5602
5738
  kind: "path",
5603
5739
  apply: "live",
5604
5740
  category: "knob",
5741
+ example: "/opt/homebrew/bin/gh",
5605
5742
  default: "gh",
5606
5743
  help: "Absen = tarik issue lewat HTTPS langsung ke api.github.com."
5607
5744
  },
@@ -5614,6 +5751,7 @@ var init_config_registry = __esm({
5614
5751
  kind: "secret",
5615
5752
  apply: "live",
5616
5753
  category: "credential",
5754
+ example: "123456789:AAE\u2026",
5617
5755
  pattern: "^\\d{5,}:[A-Za-z0-9_-]{30,}$",
5618
5756
  patternError: "format BotFather: <bot_id>:<secret>",
5619
5757
  help: "Token dari BotFather. Disimpan terenkripsi; tak pernah dikembalikan utuh."
@@ -5625,6 +5763,7 @@ var init_config_registry = __esm({
5625
5763
  kind: "secret",
5626
5764
  apply: "live",
5627
5765
  category: "credential",
5766
+ example: "hnm_agt_\u2026",
5628
5767
  pattern: "^\\S{20,}$",
5629
5768
  patternError: "plaintext AgentToken (hnm_agt_\u2026)",
5630
5769
  help: "Plaintext AgentToken ber-capability Telegram. Dipakai session operator, bukan bot."
@@ -5636,6 +5775,7 @@ var init_config_registry = __esm({
5636
5775
  kind: "string",
5637
5776
  apply: "live",
5638
5777
  category: "credential",
5778
+ example: "123456789, 987654321",
5639
5779
  pattern: "^\\d+(?:[\\s,]+\\d+)*$",
5640
5780
  patternError: "numeric user id, dipisah koma/spasi",
5641
5781
  help: "Siapa yang boleh memerintah bot. Numeric Telegram user id, bukan username."
@@ -5647,12 +5787,13 @@ var init_config_registry = __esm({
5647
5787
  kind: "string",
5648
5788
  apply: "live",
5649
5789
  category: "knob",
5790
+ example: "-1001234567890",
5650
5791
  pattern: "^-?\\d+$",
5651
5792
  patternError: "numeric chat id (channel/supergroup negatif)",
5652
5793
  help: "Tujuan Test Connection. Kosong = pakai satu-satunya id di allowlist."
5653
5794
  },
5654
5795
  // runtime
5655
- { key: "HANOMAN_EVENTS_TICK_MS", group: "runtime", label: "Interval events (ms)", kind: "int", apply: "live", category: "knob", default: "1000", min: 100 },
5796
+ { key: "HANOMAN_EVENTS_TICK_MS", group: "runtime", label: "Interval events (ms)", kind: "int", apply: "live", category: "knob", example: "1000", default: "1000", min: 100 },
5656
5797
  { key: "HANOMAN_UPDATE_FETCH", group: "runtime", label: "Deteksi update saat boot", kind: "bool", apply: "live", category: "knob", default: "1" },
5657
5798
  // SPEC-398 · ADR-0087 · deteksi update kini membandingkan semver dengan registry npm, bukan
5658
5799
  // menghitung commit di repo git (HANOMAN_REPO_ROOT dicabut bersama jalur git itu).
@@ -5663,6 +5804,7 @@ var init_config_registry = __esm({
5663
5804
  kind: "string",
5664
5805
  apply: "live",
5665
5806
  category: "knob",
5807
+ example: "https://registry.npmjs.org",
5666
5808
  default: "https://registry.npmjs.org",
5667
5809
  help: "Sumber versi terbaru paket `hanoman`."
5668
5810
  },
@@ -5673,15 +5815,16 @@ var init_config_registry = __esm({
5673
5815
  kind: "string",
5674
5816
  apply: "restart",
5675
5817
  category: "knob",
5818
+ example: "hanoman",
5676
5819
  default: "hanoman",
5677
5820
  help: "Mengubah ini TIDAK memindahkan sesi tmux yang sudah hidup \u2014 berlaku setelah restart."
5678
5821
  },
5679
5822
  // gitGraph (SPEC-233 · preferensi tampilan git graph; semua live, dikonsumsi client)
5680
- { key: "gitGraph.style", group: "gitGraph", label: "Gaya graph", kind: "string", apply: "live", category: "knob", default: "rounded", help: "rounded | angular" },
5681
- { key: "gitGraph.colours", group: "gitGraph", label: "Warna lane (CSV)", kind: "string", apply: "live", category: "knob", help: "Daftar warna hex dipisah koma; kosong = palet bawaan." },
5682
- { key: "gitGraph.dateType", group: "gitGraph", label: "Jenis tanggal", kind: "string", apply: "live", category: "knob", default: "author", help: "author | commit" },
5683
- { key: "gitGraph.commitsInitialLoad", group: "gitGraph", label: "Muat awal", kind: "int", apply: "live", category: "knob", default: "200", min: 1 },
5684
- { key: "gitGraph.commitsLoadMore", group: "gitGraph", label: "Muat lagi", kind: "int", apply: "live", category: "knob", default: "100", min: 1 },
5823
+ { key: "gitGraph.style", group: "gitGraph", label: "Gaya graph", kind: "string", apply: "live", category: "knob", example: "rounded", default: "rounded", help: "rounded | angular" },
5824
+ { key: "gitGraph.colours", group: "gitGraph", label: "Warna lane (CSV)", kind: "string", apply: "live", category: "knob", example: "#c9a227, #6b8f71, #b4614e", help: "Daftar warna hex dipisah koma; kosong = palet bawaan." },
5825
+ { key: "gitGraph.dateType", group: "gitGraph", label: "Jenis tanggal", kind: "string", apply: "live", category: "knob", example: "author", default: "author", help: "author | commit" },
5826
+ { key: "gitGraph.commitsInitialLoad", group: "gitGraph", label: "Muat awal", kind: "int", apply: "live", category: "knob", example: "200", default: "200", min: 1 },
5827
+ { key: "gitGraph.commitsLoadMore", group: "gitGraph", label: "Muat lagi", kind: "int", apply: "live", category: "knob", example: "100", default: "100", min: 1 },
5685
5828
  { key: "gitGraph.showRemoteBranches", group: "gitGraph", label: "Tampilkan remote", kind: "bool", apply: "live", category: "knob", default: "1" },
5686
5829
  { key: "gitGraph.showTags", group: "gitGraph", label: "Tampilkan tag", kind: "bool", apply: "live", category: "knob", default: "1" },
5687
5830
  { key: "gitGraph.showStashes", group: "gitGraph", label: "Tampilkan stash", kind: "bool", apply: "live", category: "knob", default: "1" },
@@ -5691,7 +5834,7 @@ var init_config_registry = __esm({
5691
5834
  { key: "gitGraph.combineLocalRemote", group: "gitGraph", label: "Gabung label local+remote", kind: "bool", apply: "live", category: "knob", default: "0" },
5692
5835
  { key: "gitGraph.markdown", group: "gitGraph", label: "Render markdown pesan", kind: "bool", apply: "live", category: "knob", default: "1" },
5693
5836
  { key: "gitGraph.emoji", group: "gitGraph", label: "Render emoji shortcode", kind: "bool", apply: "live", category: "knob", default: "1" },
5694
- { key: "gitGraph.issueLinkPattern", group: "gitGraph", label: "Pola link issue", kind: "string", apply: "live", category: "knob", help: "URL dengan $1 untuk nomor issue, mis. https://github.com/acme/app/issues/$1" },
5837
+ { key: "gitGraph.issueLinkPattern", group: "gitGraph", label: "Pola link issue", kind: "string", apply: "live", category: "knob", example: "https://github.com/acme/app/issues/$1", help: "URL dengan $1 untuk nomor issue, mis. https://github.com/acme/app/issues/$1" },
5695
5838
  // bootstrap (read-only)
5696
5839
  { key: "DATABASE_URL", group: "bootstrap", label: "DATABASE_URL", kind: "secret", apply: "restart", category: "bootstrap" },
5697
5840
  { key: "TEST_DATABASE_URL", group: "bootstrap", label: "TEST_DATABASE_URL", kind: "secret", apply: "restart", category: "bootstrap" },
@@ -5763,6 +5906,7 @@ var init_webhook = __esm({
5763
5906
  "baseSha",
5764
5907
  "headSha",
5765
5908
  "dependsOn",
5909
+ "autoMerge",
5766
5910
  "createdAt",
5767
5911
  "startedAt",
5768
5912
  "updatedAt"
@@ -5792,6 +5936,7 @@ var init_webhook = __esm({
5792
5936
  baseSha: "5117298c5a3e63af76cbadaa46e2edfa50921d7",
5793
5937
  headSha: null,
5794
5938
  dependsOn: null,
5939
+ autoMerge: null,
5795
5940
  createdAt: "2026-08-01T02:10:00.000Z",
5796
5941
  startedAt: "2026-08-01T02:12:31.000Z",
5797
5942
  updatedAt: "2026-08-01T09:41:22.000Z"
@@ -5811,6 +5956,7 @@ var init_webhook = __esm({
5811
5956
  "helpEnabled",
5812
5957
  "schedulerOptIn",
5813
5958
  "leadOptIn",
5959
+ "autoMerge",
5814
5960
  "createdAt",
5815
5961
  "updatedAt"
5816
5962
  ],
@@ -5831,6 +5977,7 @@ var init_webhook = __esm({
5831
5977
  helpEnabled: false,
5832
5978
  schedulerOptIn: true,
5833
5979
  leadOptIn: false,
5980
+ autoMerge: null,
5834
5981
  createdAt: "2026-05-02T04:00:00.000Z",
5835
5982
  updatedAt: "2026-08-01T09:00:00.000Z"
5836
5983
  }
@@ -6625,7 +6772,7 @@ var init_mcp_catalog = __esm({
6625
6772
  {
6626
6773
  name: "hanoman_lead_ask",
6627
6774
  title: "Minta putusan hanoman-lead",
6628
- description: "Minta putusan ke hanoman-lead saat menemui persimpangan yang biasanya butuh manusia. Jawabannya terbaca mesin (`decision`, `reason`, `refs`, `confidence`, `action`) dan `refs` hanya memuat rujukan yang benar-benar ada di repo. 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.",
6775
+ 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.",
6629
6776
  inputSchema: obj({
6630
6777
  properties: {
6631
6778
  project: str("Id proyek."),
@@ -6633,7 +6780,10 @@ var init_mcp_catalog = __esm({
6633
6780
  spec: str("Opsional. Id backlog yang bersangkutan."),
6634
6781
  session: str("Opsional. Id sesi yang bersangkutan."),
6635
6782
  options: strArray("Opsional. Pilihan yang tersedia, maksimum 20, masing-masing maksimum 2000 karakter. Lead memilih salah satunya."),
6636
- context: str("Opsional. Konteks pendukung, maksimum 20.000 karakter.")
6783
+ context: str("Opsional. Konteks pendukung, maksimum 20.000 karakter."),
6784
+ multi: bool("Opsional. `true` bila opsinya TIDAK saling eksklusif dan lead boleh memilih beberapa sekaligus. Menuntut `options` terisi."),
6785
+ minChoices: int("Opsional, hanya untuk `multi`. Paling sedikit berapa opsi harus dipilih."),
6786
+ maxChoices: int("Opsional, hanya untuk `multi`. Paling banyak berapa opsi boleh dipilih; tanpa ini sebanyak opsinya.")
6637
6787
  },
6638
6788
  required: ["project", "question"]
6639
6789
  }),
@@ -6650,7 +6800,10 @@ var init_mcp_catalog = __esm({
6650
6800
  ...s(a.spec) ? { specId: a.spec } : {},
6651
6801
  ...s(a.session) ? { sessionId: a.session } : {},
6652
6802
  ...Array.isArray(a.options) ? { options: a.options } : {},
6653
- ...s(a.context) ? { context: a.context } : {}
6803
+ ...s(a.context) ? { context: a.context } : {},
6804
+ // SPEC-485 · ADITIF: tanpa `multi` bentuk permintaannya identik dengan sebelum ADR-0102,
6805
+ // jadi klien MCP lama tak berubah perilakunya satu bit pun.
6806
+ ...a.multi === true ? { select: { mode: "multi", min: n(a.minChoices) ?? 0, max: n(a.maxChoices) ?? null } } : {}
6654
6807
  }
6655
6808
  }),
6656
6809
  shape: (raw) => raw
@@ -6692,6 +6845,7 @@ var init_src = __esm({
6692
6845
  init_entities();
6693
6846
  init_agent();
6694
6847
  init_custom_agent();
6848
+ init_agent_catalog();
6695
6849
  init_github();
6696
6850
  init_lead();
6697
6851
  init_dto();
@@ -6705,6 +6859,7 @@ var init_src = __esm({
6705
6859
  init_telegram();
6706
6860
  init_webhook();
6707
6861
  init_mcp();
6862
+ init_auto_merge();
6708
6863
  }
6709
6864
  });
6710
6865
 
@@ -28273,8 +28428,11 @@ var init_migrate_pg = __esm({
28273
28428
  "SyncOutbox",
28274
28429
  "SyncState",
28275
28430
  "SyncConflict",
28431
+ // SPEC-485 · ADR-0102 · LeadFlow SEBELUM LeadDecision: `flowId` menunjuk ke sana. Tanpa FK, tapi
28432
+ // urutan tabel harus tetap mencerminkan arah tautannya bagi pembaca berikutnya.
28276
28433
  "SchedulerQueueItem",
28277
28434
  "RuntimeConfig",
28435
+ "LeadFlow",
28278
28436
  "LeadDecision",
28279
28437
  // SPEC-476 · ADR-0096 · tabel gateway LOCAL-only; sumber Postgres lama lazimnya tidak punya
28280
28438
  // tabel ini dan jalur 42P01 di bawah memperlakukannya sebagai nol baris.
@@ -28559,7 +28717,7 @@ function packageJsonFor(version3, deps) {
28559
28717
  prepublishOnly: "node dist/cli.js __verify"
28560
28718
  },
28561
28719
  engines: { node: ">=20" },
28562
- files: ["bin", "dist", "web", "prisma", "README.md", "LICENSE"],
28720
+ files: ["bin", "dist", "web", "prisma", "docs", "README.md", "LICENSE"],
28563
28721
  dependencies: deps,
28564
28722
  // MIT: paket ini didistribusikan publik supaya orang `npm i -g`. `UNLICENSED` berarti "tak ada
28565
28723
  // izin pakai" — 0.1.0 terbit dengan kontradiksi itu, dan versi terbit tak bisa diperbaiki.
@@ -28581,6 +28739,9 @@ function copyPlan(repo) {
28581
28739
  { from: join10(repo, "server/prisma/schema.prisma"), to: "prisma/schema.prisma" },
28582
28740
  { from: join10(repo, "server/prisma/migrations"), to: "prisma/migrations", dir: true },
28583
28741
  { from: join10(repo, "internal/docs/operations/npm-readme.md"), to: "README.md" },
28742
+ // SPEC-489 · dibaca runtime oleh pickGuideFile (kandidat `<pkg>/docs/…`). Bukan README:
28743
+ // README paket adalah npm-readme.md (berhadapan-MANUSIA), ini naskah berhadapan-AGEN.
28744
+ { from: join10(repo, "docs/agent-integration.md"), to: "docs/agent-integration.md" },
28584
28745
  { from: join10(repo, "LICENSE"), to: "LICENSE" }
28585
28746
  ];
28586
28747
  }
@@ -28609,7 +28770,11 @@ var init_pack = __esm({
28609
28770
  "prisma/schema.prisma",
28610
28771
  "web/index.html",
28611
28772
  "README.md",
28612
- "LICENSE"
28773
+ "LICENSE",
28774
+ // SPEC-489 · naskah panduan AI agent — dibaca runtime & disajikan di GET /api/agent-integration.md.
28775
+ // Tanpa gerbang ini paket bisa terbit tanpa dokumen dan endpoint-nya 404 di SETIAP instalasi npm,
28776
+ // sementara checkout dev terlihat sehat.
28777
+ "docs/agent-integration.md"
28613
28778
  ];
28614
28779
  BIN_SHIM = `#!/usr/bin/env node
28615
28780
  // SPEC-398 \xB7 shim tipis: seluruh logika ada di dist/cli.js (bundle esbuild).