hanoman 0.9.35 → 0.9.36

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 (22) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/cli.js +57 -10
  3. package/dist/server.js +212 -90
  4. package/package.json +1 -1
  5. package/web/assets/{DalangHanomanScreen-BVu8tGjd.js → DalangHanomanScreen-NmpJIcg6.js} +1 -1
  6. package/web/assets/{DocPreviewModal-DwhXR__7.js → DocPreviewModal-DK597e3s.js} +1 -1
  7. package/web/assets/{DocsWorkspace-poV6NPLq.js → DocsWorkspace-BQX-HlzW.js} +1 -1
  8. package/web/assets/{IdeScreen-BpGoKqok.js → IdeScreen-LeUPaFsS.js} +1 -1
  9. package/web/assets/{LeadScreen-Bqj27JHC.js → LeadScreen-BVCKv46y.js} +1 -1
  10. package/web/assets/{ReviewScreen-ykVVM8ir.js → ReviewScreen-C6NcJChq.js} +1 -1
  11. package/web/assets/{SchedulerScreen-26uRd--J.js → SchedulerScreen-2AH0R3TJ.js} +1 -1
  12. package/web/assets/{SettingsScreen-D7qZqj-w.js → SettingsScreen-B4SVzh9m.js} +1 -1
  13. package/web/assets/{TeamScreen-NC9C8iL8.js → TeamScreen-6XEfuZUm.js} +1 -1
  14. package/web/assets/{TerminalScreen-Do49p9t6.js → TerminalScreen-DubD6vT-.js} +1 -1
  15. package/web/assets/{TriageScreen-DzKT-O0A.js → TriageScreen-BYJxyCar.js} +1 -1
  16. package/web/assets/{VpsScreen-bVd5YC2y.js → VpsScreen-Df37dgKx.js} +1 -1
  17. package/web/assets/{fields-Cqp-sr6L.js → fields-C94HI0V8.js} +1 -1
  18. package/web/assets/{index-BjNlrKWq.js → index-BeFSh2Ba.js} +207 -207
  19. package/web/assets/{live-COTGt6Jw.js → live-5ezoV9Fs.js} +1 -1
  20. package/web/assets/{sched-lead-shared-Xe8lxNc_.js → sched-lead-shared-BpZu6N2A.js} +1 -1
  21. package/web/assets/{settings-rebase-Cp2i2t6-.js → settings-rebase-DaTGgcuO.js} +1 -1
  22. package/web/index.html +1 -1
package/dist/server.js CHANGED
@@ -4299,6 +4299,8 @@ var init_method_catalog = __esm({
4299
4299
  "superpowers:test-driven-development",
4300
4300
  VERIFICATION_GATE
4301
4301
  ],
4302
+ // ADR-0230 · fase Review resmi: audit spec & plan vs diff + gerbang verifikasi.
4303
+ Review: ["superpowers:requesting-code-review", VERIFICATION_GATE],
4302
4304
  // SPEC-407 · fase `Goal` sengaja TANPA skill: seluruh inti flow itu membebaskan sesi dari
4303
4305
  // proses kaku. Yang tetap dijaga cuma pintu keluarnya.
4304
4306
  Verifikasi: [VERIFICATION_GATE]
@@ -4342,6 +4344,8 @@ var init_method_catalog = __esm({
4342
4344
  "mattpocock-skills:tdd",
4343
4345
  "mattpocock-skills:code-review"
4344
4346
  ],
4347
+ // ADR-0230 · fase Review resmi: audit spec & plan vs diff + gerbang verifikasi.
4348
+ Review: ["mattpocock-skills:code-review", VERIFICATION_GATE],
4345
4349
  Verifikasi: [VERIFICATION_GATE]
4346
4350
  },
4347
4351
  exitSkills: [VERIFICATION_GATE],
@@ -4382,6 +4386,8 @@ var init_method_catalog = __esm({
4382
4386
  "agent-skills:test-driven-development",
4383
4387
  "agent-skills:code-review-and-quality"
4384
4388
  ],
4389
+ // ADR-0230 · fase Review resmi: audit spec & plan vs diff + gerbang verifikasi.
4390
+ Review: ["agent-skills:code-review-and-quality", VERIFICATION_GATE],
4385
4391
  Verifikasi: [VERIFICATION_GATE]
4386
4392
  },
4387
4393
  exitSkills: [VERIFICATION_GATE],
@@ -5168,7 +5174,7 @@ var init_logs = __esm({
5168
5174
  function phaseAgentName(phase) {
5169
5175
  return PHASE_AGENT_PREFIX + phase.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5170
5176
  }
5171
- var ORCHESTRATION_FLOWS, FLOW_PHASES, PHASE_AGENT_PREFIX, isPhaseAgentName, PHASE_EVIDENCE_GRACE_MS, EXECUTE_MODE_FLOWS, REVIEWER_CELL, REVIEWER_FLOWS, REVIEWER_DEFAULTS;
5177
+ var ORCHESTRATION_FLOWS, FLOW_PHASES, PHASE_AGENT_PREFIX, isPhaseAgentName, PHASE_EVIDENCE_GRACE_MS, EXECUTE_MODE_FLOWS, REVIEWER_CELL, REVIEWER_FLOWS, REVIEWER_DEFAULTS, UNSKIPPABLE_PHASES;
5172
5178
  var init_orchestration = __esm({
5173
5179
  "../shared/src/orchestration.ts"() {
5174
5180
  "use strict";
@@ -5184,8 +5190,9 @@ var init_orchestration = __esm({
5184
5190
  "no_effort"
5185
5191
  ];
5186
5192
  FLOW_PHASES = {
5187
- feature: ["Brainstorm", "Objective", "Spec", "Plan", "Execute"],
5188
- qa: ["Audit", "Spec", "Plan", "Execute"],
5193
+ // ADR-0230 · `Review` = fase resmi sesudah Execute (dulu langkah R di dalam Execute, ADR-0170 #4).
5194
+ feature: ["Brainstorm", "Objective", "Spec", "Plan", "Execute", "Review"],
5195
+ qa: ["Audit", "Spec", "Plan", "Execute", "Review"],
5189
5196
  scaffold: ["Brainstorm", "Objective", "Doc index"],
5190
5197
  reverse: ["Scan", "Docs teknis", "Wawancara", "Konvensi & index", "Serah terima"],
5191
5198
  prd: ["Brainstorm", "PRD"],
@@ -5208,12 +5215,15 @@ var init_orchestration = __esm({
5208
5215
  ORCHESTRATION_FLOWS.filter((f) => FLOW_PHASES[f].some((p3) => p3 === "Execute" || p3 === "Goal"))
5209
5216
  );
5210
5217
  REVIEWER_CELL = "Review";
5211
- REVIEWER_FLOWS = /* @__PURE__ */ new Set(["feature", "qa"]);
5218
+ REVIEWER_FLOWS = new Set(
5219
+ ORCHESTRATION_FLOWS.filter((f) => FLOW_PHASES[f].includes(REVIEWER_CELL))
5220
+ );
5212
5221
  REVIEWER_DEFAULTS = {
5213
5222
  claude: { model: "opus", effort: "high" },
5214
5223
  codex: { model: "gpt-5.6-sol", effort: "high" },
5215
5224
  antigravity: { model: "inherit", effort: null }
5216
5225
  };
5226
+ UNSKIPPABLE_PHASES = /* @__PURE__ */ new Set([REVIEWER_CELL]);
5217
5227
  }
5218
5228
  });
5219
5229
 
@@ -5224,7 +5234,7 @@ var init_runtime_defaults = __esm({
5224
5234
  "use strict";
5225
5235
  init_orchestration();
5226
5236
  init_zod();
5227
- RUNTIME_DEFAULTS_VERSION = "2026-09-23-v2";
5237
+ RUNTIME_DEFAULTS_VERSION = "2026-10-25-v1";
5228
5238
  zRuntimeDefaultState = external_exports.enum(["seeded", "user"]);
5229
5239
  zBuiltinRuntimeDefaults = external_exports.object({
5230
5240
  version: external_exports.string().default(RUNTIME_DEFAULTS_VERSION),
@@ -5281,14 +5291,16 @@ var init_runtime_defaults = __esm({
5281
5291
  Objective: cell("sonnet", "medium"),
5282
5292
  Spec: cell("opus", "high"),
5283
5293
  Plan: cell("opus", "high"),
5284
- Execute: cell("sonnet", "high")
5294
+ Execute: cell("sonnet", "high"),
5295
+ Review: cell("opus", "high")
5285
5296
  },
5286
5297
  {
5287
5298
  Brainstorm: cell("gpt-5.6-terra", "medium"),
5288
5299
  Objective: cell("gpt-5.6-terra", "medium"),
5289
5300
  Spec: cell("gpt-5.6-sol", "high"),
5290
5301
  Plan: cell("gpt-5.6-sol", "high"),
5291
- Execute: cell("gpt-5.6-terra", "high")
5302
+ Execute: cell("gpt-5.6-terra", "high"),
5303
+ Review: cell("gpt-5.6-sol", "high")
5292
5304
  }
5293
5305
  ),
5294
5306
  qa: flow(
@@ -5296,13 +5308,15 @@ var init_runtime_defaults = __esm({
5296
5308
  Audit: cell("opus", "high"),
5297
5309
  Spec: cell("opus", "high"),
5298
5310
  Plan: cell("sonnet", "high"),
5299
- Execute: cell("sonnet", "high")
5311
+ Execute: cell("sonnet", "high"),
5312
+ Review: cell("opus", "high")
5300
5313
  },
5301
5314
  {
5302
5315
  Audit: cell("gpt-5.6-sol", "high"),
5303
5316
  Spec: cell("gpt-5.6-sol", "high"),
5304
5317
  Plan: cell("gpt-5.6-terra", "high"),
5305
- Execute: cell("gpt-5.6-terra", "high")
5318
+ Execute: cell("gpt-5.6-terra", "high"),
5319
+ Review: cell("gpt-5.6-sol", "high")
5306
5320
  }
5307
5321
  ),
5308
5322
  scaffold: flow(
@@ -5856,20 +5870,16 @@ function resolvePhasePlan(input) {
5856
5870
  const model = input.runtime === "claude" && picked === CLAUDE_DEFAULT_ALIAS ? CLAUDE_SUBAGENT_INHERIT : picked;
5857
5871
  return { model, effort: coerce3(model, override?.effort ?? cell4?.effort ?? fallback3.effort ?? "medium") };
5858
5872
  };
5859
- const reviewer = REVIEWER_FLOWS.has(input.flow) ? {
5860
- agentName: phaseAgentName(REVIEWER_CELL),
5861
- phase: "Execute",
5862
- ...resolve25(REVIEWER_CELL, REVIEWER_DEFAULTS[input.runtime])
5863
- } : void 0;
5864
5873
  return {
5865
5874
  flow: input.flow,
5866
5875
  runtime: input.runtime,
5876
+ // ADR-0170 P2 · ADR-0230 · fase Review TIDAK mewarisi orchestrator: default-nya konstanta peran,
5877
+ // sel/override `Review` menimpanya.
5867
5878
  phases: FLOW_PHASES[input.flow].map((phase) => ({
5868
5879
  phase,
5869
5880
  agentName: phaseAgentName(phase),
5870
- ...resolve25(phase, input.orchestrator)
5881
+ ...resolve25(phase, phase === REVIEWER_CELL ? REVIEWER_DEFAULTS[input.runtime] : input.orchestrator)
5871
5882
  })),
5872
- ...reviewer ? { reviewer } : {},
5873
5883
  // Amandemen ADR-0170 P2 · cara Execute mengerjakan plan; hanya dipakai sesi claude.
5874
5884
  executeMode: cfg?.executeMode ?? "inline"
5875
5885
  };
@@ -16766,15 +16776,25 @@ function parseHookEvent(body) {
16766
16776
  }
16767
16777
  const turn = zTurnEnd.safeParse(body);
16768
16778
  if (turn.success && !turn.data.stop_hook_active) {
16779
+ if (turn.data.background_tasks?.some((t) => t.status === "running")) return null;
16780
+ const message = clip3(turn.data.last_assistant_message, ASK_MESSAGE_MAX);
16769
16781
  return {
16770
16782
  source: "turn-end",
16771
- askId: turn.data.turn_id,
16783
+ askId: turn.data.turn_id ?? `stop:${turn.data.prompt_id ?? ""}:${fnv1a(message)}`,
16772
16784
  questions: [],
16773
- message: clip3(turn.data.last_assistant_message, ASK_MESSAGE_MAX)
16785
+ message
16774
16786
  };
16775
16787
  }
16776
16788
  return null;
16777
16789
  }
16790
+ function fnv1a(s2) {
16791
+ let h2 = 2166136261;
16792
+ for (let i = 0; i < s2.length; i++) {
16793
+ h2 ^= s2.charCodeAt(i);
16794
+ h2 = Math.imul(h2, 16777619);
16795
+ }
16796
+ return (h2 >>> 0).toString(16);
16797
+ }
16778
16798
  var ASK_QUESTION_MAX, ASK_MESSAGE_MAX, clip3, ASK_MAX_QUESTIONS, ASK_MAX_OPTIONS, zOption, zQuestion, zAskTool, zTurnEnd;
16779
16799
  var init_session_ask = __esm({
16780
16800
  "../shared/src/session-ask.ts"() {
@@ -16803,7 +16823,11 @@ var init_session_ask = __esm({
16803
16823
  });
16804
16824
  zTurnEnd = external_exports.object({
16805
16825
  hook_event_name: external_exports.literal("Stop"),
16806
- turn_id: external_exports.string().min(1),
16826
+ // ADR-0231 · codex mengirim `turn_id`; Stop claude (sesi bertanya lewat teks) tidak — ia membawa
16827
+ // `prompt_id` dan `background_tasks`.
16828
+ turn_id: external_exports.string().min(1).optional(),
16829
+ prompt_id: external_exports.string().optional(),
16830
+ background_tasks: external_exports.array(external_exports.object({ status: external_exports.string().optional() }).passthrough()).optional(),
16807
16831
  last_assistant_message: external_exports.string().optional(),
16808
16832
  // Codex menyalakan bendera ini saat Stop dipicu oleh hook Stop lain. Melayani giliran yang
16809
16833
  // dibangkitkan gate mode goal (ADR-0074) berarti lead menjawab dirinya sendiri.
@@ -17359,29 +17383,25 @@ function orchestratorClause(plan, o = {}) {
17359
17383
  const resume = antigravity2 ? "send_message ke conversation ID child yang sama" : codex2 ? "send_input ke agent id yang sama" : "SendMessage ke agent ID yang kamu catat";
17360
17384
  const describeRule = antigravity2 ? "Cantumkan label `Fase <Nama Fase>` dalam tugas. Pilih nama definisi native fase, JANGAN membuat agen umum pengganti. Catat conversation ID hasil invoke_subagent; ID itu dipakai untuk SEMUA relay keputusan dan rework lewat send_message, bukan invoke_subagent baru. Gunakan tool native langsung, bukan shell atau CLI hanoman untuk menyimulasikan delegasi. Bila tool tidak tersedia, laporkan galat langkah 3; jangan membuat bridge atau mengeksekusi isi fase sendiri." : codex2 ? "Deskripsi/label pemanggilan diisi persis `Fase <Nama Fase>` \u2014 tanpa nomor urut, dan BUKAN baris pertama blok serah-terima di atas." : "Deskripsi pemanggilan (parameter `description`) diisi persis `Fase <Nama Fase>` \u2014 tanpa nomor urut, dan BUKAN baris pertama blok serah-terima di atas.\nJANGAN pernah memanggil tool Agent tanpa `subagent_type` atau dengan `general-purpose`: fase itu jatuh ke agen umum ber-model orchestrator dan model/effort fasenya hilang. Sebelum memanggil, periksa ulang bahwa `subagent_type` = nama agen fase itu; bila tool menolaknya, itu galat langkah 3 \u2014 bukan alasan beralih ke agen umum. Catat agent ID yang dikembalikan hasil pemanggilan: itulah satu-satunya pegangan untuk melanjutkan agen yang SAMA. Melanjutkan agen itu (relay jawaban langkah 4) HANYA lewat SendMessage \u2014 tool Agent tak punya parameter `to`, dan memanggilnya lagi melahirkan agen BARU tanpa konteks (duplikat).";
17361
17385
  const retry = antigravity2 ? " (invoke_subagent BARU dengan nama definisi fase yang sama)" : codex2 ? "" : " (pemanggilan tool Agent BARU dengan `subagent_type` yang sama)";
17362
- const askEscalation = codex2 || antigravity2 ? "tanyakan di terminal ini lalu tunggu jawaban" : "tanyakan lewat AskUserQuestion";
17363
- const askDecisions = antigravity2 ? "di terminal ini sekaligus dalam satu pesan, tiap pertanyaan bernomor dan diakhiri `?` beserta opsi, rekomendasi dan alasannya, lalu tunggu jawaban" : codex2 ? "di terminal ini sekaligus dalam satu pesan, tiap pertanyaan bernomor dan diakhiri `?` beserta opsinya, lalu tunggu jawaban" : "lewat AskUserQuestion \u2014 tool itu memuat paling banyak 4 pertanyaan per panggilan, jadi pecah sisanya ke panggilan berikutnya sampai semuanya terjawab";
17386
+ const askEscalation = codex2 || antigravity2 ? "tanyakan di terminal ini lalu tunggu jawaban" : ASK_ESCALATION_TOOL;
17387
+ const askDecisions = antigravity2 ? "di terminal ini sekaligus dalam satu pesan, tiap pertanyaan bernomor dan diakhiri `?` beserta opsi, rekomendasi dan alasannya, lalu tunggu jawaban" : codex2 ? "di terminal ini sekaligus dalam satu pesan, tiap pertanyaan bernomor dan diakhiri `?` beserta opsinya, lalu tunggu jawaban" : ASK_DECISIONS_TOOL;
17364
17388
  const turnTaking = plan.flow === "reverse" || plan.flow === "scaffold" || plan.flow === "prd" ? " Fase yang bergiliran dengan manusia (Wawancara, Brainstorm prd/scaffold) melapor satu topik per putaran \u2014 relay tiap putaran dengan cara yang sama, bawa konteks & opsi dari laporannya, dan biarkan jawaban bebas." : "";
17365
17389
  const handoff = plan.phases.some((p3) => p3.phase === "Serah terima") ? "Sesudah fase Serah terima tercatat, tampilkan bagian `Ringkasan serah terima:` dari laporannya APA ADANYA ke manusia di terminal ini (teks biasa \u2014 informasi, bukan pertanyaan) sebelum menutup pekerjaan." : "";
17366
17390
  const runtimeLabel = (entry2) => antigravity2 ? `tier native ${entry2.model} \xB7 effort native unknown` : `${entry2.model} \xB7 ${entry2.effort}`;
17367
17391
  const phaseList = plan.phases.map((p3, i) => `${i + 1}. ${p3.phase} \u2192 \`${p3.agentName}\` \xB7 ${runtimeLabel(p3)}`).join("\n");
17368
- const reviewer = plan.reviewer && plan.phases.some((p3) => p3.phase === plan.reviewer.phase) ? plan.reviewer : void 0;
17369
- const list2 = reviewer ? `${phaseList}
17370
- Reviewer Execute \u2192 \`${reviewer.agentName}\` \xB7 ${runtimeLabel(reviewer)} (bukan fase \u2014 hanya dipanggil di langkah R)` : phaseList;
17392
+ const review = plan.phases.find((p3) => p3.phase === REVIEWER_CELL);
17371
17393
  const hasExecute = plan.phases.some((p3) => p3.phase === "Execute");
17372
- const artefactRule = "Baris `Artefak fase sebelumnya:` diisi path PERSIS yang dilaporkan fase sebelumnya di `Artefak:` laporannya" + (hasExecute ? " \u2014 untuk Execute: dokumen spec DAN berkas plan yang dilaporkan fase Plan" : "") + ", bukan pola glob.";
17373
- const reviewStep = reviewer ? "R. Review Execute: sesudah agen Execute melapor `Status: selesai` dengan bukti dan tanpa keputusan terbuka, SEBELUM menulis `Execute done`, panggil " + (antigravity2 ? `\`${reviewer.agentName}\` lewat invoke_subagent` : codex2 ? `\`${reviewer.agentName}\` lewat spawn_agent` : `tool Agent dengan \`subagent_type\` \`${reviewer.agentName}\``) + ` (deskripsi \`Review Execute\`) dengan blok serah-terima:
17374
- Tujuan: <objective backlog>
17375
- Base SHA: $HANOMAN_BASE_SHA
17394
+ const artefactRule = "Baris `Artefak fase sebelumnya:` diisi path PERSIS yang dilaporkan fase sebelumnya di `Artefak:` laporannya" + (hasExecute ? " \u2014 untuk Execute: dokumen spec DAN berkas plan yang dilaporkan fase Plan" : "") + (review ? "; untuk Review: dokumen spec & berkas plan yang SAMA dengan Execute" : "") + ", bukan pola glob.";
17395
+ const reviewStep = review ? `R. Fase Review: sesudah \`Execute done\` tercatat, panggil agen fasenya seperti langkah 1 dengan baris serah-terima tambahan:
17376
17396
  Artefak: <path PERSIS dokumen spec & berkas plan> (qa yang melewati Spec & Plan: dokumen audit)
17377
17397
  Laporan Execute: <tabel AC \u2192 bukti dari laporannya>
17378
17398
  Putaran rework: <r>/2
17379
17399
 
17380
- \`Verdict: lulus\` \u2192 langkah 2 (tulis \`Execute done\`). \`Verdict: rework\` \u2192 lanjutkan agen Execute yang SAMA lewat ${resume} dengan \`Temuan wajib diperbaiki:\` reviewer apa adanya; sesudah ia melapor \`selesai\` lagi, lanjutkan reviewer yang SAMA lewat ${resume} dengan laporan barunya. Batasnya maks 2 putaran rework: masih \`rework\` sesudah putaran ke-2 \u2192 perlakukan temuan tersisa sebagai \`Keputusan terbuka\` langkah 4 (tanyakan, teruskan jawabannya ke agen Execute, lalu review lagi). Putaran rework bukan percobaan ulang langkah 3; reviewer galat atau tanpa baris \`Verdict:\` \u2192 langkah 3 berlaku padanya, \`menunggu-keputusan\` \u2192 langkah 4. JANGAN memperbaiki temuan sendiri dan JANGAN menulis \`Execute done\` tanpa \`Verdict: lulus\`.` : "";
17400
+ \`Verdict: lulus\` \u2192 langkah 2 (tulis \`Review done\`). \`Verdict: rework\` \u2192 lanjutkan agen Execute yang SAMA lewat ${resume} dengan \`Temuan wajib diperbaiki:\` reviewer apa adanya; sesudah ia melapor \`selesai\` lagi, lanjutkan reviewer yang SAMA lewat ${resume} dengan laporan barunya. Batasnya maks 2 putaran rework: masih \`rework\` sesudah putaran ke-2 \u2192 perlakukan temuan tersisa sebagai \`Keputusan terbuka\` langkah 4 (tanyakan, teruskan jawabannya ke agen Execute, lalu review lagi). Putaran rework bukan percobaan ulang langkah 3 dan tak menulis ulang \`Execute done\`; reviewer galat atau tanpa baris \`Verdict:\` \u2192 langkah 3 berlaku padanya, \`menunggu-keputusan\` \u2192 langkah 4. JANGAN memperbaiki temuan sendiri, JANGAN menulis \`Review done\` tanpa \`Verdict: lulus\`, dan Review tak pernah \`skipped\`.` : "";
17381
17401
  return [
17382
17402
  antigravity2 ? "Sesi ini ORCHESTRATOR. Setiap fase dikerjakan child native persisten miliknya dengan tier inherit|flash|pro, bukan slug model parent atau effort independen. Runtime asynchronous: protocol wait mewajibkan menunggu laporan child sebelum fase berikutnya, relay atau marker; ini bukan jaminan foreground runtime, model exact atau effort terkunci. Jangan gunakan CLI one-shot/bridge untuk sesi kerja child. Kamu TIDAK mengerjakan isi fase sendiri." : "Sesi ini ORCHESTRATOR. Setiap fase dikerjakan subagent fase miliknya dengan model & effort yang sudah terkunci di definisinya \u2014 kamu TIDAK mengerjakan isi fase sendiri.",
17383
17403
  `Fase berurutan:
17384
- ${list2}`,
17404
+ ${phaseList}`,
17385
17405
  "Untuk SETIAP fase, berurutan:",
17386
17406
  `1. Panggil subagent fasenya lewat ${call}, tugasnya berupa blok serah-terima berbentuk tetap:
17387
17407
  Urutan: <n>/<total> \xB7 <Nama Fase>
@@ -17393,14 +17413,14 @@ Lampiran: <path INDEX.md lampiran, atau ->
17393
17413
  Percobaan: <k>/2
17394
17414
 
17395
17415
  ${artefactRule} ${describeRule}`,
17396
- '2. Baca laporannya. `Status: selesai` DENGAN bukti \u2192 SEBELUM hal lain (memanggil fase berikutnya, commit, atau push): jalankan persis `echo "<Nama Fase> done" >> "$HANOMAN_PHASE_FILE"`, lalu verifikasi dengan `tail -1 "$HANOMAN_PHASE_FILE"` bahwa barisnya benar tertulis. Kamu satu-satunya penulis berkas itu.' + (reviewer ? " Khusus Execute: langkah R dulu; marker ditulis sesudah `Verdict: lulus`." : ""),
17416
+ '2. Baca laporannya. `Status: selesai` DENGAN bukti \u2192 SEBELUM hal lain (memanggil fase berikutnya, commit, atau push): jalankan persis `echo "<Nama Fase> done" >> "$HANOMAN_PHASE_FILE"`, lalu verifikasi dengan `tail -1 "$HANOMAN_PHASE_FILE"` bahwa barisnya benar tertulis. Kamu satu-satunya penulis berkas itu.' + (review ? " Khusus Review: marker hanya sesudah `Verdict: lulus` (langkah R)." : ""),
17397
17417
  `3. \`Status: sebagian\`/\`terhalang\`, galat, atau laporan tanpa bukti \u2192 delegasikan ULANG SEKALI ke subagent fase yang sama${retry} dengan laporan gagalnya disertakan (\`Percobaan: 2/2\`). Gagal lagi \u2192 BERHENTI dan ${askEscalation} apa yang harus dilakukan. Aturan ini berlaku walau klausa otonomi di prompt ini menyuruhmu tak bertanya.`,
17398
17418
  `4. \`Status: menunggu-keputusan\` atau \`Keputusan terbuka:\` yang bukan \`-\` \u2192 SEBELUM menulis marker fase atau memanggil fase berikutnya, ajukan SEMUA pertanyaannya ${askDecisions}, lalu LANJUTKAN subagent yang SAMA lewat ${resume} dengan seluruh jawabannya. Ulangi selama subagent masih melaporkan keputusan terbuka \u2014 tak ada batas jumlah pertanyaan maupun putaran, dan giliran relay ini bukan percobaan ulang. JANGAN pernah menjawab sendiri, termasuk di sesi scheduler: yang menjawab ${DECIDER} \u2014 hanoman yang merutekannya, kamu cukup bertanya.${turnTaking}`,
17399
17419
  o.fastPath ? '5. `Rekomendasi fase: jalur-cepat` sesudah Audit \u2192 SEBELUM lanjut ke Execute: jalankan `echo "Spec skipped" >> "$HANOMAN_PHASE_FILE"` lalu `echo "Plan skipped" >> "$HANOMAN_PHASE_FILE"` (gerbang yang sama seperti langkah 2), lalu lanjut ke Execute. `penuh` \u2192 Spec \u2192 Plan \u2192 Execute.' : "",
17400
17420
  reviewStep,
17401
17421
  handoff,
17402
17422
  "DILARANG mengerjakan isi fase sendiri \u2014 termasuk saat subagent gagal. Menulis `skipped` untuk fase yang dilewati bukan mengerjakannya.",
17403
- 'Pekerjaan ini BELUM selesai sampai SEMUA fase di daftar di atas punya baris `done` atau `skipped` di $HANOMAN_PHASE_FILE \u2014 JANGAN commit/push final atau menyatakan tuntas sebelum itu. Periksa dengan `cat "$HANOMAN_PHASE_FILE"` sebelum commit terakhir.'
17423
+ "Pekerjaan ini BELUM selesai sampai SEMUA fase di daftar di atas punya baris `done` atau `skipped` di $HANOMAN_PHASE_FILE" + (review ? " (Review wajib `done`)" : "") + ' \u2014 JANGAN commit/push final atau menyatakan tuntas sebelum itu. Periksa dengan `cat "$HANOMAN_PHASE_FILE"` sebelum commit terakhir.'
17404
17424
  ].filter(Boolean).join("\n\n");
17405
17425
  }
17406
17426
  function startPrompt(flow2, spec, branchTo, autonomy, verifyScope, method, attachments, plan) {
@@ -17438,13 +17458,15 @@ function continuePrompt(flow2, spec, branchTo, autonomy, verifyScope, method, at
17438
17458
  const m = resolveMethod(method);
17439
17459
  const head = `hanoman ${flow2} \u2014 MELANJUTKAN backlog item yang sebelumnya ditandai selesai padahal pekerjaannya belum tuntas. Ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.`;
17440
17460
  const push = `Setelah selesai: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`;
17461
+ const hasReview = plan ? plan.phases.some((p3) => p3.phase === REVIEWER_CELL) : PIPELINES[flow2].includes(REVIEWER_CELL);
17441
17462
  if (plan) {
17442
17463
  return [
17443
17464
  head,
17444
- `JANGAN mengulang fase awal \u2014 spec & plan sudah ada di ${m.planDir}/**. Lanjutkan HANYA fase Execute lewat subagent fasenya.`,
17465
+ `JANGAN mengulang fase awal \u2014 spec & plan sudah ada di ${m.planDir}/**. Lanjutkan HANYA fase ` + (hasReview ? "Execute lalu Review lewat subagent fasenya." : "Execute lewat subagent fasenya."),
17445
17466
  // S3 · berkas fase tak pernah dikosongkan: baris `Execute done` run lama masih ada dan akan
17446
17467
  // memenuhi gerbang penutup sebelum agen Execute sesi ini bekerja sama sekali.
17447
- "$HANOMAN_PHASE_FILE masih memuat baris `Execute done` LAMA dari sesi sebelumnya \u2014 baris itu TIDAK berlaku untuk sesi ini dan tidak memenuhi gerbang penutup. Sesudah agen fase Execute SESI INI melapor `Status: selesai` (dan lolos langkah R bila ada), tulis baris `Execute done` BARU (verifikasi `tail -1`); commit/push final baru sah sesudah baris baru itu.",
17468
+ // ADR-0230 · sama untuk `Review done` lama.
17469
+ hasReview ? "$HANOMAN_PHASE_FILE bisa masih memuat baris `Execute done` dan `Review done` LAMA dari sesi sebelumnya \u2014 keduanya TIDAK berlaku untuk sesi ini dan tidak memenuhi gerbang penutup. Sesudah agen fase Execute SESI INI melapor `Status: selesai`, tulis baris `Execute done` BARU; sesudah agen fase Review SESI INI melapor `Verdict: lulus`, tulis baris `Review done` BARU (verifikasi `tail -1` tiap kali); commit/push final baru sah sesudah kedua baris baru itu." : "$HANOMAN_PHASE_FILE masih memuat baris `Execute done` LAMA dari sesi sebelumnya \u2014 baris itu TIDAK berlaku untuk sesi ini dan tidak memenuhi gerbang penutup. Sesudah agen fase Execute SESI INI melapor `Status: selesai`, tulis baris `Execute done` BARU (verifikasi `tail -1`); commit/push final baru sah sesudah baris baru itu.",
17448
17470
  orchestratorClause(plan),
17449
17471
  autonomyClause(autonomy),
17450
17472
  orchestratorAttachmentClause(attachments),
@@ -17454,13 +17476,13 @@ function continuePrompt(flow2, spec, branchTo, autonomy, verifyScope, method, at
17454
17476
  }
17455
17477
  return [
17456
17478
  head,
17457
- `JANGAN mengulang fase awal \u2014 spec & plan sudah ada. Lanjut di fase Execute: baca plan di ${m.planDir}/** untuk backlog item ini, periksa task yang sudah \`[x]\` dan selesaikan yang masih \`[ ]\`. Verifikasi nyata sebelum klaim selesai.`,
17479
+ `JANGAN mengulang fase awal \u2014 spec & plan sudah ada. Lanjut di fase Execute: baca plan di ${m.planDir}/** untuk backlog item ini, periksa task yang sudah \`[x]\` dan selesaikan yang masih \`[ ]\`. Verifikasi nyata sebelum klaim selesai.` + (hasReview ? ' Sesudah itu jalankan fase Review, lalu tulis `echo "Review done" >> "$HANOMAN_PHASE_FILE"` BARU (baris `Review done` lama di berkas itu tak berlaku).\n' + SELF_REVIEW_CLAUSE : ""),
17458
17480
  autonomyClause(autonomy),
17459
17481
  scopeClause(flow2, verifyScope),
17460
17482
  codeStyleClause(flow2),
17461
17483
  methodClause(m),
17462
17484
  attachmentClause(attachments),
17463
- skillInstruction(flow2, ["Execute"], m),
17485
+ skillInstruction(flow2, hasReview ? ["Execute", REVIEWER_CELL] : ["Execute"], m),
17464
17486
  push,
17465
17487
  specContext(spec)
17466
17488
  ].filter(Boolean).join("\n\n");
@@ -17633,7 +17655,7 @@ Stack: ${project.stack || "\u2014"}`,
17633
17655
  ${cron.prompt}`
17634
17656
  ].join("\n\n");
17635
17657
  }
17636
- var PIPELINES, WORK_PHASES, DECIDER, ASK_ROUTE, AMBIGUOUS, AUTONOMY_CLAUSE, AUTONOMY_CLAUSE_FULL, autonomyClause, PLAN_SHAPE, phaseInstruction, skillInstruction, auditDecisionInstruction, ESCALATION_CONTRACT, auditOnlyInstruction, auditContinuationInstruction, auditContinuationForOrchestrator, writesCode, scopeClause, codeStyleClause, methodClause, attachmentClause, orchestratorAttachmentClause, PROJECT_METHOD, specContext, goalDetail, goalBlock, goalContext, projectContext, scaffoldContext, prdBriefBlock, prdAuditBlock, prdContext, breakdownContext, prdPhaseLines, breakdownPhaseLines, resumeClause, RESUMED_WORKTREE_NOTE, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
17658
+ var PIPELINES, WORK_PHASES, DECIDER, ASK_ROUTE, ASK_ESCALATION_TOOL, ASK_DECISIONS_TOOL, AMBIGUOUS, AUTONOMY_CLAUSE, AUTONOMY_CLAUSE_FULL, autonomyClause, PLAN_SHAPE, phaseInstruction, SELF_REVIEW_CLAUSE, skillInstruction, auditDecisionInstruction, ESCALATION_CONTRACT, auditOnlyInstruction, auditContinuationInstruction, auditContinuationForOrchestrator, writesCode, scopeClause, codeStyleClause, methodClause, attachmentClause, orchestratorAttachmentClause, PROJECT_METHOD, specContext, goalDetail, goalBlock, goalContext, projectContext, scaffoldContext, prdBriefBlock, prdAuditBlock, prdContext, breakdownContext, prdPhaseLines, breakdownPhaseLines, resumeClause, RESUMED_WORKTREE_NOTE, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
17637
17659
  var init_prompt = __esm({
17638
17660
  "../runner/src/prompt.ts"() {
17639
17661
  "use strict";
@@ -17646,6 +17668,8 @@ var init_prompt = __esm({
17646
17668
  WORK_PHASES = ["Execute", "Goal", "Kerjakan"];
17647
17669
  DECIDER = "hanoman-lead bila aktif untuk project ini, selain itu manusia";
17648
17670
  ASK_ROUTE = `ajukan lewat AskUserQuestion bila agenmu punya tool itu (paling banyak 4 pertanyaan per panggilan \u2014 pecah sisanya ke panggilan berikutnya); bila tidak, tanyakan di terminal ini sekaligus, tiap pertanyaan bernomor dan diakhiri \`?\` beserta opsinya, lalu tunggu jawabannya. Tak ada batas jumlah pertanyaan: selama masih ambigu dan akan mempengaruhi hasil, tanyakan. Yang menjawab ditentukan hanoman \u2014 ${DECIDER}; jangan menjawabnya sendiri.`;
17671
+ ASK_ESCALATION_TOOL = "tanyakan lewat AskUserQuestion";
17672
+ ASK_DECISIONS_TOOL = "lewat AskUserQuestion \u2014 tool itu memuat paling banyak 4 pertanyaan per panggilan, jadi pecah sisanya ke panggilan berikutnya sampai semuanya terjawab";
17649
17673
  AMBIGUOUS = "hal yang masih ambigu dan akan mempengaruhi hasil (data model, kontrak API, scope, perilaku yang terlihat pengguna, atau asumsi yang terpaksa kamu ambil agar bisa lanjut)";
17650
17674
  AUTONOMY_CLAUSE = `Jalankan seluruh pipeline sampai tuntas tanpa berhenti di batas antar-fase. Checkpoint "review"/"approval"/"need review" milik skill superpowers BUKAN titik berhenti di sini \u2014 lanjut saja ke fase berikutnya. Berhenti HANYA untuk ${AMBIGUOUS}: JANGAN memutuskannya sendiri, ${ASK_ROUTE} Selain itu, terus lanjut.`;
17651
17675
  AUTONOMY_CLAUSE_FULL = `Kamu diluncurkan scheduler \u2014 mungkin tak ada manusia yang sedang menonton terminal ini. Tembus seluruh pipeline sampai stage \`done\`, lalu commit & push: checkpoint "review"/"approval" milik skill BUKAN titik berhenti dan jangan menunggu persetujuan siapa pun. Tetapi JANGAN memutuskan sendiri ${AMBIGUOUS}: ${ASK_ROUTE} Catat keputusan penting di pesan commit. Merge ke branch utama tetap dilakukan manusia, bukan kamu.`;
@@ -17657,8 +17681,10 @@ Setiap kali sebuah fase selesai (atau kamu putuskan dilewati), append satu baris
17657
17681
  if (!phases2.includes("Plan") || !phases2.includes("Execute")) return base3;
17658
17682
  return base3 + `
17659
17683
  Execute BELUM selesai selama plan (\`${method.planDir}/**\`) masih punya task \`- [ ]\`: kerjakan SEMUA PR/task sampai tiap kotak jadi \`- [x]\` sebelum menulis \`Execute done\`. hanoman menahan backlog di \`executing\`, bukan \`done\`, selama masih ada \`- [ ]\`.
17660
- ${PLAN_SHAPE}`;
17684
+ ${PLAN_SHAPE}` + (phases2.includes(REVIEWER_CELL) ? `
17685
+ ${SELF_REVIEW_CLAUSE}` : "");
17661
17686
  };
17687
+ SELF_REVIEW_CLAUSE = "Fase Review (sesudah `Execute done`): periksa ulang hasil Execute terhadap dokumen spec & plan backlog ini \u2014 baca seluruh perubahan sesi ini (diff dari commit awal worktree) dan jalankan test sesuai scope verifikasi. Tulis tabel `AC \u2192 putusan \u2192 bukti` (setiap acceptance criteria \u2192 perintah & hasil yang kamu baca, atau path:baris) lalu satu baris `Verdict: lulus` atau `Verdict: rework`. `rework` \u2192 perbaiki temuannya, lalu review ulang; maks 2 putaran rework, sisanya jadi keputusan terbuka. Tulis `Review done` HANYA sesudah `Verdict: lulus`; Review TIDAK PERNAH `skipped`.";
17662
17688
  skillInstruction = (flow2, phases2, method) => {
17663
17689
  const lines2 = phases2.map((p3) => {
17664
17690
  const skills2 = phaseSkillsFor(flow2, p3, method);
@@ -17969,7 +17995,7 @@ var init_settings2 = __esm({
17969
17995
  'process.stdin.on("data",c=>d+=c).on("end",()=>{let n=0;try{n=(JSON.parse(d).background_tasks||[]).filter(t=>t&&t.type==="subagent"&&t.status==="running").length}catch{}require("fs").writeFileSync(process.argv[1],"1\\n".repeat(n))})'
17970
17996
  ].join(";");
17971
17997
  busySnapshotCommand = (quotedSubFile) => `node -e '${BUSY_SNAPSHOT_SCRIPT}' ${quotedSubFile} >/dev/null 2>&1`;
17972
- guardSettings = (decisionFile, goal, eventHook, subagentStatusLine) => {
17998
+ guardSettings = (decisionFile, goal, eventHook, subagentStatusLine, askText) => {
17973
17999
  const hooks2 = {};
17974
18000
  if (decisionFile) {
17975
18001
  const f = `'${decisionFile.split("'").join("'\\''")}'`;
@@ -17997,6 +18023,8 @@ var init_settings2 = __esm({
17997
18023
  hooks2.UserPromptSubmit = [{ hooks: [{ type: "command", command: `: > ${f}; : > ${sub}` }] }];
17998
18024
  }
17999
18025
  }
18026
+ if (eventHook && askText)
18027
+ (hooks2.Stop ??= []).push({ hooks: [{ type: "command", command: EVENT_HOOK_COMMAND }] });
18000
18028
  if (goal) (hooks2.Stop ??= []).push({ hooks: [{ type: "prompt", prompt: goal }] });
18001
18029
  return {
18002
18030
  hooks: hooks2,
@@ -18025,7 +18053,7 @@ function defaultGoalCondition({ flow: flow2, specId, branchTo, spec }) {
18025
18053
  const phases2 = PIPELINES[flow2];
18026
18054
  const planGate = phases2.includes("Plan") && phases2.includes("Execute");
18027
18055
  const clauses = [
18028
- `1. output \`cat "$HANOMAN_PHASE_FILE"\` yang memuat satu baris untuk SETIAP fase ${phases2.join(" \u2192 ")}, masing-masing berakhiran \`done\` atau \`skipped\`;`
18056
+ `1. output \`cat "$HANOMAN_PHASE_FILE"\` yang memuat satu baris untuk SETIAP fase ${phases2.join(" \u2192 ")}, masing-masing berakhiran \`done\` atau \`skipped\`` + phases2.filter((p3) => UNSKIPPABLE_PHASES.has(p3)).map((p3) => ` (\`${p3}\` wajib \`done\`)`).join("") + ";"
18029
18057
  ];
18030
18058
  if (planGate) {
18031
18059
  const dirs = PLAN_DIRS.map((d) => `${d}/`).join(" ");
@@ -18098,7 +18126,8 @@ function codexGoalScript(o) {
18098
18126
  ];
18099
18127
  for (const p3 of phases2) {
18100
18128
  lines2.push(
18101
- `grep -qE ${shq(`^${p3} (done|skipped)[[:space:]]*$`)} "$PF" 2>/dev/null || missing="$missing\\n- fase ${p3} belum tercatat di \\$HANOMAN_PHASE_FILE"`
18129
+ // ADR-0230 · fase `UNSKIPPABLE_PHASES` (Review) hanya lolos dengan `done`.
18130
+ `grep -qE ${shq(`^${p3} ${UNSKIPPABLE_PHASES.has(p3) ? "done" : "(done|skipped)"}[[:space:]]*$`)} "$PF" 2>/dev/null || missing="$missing\\n- fase ${p3} belum tercatat di \\$HANOMAN_PHASE_FILE"`
18102
18131
  );
18103
18132
  }
18104
18133
  if (planGate) {
@@ -18175,7 +18204,7 @@ function agentFlags(o) {
18175
18204
  ...o.effort ? ["--effort", o.effort] : [],
18176
18205
  "--dangerously-skip-permissions",
18177
18206
  "--settings",
18178
- JSON.stringify(guardSettings(o.decisionFile, o.goal, o.eventHook, o.subagentStatusLine))
18207
+ JSON.stringify(guardSettings(o.decisionFile, o.goal, o.eventHook, o.subagentStatusLine, o.askText))
18179
18208
  ];
18180
18209
  }
18181
18210
  var init_agent_cli = __esm({
@@ -18449,7 +18478,7 @@ function agentDelegationClause(defs, runtime = "claude") {
18449
18478
  if (defs.every((def2) => def2.kind === "phase")) return "";
18450
18479
  const codex2 = runtime === "codex";
18451
18480
  const antigravity2 = runtime === "antigravity";
18452
- const relay = antigravity2 ? "ajukan semuanya di terminal ini (bernomor, tiap pertanyaan diakhiri `?`), lalu teruskan jawabannya lewat send_message ke conversation ID child yang sama" : codex2 ? "ajukan semuanya di terminal ini (bernomor, tiap pertanyaan diakhiri `?`), lalu teruskan jawabannya lewat send_input ke agent id yang sama" : "ajukan semuanya lewat AskUserQuestion (pecah per 4 pertanyaan), lalu teruskan jawabannya lewat SendMessage ke agent ID yang sama";
18481
+ const relay = antigravity2 ? "ajukan semuanya di terminal ini (bernomor, tiap pertanyaan diakhiri `?`), lalu teruskan jawabannya lewat send_message ke conversation ID child yang sama" : codex2 ? "ajukan semuanya di terminal ini (bernomor, tiap pertanyaan diakhiri `?`), lalu teruskan jawabannya lewat send_input ke agent id yang sama" : ASK_RELAY_TOOL;
18453
18482
  return [
18454
18483
  "",
18455
18484
  "",
@@ -18458,7 +18487,7 @@ function agentDelegationClause(defs, runtime = "claude") {
18458
18487
  ""
18459
18488
  ].join("\n");
18460
18489
  }
18461
- var PHASE_CONTEXT_HEADER, liveMentions, policyOf, READ_ONLY_DENIED_OPERATORS, READ_ONLY_DENIED_COMMANDS, deniedCommandsProse, readOnlyAllowlistLines, NO_FAKE_EXPERIMENT, policyLines, ownPolicyLines, workLimitClause, STATUS_LINE, handoffClause, contractReference, READ_ONLY_TOOLS;
18490
+ var ASK_RELAY_TOOL, PHASE_CONTEXT_HEADER, liveMentions, policyOf, READ_ONLY_DENIED_OPERATORS, READ_ONLY_DENIED_COMMANDS, deniedCommandsProse, readOnlyAllowlistLines, NO_FAKE_EXPERIMENT, policyLines, ownPolicyLines, workLimitClause, STATUS_LINE, handoffClause, contractReference, READ_ONLY_TOOLS;
18462
18491
  var init_custom_agents = __esm({
18463
18492
  "../runner/src/custom-agents.ts"() {
18464
18493
  "use strict";
@@ -18466,6 +18495,7 @@ var init_custom_agents = __esm({
18466
18495
  init_code_style();
18467
18496
  init_prompt();
18468
18497
  init_agent_readonly();
18498
+ ASK_RELAY_TOOL = "ajukan semuanya lewat AskUserQuestion (pecah per 4 pertanyaan), lalu teruskan jawabannya lewat SendMessage ke agent ID yang sama";
18469
18499
  PHASE_CONTEXT_HEADER = "=== KONTEKS ===";
18470
18500
  liveMentions = (def2, roster) => {
18471
18501
  if (def2.workspacePolicy === "read-only") return [];
@@ -18594,6 +18624,35 @@ var init_custom_agents = __esm({
18594
18624
  }
18595
18625
  });
18596
18626
 
18627
+ // ../runner/src/ask-via.ts
18628
+ function textAsk(s2) {
18629
+ let out4 = s2;
18630
+ for (const [tool, text2] of SWAPS) out4 = out4.split(tool).join(text2);
18631
+ return out4;
18632
+ }
18633
+ var NO_TOOL, TEXT_ASK_ROUTE, SWAPS;
18634
+ var init_ask_via = __esm({
18635
+ "../runner/src/ask-via.ts"() {
18636
+ "use strict";
18637
+ init_prompt();
18638
+ init_custom_agents();
18639
+ NO_TOOL = "JANGAN memakai AskUserQuestion (juga bila sebuah skill menyuruhnya)";
18640
+ TEXT_ASK_ROUTE = `${NO_TOOL}: tanyakan di terminal ini sekaligus dalam satu pesan, tiap pertanyaan bernomor dan diakhiri \`?\` beserta opsi, rekomendasi dan alasannya, lalu berhenti dan tunggu jawabannya. Tak ada batas jumlah pertanyaan: selama masih ambigu dan akan mempengaruhi hasil, tanyakan. Yang menjawab ditentukan hanoman \u2014 ${DECIDER}; jangan menjawabnya sendiri.`;
18641
+ SWAPS = [
18642
+ [ASK_ROUTE, TEXT_ASK_ROUTE],
18643
+ [
18644
+ ASK_DECISIONS_TOOL,
18645
+ `di terminal ini sekaligus dalam satu pesan (${NO_TOOL}), tiap pertanyaan bernomor dan diakhiri \`?\` beserta opsi, rekomendasi dan alasannya, lalu berhenti dan tunggu jawaban`
18646
+ ],
18647
+ [
18648
+ ASK_RELAY_TOOL,
18649
+ `ajukan semuanya di terminal ini (${NO_TOOL}; bernomor, tiap pertanyaan diakhiri \`?\`), lalu teruskan jawabannya lewat SendMessage ke agent ID yang sama`
18650
+ ],
18651
+ [ASK_ESCALATION_TOOL, `tanyakan di terminal ini (${NO_TOOL}) lalu berhenti dan tunggu jawaban`]
18652
+ ];
18653
+ }
18654
+ });
18655
+
18597
18656
  // ../runner/src/runtime-profile.ts
18598
18657
  function resolveHardening(env) {
18599
18658
  if (env.HANOMAN_HARDENING === "1") return true;
@@ -18853,7 +18912,7 @@ function phaseAgentInstructions(entry2, index, plan, ctx) {
18853
18912
  const guide = PROJECT_FLOWS.has(flow2) ? projectGuide(flow2, phase, ctx) : backlogGuide(flow2, phase, ctx);
18854
18913
  const executeMode = plan.executeMode ?? "inline";
18855
18914
  const skills2 = phaseSkillsFor(flow2, phase, method, plan.runtime, executeMode);
18856
- const skipSkillFinalReview = phase === "Execute" && plan.runtime === "claude" && executeMode === "subagent" && !!plan.reviewer && skills2.includes("superpowers:subagent-driven-development");
18915
+ const skipSkillFinalReview = phase === "Execute" && plan.runtime === "claude" && executeMode === "subagent" && plan.phases.some((p3) => p3.phase === REVIEWER_CELL) && skills2.includes("superpowers:subagent-driven-development");
18857
18916
  const work = WORK_PHASES.includes(phase);
18858
18917
  const verifies = work || phase === "Verifikasi";
18859
18918
  return [
@@ -18862,7 +18921,7 @@ function phaseAgentInstructions(entry2, index, plan, ctx) {
18862
18921
  DOC_WRITING_PHASES.has(phase) ? `=== STANDAR DOCS ===
18863
18922
  ${REVERSE_STANDARD}` : "",
18864
18923
  skills2.length ? `Skills ${method.label} WAJIB untuk fase ini \u2014 muat & ikuti dengan mekanisme yang tersedia di agenmu: ` + skills2.join(", ") : "",
18865
- skipSkillFinalReview ? "LEWATI final whole-branch review milik skill subagent-driven-development: review akhir dipegang reviewer `hanoman-fase-review` yang dipanggil orchestrator sesudah fase ini. Review per task tetap jalan." : "",
18924
+ skipSkillFinalReview ? "LEWATI final whole-branch review milik skill subagent-driven-development: review akhir dipegang fase Review (`hanoman-fase-review`) yang dipanggil orchestrator sesudah fase ini. Review per task tetap jalan." : "",
18866
18925
  verifies ? scopeClause(flow2, ctx.verifyScope) : "",
18867
18926
  work ? codeStyleClause(flow2) : "",
18868
18927
  methodClause(method),
@@ -18877,40 +18936,29 @@ ${REVERSE_STANDARD}` : "",
18877
18936
  ].filter(Boolean).join("\n\n");
18878
18937
  }
18879
18938
  function buildPhaseAgents(plan, ctx) {
18880
- const phases2 = plan.phases.map((entry2, index) => ({
18881
- kind: "phase",
18882
- phase: entry2.phase,
18883
- name: entry2.agentName,
18884
- description: `Fase ${entry2.phase} flow ${plan.flow} hanoman \u2014 hanya dipanggil orchestrator sesi ini.`,
18885
- instructions: phaseAgentInstructions(entry2, index, plan, ctx),
18886
- // T2 · konteks bersama dibawa TERPISAH (lihat `phasePromptOf`): claude merujuknya lewat satu
18887
- // berkas di temp dir sesi, codex tetap inline.
18888
- context: ctx.context,
18889
- tools: null,
18890
- model: entry2.model,
18891
- effort: entry2.effort,
18892
- mentions: [],
18893
- executeMode: plan.executeMode ?? "inline"
18894
- }));
18895
- const r = plan.reviewer;
18896
- if (!r || !plan.phases.some((p3) => p3.phase === r.phase)) return phases2;
18897
- return [...phases2, {
18898
- kind: "phase",
18899
- phase: r.phase,
18900
- name: r.agentName,
18901
- description: `Reviewer independen hasil fase Execute flow ${plan.flow} hanoman \u2014 hanya dipanggil orchestrator sesi ini, sesudah Execute melapor selesai.`,
18902
- instructions: reviewerInstructions(plan, ctx),
18903
- context: ctx.context,
18904
- tools: null,
18905
- model: r.model,
18906
- effort: r.effort,
18907
- mentions: []
18908
- }];
18939
+ return plan.phases.map((entry2, index) => {
18940
+ const review = entry2.phase === REVIEWER_CELL;
18941
+ return {
18942
+ kind: "phase",
18943
+ phase: entry2.phase,
18944
+ name: entry2.agentName,
18945
+ description: review ? `Reviewer independen hasil fase Execute flow ${plan.flow} hanoman (fase Review) \u2014 hanya dipanggil orchestrator sesi ini, sesudah Execute ditutup.` : `Fase ${entry2.phase} flow ${plan.flow} hanoman \u2014 hanya dipanggil orchestrator sesi ini.`,
18946
+ instructions: review ? reviewerInstructions(plan, ctx) : phaseAgentInstructions(entry2, index, plan, ctx),
18947
+ // T2 · konteks bersama dibawa TERPISAH (lihat `phasePromptOf`): claude merujuknya lewat satu
18948
+ // berkas di temp dir sesi, codex tetap inline.
18949
+ context: ctx.context,
18950
+ tools: null,
18951
+ model: entry2.model,
18952
+ effort: entry2.effort,
18953
+ mentions: [],
18954
+ ...review ? {} : { executeMode: plan.executeMode ?? "inline" }
18955
+ };
18956
+ });
18909
18957
  }
18910
18958
  function reviewerInstructions(plan, ctx) {
18911
18959
  const m = ctx.method;
18912
18960
  return [
18913
- `Kamu reviewer independen hasil fase Execute flow ${plan.flow} hanoman. Kamu dipanggil orchestrator sesudah agen Execute melapor \`Status: selesai\` dan SEBELUM fase itu ditutup. Kamu pengadu janji: yang kamu nilai bukan bagus-tidaknya kode, melainkan apakah yang DIMINTA benar-benar ada dan TERBUKTI pada keadaan akhir worktree ini.`,
18961
+ `Kamu agen fase Review \u2014 reviewer independen hasil fase Execute flow ${plan.flow} hanoman. Kamu dipanggil orchestrator sesudah fase Execute ditutup, dan fase Review baru ditutup bila kamu memberi \`Verdict: lulus\`. Kamu pengadu janji: yang kamu nilai bukan bagus-tidaknya kode, melainkan apakah yang DIMINTA benar-benar ada dan TERBUKTI pada keadaan akhir worktree ini.`,
18914
18962
  "Prosedur:",
18915
18963
  [
18916
18964
  `1. Baca dokumen spec & plan dari baris \`Artefak:\` serah-terima (qa jalur-cepat: dokumen audit adalah doc-of-record; bila path tak ada, cari berkas ber-spec-id di \`${m.specDir}/\` & \`${m.planDir}/\`). Plan yang menyimpang dari spec adalah temuan.`,
@@ -20899,6 +20947,10 @@ var init_antigravity_agent_config = __esm({
20899
20947
  var src_exports = {};
20900
20948
  __export(src_exports, {
20901
20949
  ANTIGRAVITY_ENV_KEYS: () => ANTIGRAVITY_ENV_KEYS,
20950
+ ASK_DECISIONS_TOOL: () => ASK_DECISIONS_TOOL,
20951
+ ASK_ESCALATION_TOOL: () => ASK_ESCALATION_TOOL,
20952
+ ASK_RELAY_TOOL: () => ASK_RELAY_TOOL,
20953
+ ASK_ROUTE: () => ASK_ROUTE,
20902
20954
  AUTONOMY_CLAUSE: () => AUTONOMY_CLAUSE,
20903
20955
  CODEX_NATIVE_AGENTS_MIN_CLIENT: () => CODEX_NATIVE_AGENTS_MIN_CLIENT,
20904
20956
  CODE_STYLE_CLAUSE: () => CODE_STYLE_CLAUSE,
@@ -20921,8 +20973,10 @@ __export(src_exports, {
20921
20973
  REVERSE_PHASE_GUIDE: () => REVERSE_PHASE_GUIDE,
20922
20974
  REVERSE_STANDARD: () => REVERSE_STANDARD,
20923
20975
  SCAFFOLD_PHASE_GUIDE: () => SCAFFOLD_PHASE_GUIDE,
20976
+ SELF_REVIEW_CLAUSE: () => SELF_REVIEW_CLAUSE,
20924
20977
  SUBAGENT_STATUSLINE_SCRIPT: () => SUBAGENT_STATUSLINE_SCRIPT,
20925
20978
  SkillError: () => SkillError,
20979
+ TEXT_ASK_ROUTE: () => TEXT_ASK_ROUTE,
20926
20980
  WORK_PHASES: () => WORK_PHASES,
20927
20981
  agentContractOf: () => agentContractOf,
20928
20982
  agentDefinitionHash: () => agentDefinitionHash,
@@ -21023,6 +21077,7 @@ __export(src_exports, {
21023
21077
  startProjectPrompt: () => startProjectPrompt,
21024
21078
  startPrompt: () => startPrompt,
21025
21079
  startScaffoldPrompt: () => startScaffoldPrompt,
21080
+ textAsk: () => textAsk,
21026
21081
  tomlBasicString: () => tomlBasicString,
21027
21082
  validateAgentEvalManifest: () => validateAgentEvalManifest,
21028
21083
  verifyScopeClause: () => verifyScopeClause,
@@ -21046,6 +21101,7 @@ var init_src2 = __esm({
21046
21101
  init_codex_settings();
21047
21102
  init_agent_cli();
21048
21103
  init_custom_agents();
21104
+ init_ask_via();
21049
21105
  init_agent_definition();
21050
21106
  init_phase_agents();
21051
21107
  init_agent_readonly();
@@ -21344,6 +21400,7 @@ function stageFor(phases2) {
21344
21400
  if (WORK_PHASES.includes(p3.name) && p3.state === "active")
21345
21401
  best = Math.max(best, STAGES.indexOf("executing"));
21346
21402
  if (p3.state !== "done" && p3.state !== "skipped") continue;
21403
+ if (p3.state === "skipped" && UNSKIPPABLE_PHASES.has(p3.name)) continue;
21347
21404
  const s2 = REACHED[p3.name];
21348
21405
  if (s2) best = Math.max(best, STAGES.indexOf(s2));
21349
21406
  }
@@ -21458,8 +21515,11 @@ var init_session_phases = __esm({
21458
21515
  Audit: "objective",
21459
21516
  Spec: "spec-ready",
21460
21517
  Plan: "planned",
21518
+ // ADR-0230 · flow feature & qa ditutup fase Review resmi: Execute selesai baru `executing`, Review
21519
+ // yang mencapai `done`. `Review` unik lintas PIPELINES dan bukan fase kerja (`WORK_PHASES`).
21461
21520
  Laporan: "done",
21462
- Execute: "done",
21521
+ Execute: "executing",
21522
+ Review: "done",
21463
21523
  // SPEC-407 · ADR-0089 · flow goal (Goal → Verifikasi): fase kerja mencapai `executing`, fase
21464
21524
  // verifikasi yang mencapai `done`. Kedua nama unik lintas PIPELINES — peta ini berkunci nama.
21465
21525
  Goal: "executing",
@@ -21489,7 +21549,7 @@ var init_session_phases = __esm({
21489
21549
  if (PLAN_DIRS.some((d) => rel.startsWith(`${d}/`) && !rel.slice(d.length + 1).includes("/"))) return false;
21490
21550
  return re.test(rel.slice(rel.lastIndexOf("/") + 1).toLowerCase());
21491
21551
  });
21492
- phasesComplete = (phases2) => phases2.length > 0 && phases2.every((p3) => p3.state === "done" || p3.state === "skipped");
21552
+ phasesComplete = (phases2) => phases2.length > 0 && phases2.every((p3) => p3.state === "done" || p3.state === "skipped" && !UNSKIPPABLE_PHASES.has(p3.name));
21493
21553
  }
21494
21554
  });
21495
21555
 
@@ -48973,8 +49033,13 @@ function createSession(projectId, cwd, opts2 = {}) {
48973
49033
  }
48974
49034
  const agentForDefs = opts2.agent ?? "claude";
48975
49035
  const sessionEffort = agentForDefs === "codex" && opts2.model && opts2.effort ? coerceCodexEffort(opts2.model, opts2.effort) : opts2.effort;
48976
- const requestedPhaseDefs = opts2.command ? [] : opts2.phaseAgents ?? [];
48977
- const bornPrompt = (orch) => orch ? opts2.prompt : opts2.legacyPrompt ?? opts2.prompt;
49036
+ const askText = !opts2.command && agentForDefs === "claude" && opts2.askVia === "text";
49037
+ const asked = (s2) => askText ? textAsk(s2) : s2;
49038
+ const requestedPhaseDefs = opts2.command ? [] : (opts2.phaseAgents ?? []).map((d) => askText ? { ...d, instructions: textAsk(d.instructions) } : d);
49039
+ const bornPrompt = (orch) => {
49040
+ const p3 = orch ? opts2.prompt : opts2.legacyPrompt ?? opts2.prompt;
49041
+ return p3 === void 0 ? p3 : asked(p3);
49042
+ };
48978
49043
  const selectionContext = {
48979
49044
  projectId,
48980
49045
  runtime: agentForDefs,
@@ -49060,7 +49125,7 @@ function createSession(projectId, cwd, opts2 = {}) {
49060
49125
  return [error instanceof Error ? error.message : String(error)];
49061
49126
  }
49062
49127
  agentsFile = file;
49063
- rosterBlock = agentDelegationClause(customDefs, "claude");
49128
+ rosterBlock = asked(agentDelegationClause(customDefs, "claude"));
49064
49129
  liveAgentDefs = defs;
49065
49130
  renderedDefs = defs;
49066
49131
  return [];
@@ -49166,7 +49231,8 @@ function createSession(projectId, cwd, opts2 = {}) {
49166
49231
  // SPEC-909 · ADR-0146 · sesi ber-`opts.command` tak pernah sampai ke sini (cabang shell
49167
49232
  // mentah di atas), jadi hook event hanya pernah terpasang di sesi agen.
49168
49233
  eventHook: true,
49169
- subagentStatusLine: statusLineCommand
49234
+ subagentStatusLine: statusLineCommand,
49235
+ askText
49170
49236
  }).map(sq).join(" ");
49171
49237
  const agentsArg = agentsFile ? `--agents "$(cat ${sq(agentsFile)})"` : "";
49172
49238
  const nativeAgentArgs = agent === "codex" ? codexAgentArgs.map(sq).join(" ") : "";
@@ -58206,6 +58272,22 @@ function statesFor(raw) {
58206
58272
  if (key.startsWith(prefix)) orchestration[key] = state;
58207
58273
  }
58208
58274
  }
58275
+ const rawOrchestration = isRecord(object2.orchestration) ? object2.orchestration : {};
58276
+ for (const flow2 of ORCHESTRATION_FLOWS) {
58277
+ const rawFlow = isRecord(rawOrchestration[flow2]) ? rawOrchestration[flow2] : {};
58278
+ for (const runtime of ["claude", "codex"]) {
58279
+ const rawRuntime = isRecord(rawFlow[runtime]) ? rawFlow[runtime] : {};
58280
+ for (const phase of FLOW_PHASES[flow2]) {
58281
+ const rawCell = rawRuntime[phase];
58282
+ if (!isRecord(rawCell)) continue;
58283
+ for (const field of ["model", "effort"]) {
58284
+ const key = `${flow2}.${runtime}.${phase}.${field}`;
58285
+ if (!(key in marker.data.orchestration) && rawCell[field] !== void 0 && rawCell[field] !== null)
58286
+ orchestration[key] = "user";
58287
+ }
58288
+ }
58289
+ }
58290
+ }
58209
58291
  const rawMarker = isRecord(object2.builtinRuntimeDefaults) ? object2.builtinRuntimeDefaults : {};
58210
58292
  return {
58211
58293
  claude: marker.data.claude,
@@ -59428,7 +59510,7 @@ var init_flow = __esm({
59428
59510
  });
59429
59511
 
59430
59512
  // src/services/lead/pane.ts
59431
- function readCodexTurn(message) {
59513
+ function readTurn(message) {
59432
59514
  const body = message.trim();
59433
59515
  if (!body) return { asking: false, reason: "giliran codex berakhir tanpa pesan" };
59434
59516
  if (CODEX_FINISHED.some((re) => re.test(body)))
@@ -60163,7 +60245,7 @@ async function runChain(ask, s2, deps) {
60163
60245
  const stop = (reason, failed = false) => ({ acted, done: false, failed, reason, at, flowId, step });
60164
60246
  const asked = ask.source === "ask-tool";
60165
60247
  if (ask.source === "turn-end") {
60166
- const read2 = readCodexTurn(ask.message);
60248
+ const read2 = readTurn(ask.message);
60167
60249
  if (!read2.asking) return stop(read2.reason);
60168
60250
  }
60169
60251
  const steps = ask.source === "ask-tool" ? ask.questions : [null];
@@ -66234,6 +66316,28 @@ var init_project_checkout = __esm({
66234
66316
  }
66235
66317
  });
66236
66318
 
66319
+ // src/services/lead/ask-via.ts
66320
+ function leadDecides(cfg, optIn, projectId) {
66321
+ return optIn.includes(projectId) && leadActive(cfg, projectId);
66322
+ }
66323
+ async function askViaFor(projectId, agent, deps = prodDeps2) {
66324
+ if ((agent ?? "claude") !== "claude") return void 0;
66325
+ try {
66326
+ const [cfg, optIn] = await Promise.all([deps.cfg(), deps.optIn()]);
66327
+ return leadDecides(cfg, optIn, projectId) ? "text" : "tool";
66328
+ } catch {
66329
+ return "tool";
66330
+ }
66331
+ }
66332
+ var prodDeps2;
66333
+ var init_ask_via2 = __esm({
66334
+ "src/services/lead/ask-via.ts"() {
66335
+ "use strict";
66336
+ init_config5();
66337
+ prodDeps2 = { cfg: getLead, optIn: leadProjects };
66338
+ }
66339
+ });
66340
+
66237
66341
  // src/services/session-launch.ts
66238
66342
  function resumeState(repoDir, worktree, branchTo, headSha) {
66239
66343
  if (realGit.worktreeAlive(worktree)) return { worktreeKept: true };
@@ -66351,7 +66455,7 @@ async function startSpecSession(spec, opts2) {
66351
66455
  const resumeCtx = resume ? buildResumeCtx(repoDir, id2, opts2.flow, resume.worktreeKept) : void 0;
66352
66456
  let plan = fullPlan;
66353
66457
  if (fullPlan && isContinue && !isGoalFlow) {
66354
- const phases2 = fullPlan.phases.filter((p3) => p3.phase === "Execute");
66458
+ const phases2 = fullPlan.phases.filter((p3) => p3.phase === "Execute" || p3.phase === REVIEWER_CELL);
66355
66459
  plan = phases2.length ? { ...fullPlan, phases: phases2 } : null;
66356
66460
  }
66357
66461
  const buildPrompt = (p3) => {
@@ -66381,6 +66485,7 @@ async function startSpecSession(spec, opts2) {
66381
66485
  ...resumeCtx ? { resume: resumeCtx } : {}
66382
66486
  }) : [];
66383
66487
  const scopeEnv = { HANOMAN_BASE_SHA: baseSha, HANOMAN_VERIFY_SCOPE: verifyScope };
66488
+ const askVia = await askViaFor(spec.projectId, agent);
66384
66489
  const s2 = createSession(spec.projectId, worktree, {
66385
66490
  specId: spec.id,
66386
66491
  flow: opts2.flow,
@@ -66388,13 +66493,14 @@ async function startSpecSession(spec, opts2) {
66388
66493
  effort: effort2,
66389
66494
  goal,
66390
66495
  agent,
66496
+ askVia,
66391
66497
  phaseFile: phaseFilePath(repoDir, id2),
66392
66498
  decisionFile: decisionFilePath(repoDir, id2),
66393
66499
  attachmentsDir: attachments.items.length ? attachments.dir : void 0,
66394
66500
  prompt,
66395
66501
  legacyPrompt,
66396
66502
  phaseAgents,
66397
- // S3 · SPEC-172 · berkas fase continue masih memuat `Execute done` run lama; fase yang DIKERJAKAN
66503
+ // S3 · SPEC-172 · berkas fase continue masih memuat `Execute done` (ADR-0230 · dan `Review done`) run lama; fase yang DIKERJAKAN
66398
66504
  // ULANG sesi ini tak boleh dihitung `doneAtBirth`, supaya ⚠ bukti tetap bisa menyala.
66399
66505
  ...isContinue && plan ? { rerunPhases: plan.phases.map((p3) => p3.phase) } : {},
66400
66506
  env: scopeEnv
@@ -66425,6 +66531,7 @@ var init_session_launch = __esm({
66425
66531
  init_remote_session();
66426
66532
  init_registry();
66427
66533
  init_event_log();
66534
+ init_ask_via2();
66428
66535
  init_pty();
66429
66536
  LaunchError = class extends Error {
66430
66537
  // SPEC-447 · `blockers` hanya terisi untuk kind "blocked"; route memetakannya ke body 409.
@@ -79257,6 +79364,7 @@ async function refuseSelfDecision(input) {
79257
79364
  // src/routes/terminal.ts
79258
79365
  init_tui_dialog();
79259
79366
  init_runtime();
79367
+ init_ask_via2();
79260
79368
  var ATTACHMENT_MIME = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
79261
79369
  var DIALOG_ANSWER_ERROR = {
79262
79370
  stale: "layar dialog sudah berubah \u2014 muat ulang pertanyaannya",
@@ -79378,12 +79486,14 @@ async function terminal_default(app3, opts2) {
79378
79486
  const brief = { id: project.id, name: project.name, desc: project.desc, stack: project.stack };
79379
79487
  const plan = sessionPhasePlan(await getSetting(), "reverse", agent2, { model: model2, effort: effort3 });
79380
79488
  const legacyPrompt = startProjectPrompt("reverse", brief, "reverse-docs") + resumeNote(reused);
79489
+ const askVia = await askViaFor(project.id, agent2);
79381
79490
  const s3 = createSession(project.id, wt, {
79382
79491
  id: id2,
79383
79492
  flow: "reverse",
79384
79493
  model: model2,
79385
79494
  effort: effort3,
79386
79495
  agent: agent2,
79496
+ askVia,
79387
79497
  phaseFile: phaseFilePath(repoDir, id2),
79388
79498
  decisionFile: decisionFilePath(repoDir, id2),
79389
79499
  prompt: plan ? startProjectPrompt("reverse", brief, "reverse-docs", plan) + resumeNote(reused) : legacyPrompt,
@@ -79416,12 +79526,14 @@ async function terminal_default(app3, opts2) {
79416
79526
  const brief = { id: project.id, name: project.name, desc: project.desc, stack: project.stack };
79417
79527
  const plan = sessionPhasePlan(await getSetting(), "scaffold", agent2, { model: model2, effort: effort3 });
79418
79528
  const legacyPrompt = startScaffoldPrompt(brief, "scaffold-docs") + resumeNote(reused);
79529
+ const askVia = await askViaFor(project.id, agent2);
79419
79530
  const s3 = createSession(project.id, wt, {
79420
79531
  id: id2,
79421
79532
  flow: "scaffold",
79422
79533
  model: model2,
79423
79534
  effort: effort3,
79424
79535
  agent: agent2,
79536
+ askVia,
79425
79537
  phaseFile: phaseFilePath(repoDir, id2),
79426
79538
  decisionFile: decisionFilePath(repoDir, id2),
79427
79539
  prompt: plan ? startScaffoldPrompt(brief, "scaffold-docs", plan) + resumeNote(reused) : legacyPrompt,
@@ -79458,6 +79570,7 @@ async function terminal_default(app3, opts2) {
79458
79570
  const audit = auditDoc ? { id: fromAudit, path: auditDoc.path, content: auditDoc.content } : void 0;
79459
79571
  const plan = sessionPhasePlan(await getSetting(), "prd", agent2, { model: model2, effort: effort3 });
79460
79572
  const legacyPrompt = startPrdPrompt(project_, brief, `prd/${slug2}`, audit) + resumeNote(reused);
79573
+ const askVia = await askViaFor(project.id, agent2);
79461
79574
  const s3 = createSession(project.id, wt, {
79462
79575
  id: id2,
79463
79576
  flow: "prd",
@@ -79465,6 +79578,7 @@ async function terminal_default(app3, opts2) {
79465
79578
  model: model2,
79466
79579
  effort: effort3,
79467
79580
  agent: agent2,
79581
+ askVia,
79468
79582
  phaseFile: phaseFilePath(repoDir, id2),
79469
79583
  decisionFile: decisionFilePath(repoDir, id2),
79470
79584
  prompt: plan ? startPrdPrompt(project_, brief, `prd/${slug2}`, audit, plan) + resumeNote(reused) : legacyPrompt,
@@ -79506,6 +79620,7 @@ async function terminal_default(app3, opts2) {
79506
79620
  const prd = { title, path: prdPath, content };
79507
79621
  const plan = sessionPhasePlan(await getSetting(), "breakdown", agent2, { model: model2, effort: effort3 });
79508
79622
  const legacyPrompt = startBreakdownPrompt(project_, prd, `breakdown/${slug2}`) + resumeNote(reused);
79623
+ const askVia = await askViaFor(project.id, agent2);
79509
79624
  const s3 = createSession(project.id, wt, {
79510
79625
  id: id2,
79511
79626
  flow: "breakdown",
@@ -79513,6 +79628,7 @@ async function terminal_default(app3, opts2) {
79513
79628
  model: model2,
79514
79629
  effort: effort3,
79515
79630
  agent: agent2,
79631
+ askVia,
79516
79632
  phaseFile: phaseFilePath(repoDir, id2),
79517
79633
  decisionFile: decisionFilePath(repoDir, id2),
79518
79634
  prompt: plan ? startBreakdownPrompt(project_, prd, `breakdown/${slug2}`, plan) + resumeNote(reused) : legacyPrompt,
@@ -87000,6 +87116,7 @@ init_settings3();
87000
87116
  init_codex_trust();
87001
87117
  init_qa_attachment_transfer();
87002
87118
  init_uploads();
87119
+ init_ask_via2();
87003
87120
  import { createHash as createHash21 } from "node:crypto";
87004
87121
  import { execFileSync as execFileSync10 } from "node:child_process";
87005
87122
  import { join as join50 } from "node:path";
@@ -87072,6 +87189,7 @@ ${files2.join("\n")}` : "",
87072
87189
  CODE_STYLE_CLAUSE,
87073
87190
  reused ? RESUMED_WORKTREE_NOTE : ""
87074
87191
  ].filter(Boolean).join("\n\n");
87192
+ const askVia = await askViaFor(d.projectId, agent);
87075
87193
  const s2 = createSession(d.projectId, wt, {
87076
87194
  id: id2,
87077
87195
  branch,
@@ -87079,6 +87197,7 @@ ${files2.join("\n")}` : "",
87079
87197
  model,
87080
87198
  effort: effort2,
87081
87199
  prompt,
87200
+ askVia,
87082
87201
  env: { HANOMAN_BASE_SHA: baseSha, HANOMAN_VERIFY_SCOPE: verifyScope },
87083
87202
  attachmentsDir: files2.length ? sessionUploadDir(id2) : void 0
87084
87203
  });
@@ -90581,6 +90700,7 @@ init_settings3();
90581
90700
  init_codex_trust();
90582
90701
  init_pty();
90583
90702
  init_session_launch_gate();
90703
+ init_ask_via2();
90584
90704
  var cronSessionId = (cronId) => `cron-${cronId.toLowerCase().replace(/[^a-z0-9_-]/g, "_")}`;
90585
90705
  async function liveCronSession(cronId) {
90586
90706
  const s2 = await getSessionAsync(cronSessionId(cronId));
@@ -90601,11 +90721,13 @@ async function startCronSession(cron) {
90601
90721
  if (agent === "codex") ensureCodexTrust(repoDir);
90602
90722
  const wt = `${repoDir}/.worktrees/${id2}`;
90603
90723
  if (!realGit.worktreeAlive(wt)) realGit.addWorktree(repoDir, wt, "HEAD");
90724
+ const askVia = await askViaFor(cron.projectId, agent);
90604
90725
  const s2 = createSession(cron.projectId, wt, {
90605
90726
  id: id2,
90606
90727
  agent,
90607
90728
  model,
90608
90729
  effort: effort2,
90730
+ askVia,
90609
90731
  prompt: cronPrompt(
90610
90732
  { id: project.id, name: project.name, desc: project.desc, stack: project.stack },
90611
90733
  { name: cron.name, prompt: cron.prompt }
@@ -90726,7 +90848,7 @@ async function tick4(now, deps, end2 = prodEnd) {
90726
90848
  console.error("scheduler drain:", e);
90727
90849
  }
90728
90850
  }
90729
- var prodDeps2 = {
90851
+ var prodDeps3 = {
90730
90852
  liveCount: async () => (await listSessionsAsync()).filter((s2) => !s2.exited).length,
90731
90853
  isLive: async (specId) => {
90732
90854
  const s2 = await getSessionAsync(sessionIdForSpec(specId));
@@ -90760,7 +90882,7 @@ var prodDeps2 = {
90760
90882
  };
90761
90883
  var TICK_MS3 = 1e4;
90762
90884
  var timer7;
90763
- function startScheduler(deps = prodDeps2) {
90885
+ function startScheduler(deps = prodDeps3) {
90764
90886
  if (timer7) return;
90765
90887
  timer7 = setInterval(() => void tick4(Date.now(), deps), TICK_MS3);
90766
90888
  timer7.unref();
@@ -90797,7 +90919,7 @@ init_db();
90797
90919
  init_session_launch();
90798
90920
  init_session_admission2();
90799
90921
  init_notifications2();
90800
- var prodDeps3 = {
90922
+ var prodDeps4 = {
90801
90923
  // ADR-0170 · mengamandemen ADR-0169 keputusan #4: `bypassCapacity` DICABUT. Auto-resume kini
90802
90924
  // tunduk cap/beban host seperti "Lanjutkan" manual — mesin 8 GB operator sudah dua kali kernel
90803
90925
  // panic akibat batch sesi paralel tepat SAAT boot (memori mac-mini-8gb-panic-agen-paralel),
@@ -90806,7 +90928,7 @@ var prodDeps3 = {
90806
90928
  recordFail: recordFailure,
90807
90929
  recordDeferred: recordFailure
90808
90930
  };
90809
- async function resumeReconciledSessions(cutoff, deps = prodDeps3) {
90931
+ async function resumeReconciledSessions(cutoff, deps = prodDeps4) {
90810
90932
  const report = { resumed: [], failed: [], deferred: [] };
90811
90933
  const specIds = await reconciledSpecIdsSince(cutoff);
90812
90934
  if (!specIds.length) return report;