hanoman 0.5.0 → 0.6.0

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 (25) hide show
  1. package/README.md +2 -0
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli.js +213 -27
  4. package/dist/server.js +529 -155
  5. package/package.json +1 -1
  6. package/web/assets/{ChangelogScreen-CqVppy70.js → ChangelogScreen-DVMpzy2Y.js} +1 -1
  7. package/web/assets/{DalangHanomanScreen-DRFC8ZdR.js → DalangHanomanScreen-aY89PmaJ.js} +1 -1
  8. package/web/assets/{DocPreviewModal-Di28IHju.js → DocPreviewModal-BTwj2rmX.js} +1 -1
  9. package/web/assets/{DocsWorkspace-DY0x50eq.js → DocsWorkspace-CgM5rgcr.js} +1 -1
  10. package/web/assets/{IdeScreen-BpSi7o5r.js → IdeScreen-B7oDlNQ0.js} +1 -1
  11. package/web/assets/{LeadScreen-BRzdha5a.js → LeadScreen-BzNU_kBC.js} +1 -1
  12. package/web/assets/{ReviewScreen-D2SwZTqF.js → ReviewScreen-Dk0Zy4jJ.js} +1 -1
  13. package/web/assets/{SchedulerScreen-w6hID1yX.js → SchedulerScreen-CxYqEqnw.js} +1 -1
  14. package/web/assets/SettingsScreen-D1Fop_V6.js +62 -0
  15. package/web/assets/{TeamScreen-D0W-oNAi.js → TeamScreen-DwtrDuZG.js} +1 -1
  16. package/web/assets/TerminalScreen-CViC7rot.js +9 -0
  17. package/web/assets/{TriageScreen-CmVGUNpA.js → TriageScreen-DjCXx0F4.js} +1 -1
  18. package/web/assets/{VpsScreen-BKoJ_dAB.js → VpsScreen-C2WoJQ_j.js} +1 -1
  19. package/web/assets/{diff-view-B_pIZnWa.js → diff-view-Bep86yt6.js} +1 -1
  20. package/web/assets/index-Bd9gXx1g.js +923 -0
  21. package/web/assets/{live-DJzk8BTb.js → live-BlNZnTbo.js} +1 -1
  22. package/web/index.html +1 -1
  23. package/web/assets/SettingsScreen-DC4OFYQ-.js +0 -62
  24. package/web/assets/TerminalScreen-B2keWEDd.js +0 -9
  25. package/web/assets/index-C_-gADIr.js +0 -923
package/dist/server.js CHANGED
@@ -4793,6 +4793,214 @@ var init_logs = __esm({
4793
4793
  }
4794
4794
  });
4795
4795
 
4796
+ // ../shared/src/orchestration.ts
4797
+ function phaseAgentName(phase) {
4798
+ return PHASE_AGENT_PREFIX + phase.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4799
+ }
4800
+ var ORCHESTRATION_FLOWS, FLOW_PHASES, PHASE_AGENT_PREFIX, isPhaseAgentName, PHASE_EVIDENCE_GRACE_MS;
4801
+ var init_orchestration = __esm({
4802
+ "../shared/src/orchestration.ts"() {
4803
+ "use strict";
4804
+ ORCHESTRATION_FLOWS = [
4805
+ "feature",
4806
+ "qa",
4807
+ "scaffold",
4808
+ "reverse",
4809
+ "prd",
4810
+ "audit",
4811
+ "breakdown",
4812
+ "goal",
4813
+ "no_effort"
4814
+ ];
4815
+ FLOW_PHASES = {
4816
+ feature: ["Brainstorm", "Objective", "Spec", "Plan", "Execute"],
4817
+ qa: ["Audit", "Spec", "Plan", "Execute"],
4818
+ scaffold: ["Brainstorm", "Objective", "Doc index"],
4819
+ reverse: ["Scan", "Docs teknis", "Wawancara", "Konvensi & index", "Serah terima"],
4820
+ prd: ["Brainstorm", "PRD"],
4821
+ audit: ["Audit", "Laporan"],
4822
+ breakdown: ["Analisis", "Breakdown"],
4823
+ // SPEC-337 · ADR-0075 · audit lintas project: fase & stage-map identik audit-only, scope-nya
4824
+ // yang berbeda (project utama + tetangga ProjectLink).
4825
+ // SPEC-407 · ADR-0089 · backlog goal: tak ada fase perencanaan sama sekali. `Goal` = kerjakan,
4826
+ // `Verifikasi` = buktikan. Kedua nama unik lintas daftar ini — syarat peta REACHED di server,
4827
+ // yang berkunci nama fase saja.
4828
+ goal: ["Goal", "Verifikasi"],
4829
+ // SPEC-825 · ADR-0123 · task remeh: SATU fase. Nama `Kerjakan` unik lintas daftar ini — syarat
4830
+ // peta REACHED server, yang berkunci nama fase saja.
4831
+ no_effort: ["Kerjakan"]
4832
+ };
4833
+ PHASE_AGENT_PREFIX = "hanoman-fase-";
4834
+ isPhaseAgentName = (name2) => name2.startsWith(PHASE_AGENT_PREFIX);
4835
+ PHASE_EVIDENCE_GRACE_MS = 6e4;
4836
+ }
4837
+ });
4838
+
4839
+ // ../shared/src/runtime-defaults.ts
4840
+ var RUNTIME_DEFAULTS_VERSION, zRuntimeDefaultState, zBuiltinRuntimeDefaults, BUILTIN_CLAUDE_RUNTIME_DEFAULTS, BUILTIN_CODEX_RUNTIME_DEFAULTS, LEGACY_CLAUDE_RUNTIME_DEFAULTS, LEGACY_CODEX_RUNTIME_DEFAULTS, LEGACY_ORCHESTRATION_FLOW_DEFAULT, cell, flow, BUILTIN_ORCHESTRATION_DEFAULTS, seededFlows, BUILTIN_RUNTIME_DEFAULTS;
4841
+ var init_runtime_defaults = __esm({
4842
+ "../shared/src/runtime-defaults.ts"() {
4843
+ "use strict";
4844
+ init_orchestration();
4845
+ init_zod();
4846
+ RUNTIME_DEFAULTS_VERSION = "2026-09-17-v1";
4847
+ zRuntimeDefaultState = external_exports.enum(["seeded", "user"]);
4848
+ zBuiltinRuntimeDefaults = external_exports.object({
4849
+ version: external_exports.string().default(RUNTIME_DEFAULTS_VERSION),
4850
+ claude: external_exports.object({
4851
+ model: zRuntimeDefaultState.default("seeded"),
4852
+ effort: zRuntimeDefaultState.default("seeded")
4853
+ }).default({}),
4854
+ codex: external_exports.object({
4855
+ model: zRuntimeDefaultState.default("seeded"),
4856
+ effort: zRuntimeDefaultState.default("seeded")
4857
+ }).default({}),
4858
+ orchestration: external_exports.record(external_exports.string(), zRuntimeDefaultState).default({})
4859
+ });
4860
+ BUILTIN_CLAUDE_RUNTIME_DEFAULTS = {
4861
+ model: "claude-sonnet-5",
4862
+ effort: "medium"
4863
+ };
4864
+ BUILTIN_CODEX_RUNTIME_DEFAULTS = {
4865
+ model: "gpt-5.6-terra",
4866
+ effort: "medium"
4867
+ };
4868
+ LEGACY_CLAUDE_RUNTIME_DEFAULTS = {
4869
+ model: "claude-opus-5",
4870
+ effort: "xhigh"
4871
+ };
4872
+ LEGACY_CODEX_RUNTIME_DEFAULTS = {
4873
+ model: "gpt-5.6-sol",
4874
+ effort: "xhigh"
4875
+ };
4876
+ LEGACY_ORCHESTRATION_FLOW_DEFAULT = {
4877
+ enabled: true,
4878
+ claude: {},
4879
+ codex: {}
4880
+ };
4881
+ cell = (model, effort2) => ({ model, effort: effort2 });
4882
+ flow = (claude, codex2, enabled = true) => ({ enabled, claude, codex: codex2 });
4883
+ BUILTIN_ORCHESTRATION_DEFAULTS = {
4884
+ feature: flow(
4885
+ {
4886
+ Brainstorm: cell("claude-sonnet-5", "medium"),
4887
+ Objective: cell("claude-sonnet-5", "medium"),
4888
+ Spec: cell("claude-opus-5", "medium"),
4889
+ Plan: cell("claude-sonnet-5", "medium"),
4890
+ Execute: cell("claude-sonnet-5", "medium")
4891
+ },
4892
+ {
4893
+ Brainstorm: cell("gpt-5.6-terra", "medium"),
4894
+ Objective: cell("gpt-5.6-terra", "medium"),
4895
+ Spec: cell("gpt-5.6-sol", "medium"),
4896
+ Plan: cell("gpt-5.6-terra", "medium"),
4897
+ Execute: cell("gpt-5.6-terra", "medium")
4898
+ }
4899
+ ),
4900
+ qa: flow(
4901
+ {
4902
+ Audit: cell("claude-sonnet-5", "medium"),
4903
+ Spec: cell("claude-opus-5", "medium"),
4904
+ Plan: cell("claude-sonnet-5", "medium"),
4905
+ Execute: cell("claude-sonnet-5", "medium")
4906
+ },
4907
+ {
4908
+ Audit: cell("gpt-5.6-terra", "medium"),
4909
+ Spec: cell("gpt-5.6-sol", "medium"),
4910
+ Plan: cell("gpt-5.6-terra", "medium"),
4911
+ Execute: cell("gpt-5.6-terra", "medium")
4912
+ }
4913
+ ),
4914
+ scaffold: flow(
4915
+ {
4916
+ Brainstorm: cell("claude-sonnet-5", "medium"),
4917
+ Objective: cell("claude-sonnet-5", "medium"),
4918
+ "Doc index": cell("claude-opus-5", "medium")
4919
+ },
4920
+ {
4921
+ Brainstorm: cell("gpt-5.6-terra", "medium"),
4922
+ Objective: cell("gpt-5.6-terra", "medium"),
4923
+ "Doc index": cell("gpt-5.6-sol", "medium")
4924
+ }
4925
+ ),
4926
+ reverse: flow(
4927
+ {
4928
+ Scan: cell("claude-sonnet-5", "medium"),
4929
+ "Docs teknis": cell("claude-sonnet-5", "medium"),
4930
+ Wawancara: cell("claude-sonnet-5", "medium"),
4931
+ "Konvensi & index": cell("claude-sonnet-5", "medium"),
4932
+ "Serah terima": cell("claude-sonnet-5", "medium")
4933
+ },
4934
+ {
4935
+ Scan: cell("gpt-5.6-terra", "medium"),
4936
+ "Docs teknis": cell("gpt-5.6-terra", "medium"),
4937
+ Wawancara: cell("gpt-5.6-terra", "medium"),
4938
+ "Konvensi & index": cell("gpt-5.6-terra", "medium"),
4939
+ "Serah terima": cell("gpt-5.6-terra", "medium")
4940
+ }
4941
+ ),
4942
+ prd: flow(
4943
+ {
4944
+ Brainstorm: cell("claude-sonnet-5", "medium"),
4945
+ PRD: cell("claude-opus-5", "medium")
4946
+ },
4947
+ {
4948
+ Brainstorm: cell("gpt-5.6-terra", "medium"),
4949
+ PRD: cell("gpt-5.6-sol", "medium")
4950
+ }
4951
+ ),
4952
+ audit: flow(
4953
+ {
4954
+ Audit: cell("claude-sonnet-5", "medium"),
4955
+ Laporan: cell("claude-sonnet-5", "medium")
4956
+ },
4957
+ {
4958
+ Audit: cell("gpt-5.6-terra", "medium"),
4959
+ Laporan: cell("gpt-5.6-terra", "medium")
4960
+ }
4961
+ ),
4962
+ breakdown: flow(
4963
+ {
4964
+ Analisis: cell("claude-opus-5", "medium"),
4965
+ Breakdown: cell("claude-opus-5", "medium")
4966
+ },
4967
+ {
4968
+ Analisis: cell("gpt-5.6-sol", "medium"),
4969
+ Breakdown: cell("gpt-5.6-sol", "medium")
4970
+ }
4971
+ ),
4972
+ goal: flow(
4973
+ {
4974
+ Goal: cell("claude-sonnet-5", "medium"),
4975
+ Verifikasi: cell("claude-sonnet-5", "medium")
4976
+ },
4977
+ {
4978
+ Goal: cell("gpt-5.6-terra", "medium"),
4979
+ Verifikasi: cell("gpt-5.6-terra", "medium")
4980
+ }
4981
+ ),
4982
+ no_effort: flow(
4983
+ { Kerjakan: cell("claude-haiku-4-5", "low") },
4984
+ { Kerjakan: cell("gpt-5.6-terra", "low") },
4985
+ false
4986
+ )
4987
+ };
4988
+ seededFlows = Object.fromEntries(ORCHESTRATION_FLOWS.flatMap((flow2) => [
4989
+ [`${flow2}.enabled`, "seeded"],
4990
+ ...["claude", "codex"].flatMap((runtime) => FLOW_PHASES[flow2].flatMap((phase) => [
4991
+ [`${flow2}.${runtime}.${phase}.model`, "seeded"],
4992
+ [`${flow2}.${runtime}.${phase}.effort`, "seeded"]
4993
+ ]))
4994
+ ]));
4995
+ BUILTIN_RUNTIME_DEFAULTS = {
4996
+ version: RUNTIME_DEFAULTS_VERSION,
4997
+ claude: { model: "seeded", effort: "seeded" },
4998
+ codex: { model: "seeded", effort: "seeded" },
4999
+ orchestration: seededFlows
5000
+ };
5001
+ }
5002
+ });
5003
+
4796
5004
  // ../shared/src/entities.ts
4797
5005
  function replaceModelCatalog(claude, codex2) {
4798
5006
  MODELS = claude;
@@ -4826,7 +5034,7 @@ function cmpVersion(a, b) {
4826
5034
  }
4827
5035
  return 0;
4828
5036
  }
4829
- var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSourceChange, zManualDone, zSpec, NOTIFY_SOUNDS, MODELS, EFFORTS, E_5_6, E_LUNA, E_BASE, CODEX_MODELS, RETIRED_CODEX_MODELS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, CHANGELOG_ENGINE_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zPortalChat, PORTAL_CHAT_DEFAULTS, zPhaseCell, zFlowOrchestration, zOrchestration, ORCHESTRATION_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
5037
+ var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSourceChange, zManualDone, zSpec, NOTIFY_SOUNDS, MODELS, EFFORTS, E_5_6, E_LUNA, E_BASE, CODEX_MODELS, RETIRED_CODEX_MODELS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, CHANGELOG_ENGINE_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zPortalChat, PORTAL_CHAT_DEFAULTS, zPhaseCell, zPhaseOverride, zPhaseOverrides, zFlowOrchestration, zOrchestration, ORCHESTRATION_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
4830
5038
  var init_entities = __esm({
4831
5039
  "../shared/src/entities.ts"() {
4832
5040
  "use strict";
@@ -4838,6 +5046,7 @@ var init_entities = __esm({
4838
5046
  init_method_catalog();
4839
5047
  init_relay();
4840
5048
  init_logs();
5049
+ init_runtime_defaults();
4841
5050
  zProject = external_exports.object({
4842
5051
  id: external_exports.string(),
4843
5052
  name: external_exports.string(),
@@ -4971,8 +5180,8 @@ var init_entities = __esm({
4971
5180
  "gpt-5.3-codex-spark": "gpt-5.5"
4972
5181
  };
4973
5182
  zCodex = external_exports.object({
4974
- model: external_exports.string().default("gpt-5.6-sol"),
4975
- effort: external_exports.string().default("xhigh")
5183
+ model: external_exports.string().default("gpt-5.6-terra"),
5184
+ effort: external_exports.string().default("medium")
4976
5185
  });
4977
5186
  CODEX_DEFAULTS = zCodex.parse({});
4978
5187
  zSourceCommon = { enabled: external_exports.boolean().default(false) };
@@ -5063,6 +5272,11 @@ var init_entities = __esm({
5063
5272
  model: external_exports.string().nullable().default(null),
5064
5273
  effort: external_exports.string().nullable().default(null)
5065
5274
  });
5275
+ zPhaseOverride = external_exports.object({
5276
+ model: external_exports.string().optional(),
5277
+ effort: external_exports.string().optional()
5278
+ });
5279
+ zPhaseOverrides = external_exports.record(external_exports.string(), zPhaseOverride);
5066
5280
  zFlowOrchestration = external_exports.object({
5067
5281
  enabled: external_exports.boolean().default(true),
5068
5282
  claude: external_exports.record(external_exports.string(), zPhaseCell).default({}),
@@ -5079,10 +5293,10 @@ var init_entities = __esm({
5079
5293
  goal: zFlowOrchestration.default({}),
5080
5294
  no_effort: zFlowOrchestration.default({})
5081
5295
  });
5082
- ORCHESTRATION_DEFAULTS = zOrchestration.parse({});
5296
+ ORCHESTRATION_DEFAULTS = zOrchestration.parse(BUILTIN_ORCHESTRATION_DEFAULTS);
5083
5297
  zSetting = external_exports.object({
5084
- model: external_exports.string().default("claude-opus-5"),
5085
- effort: external_exports.string().default("xhigh"),
5298
+ model: external_exports.string().default("claude-sonnet-5"),
5299
+ effort: external_exports.string().default("medium"),
5086
5300
  autoDefault: external_exports.boolean(),
5087
5301
  autoScaffold: external_exports.boolean(),
5088
5302
  notifyFail: external_exports.boolean(),
@@ -5143,7 +5357,10 @@ var init_entities = __esm({
5143
5357
  builtinAgents: external_exports.record(external_exports.string(), external_exports.string()).default({}),
5144
5358
  // SPEC-950 · marker safety policy lokal. Terpisah dari fingerprint konten agar policy sekali
5145
5359
  // jalan tidak membuat seed memperlakukan konfigurasi operator sebagai bawaan.
5146
- builtinAgentPolicies: external_exports.record(external_exports.string(), external_exports.string()).default({})
5360
+ builtinAgentPolicies: external_exports.record(external_exports.string(), external_exports.string()).default({}),
5361
+ // Marker lokal per bagian: `seeded` boleh menerima rekomendasi baru saat update, `user` harus
5362
+ // dipertahankan. Setting tidak ikut sync, sehingga keputusan operator tetap milik mesin ini.
5363
+ builtinRuntimeDefaults: zBuiltinRuntimeDefaults.default(BUILTIN_RUNTIME_DEFAULTS)
5147
5364
  });
5148
5365
  zNotification = external_exports.object({
5149
5366
  id: external_exports.string(),
@@ -5177,38 +5394,6 @@ var init_entities = __esm({
5177
5394
  }
5178
5395
  });
5179
5396
 
5180
- // ../shared/src/orchestration.ts
5181
- function phaseAgentName(phase) {
5182
- return PHASE_AGENT_PREFIX + phase.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5183
- }
5184
- var FLOW_PHASES, PHASE_AGENT_PREFIX, isPhaseAgentName, PHASE_EVIDENCE_GRACE_MS;
5185
- var init_orchestration = __esm({
5186
- "../shared/src/orchestration.ts"() {
5187
- "use strict";
5188
- FLOW_PHASES = {
5189
- feature: ["Brainstorm", "Objective", "Spec", "Plan", "Execute"],
5190
- qa: ["Audit", "Spec", "Plan", "Execute"],
5191
- scaffold: ["Brainstorm", "Objective", "Doc index"],
5192
- reverse: ["Scan", "Docs teknis", "Wawancara", "Konvensi & index", "Serah terima"],
5193
- prd: ["Brainstorm", "PRD"],
5194
- audit: ["Audit", "Laporan"],
5195
- breakdown: ["Analisis", "Breakdown"],
5196
- // SPEC-337 · ADR-0075 · audit lintas project: fase & stage-map identik audit-only, scope-nya
5197
- // yang berbeda (project utama + tetangga ProjectLink).
5198
- // SPEC-407 · ADR-0089 · backlog goal: tak ada fase perencanaan sama sekali. `Goal` = kerjakan,
5199
- // `Verifikasi` = buktikan. Kedua nama unik lintas daftar ini — syarat peta REACHED di server,
5200
- // yang berkunci nama fase saja.
5201
- goal: ["Goal", "Verifikasi"],
5202
- // SPEC-825 · ADR-0123 · task remeh: SATU fase. Nama `Kerjakan` unik lintas daftar ini — syarat
5203
- // peta REACHED server, yang berkunci nama fase saja.
5204
- no_effort: ["Kerjakan"]
5205
- };
5206
- PHASE_AGENT_PREFIX = "hanoman-fase-";
5207
- isPhaseAgentName = (name2) => name2.startsWith(PHASE_AGENT_PREFIX);
5208
- PHASE_EVIDENCE_GRACE_MS = 6e4;
5209
- }
5210
- });
5211
-
5212
5397
  // ../shared/src/orchestration-plan.ts
5213
5398
  function codexNativeAgentsSupported(version) {
5214
5399
  const parsed = version ? /(\d+)\.(\d+)\.(\d+)/.exec(version)?.[0] : null;
@@ -5223,13 +5408,14 @@ function resolvePhasePlan(input) {
5223
5408
  flow: input.flow,
5224
5409
  runtime: input.runtime,
5225
5410
  phases: FLOW_PHASES[input.flow].map((phase) => {
5226
- const cell = cells[phase];
5227
- const model = cell?.model ?? input.orchestrator.model;
5411
+ const cell2 = cells[phase];
5412
+ const override = input.phaseOverrides?.[phase];
5413
+ const model = override?.model ?? cell2?.model ?? input.orchestrator.model;
5228
5414
  return {
5229
5415
  phase,
5230
5416
  agentName: phaseAgentName(phase),
5231
5417
  model,
5232
- effort: coerce3(model, cell?.effort ?? input.orchestrator.effort)
5418
+ effort: coerce3(model, override?.effort ?? cell2?.effort ?? input.orchestrator.effort)
5233
5419
  };
5234
5420
  })
5235
5421
  };
@@ -6910,7 +7096,7 @@ var init_dto = __esm({
6910
7096
  }).default({ inFlight: 0, queued: 0, capacity: 1 })
6911
7097
  });
6912
7098
  zFlow = external_exports.enum(["feature", "qa", "scaffold", "reverse", "prd", "audit", "breakdown", "goal", "no_effort"]);
6913
- isGoalShapedFlow = (flow) => flow === "goal" || flow === "no_effort";
7099
+ isGoalShapedFlow = (flow2) => flow2 === "goal" || flow2 === "no_effort";
6914
7100
  zPrdBrief = external_exports.object({
6915
7101
  title: external_exports.string().min(1),
6916
7102
  context: external_exports.string(),
@@ -7026,6 +7212,9 @@ var init_dto = __esm({
7026
7212
  flow: zFlow,
7027
7213
  model: external_exports.string().optional(),
7028
7214
  effort: external_exports.string().optional(),
7215
+ // Override model/effort subagent hanya berlaku untuk sesi ini; kosong berarti memakai
7216
+ // rekomendasi bawaan atau matriks Settings pada fase tersebut.
7217
+ phaseOverrides: zPhaseOverrides.optional(),
7029
7218
  goal: external_exports.boolean().optional(),
7030
7219
  goalCondition: external_exports.string().max(4e3).optional(),
7031
7220
  agent: zAgent.optional(),
@@ -11244,9 +11433,9 @@ var init_sessions = __esm({
11244
11433
  }
11245
11434
  const project = String(a.project);
11246
11435
  if (a.shell === true) return { method: "POST", path: "/terminal/sessions", body: { project, shell: true } };
11247
- const flow = s(a.flow);
11248
- if (flow === "breakdown") return { method: "POST", path: "/terminal/sessions", body: { project, flow, prdPath: String(a.prdPath) } };
11249
- if (flow === "reverse" || flow === "scaffold") return { method: "POST", path: "/terminal/sessions", body: { project, flow } };
11436
+ const flow2 = s(a.flow);
11437
+ if (flow2 === "breakdown") return { method: "POST", path: "/terminal/sessions", body: { project, flow: flow2, prdPath: String(a.prdPath) } };
11438
+ if (flow2 === "reverse" || flow2 === "scaffold") return { method: "POST", path: "/terminal/sessions", body: { project, flow: flow2 } };
11250
11439
  const body = { project };
11251
11440
  for (const k of ["agent", "model", "effort"]) if (s(a[k]) !== void 0) body[k] = s(a[k]);
11252
11441
  return { method: "POST", path: "/terminal/sessions", body };
@@ -12563,6 +12752,7 @@ var init_src = __esm({
12563
12752
  init_pending();
12564
12753
  init_team();
12565
12754
  init_model_catalog();
12755
+ init_runtime_defaults();
12566
12756
  }
12567
12757
  });
12568
12758
 
@@ -12797,10 +12987,10 @@ var init_goal_spec = __esm({
12797
12987
  function guideLine(guide, phase) {
12798
12988
  return guide.split("\n").find((line) => line.startsWith(`- ${phase}:`)) ?? "";
12799
12989
  }
12800
- function phaseSkillsFor(flow, phase, method) {
12990
+ function phaseSkillsFor(flow2, phase, method) {
12801
12991
  const own = method.phaseSkills[phase] ?? [];
12802
- const phases = PIPELINES[flow];
12803
- return writesCode(flow) && phase === phases[phases.length - 1] ? [.../* @__PURE__ */ new Set([...own, ...method.exitSkills])] : [...own];
12992
+ const phases = PIPELINES[flow2];
12993
+ return writesCode(flow2) && phase === phases[phases.length - 1] ? [.../* @__PURE__ */ new Set([...own, ...method.exitSkills])] : [...own];
12804
12994
  }
12805
12995
  function orchestratorClause(plan, o = {}) {
12806
12996
  const codex2 = plan.runtime === "codex";
@@ -12833,15 +13023,15 @@ ${describeRule}`,
12833
13023
  '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.'
12834
13024
  ].filter(Boolean).join("\n\n");
12835
13025
  }
12836
- function startPrompt(flow, spec, branchTo, autonomy, verifyScope, method, attachments, plan) {
13026
+ function startPrompt(flow2, spec, branchTo, autonomy, verifyScope, method, attachments, plan) {
12837
13027
  const m = resolveMethod(method);
12838
- const head = `hanoman ${flow}. Ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.`;
13028
+ const head = `hanoman ${flow2}. Ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.`;
12839
13029
  const push = `Setelah fase terakhir: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`;
12840
13030
  if (plan) {
12841
13031
  return [
12842
13032
  head,
12843
- orchestratorClause(plan, { fastPath: flow === "qa" }),
12844
- auditContinuationForOrchestrator(flow, spec),
13033
+ orchestratorClause(plan, { fastPath: flow2 === "qa" }),
13034
+ auditContinuationForOrchestrator(flow2, spec),
12845
13035
  autonomyClause(autonomy),
12846
13036
  attachmentClause(attachments),
12847
13037
  push,
@@ -12850,23 +13040,23 @@ function startPrompt(flow, spec, branchTo, autonomy, verifyScope, method, attach
12850
13040
  }
12851
13041
  return [
12852
13042
  head,
12853
- phaseInstruction(PIPELINES[flow], m),
12854
- auditDecisionInstruction(flow),
12855
- auditContinuationInstruction(flow, spec),
12856
- auditOnlyInstruction(flow),
13043
+ phaseInstruction(PIPELINES[flow2], m),
13044
+ auditDecisionInstruction(flow2),
13045
+ auditContinuationInstruction(flow2, spec),
13046
+ auditOnlyInstruction(flow2),
12857
13047
  autonomyClause(autonomy),
12858
- scopeClause(flow, verifyScope),
12859
- codeStyleClause(flow),
13048
+ scopeClause(flow2, verifyScope),
13049
+ codeStyleClause(flow2),
12860
13050
  methodClause(m),
12861
13051
  attachmentClause(attachments),
12862
- skillInstruction(flow, PIPELINES[flow], m),
13052
+ skillInstruction(flow2, PIPELINES[flow2], m),
12863
13053
  push,
12864
13054
  specContext(spec)
12865
13055
  ].filter(Boolean).join("\n\n");
12866
13056
  }
12867
- function continuePrompt(flow, spec, branchTo, autonomy, verifyScope, method, attachments, plan) {
13057
+ function continuePrompt(flow2, spec, branchTo, autonomy, verifyScope, method, attachments, plan) {
12868
13058
  const m = resolveMethod(method);
12869
- const head = `hanoman ${flow} \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.`;
13059
+ 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.`;
12870
13060
  const push = `Setelah selesai: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`;
12871
13061
  if (plan) {
12872
13062
  return [
@@ -12883,26 +13073,26 @@ function continuePrompt(flow, spec, branchTo, autonomy, verifyScope, method, att
12883
13073
  head,
12884
13074
  `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.`,
12885
13075
  autonomyClause(autonomy),
12886
- scopeClause(flow, verifyScope),
12887
- codeStyleClause(flow),
13076
+ scopeClause(flow2, verifyScope),
13077
+ codeStyleClause(flow2),
12888
13078
  methodClause(m),
12889
13079
  attachmentClause(attachments),
12890
- skillInstruction(flow, ["Execute"], m),
13080
+ skillInstruction(flow2, ["Execute"], m),
12891
13081
  push,
12892
13082
  specContext(spec)
12893
13083
  ].filter(Boolean).join("\n\n");
12894
13084
  }
12895
- function resumePrompt(flow, spec, branchTo, resume, autonomy, verifyScope, method, attachments, plan) {
13085
+ function resumePrompt(flow2, spec, branchTo, resume, autonomy, verifyScope, method, attachments, plan) {
12896
13086
  const m = resolveMethod(method);
12897
13087
  const auditDecided = resume.recorded.some((line) => line.startsWith("Audit "));
12898
- const head = `hanoman ${flow} \u2014 MELANJUTKAN sesi backlog yang sudah berjalan. Ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.`;
12899
- const resumed = resumeClause(resume, branchTo, m.planDir, PIPELINES[flow].includes("Plan"));
13088
+ const head = `hanoman ${flow2} \u2014 MELANJUTKAN sesi backlog yang sudah berjalan. Ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.`;
13089
+ const resumed = resumeClause(resume, branchTo, m.planDir, PIPELINES[flow2].includes("Plan"));
12900
13090
  const push = `Setelah fase terakhir: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`;
12901
13091
  if (plan) {
12902
13092
  return [
12903
13093
  head,
12904
13094
  resumed,
12905
- orchestratorClause(plan, { fastPath: flow === "qa" && !auditDecided }),
13095
+ orchestratorClause(plan, { fastPath: flow2 === "qa" && !auditDecided }),
12906
13096
  autonomyClause(autonomy),
12907
13097
  attachmentClause(attachments),
12908
13098
  push,
@@ -12912,21 +13102,21 @@ function resumePrompt(flow, spec, branchTo, resume, autonomy, verifyScope, metho
12912
13102
  return [
12913
13103
  head,
12914
13104
  resumed,
12915
- phaseInstruction(PIPELINES[flow], m),
12916
- auditDecided ? "" : auditDecisionInstruction(flow),
13105
+ phaseInstruction(PIPELINES[flow2], m),
13106
+ auditDecided ? "" : auditDecisionInstruction(flow2),
12917
13107
  autonomyClause(autonomy),
12918
- scopeClause(flow, verifyScope),
12919
- codeStyleClause(flow),
13108
+ scopeClause(flow2, verifyScope),
13109
+ codeStyleClause(flow2),
12920
13110
  methodClause(m),
12921
13111
  attachmentClause(attachments),
12922
- skillInstruction(flow, PIPELINES[flow], m),
13112
+ skillInstruction(flow2, PIPELINES[flow2], m),
12923
13113
  push,
12924
13114
  specContext(spec)
12925
13115
  ].filter(Boolean).join("\n\n");
12926
13116
  }
12927
- function startGoalPrompt(flow, spec, branchTo, opts = {}) {
13117
+ function startGoalPrompt(flow2, spec, branchTo, opts = {}) {
12928
13118
  const m = resolveMethod(opts.method);
12929
- const noEffort = flow === "no_effort";
13119
+ const noEffort = flow2 === "no_effort";
12930
13120
  const head = noEffort ? "hanoman no-effort \u2014 sesi ini mengerjakan SATU pekerjaan remeh lalu berhenti. TIDAK ada fase Brainstorm, Objective, Spec, Plan, maupun fase pembuktian terpisah: jangan menulis design doc, jangan menulis plan berkotak, jangan memecah pekerjaan ini jadi backlog baru, dan jangan menambah fase sendiri. Langsung kerjakan, buktikan seperlunya di fase yang sama, lalu berhenti. Tetap ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama." : "hanoman goal \u2014 sesi ini mengejar SATU goal sampai tercapai. TIDAK ada fase Brainstorm, Objective, Spec, maupun Plan: jangan menulis design doc, jangan menulis plan berkotak, jangan memecah pekerjaan ini jadi backlog baru. Langsung kerjakan goal-nya. Tetap ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.";
12931
13121
  const resumed = opts.resume ? resumeClause(opts.resume, branchTo, m.planDir, false) : "";
12932
13122
  const push = `Setelah fase terakhir: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`;
@@ -12946,31 +13136,31 @@ function startGoalPrompt(flow, spec, branchTo, opts = {}) {
12946
13136
  head,
12947
13137
  resumed,
12948
13138
  goalDetail(spec),
12949
- phaseInstruction(PIPELINES[flow], m),
13139
+ phaseInstruction(PIPELINES[flow2], m),
12950
13140
  noEffort ? "" : "Fase Verifikasi bukan formalitas: jalankan perintah yang membuktikan goal-nya tercapai (test/typecheck/benchmark/perintah yang relevan) dan baca outputnya. Klaim tanpa output bukan bukti.",
12951
13141
  autonomyClause(opts.autonomy),
12952
- scopeClause(flow, opts.verifyScope),
12953
- codeStyleClause(flow),
13142
+ scopeClause(flow2, opts.verifyScope),
13143
+ codeStyleClause(flow2),
12954
13144
  methodClause(m),
12955
13145
  attachmentClause(opts.attachments),
12956
- skillInstruction(flow, PIPELINES[flow], m),
13146
+ skillInstruction(flow2, PIPELINES[flow2], m),
12957
13147
  push,
12958
13148
  goalBlock(spec)
12959
13149
  ].filter(Boolean).join("\n\n");
12960
13150
  }
12961
- function startProjectPrompt(flow, project, branchTo, plan) {
13151
+ function startProjectPrompt(flow2, project, branchTo, plan) {
12962
13152
  const push = `Setiap fase selesai: commit hasilnya, lalu \`git push origin HEAD:refs/heads/${branchTo}\` \u2014 push per fase, supaya pekerjaan tak hilang bila worktree lenyap. Bila remote origin tidak ada, lewati push dan catat itu di laporan akhir \u2014 jangan gagal diam-diam. Worktree ini detached HEAD \u2014 memang disengaja. Manusia yang me-review dan merge branch ${branchTo}.`;
12963
13153
  if (plan) {
12964
13154
  return [
12965
- `hanoman ${flow}. Susun Source of Truth repo ini dari kodenya di internal/docs/** lewat subagent fase; STANDAR DOCS ada di definisi agen fase penulis docs.`,
13155
+ `hanoman ${flow2}. Susun Source of Truth repo ini dari kodenya di internal/docs/** lewat subagent fase; STANDAR DOCS ada di definisi agen fase penulis docs.`,
12966
13156
  orchestratorClause(plan),
12967
13157
  push,
12968
13158
  projectContext(project)
12969
13159
  ].join("\n\n");
12970
13160
  }
12971
13161
  return [
12972
- `hanoman ${flow}. Susun Source of Truth repo ini dari kodenya di internal/docs/**, mengikuti STANDAR DOCS di bagian bawah prompt ini.`,
12973
- phaseInstruction(PIPELINES[flow], PROJECT_METHOD),
13162
+ `hanoman ${flow2}. Susun Source of Truth repo ini dari kodenya di internal/docs/**, mengikuti STANDAR DOCS di bagian bawah prompt ini.`,
13163
+ phaseInstruction(PIPELINES[flow2], PROJECT_METHOD),
12974
13164
  REVERSE_PHASE_GUIDE,
12975
13165
  push,
12976
13166
  projectContext(project),
@@ -13081,15 +13271,15 @@ Setiap kali sebuah fase selesai (atau kamu putuskan dilewati), append satu baris
13081
13271
  return base2 + `
13082
13272
  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 \`- [ ]\`.`;
13083
13273
  };
13084
- skillInstruction = (flow, phases, method) => {
13274
+ skillInstruction = (flow2, phases, method) => {
13085
13275
  const lines2 = phases.map((p3) => {
13086
- const skills = phaseSkillsFor(flow, p3, method);
13276
+ const skills = phaseSkillsFor(flow2, p3, method);
13087
13277
  return skills.length ? `- ${p3}: ${skills.join(", ")}` : "";
13088
13278
  }).filter(Boolean);
13089
13279
  return lines2.length ? `Skills ${method.label} WAJIB: sebelum mengerjakan fase di bawah, muat & ikuti skill-nya dengan mekanisme yang tersedia di agenmu \u2014 bila skill relevan tersedia, pakai.
13090
13280
  ${lines2.join("\n")}` : "";
13091
13281
  };
13092
- auditDecisionInstruction = (flow) => flow !== "qa" ? "" : 'Keputusan pasca-Audit (qa): bila temuan berconfidence tinggi dan perbaikannya bisa dikerjakan langsung (diff kecil, akar masalah jelas), LEWATI Spec dan Plan \u2014 tandai keduanya `skipped` (`echo "Spec skipped" >> "$HANOMAN_PHASE_FILE"` lalu `echo "Plan skipped" >> "$HANOMAN_PHASE_FILE"`) dan langsung ke Execute; dokumen audit menjadi doc-of-record perbaikan itu. Bila temuan luas, berisiko, atau ambigu, jalankan Spec \u2192 Plan \u2192 Execute penuh. Keputusan ini milikmu berdasarkan hasil Audit, bukan default \u2014 jangan bayar perencanaan yang tak perlu untuk perbaikan sepele.';
13282
+ auditDecisionInstruction = (flow2) => flow2 !== "qa" ? "" : 'Keputusan pasca-Audit (qa): bila temuan berconfidence tinggi dan perbaikannya bisa dikerjakan langsung (diff kecil, akar masalah jelas), LEWATI Spec dan Plan \u2014 tandai keduanya `skipped` (`echo "Spec skipped" >> "$HANOMAN_PHASE_FILE"` lalu `echo "Plan skipped" >> "$HANOMAN_PHASE_FILE"`) dan langsung ke Execute; dokumen audit menjadi doc-of-record perbaikan itu. Bila temuan luas, berisiko, atau ambigu, jalankan Spec \u2192 Plan \u2192 Execute penuh. Keputusan ini milikmu berdasarkan hasil Audit, bukan default \u2014 jangan bayar perencanaan yang tak perlu untuk perbaikan sepele.';
13093
13283
  ESCALATION_CONTRACT = [
13094
13284
  "REKOMENDASI ESKALASI (wajib, terbaca mesin). Di bagian akhir dokumen audit, tulis bagian",
13095
13285
  "`## Rekomendasi eskalasi` berisi penjelasan singkat untuk manusia, LALU tepat SATU blok ```json",
@@ -13112,28 +13302,28 @@ ${lines2.join("\n")}` : "";
13112
13302
  "",
13113
13303
  "`alternatives` boleh array kosong. Jangan menulis blok json lain di dokumen itu."
13114
13304
  ].join("\n");
13115
- auditOnlyInstruction = (flow) => flow !== "audit" ? "" : "Ini audit-only: investigasi SAJA, JANGAN menulis perbaikan kode apa pun. Fase Audit (systematic-debugging): telusuri akar masalah / log / jawaban dan nilai apakah issue terdefinisi dengan baik. Fase Laporan: tulis DOKUMEN AUDIT ke Source of Truth `internal/docs/research/audit-<spec-id>-<slug>.md` (ikuti konvensi audit yang ada), tautkan di `internal/docs/README.md`, memuat: keluhan/pertanyaan, temuan (dengan bukti/log), apakah issue terdefinisi baik, dan rekomendasi tindak lanjut. Commit dokumen itu lalu push. Tak ada kode fitur.\n\n" + ESCALATION_CONTRACT;
13116
- auditContinuationInstruction = (flow, spec) => {
13117
- if (flow !== "qa" && flow !== "feature") return "";
13305
+ auditOnlyInstruction = (flow2) => flow2 !== "audit" ? "" : "Ini audit-only: investigasi SAJA, JANGAN menulis perbaikan kode apa pun. Fase Audit (systematic-debugging): telusuri akar masalah / log / jawaban dan nilai apakah issue terdefinisi dengan baik. Fase Laporan: tulis DOKUMEN AUDIT ke Source of Truth `internal/docs/research/audit-<spec-id>-<slug>.md` (ikuti konvensi audit yang ada), tautkan di `internal/docs/README.md`, memuat: keluhan/pertanyaan, temuan (dengan bukti/log), apakah issue terdefinisi baik, dan rekomendasi tindak lanjut. Commit dokumen itu lalu push. Tak ada kode fitur.\n\n" + ESCALATION_CONTRACT;
13306
+ auditContinuationInstruction = (flow2, spec) => {
13307
+ if (flow2 !== "qa" && flow2 !== "feature") return "";
13118
13308
  const fromAudit = spec.payload && typeof spec.payload === "object" ? spec.payload.fromAudit : void 0;
13119
13309
  if (typeof fromAudit !== "string" || !fromAudit) return "";
13120
13310
  const doc = `internal/docs/research/audit-${fromAudit.toLowerCase()}-*.md`;
13121
- if (flow === "feature")
13311
+ if (flow2 === "feature")
13122
13312
  return `Backlog brief ini LANJUTAN dari audit ${fromAudit}. Worktree ini lahir dari branch audit itu, jadi dokumen audit sudah ada di ${doc}. BACA dokumen itu lebih dulu dan pakai sebagai bahan fase Brainstorm & Objective \u2014 temuannya sudah terbukti, jangan menginvestigasi ulang dari nol. Semua fase tetap dijalankan: dokumen audit memuat TEMUAN, bukan bentuk solusi, jadi perancangan fitur tetap pekerjaanmu.`;
13123
13313
  return `Backlog qa ini LANJUTAN dari audit ${fromAudit}. Worktree ini lahir dari branch audit itu, jadi dokumen audit sudah ada di ${doc}. JANGAN mengulang investigasi fase Audit dari nol \u2014 baca dokumen audit itu sebagai temuan, tandai fase Audit dilewati (\`echo "Audit skipped" >> "$HANOMAN_PHASE_FILE"\`), lalu ambil keputusan pasca-Audit: perbaikan jelas & kecil \u2192 langsung Execute (tandai \`Spec skipped\` dan \`Plan skipped\` bila sesuai); selain itu Spec \u2192 Plan \u2192 Execute penuh.`;
13124
13314
  };
13125
- auditContinuationForOrchestrator = (flow, spec) => {
13126
- if (flow !== "qa" && flow !== "feature") return "";
13315
+ auditContinuationForOrchestrator = (flow2, spec) => {
13316
+ if (flow2 !== "qa" && flow2 !== "feature") return "";
13127
13317
  const fromAudit = spec.payload && typeof spec.payload === "object" ? spec.payload.fromAudit : void 0;
13128
13318
  if (typeof fromAudit !== "string" || !fromAudit) return "";
13129
13319
  const doc = `internal/docs/research/audit-${fromAudit.toLowerCase()}-*.md`;
13130
- if (flow === "feature")
13320
+ if (flow2 === "feature")
13131
13321
  return `Backlog brief ini LANJUTAN dari audit ${fromAudit}. Worktree ini lahir dari branch audit itu, jadi dokumen audit sudah ada di ${doc}. Saat memanggil agen fase Brainstorm, cantumkan path itu di baris \`Artefak fase sebelumnya:\` blok serah-terima \u2014 JANGAN membacanya sendiri untuk merancang fitur ini. Saat memanggil agen fase Objective, baris itu biasanya sudah membawa path artefak Brainstorm (dokumen spec yang ditulis fase itu); tambahkan path dokumen audit di samping path itu, bukan menggantikannya. Temuannya sudah terbukti, tapi bentuk solusinya belum; itu tetap dikerjakan agen fasenya masing-masing, bukan olehmu.`;
13132
13322
  return `Backlog qa ini LANJUTAN dari audit ${fromAudit}. Temuannya sudah terbukti di ${doc} \u2014 JANGAN mendelegasikan ulang fase Audit. Sebelum fase lain: jalankan persis \`echo "Audit skipped" >> "$HANOMAN_PHASE_FILE"\`, verifikasi dengan \`tail -1 "$HANOMAN_PHASE_FILE"\` bahwa barisnya benar tertulis (gerbang yang sama seperti langkah 2), lalu cantumkan path dokumen audit itu di baris \`Artefak fase sebelumnya:\` saat memanggil agen fase Spec. Keputusan routing di sini menggantikan langkah 5 di atas \u2014 JANGAN menunggu frasa \`Rekomendasi fase: jalur-cepat\` dari agen Audit karena Audit tidak dijalankan pada kelanjutan ini. Kamu diizinkan mengambil SATU keputusan ROUTING dari isi dokumen audit itu SAJA, dibaca langsung dari kriterianya: temuan berconfidence tinggi dan perbaikan langsung berdiff kecil \u2192 tandai \`Spec skipped\` lalu \`Plan skipped\` (gerbang yang sama) lalu panggil agen fase Execute; selebihnya panggil Spec \u2192 Plan \u2192 Execute penuh lewat agen fasenya masing-masing. Kamu TIDAK menginvestigasi ulang maupun merancang perbaikannya sendiri \u2014 itu tetap pekerjaan agen fase.`;
13133
13323
  };
13134
- writesCode = (flow) => PIPELINES[flow].some((p3) => WORK_PHASES.includes(p3));
13135
- scopeClause = (flow, scope) => scope && writesCode(flow) ? verifyScopeClause(scope) : "";
13136
- codeStyleClause = (flow) => writesCode(flow) ? CODE_STYLE_CLAUSE : "";
13324
+ writesCode = (flow2) => PIPELINES[flow2].some((p3) => WORK_PHASES.includes(p3));
13325
+ scopeClause = (flow2, scope) => scope && writesCode(flow2) ? verifyScopeClause(scope) : "";
13326
+ codeStyleClause = (flow2) => writesCode(flow2) ? CODE_STYLE_CLAUSE : "";
13137
13327
  methodClause = (method) => method.extraClause ?? "";
13138
13328
  attachmentClause = (ctx) => {
13139
13329
  if (!ctx || ctx.items.length === 0) return "";
@@ -13410,7 +13600,7 @@ var init_settings2 = __esm({
13410
13600
  });
13411
13601
 
13412
13602
  // ../runner/src/goal.ts
13413
- function goalFlowCondition(flow, specId, branchTo, spec) {
13603
+ function goalFlowCondition(flow2, specId, branchTo, spec) {
13414
13604
  const g = readGoalPayload(spec?.payload);
13415
13605
  const goal = g?.goal || (spec?.objective ?? "").trim() || "(goal tak tercatat di backlog item)";
13416
13606
  const bukti = g?.done || goal;
@@ -13418,14 +13608,14 @@ function goalFlowCondition(flow, specId, branchTo, spec) {
13418
13608
  `Sesi goal hanoman ${specId}. GOAL: ${goal}`,
13419
13609
  "Sesi ini hanya boleh berhenti bila transkrip TERBARU memuat bukti langsung semua hal berikut:",
13420
13610
  `1. goal tercapai \u2014 ${bukti};`,
13421
- `2. output \`cat "$HANOMAN_PHASE_FILE"\` yang memuat satu baris untuk SETIAP fase ${PIPELINES[flow].join(" \u2192 ")}, masing-masing berakhiran \`done\` atau \`skipped\`;`,
13611
+ `2. output \`cat "$HANOMAN_PHASE_FILE"\` yang memuat satu baris untuk SETIAP fase ${PIPELINES[flow2].join(" \u2192 ")}, masing-masing berakhiran \`done\` atau \`skipped\`;`,
13422
13612
  `3. output \`git push origin HEAD:refs/heads/${branchTo}\` yang SUKSES sesudah commit terakhir.`,
13423
13613
  "Bila salah satu bukti tak ada di transkrip terbaru, kondisi BELUM terpenuhi: jalankan perintah verifikasinya, tuntaskan yang masih kurang, lalu lanjutkan \u2014 jangan berhenti."
13424
13614
  ].join("\n");
13425
13615
  }
13426
- function defaultGoalCondition({ flow, specId, branchTo, spec }) {
13427
- if (isGoalShapedFlow(flow)) return goalFlowCondition(flow, specId, branchTo, spec);
13428
- const phases = PIPELINES[flow];
13616
+ function defaultGoalCondition({ flow: flow2, specId, branchTo, spec }) {
13617
+ if (isGoalShapedFlow(flow2)) return goalFlowCondition(flow2, specId, branchTo, spec);
13618
+ const phases = PIPELINES[flow2];
13429
13619
  const planGate = phases.includes("Plan") && phases.includes("Execute");
13430
13620
  const clauses = [
13431
13621
  `1. output \`cat "$HANOMAN_PHASE_FILE"\` yang memuat satu baris untuk SETIAP fase ${phases.join(" \u2192 ")}, masing-masing berakhiran \`done\` atau \`skipped\`;`
@@ -13440,7 +13630,7 @@ function defaultGoalCondition({ flow, specId, branchTo, spec }) {
13440
13630
  `${planGate ? 3 : 2}. output \`git push origin HEAD:refs/heads/${branchTo}\` yang SUKSES sesudah commit terakhir.`
13441
13631
  );
13442
13632
  return [
13443
- `Sesi backlog hanoman ${specId} (flow ${flow}) hanya boleh berhenti bila transkrip TERBARU memuat bukti langsung semua hal berikut:`,
13633
+ `Sesi backlog hanoman ${specId} (flow ${flow2}) hanya boleh berhenti bila transkrip TERBARU memuat bukti langsung semua hal berikut:`,
13444
13634
  ...clauses,
13445
13635
  `Bila salah satu bukti tak ada di transkrip terbaru, kondisi BELUM terpenuhi: jalankan perintah verifikasinya, tuntaskan yang masih kurang, lalu lanjutkan \u2014 jangan berhenti.`
13446
13636
  ].join("\n");
@@ -14044,7 +14234,7 @@ function fromAuditOf(payload) {
14044
14234
  const value = payload.fromAudit;
14045
14235
  return typeof value === "string" && value ? value : void 0;
14046
14236
  }
14047
- function backlogGuide(flow, phase, ctx) {
14237
+ function backlogGuide(flow2, phase, ctx) {
14048
14238
  const m = ctx.method;
14049
14239
  switch (phase) {
14050
14240
  case "Brainstorm":
@@ -14052,13 +14242,13 @@ function backlogGuide(flow, phase, ctx) {
14052
14242
  case "Objective":
14053
14243
  return "- Objective: baca dokumen spec dari fase Brainstorm (path-nya di serah-terima), lalu tambahkan bagian `## Objective` berisi SATU objective terukur beserta kriteria suksesnya.";
14054
14244
  case "Spec":
14055
- return flow === "qa" ? `- Spec: tulis dokumen spec perbaikan di \`${m.specDir}/\` dari dokumen audit fase Audit (path-nya di serah-terima): akar masalah, bentuk perbaikan, dan acceptance criteria gaya EARS.` + (ctx.fromAudit ? ` Backlog ini LANJUTAN audit ${ctx.fromAudit} \u2014 fase Audit sengaja DILEWATI orchestrator, jadi baca dokumennya di \`internal/docs/research/audit-${ctx.fromAudit.toLowerCase()}-*.md\` (path juga ada di serah-terima) sebagai temuan; jangan menginvestigasi ulang dari nol.` : "") : "- Spec: lengkapi dokumen spec yang sama \u2014 arsitektur, komponen, kontrak data/API, penanganan galat, dan acceptance criteria gaya EARS. Perbarui docs Source of Truth yang tersentuh.";
14245
+ return flow2 === "qa" ? `- Spec: tulis dokumen spec perbaikan di \`${m.specDir}/\` dari dokumen audit fase Audit (path-nya di serah-terima): akar masalah, bentuk perbaikan, dan acceptance criteria gaya EARS.` + (ctx.fromAudit ? ` Backlog ini LANJUTAN audit ${ctx.fromAudit} \u2014 fase Audit sengaja DILEWATI orchestrator, jadi baca dokumennya di \`internal/docs/research/audit-${ctx.fromAudit.toLowerCase()}-*.md\` (path juga ada di serah-terima) sebagai temuan; jangan menginvestigasi ulang dari nol.` : "") : "- Spec: lengkapi dokumen spec yang sama \u2014 arsitektur, komponen, kontrak data/API, penanganan galat, dan acceptance criteria gaya EARS. Perbarui docs Source of Truth yang tersentuh.";
14056
14246
  case "Plan":
14057
14247
  return `- Plan: tulis plan implementasi berkotak \`- [ ]\` di \`${m.planDir}/\` untuk backlog ini dari dokumen spec-nya (path di serah-terima).`;
14058
14248
  case "Execute":
14059
14249
  return `- Execute: kerjakan plan di \`${m.planDir}/**\` untuk backlog ini. Execute BELUM selesai selama plan masih punya task \`- [ ]\`: kerjakan SEMUA task sampai tiap kotak jadi \`- [x]\` sebelum melapor \`Status: selesai\`. hanoman menahan backlog di \`executing\`, bukan \`done\`, selama masih ada \`- [ ]\`.`;
14060
14250
  case "Audit":
14061
- return flow === "audit" ? "- Audit: ini audit-only \u2014 investigasi SAJA, JANGAN menulis perbaikan kode apa pun. Telusuri akar masalah / log / jawaban dan nilai apakah issue terdefinisi dengan baik. Laporkan temuan beserta buktinya; dokumennya ditulis fase Laporan." : "- Audit: telusuri akar masalah dengan bukti (reproduksi, log, kode) dan tulis dokumen audit ke `internal/docs/research/audit-<spec-id>-<slug>.md`. Lalu putuskan jalurnya dari hasil Audit, bukan default: bila temuan berconfidence tinggi dan perbaikannya bisa dikerjakan langsung (diff kecil, akar masalah jelas), tulis `Rekomendasi fase: jalur-cepat` beserta alasannya \u2014 orchestrator akan melewati Spec & Plan dan dokumen audit menjadi doc-of-record perbaikan itu. Bila temuan luas, berisiko, atau ambigu, tulis `Rekomendasi fase: penuh`.";
14251
+ return flow2 === "audit" ? "- Audit: ini audit-only \u2014 investigasi SAJA, JANGAN menulis perbaikan kode apa pun. Telusuri akar masalah / log / jawaban dan nilai apakah issue terdefinisi dengan baik. Laporkan temuan beserta buktinya; dokumennya ditulis fase Laporan." : "- Audit: telusuri akar masalah dengan bukti (reproduksi, log, kode) dan tulis dokumen audit ke `internal/docs/research/audit-<spec-id>-<slug>.md`. Lalu putuskan jalurnya dari hasil Audit, bukan default: bila temuan berconfidence tinggi dan perbaikannya bisa dikerjakan langsung (diff kecil, akar masalah jelas), tulis `Rekomendasi fase: jalur-cepat` beserta alasannya \u2014 orchestrator akan melewati Spec & Plan dan dokumen audit menjadi doc-of-record perbaikan itu. Bila temuan luas, berisiko, atau ambigu, tulis `Rekomendasi fase: penuh`.";
14062
14252
  case "Laporan":
14063
14253
  return "- Laporan: tulis DOKUMEN AUDIT ke Source of Truth `internal/docs/research/audit-<spec-id>-<slug>.md` (ikuti konvensi audit yang ada), tautkan di `internal/docs/README.md`, memuat: keluhan/pertanyaan, temuan (dengan bukti/log), apakah issue terdefinisi baik, dan rekomendasi tindak lanjut. Commit dokumen itu. Tak ada kode fitur.\n\n" + ESCALATION_CONTRACT;
14064
14254
  case "Goal":
@@ -14071,27 +14261,27 @@ function backlogGuide(flow, phase, ctx) {
14071
14261
  return "";
14072
14262
  }
14073
14263
  }
14074
- function projectGuide(flow, phase, ctx) {
14075
- if (flow === "reverse") return guideLine(REVERSE_PHASE_GUIDE, phase);
14076
- if (flow === "scaffold") return guideLine(SCAFFOLD_PHASE_GUIDE, phase);
14077
- const slug = ctx.prd?.slug ?? flow;
14078
- const lines2 = flow === "prd" ? prdPhaseLines(slug) : breakdownPhaseLines(slug, ctx.prd?.title ?? slug);
14264
+ function projectGuide(flow2, phase, ctx) {
14265
+ if (flow2 === "reverse") return guideLine(REVERSE_PHASE_GUIDE, phase);
14266
+ if (flow2 === "scaffold") return guideLine(SCAFFOLD_PHASE_GUIDE, phase);
14267
+ const slug = ctx.prd?.slug ?? flow2;
14268
+ const lines2 = flow2 === "prd" ? prdPhaseLines(slug) : breakdownPhaseLines(slug, ctx.prd?.title ?? slug);
14079
14269
  return lines2[phase] ?? "";
14080
14270
  }
14081
14271
  function phaseAgentInstructions(entry, index, plan, ctx) {
14082
- const { flow, method } = ctx;
14272
+ const { flow: flow2, method } = ctx;
14083
14273
  const phase = entry.phase;
14084
- const guide = PROJECT_FLOWS.has(flow) ? projectGuide(flow, phase, ctx) : backlogGuide(flow, phase, ctx);
14085
- const skills = phaseSkillsFor(flow, phase, method);
14274
+ const guide = PROJECT_FLOWS.has(flow2) ? projectGuide(flow2, phase, ctx) : backlogGuide(flow2, phase, ctx);
14275
+ const skills = phaseSkillsFor(flow2, phase, method);
14086
14276
  const work = WORK_PHASES.includes(phase);
14087
14277
  return [
14088
- `Kamu agen fase "${phase}" (fase ${index + 1}/${plan.phases.length}) flow ${flow} hanoman. Ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.`,
14278
+ `Kamu agen fase "${phase}" (fase ${index + 1}/${plan.phases.length}) flow ${flow2} hanoman. Ikuti internal/docs sebagai Source of Truth; perbarui docs yang tersentuh dan link-nya di index, dalam commit yang sama.`,
14089
14279
  guide,
14090
14280
  DOC_WRITING_PHASES.has(phase) ? `=== STANDAR DOCS ===
14091
14281
  ${REVERSE_STANDARD}` : "",
14092
14282
  skills.length ? `Skills ${method.label} WAJIB untuk fase ini \u2014 muat & ikuti dengan mekanisme yang tersedia di agenmu: ` + skills.join(", ") : "",
14093
- work ? scopeClause(flow, ctx.verifyScope) : "",
14094
- work ? codeStyleClause(flow) : "",
14283
+ work ? scopeClause(flow2, ctx.verifyScope) : "",
14284
+ work ? codeStyleClause(flow2) : "",
14095
14285
  methodClause(method),
14096
14286
  ATTACHMENT_NOTE,
14097
14287
  PHASE_AGENT_AUTONOMY,
@@ -14933,10 +15123,10 @@ function recorded(file) {
14933
15123
  }
14934
15124
  return out4;
14935
15125
  }
14936
- function readPhases(file, flow) {
15126
+ function readPhases(file, flow2) {
14937
15127
  const seen2 = recorded(file);
14938
15128
  let activeTaken = false;
14939
- return PIPELINES[flow].map((name2) => {
15129
+ return PIPELINES[flow2].map((name2) => {
14940
15130
  const state = seen2.get(name2);
14941
15131
  if (state) return { name: name2, state };
14942
15132
  if (activeTaken) return { name: name2, state: "pending" };
@@ -15991,7 +16181,7 @@ function parsePanes(out4) {
15991
16181
  n2,
15992
16182
  projectId,
15993
16183
  specId,
15994
- flow,
16184
+ flow2,
15995
16185
  phaseFile,
15996
16186
  cwd,
15997
16187
  dead,
@@ -16017,7 +16207,7 @@ function parsePanes(out4) {
16017
16207
  id: n2.slice(PREFIX.length),
16018
16208
  projectId: projectId ?? "",
16019
16209
  specId: specId || void 0,
16020
- flow: flow || void 0,
16210
+ flow: flow2 || void 0,
16021
16211
  phaseFile: phaseFile || void 0,
16022
16212
  cwd: cwd ?? "",
16023
16213
  exited,
@@ -16821,7 +17011,7 @@ exit 1
16821
17011
  id: id2,
16822
17012
  projectId,
16823
17013
  specId,
16824
- flow,
17014
+ flow: flow2,
16825
17015
  cwd,
16826
17016
  exited,
16827
17017
  code,
@@ -16837,7 +17027,7 @@ exit 1
16837
17027
  id: id2,
16838
17028
  projectId,
16839
17029
  specId,
16840
- flow,
17030
+ flow: flow2,
16841
17031
  cwd,
16842
17032
  exited,
16843
17033
  branch,
@@ -17654,7 +17844,7 @@ var init_settings3 = __esm({
17654
17844
  "use strict";
17655
17845
  init_db();
17656
17846
  init_src();
17657
- STEP = { model: "claude-opus-5", effort: "xhigh" };
17847
+ STEP = { model: "claude-sonnet-5", effort: "medium" };
17658
17848
  DEFAULT_SETTING = {
17659
17849
  ...STEP,
17660
17850
  autoDefault: true,
@@ -17698,8 +17888,10 @@ var init_settings3 = __esm({
17698
17888
  // SPEC-1215 · ADR-0166
17699
17889
  builtinAgents: {},
17700
17890
  // SPEC-881 · ADR-0136 · sidik jari seed (lokal, tak disync)
17701
- builtinAgentPolicies: {}
17891
+ builtinAgentPolicies: {},
17702
17892
  // SPEC-950 · marker safety policy sekali-jalan (lokal)
17893
+ builtinRuntimeDefaults: BUILTIN_RUNTIME_DEFAULTS
17894
+ // seed rekomendasi model/fase; user edit dipertahankan
17703
17895
  };
17704
17896
  RETIRED_MODELS = { "claude-opus-4-8": "claude-opus-5" };
17705
17897
  }
@@ -18671,6 +18863,172 @@ var require_src = __commonJS({
18671
18863
  }
18672
18864
  });
18673
18865
 
18866
+ // src/services/runtime-defaults.ts
18867
+ function inferLegacyStates(raw) {
18868
+ const object = isRecord(raw) ? raw : {};
18869
+ const has = (key) => Object.prototype.hasOwnProperty.call(object, key);
18870
+ const rawCodex = object.codex;
18871
+ const rawOrchestration = isRecord(object.orchestration) ? object.orchestration : {};
18872
+ const orchestration = seededFlowStates();
18873
+ for (const flow2 of ORCHESTRATION_FLOWS) {
18874
+ const rawFlow = rawOrchestration[flow2];
18875
+ if (!has("orchestration") || rawFlow === void 0) continue;
18876
+ if (!legacyFlow(rawFlow)) {
18877
+ const flowObject = isRecord(rawFlow) ? rawFlow : {};
18878
+ if (Object.prototype.hasOwnProperty.call(flowObject, "enabled") && flowObject.enabled !== true)
18879
+ orchestration[`${flow2}.enabled`] = "user";
18880
+ for (const runtime of ["claude", "codex"]) {
18881
+ const rawRuntime = isRecord(flowObject[runtime]) ? flowObject[runtime] : {};
18882
+ for (const phase of FLOW_PHASES[flow2]) {
18883
+ const rawCell = rawRuntime[phase];
18884
+ if (!isRecord(rawCell)) continue;
18885
+ for (const field of ["model", "effort"]) {
18886
+ if (Object.prototype.hasOwnProperty.call(rawCell, field))
18887
+ orchestration[`${flow2}.${runtime}.${phase}.${field}`] = "user";
18888
+ }
18889
+ }
18890
+ }
18891
+ }
18892
+ }
18893
+ const claudeModel = !has("model") || object.model === LEGACY_CLAUDE_RUNTIME_DEFAULTS.model ? "seeded" : "user";
18894
+ const claudeEffort = !has("effort") || object.effort === LEGACY_CLAUDE_RUNTIME_DEFAULTS.effort ? "seeded" : "user";
18895
+ const rawCodexObject = isRecord(rawCodex) ? rawCodex : {};
18896
+ const codexModel2 = !has("codex") || !Object.prototype.hasOwnProperty.call(rawCodexObject, "model") || rawCodexObject.model === LEGACY_CODEX_RUNTIME_DEFAULTS.model ? "seeded" : "user";
18897
+ const codexEffort = !has("codex") || !Object.prototype.hasOwnProperty.call(rawCodexObject, "effort") || rawCodexObject.effort === LEGACY_CODEX_RUNTIME_DEFAULTS.effort ? "seeded" : "user";
18898
+ return {
18899
+ claude: { model: claudeModel, effort: claudeEffort },
18900
+ codex: { model: codexModel2, effort: codexEffort },
18901
+ orchestration
18902
+ };
18903
+ }
18904
+ function statesFor(raw, setting) {
18905
+ const object = isRecord(raw) ? raw : {};
18906
+ const marker = zBuiltinRuntimeDefaults.safeParse(object.builtinRuntimeDefaults);
18907
+ if (marker.success) {
18908
+ const orchestration = seededFlowStates();
18909
+ for (const flow2 of ORCHESTRATION_FLOWS) {
18910
+ const prefix = `${flow2}.`;
18911
+ for (const [key, state] of Object.entries(marker.data.orchestration)) {
18912
+ if (key.startsWith(prefix)) orchestration[key] = state;
18913
+ }
18914
+ }
18915
+ return { claude: marker.data.claude, codex: marker.data.codex, orchestration };
18916
+ }
18917
+ return inferLegacyStates(raw);
18918
+ }
18919
+ function applyRuntimeDefaults(raw) {
18920
+ const isFresh = raw === void 0 || raw === null;
18921
+ const parsed = isFresh ? zSetting.safeParse({ autoDefault: true, autoScaffold: true, notifyFail: true }) : zSetting.safeParse(raw);
18922
+ if (!parsed.success) return null;
18923
+ const states = statesFor(raw, parsed.data);
18924
+ const orchestration = { ...parsed.data.orchestration };
18925
+ for (const flow2 of ORCHESTRATION_FLOWS) {
18926
+ const builtIn = BUILTIN_ORCHESTRATION_DEFAULTS[flow2];
18927
+ const existing = parsed.data.orchestration[flow2];
18928
+ const nextFlow = {
18929
+ ...existing,
18930
+ enabled: states.orchestration[`${flow2}.enabled`] !== "user" ? builtIn.enabled : existing.enabled,
18931
+ claude: { ...existing.claude },
18932
+ codex: { ...existing.codex }
18933
+ };
18934
+ for (const runtime of ["claude", "codex"]) {
18935
+ for (const phase of FLOW_PHASES[flow2]) {
18936
+ const existingCell = nextFlow[runtime][phase] ?? { model: null, effort: null };
18937
+ const builtInCell = builtIn[runtime][phase];
18938
+ nextFlow[runtime][phase] = {
18939
+ model: states.orchestration[`${flow2}.${runtime}.${phase}.model`] !== "user" ? builtInCell.model : existingCell.model,
18940
+ effort: states.orchestration[`${flow2}.${runtime}.${phase}.effort`] !== "user" ? builtInCell.effort : existingCell.effort
18941
+ };
18942
+ }
18943
+ }
18944
+ orchestration[flow2] = nextFlow;
18945
+ }
18946
+ const data = zSetting.parse({
18947
+ ...parsed.data,
18948
+ model: states.claude.model === "seeded" ? BUILTIN_CLAUDE_RUNTIME_DEFAULTS.model : parsed.data.model,
18949
+ effort: states.claude.effort === "seeded" ? BUILTIN_CLAUDE_RUNTIME_DEFAULTS.effort : parsed.data.effort,
18950
+ codex: {
18951
+ model: states.codex.model === "seeded" ? BUILTIN_CODEX_RUNTIME_DEFAULTS.model : parsed.data.codex.model,
18952
+ effort: states.codex.effort === "seeded" ? BUILTIN_CODEX_RUNTIME_DEFAULTS.effort : parsed.data.codex.effort
18953
+ },
18954
+ orchestration,
18955
+ builtinRuntimeDefaults: markerFrom(states)
18956
+ });
18957
+ return { data, changed: isFresh || !same(raw, data) };
18958
+ }
18959
+ async function seedRuntimeDefaults() {
18960
+ try {
18961
+ const raw = (await prisma.setting.findUnique({ where: { id: 1 } }))?.data;
18962
+ const result = applyRuntimeDefaults(raw);
18963
+ if (!result?.changed) return;
18964
+ await prisma.setting.upsert({
18965
+ where: { id: 1 },
18966
+ update: { data: result.data },
18967
+ create: { id: 1, data: result.data }
18968
+ });
18969
+ } catch {
18970
+ }
18971
+ }
18972
+ function markRuntimeDefaultsUserEdited(before2, next) {
18973
+ const parsed = zBuiltinRuntimeDefaults.safeParse(before2.builtinRuntimeDefaults);
18974
+ const current = parsed.success ? parsed.data : {
18975
+ ...BUILTIN_RUNTIME_DEFAULTS,
18976
+ claude: { ...BUILTIN_RUNTIME_DEFAULTS.claude },
18977
+ codex: { ...BUILTIN_RUNTIME_DEFAULTS.codex },
18978
+ orchestration: { ...BUILTIN_RUNTIME_DEFAULTS.orchestration }
18979
+ };
18980
+ const orchestration = { ...seededFlowStates(), ...current.orchestration };
18981
+ if (before2.model !== next.model) current.claude.model = "user";
18982
+ if (before2.effort !== next.effort) current.claude.effort = "user";
18983
+ if (before2.codex.model !== next.codex.model) current.codex.model = "user";
18984
+ if (before2.codex.effort !== next.codex.effort) current.codex.effort = "user";
18985
+ for (const flow2 of ORCHESTRATION_FLOWS) {
18986
+ if (before2.orchestration[flow2].enabled !== next.orchestration[flow2].enabled)
18987
+ orchestration[`${flow2}.enabled`] = "user";
18988
+ for (const runtime of ["claude", "codex"]) {
18989
+ for (const phase of FLOW_PHASES[flow2]) {
18990
+ for (const field of ["model", "effort"]) {
18991
+ if (before2.orchestration[flow2][runtime][phase]?.[field] !== next.orchestration[flow2][runtime][phase]?.[field])
18992
+ orchestration[`${flow2}.${runtime}.${phase}.${field}`] = "user";
18993
+ }
18994
+ }
18995
+ }
18996
+ }
18997
+ return {
18998
+ version: RUNTIME_DEFAULTS_VERSION,
18999
+ claude: { ...current.claude },
19000
+ codex: { ...current.codex },
19001
+ orchestration
19002
+ };
19003
+ }
19004
+ var isRecord, stable, same, seededFlowStates, markerFrom, legacyFlow;
19005
+ var init_runtime_defaults2 = __esm({
19006
+ "src/services/runtime-defaults.ts"() {
19007
+ "use strict";
19008
+ init_src();
19009
+ init_db();
19010
+ isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19011
+ stable = (value) => {
19012
+ if (Array.isArray(value)) return value.map(stable);
19013
+ if (!isRecord(value)) return value;
19014
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])]));
19015
+ };
19016
+ same = (a, b) => JSON.stringify(stable(a)) === JSON.stringify(stable(b));
19017
+ seededFlowStates = () => Object.fromEntries(Object.keys(BUILTIN_RUNTIME_DEFAULTS.orchestration).map((key) => [key, "seeded"]));
19018
+ markerFrom = (states) => ({
19019
+ version: RUNTIME_DEFAULTS_VERSION,
19020
+ claude: states.claude,
19021
+ codex: states.codex,
19022
+ orchestration: states.orchestration
19023
+ });
19024
+ legacyFlow = (value) => {
19025
+ if (!isRecord(value)) return false;
19026
+ const parsed = zFlowOrchestration.safeParse(value);
19027
+ return parsed.success && same(parsed.data, LEGACY_ORCHESTRATION_FLOW_DEFAULT);
19028
+ };
19029
+ }
19030
+ });
19031
+
18674
19032
  // src/services/agent-token.ts
18675
19033
  import { randomBytes as randomBytes2, createHash as createHash2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
18676
19034
  function toAgentTokenView(t) {
@@ -25404,6 +25762,7 @@ async function refreshCustomAgentRuntimeSupport(probe = defaultCodexSupportProbe
25404
25762
  return refresh;
25405
25763
  }
25406
25764
  async function installCustomAgents() {
25765
+ await seedRuntimeDefaults();
25407
25766
  await seedBuiltinAgents();
25408
25767
  await loadCustomAgents();
25409
25768
  await refreshCustomAgentRuntimeSupport();
@@ -25426,6 +25785,7 @@ var init_custom_agents2 = __esm({
25426
25785
  init_pty();
25427
25786
  init_agent_tool_catalog();
25428
25787
  init_builtin_agents2();
25788
+ init_runtime_defaults2();
25429
25789
  init_codex_version();
25430
25790
  cache4 = [];
25431
25791
  codexNativeSupport = { version: null, ok: false };
@@ -35529,10 +35889,10 @@ var _Tokenizer = class {
35529
35889
  });
35530
35890
  }
35531
35891
  for (const row of rows) {
35532
- item.rows.push(splitCells(row, item.header.length).map((cell) => {
35892
+ item.rows.push(splitCells(row, item.header.length).map((cell2) => {
35533
35893
  return {
35534
- text: cell,
35535
- tokens: this.lexer.inline(cell)
35894
+ text: cell2,
35895
+ tokens: this.lexer.inline(cell2)
35536
35896
  };
35537
35897
  }));
35538
35898
  }
@@ -36528,19 +36888,19 @@ var _Parser = class __Parser {
36528
36888
  case "table": {
36529
36889
  const tableToken = token;
36530
36890
  let header2 = "";
36531
- let cell = "";
36891
+ let cell2 = "";
36532
36892
  for (let j = 0; j < tableToken.header.length; j++) {
36533
- cell += this.renderer.tablecell(this.parseInline(tableToken.header[j].tokens), { header: true, align: tableToken.align[j] });
36893
+ cell2 += this.renderer.tablecell(this.parseInline(tableToken.header[j].tokens), { header: true, align: tableToken.align[j] });
36534
36894
  }
36535
- header2 += this.renderer.tablerow(cell);
36895
+ header2 += this.renderer.tablerow(cell2);
36536
36896
  let body = "";
36537
36897
  for (let j = 0; j < tableToken.rows.length; j++) {
36538
36898
  const row = tableToken.rows[j];
36539
- cell = "";
36899
+ cell2 = "";
36540
36900
  for (let k = 0; k < row.length; k++) {
36541
- cell += this.renderer.tablecell(this.parseInline(row[k].tokens), { header: false, align: tableToken.align[k] });
36901
+ cell2 += this.renderer.tablecell(this.parseInline(row[k].tokens), { header: false, align: tableToken.align[k] });
36542
36902
  }
36543
- body += this.renderer.tablerow(cell);
36903
+ body += this.renderer.tablerow(cell2);
36544
36904
  }
36545
36905
  out4 += this.renderer.table(header2, body);
36546
36906
  continue;
@@ -36751,12 +37111,12 @@ var Marked = class {
36751
37111
  switch (token.type) {
36752
37112
  case "table": {
36753
37113
  const tableToken = token;
36754
- for (const cell of tableToken.header) {
36755
- values = values.concat(this.walkTokens(cell.tokens, callback));
37114
+ for (const cell2 of tableToken.header) {
37115
+ values = values.concat(this.walkTokens(cell2.tokens, callback));
36756
37116
  }
36757
37117
  for (const row of tableToken.rows) {
36758
- for (const cell of row) {
36759
- values = values.concat(this.walkTokens(cell.tokens, callback));
37118
+ for (const cell2 of row) {
37119
+ values = values.concat(this.walkTokens(cell2.tokens, callback));
36760
37120
  }
36761
37121
  }
36762
37122
  break;
@@ -37909,6 +38269,7 @@ async function notifications_default(app3) {
37909
38269
  init_src();
37910
38270
  init_db();
37911
38271
  init_settings3();
38272
+ init_runtime_defaults2();
37912
38273
  init_bootstrap();
37913
38274
  init_config4();
37914
38275
  async function settings_default(app3) {
@@ -37917,8 +38278,18 @@ async function settings_default(app3) {
37917
38278
  const parsed = zSetting.safeParse(req.body);
37918
38279
  if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
37919
38280
  const before2 = await getSetting();
37920
- const data = {
38281
+ const input = req.body && typeof req.body === "object" && !Array.isArray(req.body) ? req.body : {};
38282
+ const has = (key) => Object.prototype.hasOwnProperty.call(input, key);
38283
+ const runtimePreserved = {
37921
38284
  ...parsed.data,
38285
+ model: has("model") ? parsed.data.model : before2.model,
38286
+ effort: has("effort") ? parsed.data.effort : before2.effort,
38287
+ codex: has("codex") ? parsed.data.codex : before2.codex,
38288
+ orchestration: has("orchestration") ? parsed.data.orchestration : before2.orchestration
38289
+ };
38290
+ const data = {
38291
+ ...runtimePreserved,
38292
+ builtinRuntimeDefaults: markRuntimeDefaultsUserEdited(before2, runtimePreserved),
37922
38293
  remoteControl: before2.remoteControl,
37923
38294
  logShipping: before2.logShipping,
37924
38295
  logRetention: before2.logRetention
@@ -38549,8 +38920,8 @@ init_pty();
38549
38920
  init_session_phases();
38550
38921
  init_notifications2();
38551
38922
  init_stage_machine();
38552
- async function advanceStage(specId, repoDir, sessionId2, flow, worktree) {
38553
- const next = stageForRun(readPhases(phaseFilePath(repoDir, sessionId2), flow), worktree, specId);
38923
+ async function advanceStage(specId, repoDir, sessionId2, flow2, worktree) {
38924
+ const next = stageForRun(readPhases(phaseFilePath(repoDir, sessionId2), flow2), worktree, specId);
38554
38925
  if (!next) return;
38555
38926
  const spec = await prisma.spec.findUnique({ where: { id: specId }, select: { stage: true, title: true, projectId: true } });
38556
38927
  if (!spec || STAGES.indexOf(next) <= STAGES.indexOf(spec.stage)) return;
@@ -39524,12 +39895,13 @@ init_pty();
39524
39895
  // src/services/orchestration.ts
39525
39896
  init_src();
39526
39897
  init_pty();
39527
- function sessionPhasePlan(setting, flow, agent, orchestrator) {
39898
+ function sessionPhasePlan(setting, flow2, agent, orchestrator, phaseOverrides) {
39528
39899
  return resolvePhasePlan({
39529
- flow,
39900
+ flow: flow2,
39530
39901
  runtime: agent,
39531
39902
  orchestration: setting.orchestration,
39532
39903
  orchestrator,
39904
+ phaseOverrides,
39533
39905
  nativeAgents: nativeAgentsAvailable(agent)
39534
39906
  });
39535
39907
  }
@@ -39551,8 +39923,8 @@ function resumeState(repoDir, worktree, branchTo, headSha) {
39551
39923
  const base2 = realGit.revParse(repoDir, `origin/${branchTo}`) ?? realGit.revParse(repoDir, branchTo) ?? (headSha ? realGit.revParse(repoDir, headSha) : null);
39552
39924
  return base2 ? { worktreeKept: false, base: base2 } : null;
39553
39925
  }
39554
- function buildResumeCtx(repoDir, id2, flow, worktreeKept) {
39555
- const phases = readPhases(phaseFilePath(repoDir, id2), flow);
39926
+ function buildResumeCtx(repoDir, id2, flow2, worktreeKept) {
39927
+ const phases = readPhases(phaseFilePath(repoDir, id2), flow2);
39556
39928
  return {
39557
39929
  recorded: phases.filter((p3) => p3.state === "done" || p3.state === "skipped").map((p3) => `${p3.name} ${p3.state}`),
39558
39930
  next: phases.find((p3) => p3.state === "active")?.name,
@@ -39629,7 +40001,7 @@ async function startSpecSession(spec, opts) {
39629
40001
  payload: spec.payload ?? void 0
39630
40002
  };
39631
40003
  const resumeCtx = resume ? buildResumeCtx(repoDir, id2, opts.flow, resume.worktreeKept) : void 0;
39632
- const fullPlan = sessionPhasePlan(setting, opts.flow, agent, { model, effort: effort2 });
40004
+ const fullPlan = sessionPhasePlan(setting, opts.flow, agent, { model, effort: effort2 }, opts.phaseOverrides);
39633
40005
  let plan = fullPlan;
39634
40006
  if (fullPlan && isContinue && !isGoalFlow) {
39635
40007
  const phases = fullPlan.phases.filter((p3) => p3.phase === "Execute");
@@ -41254,7 +41626,7 @@ var notifTitle = (kind, question, answer, confidence) => {
41254
41626
  async function decide(req, deps = prodDecideDeps) {
41255
41627
  const cfg = await getLead();
41256
41628
  if (!leadActive(cfg, req.projectId)) return null;
41257
- const flow = req.flowId ? await joinFlow(req.flowId) : await openFlow({
41629
+ const flow2 = req.flowId ? await joinFlow(req.flowId) : await openFlow({
41258
41630
  projectId: req.projectId,
41259
41631
  specId: req.specId,
41260
41632
  sessionId: req.sessionId,
@@ -41262,7 +41634,7 @@ async function decide(req, deps = prodDecideDeps) {
41262
41634
  title: req.question,
41263
41635
  ttlMin: cfg.flowTtlMin
41264
41636
  });
41265
- const step = flow.steps + 1;
41637
+ const step = flow2.steps + 1;
41266
41638
  const project = await prisma.project.findUnique({ where: { id: req.projectId }, select: { name: true } });
41267
41639
  const spec = req.specId ? await prisma.spec.findUnique({
41268
41640
  where: { id: req.specId },
@@ -41274,7 +41646,7 @@ async function decide(req, deps = prodDecideDeps) {
41274
41646
  orderBy: { createdAt: "desc" },
41275
41647
  take: 10
41276
41648
  });
41277
- const chainRows = req.flowId ? await prisma.leadDecision.findMany({ where: { flowId: flow.id }, orderBy: { createdAt: "asc" }, take: 10 }) : [];
41649
+ const chainRows = req.flowId ? await prisma.leadDecision.findMany({ where: { flowId: flow2.id }, orderBy: { createdAt: "asc" }, take: 10 }) : [];
41278
41650
  const options2 = req.options ?? [];
41279
41651
  const bounds = normalizeSelect(req.select ?? { mode: "single", min: 0, max: null }, options2.length);
41280
41652
  const ctx = {
@@ -41319,15 +41691,15 @@ async function decide(req, deps = prodDecideDeps) {
41319
41691
  });
41320
41692
  } catch (e) {
41321
41693
  if (e instanceof LeadBusyError) {
41322
- if (!req.flowId) await closeFlow(flow.id, "kedaluwarsa").catch(() => null);
41694
+ if (!req.flowId) await closeFlow(flow2.id, "kedaluwarsa").catch(() => null);
41323
41695
  throw e;
41324
41696
  }
41325
- return closeAfter(await fail(req, deps, `lead tak menghasilkan keputusan: ${e.message}`, flow.id, step), flow.id, req.chain);
41697
+ return closeAfter(await fail(req, deps, `lead tak menghasilkan keputusan: ${e.message}`, flow2.id, step), flow2.id, req.chain);
41326
41698
  } finally {
41327
41699
  if (req.sessionId) clearQueued(req.sessionId);
41328
41700
  }
41329
41701
  const verdict = parseLeadVerdict(raw);
41330
- if (!verdict) return closeAfter(await fail(req, deps, "keluaran lead tak memuat blok json keputusan yang sah", flow.id, step), flow.id, req.chain);
41702
+ if (!verdict) return closeAfter(await fail(req, deps, "keluaran lead tak memuat blok json keputusan yang sah", flow2.id, step), flow2.id, req.chain);
41331
41703
  const refs2 = keepExistingRefs(verdict.refs, repoDir);
41332
41704
  const allowed = leadActionAllowed(verdict.action);
41333
41705
  const kind = allowed ? req.kind : "refusal";
@@ -41385,12 +41757,12 @@ ${notes.join("\n\n")}` : "";
41385
41757
  choiceIndex: choice?.index ?? null,
41386
41758
  choices,
41387
41759
  select: bounds,
41388
- flowId: flow.id,
41760
+ flowId: flow2.id,
41389
41761
  step,
41390
41762
  options: options2,
41391
41763
  missing
41392
41764
  });
41393
- await closeAfter(row, flow.id, req.chain);
41765
+ await closeAfter(row, flow2.id, req.chain);
41394
41766
  if (weighty) {
41395
41767
  await deps.notify(
41396
41768
  row.id,
@@ -41953,6 +42325,8 @@ async function terminal_default(app3, opts) {
41953
42325
  // SPEC-376 · ADR-0080
41954
42326
  method: parsed.data.method,
41955
42327
  // SPEC-734 · ADR-0113
42328
+ phaseOverrides: parsed.data.phaseOverrides,
42329
+ // ADR-0164 · override subagent per sesi
41956
42330
  force: parsed.data.force
41957
42331
  // SPEC-447 · ADR-0093
41958
42332
  });
@@ -50166,7 +50540,7 @@ var reconcileProdDeps = {
50166
50540
  const s2 = getSession(sessionId2);
50167
50541
  return s2 ? { exited: s2.exited, flow: s2.flow, phaseFile: s2.phaseFile, cwd: s2.cwd } : void 0;
50168
50542
  },
50169
- deriveStage: (phaseFile, flow, cwd, specId) => stageForRun(readPhases(phaseFile, flow), cwd, specId),
50543
+ deriveStage: (phaseFile, flow2, cwd, specId) => stageForRun(readPhases(phaseFile, flow2), cwd, specId),
50170
50544
  headSha: (wt) => {
50171
50545
  try {
50172
50546
  return realGit.headSha(wt);