hanoman 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.3",
3
- "sha": "7f99c250",
4
- "builtAt": "2026-08-29T10:43:44.678Z"
2
+ "version": "0.2.4",
3
+ "sha": "5675d4f5",
4
+ "builtAt": "2026-08-31T14:43:04.080Z"
5
5
  }
package/dist/cli.js CHANGED
@@ -4413,6 +4413,15 @@ var init_auto_merge = __esm({
4413
4413
  });
4414
4414
 
4415
4415
  // ../shared/src/entities.ts
4416
+ function cmpVersion(a, b) {
4417
+ const pa = a.split(".").map(Number);
4418
+ const pb = b.split(".").map(Number);
4419
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
4420
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
4421
+ if (d !== 0) return d;
4422
+ }
4423
+ return 0;
4424
+ }
4416
4425
  var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSourceChange, zManualDone, zSpec, NOTIFY_SOUNDS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, CHANGELOG_ENGINE_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zPortalChat, PORTAL_CHAT_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
4417
4426
  var init_entities = __esm({
4418
4427
  "../shared/src/entities.ts"() {
@@ -4666,7 +4675,10 @@ var init_entities = __esm({
4666
4675
  //
4667
4676
  // LOKAL per mesin: `setting` TIDAK ada di FIELDS sync (server/src/services/sync.ts), jadi dua
4668
4677
  // mesin dengan versi hanoman berbeda tak bisa saling menimpa definisi bolak-balik.
4669
- builtinAgents: external_exports.record(external_exports.string(), external_exports.string()).default({})
4678
+ builtinAgents: external_exports.record(external_exports.string(), external_exports.string()).default({}),
4679
+ // SPEC-950 · marker safety policy lokal. Terpisah dari fingerprint konten agar policy sekali
4680
+ // jalan tidak membuat seed memperlakukan konfigurasi operator sebagai bawaan.
4681
+ builtinAgentPolicies: external_exports.record(external_exports.string(), external_exports.string()).default({})
4670
4682
  });
4671
4683
  zNotification = external_exports.object({
4672
4684
  id: external_exports.string(),
@@ -4789,13 +4801,28 @@ var init_agent = __esm({
4789
4801
  });
4790
4802
 
4791
4803
  // ../shared/src/custom-agent.ts
4792
- var AGENT_RUNTIMES, zAgentRuntime, AGENT_NAME_RE, DEFAULT_AGENT_TOOLS, zCustomAgent, zCreateCustomAgent, zUpdateCustomAgent;
4804
+ function validateCreateWorkspacePolicy(value, ctx) {
4805
+ if (value.workspacePolicy === "isolated-worktree" && value.runtime !== "claude") {
4806
+ ctx.addIssue({
4807
+ code: external_exports.ZodIssueCode.custom,
4808
+ path: ["workspacePolicy"],
4809
+ message: "isolated-worktree hanya tersedia untuk Claude Code"
4810
+ });
4811
+ }
4812
+ }
4813
+ var AGENT_RUNTIMES, zAgentRuntime, AGENT_ACTIVATIONS, zAgentActivation, AGENT_EFFORTS, zAgentEffort, AGENT_WORKSPACE_POLICIES, zAgentWorkspacePolicy, AGENT_NAME_RE, DEFAULT_AGENT_TOOLS, zCustomAgent, zCreateCustomAgentFields, zCreateCustomAgent, zUpdateCustomAgent;
4793
4814
  var init_custom_agent = __esm({
4794
4815
  "../shared/src/custom-agent.ts"() {
4795
4816
  "use strict";
4796
4817
  init_zod();
4797
4818
  AGENT_RUNTIMES = ["claude", "codex"];
4798
4819
  zAgentRuntime = external_exports.enum(AGENT_RUNTIMES);
4820
+ AGENT_ACTIVATIONS = ["always", "smart"];
4821
+ zAgentActivation = external_exports.enum(AGENT_ACTIVATIONS);
4822
+ AGENT_EFFORTS = ["ultra", "max", "xhigh", "high", "medium", "low", "ultracode"];
4823
+ zAgentEffort = external_exports.enum(AGENT_EFFORTS);
4824
+ AGENT_WORKSPACE_POLICIES = ["inherit", "read-only", "isolated-worktree"];
4825
+ zAgentWorkspacePolicy = external_exports.enum(AGENT_WORKSPACE_POLICIES);
4799
4826
  AGENT_NAME_RE = /^[a-z][a-z0-9-]{1,39}$/;
4800
4827
  DEFAULT_AGENT_TOOLS = [
4801
4828
  "Read",
@@ -4817,11 +4844,16 @@ var init_custom_agent = __esm({
4817
4844
  model: external_exports.string().nullable(),
4818
4845
  mentions: external_exports.array(external_exports.string()).nullable(),
4819
4846
  runtime: external_exports.enum(AGENT_RUNTIMES).nullable(),
4847
+ activation: zAgentActivation.default("always"),
4848
+ effort: zAgentEffort.nullable().default(null),
4849
+ workspacePolicy: zAgentWorkspacePolicy.default("inherit"),
4850
+ maxTurns: external_exports.number().int().min(1).max(200).nullable().default(null),
4851
+ timeoutSeconds: external_exports.number().int().min(30).max(3600).nullable().default(null),
4820
4852
  enabled: external_exports.boolean(),
4821
4853
  createdAt: external_exports.string(),
4822
4854
  updatedAt: external_exports.string()
4823
4855
  });
4824
- zCreateCustomAgent = external_exports.object({
4856
+ zCreateCustomAgentFields = external_exports.object({
4825
4857
  projectId: external_exports.string().nullable().optional(),
4826
4858
  name: external_exports.string().regex(AGENT_NAME_RE),
4827
4859
  description: external_exports.string().trim().min(1).max(500),
@@ -4831,9 +4863,23 @@ var init_custom_agent = __esm({
4831
4863
  mentions: external_exports.array(external_exports.string()).nullable().optional(),
4832
4864
  // SPEC-484 · ADR-0101 · PENYARING mesin sesi, bukan pemilih proses. null/absen = ikut sesi induk.
4833
4865
  runtime: external_exports.enum(AGENT_RUNTIMES).nullable().optional(),
4866
+ activation: zAgentActivation.optional(),
4867
+ effort: zAgentEffort.nullable().optional(),
4868
+ workspacePolicy: zAgentWorkspacePolicy.optional(),
4869
+ maxTurns: external_exports.number().int().min(1).max(200).nullable().optional(),
4870
+ timeoutSeconds: external_exports.number().int().min(30).max(3600).nullable().optional(),
4834
4871
  enabled: external_exports.boolean().optional()
4835
4872
  });
4836
- zUpdateCustomAgent = zCreateCustomAgent.omit({ name: true, projectId: true }).partial();
4873
+ zCreateCustomAgent = zCreateCustomAgentFields.superRefine(validateCreateWorkspacePolicy);
4874
+ zUpdateCustomAgent = zCreateCustomAgentFields.omit({ name: true, projectId: true }).partial().superRefine((value, ctx) => {
4875
+ if (value.workspacePolicy === "isolated-worktree" && value.runtime !== void 0 && value.runtime !== "claude") {
4876
+ ctx.addIssue({
4877
+ code: external_exports.ZodIssueCode.custom,
4878
+ path: ["workspacePolicy"],
4879
+ message: "isolated-worktree hanya tersedia untuk Claude Code"
4880
+ });
4881
+ }
4882
+ });
4837
4883
  }
4838
4884
  });
4839
4885
 
@@ -4848,6 +4894,12 @@ var init_builtin_agents = __esm({
4848
4894
  description: "Gunakan saat perlu tahu DI MANA sesuatu dikerjakan di basis kode, atau bagaimana sebuah alur data mengalir, sebelum menyentuh kode. Ia menyapu banyak berkas dan mengembalikan peta ringkas berisi jangkar path:baris \u2014 bukan isi berkas. Panggil dia alih-alih membaca belasan berkas sendiri.",
4849
4895
  tools: ["Read", "Glob", "Grep"],
4850
4896
  enabledByDefault: true,
4897
+ activation: "smart",
4898
+ effort: "low",
4899
+ workspacePolicy: "read-only",
4900
+ maxTurns: null,
4901
+ timeoutSeconds: null,
4902
+ models: { claude: "haiku", codex: "gpt-5.6-terra" },
4851
4903
  instructions: [
4852
4904
  "Kamu navigator basis kode. Tugasmu MENJAWAB, bukan menyalin.",
4853
4905
  "",
@@ -4874,6 +4926,12 @@ var init_builtin_agents = __esm({
4874
4926
  description: "Gunakan saat ada bug, test merah, atau perilaku tak terduga yang belum jelas sebabnya. Ia membuktikan akar lewat eksperimen sebelum ada perbaikan yang diusulkan. Jangan panggil dia untuk memperbaiki \u2014 dia mendiagnosis.",
4875
4927
  tools: ["Read", "Glob", "Grep", "Bash"],
4876
4928
  enabledByDefault: false,
4929
+ activation: "smart",
4930
+ effort: "high",
4931
+ workspacePolicy: "read-only",
4932
+ maxTurns: null,
4933
+ timeoutSeconds: null,
4934
+ models: { claude: "sonnet", codex: "gpt-5.6" },
4877
4935
  instructions: [
4878
4936
  "Kamu diagnostikus. Kamu TIDAK memperbaiki kode \u2014 kamu membuktikan sebabnya.",
4879
4937
  "",
@@ -4903,7 +4961,13 @@ var init_builtin_agents = __esm({
4903
4961
  name: "qa-verifier",
4904
4962
  description: "Gunakan SEBELUM menyatakan pekerjaan selesai atau test hijau. Ia menjalankan test yang tersentuh perubahan, memisahkan gagal palsu dari regresi, dan membuktikan bahwa test yang lulus itu benar-benar menguji perubahannya.",
4905
4963
  tools: ["Read", "Glob", "Grep", "Bash"],
4906
- enabledByDefault: true,
4964
+ enabledByDefault: false,
4965
+ activation: "smart",
4966
+ effort: "medium",
4967
+ workspacePolicy: "isolated-worktree",
4968
+ maxTurns: 40,
4969
+ timeoutSeconds: 900,
4970
+ models: { claude: "sonnet", codex: "gpt-5.6-terra" },
4907
4971
  instructions: [
4908
4972
  "Kamu gerbang terakhir sebelum sesuatu diumumkan hijau. Tugasmu MERAGUKAN kehijauan itu.",
4909
4973
  "",
@@ -4915,18 +4979,18 @@ var init_builtin_agents = __esm({
4915
4979
  " paralelisme antar-berkas test, sisa proses/soket/port dari run sebelumnya, variabel",
4916
4980
  " lingkungan yang bocor dari shell, dan test yang memang sudah merah SEBELUM perubahan.",
4917
4981
  " Cara memutuskannya: jalankan ulang test itu SENDIRIAN, dengan state yang bersih.",
4918
- "4. UJI RELEVANSI \u2014 langkah yang hampir tak pernah dilakukan siapa pun, dan tanpa ini 'hijau'",
4919
- " tak berarti apa-apa: siapkan pohon kerja terpisah di commit SEBELUM perubahan",
4920
- " (`git worktree add --detach <dir> <base-sha>`), pasang test barunya di sana, jalankan,",
4921
- " dan tuntut test itu MERAH. Test yang tetap hijau tanpa perubahan tidak membuktikan apa",
4922
- " pun tentang perubahan itu.",
4923
- "5. Bersihkan pohon kerja sementara (`git worktree remove`).",
4982
+ "4. UJI RELEVANSI hanya di worktree sementara dari `baseSha`, tidak pernah di worktree",
4983
+ " parent: buat worktree terpisah, pasang patch test di sana, jalankan, dan tuntut test",
4984
+ " itu MERAH. Bila `baseSha` atau patch test tidak tersedia, laporkan `belum terbukti`;",
4985
+ " jangan mencoba eksperimen kontrol di source parent.",
4986
+ "5. Bersihkan worktree sementara milikmu. Laporkan secara eksplisit bila cleanup gagal.",
4924
4987
  "",
4925
4988
  "Larangan keras:",
4926
4989
  "- JANGAN `git stash` untuk apa pun. Tumpukan stash milik REPO, bukan pohon kerja \u2014 sesi lain",
4927
4990
  " bisa mem-pop stash milikmu, dan kamu bisa mem-pop milik mereka. Isolasi memakai",
4928
4991
  " `git worktree add`, titik.",
4929
4992
  "- JANGAN mengubah test agar lulus. Bila test-nya yang salah, itu temuan, bukan pekerjaan.",
4993
+ "- JANGAN mengubah satu byte pun di worktree parent, termasuk berkas probe sementara.",
4930
4994
  "",
4931
4995
  "Gerbang bukti: setiap klaim membawa perintah DAN potongan keluarannya. Tanpa keluaran, tanpa",
4932
4996
  "klaim. 'Semua test lulus' tanpa keluaran adalah kegagalanmu, bukan laporan.",
@@ -4941,6 +5005,12 @@ var init_builtin_agents = __esm({
4941
5005
  description: "Gunakan saat test yang ada hanya menguji jalur mulus dan kamu ingin batas-batas kontrak benar-benar tertutup. Ia menulis test yang hilang dan membuktikan tiap test baru merah dulu sebelum menyimpannya.",
4942
5006
  tools: ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
4943
5007
  enabledByDefault: false,
5008
+ activation: "smart",
5009
+ effort: "high",
5010
+ workspacePolicy: "isolated-worktree",
5011
+ maxTurns: null,
5012
+ timeoutSeconds: null,
5013
+ models: { claude: "sonnet", codex: "gpt-5.6" },
4944
5014
  instructions: [
4945
5015
  "Kamu penambal jalur bahagia. Cakupan yang terlihat baik bukan urusanmu \u2014 kontrak yang tak",
4946
5016
  "pernah diuji itu urusanmu.",
@@ -4973,6 +5043,12 @@ var init_builtin_agents = __esm({
4973
5043
  description: "Gunakan sesudah perubahan selesai untuk menemukan tempat LAIN yang seharusnya ikut berubah tapi tidak: daftar kolom, cermin tipe antar-paket, enum kembar, dokumen kontrak, tabel konstanta. Ia mencari kegagalan senyap \u2014 yang tak memunculkan satu pun error.",
4974
5044
  tools: ["Read", "Glob", "Grep", "Bash"],
4975
5045
  enabledByDefault: true,
5046
+ activation: "smart",
5047
+ effort: "medium",
5048
+ workspacePolicy: "read-only",
5049
+ maxTurns: null,
5050
+ timeoutSeconds: null,
5051
+ models: { claude: "sonnet", codex: "gpt-5.6-terra" },
4976
5052
  instructions: [
4977
5053
  "Kamu pencari cermin yang hanyut. Kelas bug yang kamu buru punya satu ciri: TIDAK ADA yang",
4978
5054
  "error. Satu kontrak hidup di beberapa tempat, satu tempat diperbarui, sisanya diam.",
@@ -5005,6 +5081,12 @@ var init_builtin_agents = __esm({
5005
5081
  description: "Gunakan sebelum menutup pekerjaan untuk mengadu apa yang DIMINTA dengan apa yang benar-benar ada di diff. Ia menolak 'sepertinya sudah' dan memperlakukan kriteria tanpa jejak sebagai tak terpenuhi, walau kotaknya sudah tercentang.",
5006
5082
  tools: ["Read", "Glob", "Grep", "Bash"],
5007
5083
  enabledByDefault: false,
5084
+ activation: "smart",
5085
+ effort: "high",
5086
+ workspacePolicy: "read-only",
5087
+ maxTurns: null,
5088
+ timeoutSeconds: null,
5089
+ models: { claude: "sonnet", codex: "gpt-5.6-terra" },
5008
5090
  instructions: [
5009
5091
  "Kamu pengadu janji. Kamu tak menilai bagus atau tidaknya kode \u2014 kamu menilai apakah yang",
5010
5092
  "diminta benar-benar ada.",
@@ -5033,6 +5115,12 @@ var init_builtin_agents = __esm({
5033
5115
  description: "Gunakan sebelum menggabungkan perubahan yang menyentuh route, handler, job, CLI, atau apa pun yang menerima input dari luar. Ia menelusuri jalur konkret dari input tak terpercaya sampai ke tempat ia melukai, dan menolak melaporkan kekhawatiran yang tak bisa ia buktikan jalurnya.",
5034
5116
  tools: ["Read", "Glob", "Grep", "Bash"],
5035
5117
  enabledByDefault: true,
5118
+ activation: "smart",
5119
+ effort: "high",
5120
+ workspacePolicy: "read-only",
5121
+ maxTurns: null,
5122
+ timeoutSeconds: null,
5123
+ models: { claude: "sonnet", codex: "gpt-5.6" },
5036
5124
  instructions: [
5037
5125
  "Kamu penelusur sumber-ke-sink. Daftar kekhawatiran umum tak mengubah apa pun; yang mengubah",
5038
5126
  "adalah satu jalur konkret dari input yang tak dipercaya sampai ke tempat ia melukai.",
@@ -5067,6 +5155,12 @@ var init_builtin_agents = __esm({
5067
5155
  description: "Gunakan saat diff menambah atau menaikkan versi dependensi. Ia memeriksa advisory, lisensi, tanda pemeliharaan, dan \u2014 yang paling sering terlewat \u2014 apakah fungsinya sudah tersedia tanpa dependensi baru itu.",
5068
5156
  tools: ["Read", "Glob", "Grep", "Bash", "WebSearch", "WebFetch"],
5069
5157
  enabledByDefault: false,
5158
+ activation: "smart",
5159
+ effort: "medium",
5160
+ workspacePolicy: "read-only",
5161
+ maxTurns: null,
5162
+ timeoutSeconds: null,
5163
+ models: { claude: "haiku", codex: "gpt-5.6-terra" },
5070
5164
  instructions: [
5071
5165
  "Kamu gerbang rantai pasok. Satu dependensi masuk lewat satu baris diff dan tak pernah",
5072
5166
  "diperiksa lagi seumur hidup proyek \u2014 pemeriksaan itu terjadi sekarang atau tidak sama sekali.",
@@ -6217,6 +6311,8 @@ var init_api = __esm({
6217
6311
  leadFlowCancel: (id) => `${API}/lead/flows/${encodeURIComponent(id)}/cancel`,
6218
6312
  // SPEC-450 · ADR-0094 · katalog custom agent. `?projectId=` → himpunan EFEKTIF (global+project).
6219
6313
  customAgents: `${API}/custom-agents`,
6314
+ customAgentMetrics: `${API}/custom-agents/metrics`,
6315
+ customAgentInvocation: (id) => `${API}/custom-agents/invocations/${encodeURIComponent(id)}`,
6220
6316
  // SPEC-484 · ADR-0101 · sumber daftar tools/model/runtime untuk form (mention dari `customAgents`).
6221
6317
  customAgentCatalog: `${API}/custom-agents/catalog`,
6222
6318
  // SPEC-481 · ADR-0100 · webhook keluar (cookie-only)
@@ -11142,17 +11238,27 @@ var init_git = __esm({
11142
11238
  });
11143
11239
 
11144
11240
  // ../runner/src/settings.ts
11145
- var EVENT_HOOK_COMMAND;
11241
+ var EVENT_SPOOL_SCRIPT, EVENT_HOOK_COMMAND;
11146
11242
  var init_settings2 = __esm({
11147
11243
  "../runner/src/settings.ts"() {
11148
11244
  "use strict";
11245
+ EVENT_SPOOL_SCRIPT = [
11246
+ 'const fs=require("node:fs"),p=require("node:path"),c=require("node:crypto")',
11247
+ 'let d=""',
11248
+ 'process.stdin.setEncoding("utf8")',
11249
+ 'process.stdin.on("data",x=>d+=x)',
11250
+ 'process.stdin.on("end",()=>{let t="";try{const n=Date.now()+"-"+process.pid+"-"+c.randomUUID();t=p.join(process.env.HANOMAN_EVENT_DIR,n+".tmp");const f=p.join(process.env.HANOMAN_EVENT_DIR,n+".json");fs.writeFileSync(t,d,{flag:"wx",mode:0o600});fs.renameSync(t,f)}catch{if(t)try{fs.rmSync(t,{force:true})}catch{}}})'
11251
+ ].join(";");
11149
11252
  EVENT_HOOK_COMMAND = [
11253
+ 'if [ -n "${HANOMAN_EVENT_DIR:-}" ]; then',
11254
+ `node -e '${EVENT_SPOOL_SCRIPT}' >/dev/null 2>&1;`,
11255
+ "else",
11150
11256
  'curl -sS -m 2 -X POST "$HANOMAN_EVENT_URL"',
11151
11257
  "-H 'content-type: application/json'",
11152
11258
  '-H "authorization: Bearer $HANOMAN_EVENT_TOKEN"',
11153
11259
  '-H "x-hanoman-session: $HANOMAN_SESSION_ID"',
11154
11260
  '${HANOMAN_EVENT_HOST:+-H "host: $HANOMAN_EVENT_HOST"}',
11155
- "--data-binary @- >/dev/null 2>&1; exit 0"
11261
+ "--data-binary @- >/dev/null 2>&1; fi; exit 0"
11156
11262
  ].join(" ");
11157
11263
  }
11158
11264
  });
@@ -11195,6 +11301,75 @@ var init_custom_agents = __esm({
11195
11301
  }
11196
11302
  });
11197
11303
 
11304
+ // ../runner/src/agent-readonly.ts
11305
+ var init_agent_readonly = __esm({
11306
+ "../runner/src/agent-readonly.ts"() {
11307
+ "use strict";
11308
+ }
11309
+ });
11310
+
11311
+ // ../runner/src/runtime-profile.ts
11312
+ function resolveHardening(env) {
11313
+ if (env.HANOMAN_HARDENING === "1") return true;
11314
+ return env.HANOMAN_SESSION_SANDBOX === "podman" || filled(env.HANOMAN_PUBLIC_ORIGINS) || filled(env.HANOMAN_TRUST_PROXY);
11315
+ }
11316
+ var filled;
11317
+ var init_runtime_profile = __esm({
11318
+ "../runner/src/runtime-profile.ts"() {
11319
+ "use strict";
11320
+ filled = (v) => !!v && v.trim() !== "";
11321
+ }
11322
+ });
11323
+
11324
+ // ../runner/src/codex-agent-config.ts
11325
+ function codexNativeAgentsSupported(version3) {
11326
+ const parsed = version3 ? /(\d+)\.(\d+)\.(\d+)/.exec(version3)?.[0] : null;
11327
+ return parsed ? cmpVersion(parsed, CODEX_NATIVE_AGENTS_MIN_CLIENT) >= 0 : false;
11328
+ }
11329
+ function codexNativeVersionProbe(env, codexBin = env.HANOMAN_CODEX_BIN ?? "codex") {
11330
+ const sandbox = env.HANOMAN_SESSION_SANDBOX ?? (resolveHardening(env) ? "required" : "off");
11331
+ if (sandbox !== "podman") return { bin: codexBin, args: ["--version"] };
11332
+ return {
11333
+ bin: env.HANOMAN_PODMAN_BIN ?? "podman",
11334
+ args: [
11335
+ "run",
11336
+ "--rm",
11337
+ "--read-only",
11338
+ "--cap-drop=ALL",
11339
+ "--userns=keep-id",
11340
+ "--network",
11341
+ "none",
11342
+ env.HANOMAN_SESSION_IMAGE ?? "hanoman-agent:latest",
11343
+ "/bin/sh",
11344
+ "-lc",
11345
+ `${shellQuote(codexBin)} --version`
11346
+ ]
11347
+ };
11348
+ }
11349
+ var CODEX_NATIVE_AGENTS_MIN_CLIENT, shellQuote;
11350
+ var init_codex_agent_config = __esm({
11351
+ "../runner/src/codex-agent-config.ts"() {
11352
+ "use strict";
11353
+ init_src();
11354
+ init_custom_agents();
11355
+ init_runtime_profile();
11356
+ CODEX_NATIVE_AGENTS_MIN_CLIENT = "0.151.0";
11357
+ shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
11358
+ }
11359
+ });
11360
+
11361
+ // ../runner/src/custom-agent-eval.ts
11362
+ var init_custom_agent_eval = __esm({
11363
+ "../runner/src/custom-agent-eval.ts"() {
11364
+ "use strict";
11365
+ init_src();
11366
+ init_codex_agent_config();
11367
+ init_codex_settings();
11368
+ init_custom_agents();
11369
+ init_settings2();
11370
+ }
11371
+ });
11372
+
11198
11373
  // ../runner/src/paths.ts
11199
11374
  import { homedir } from "node:os";
11200
11375
  import { dirname, isAbsolute, join, resolve } from "node:path";
@@ -11529,19 +11704,6 @@ var init_config_env = __esm({
11529
11704
  }
11530
11705
  });
11531
11706
 
11532
- // ../runner/src/runtime-profile.ts
11533
- function resolveHardening(env) {
11534
- if (env.HANOMAN_HARDENING === "1") return true;
11535
- return env.HANOMAN_SESSION_SANDBOX === "podman" || filled(env.HANOMAN_PUBLIC_ORIGINS) || filled(env.HANOMAN_TRUST_PROXY);
11536
- }
11537
- var filled;
11538
- var init_runtime_profile = __esm({
11539
- "../runner/src/runtime-profile.ts"() {
11540
- "use strict";
11541
- filled = (v) => !!v && v.trim() !== "";
11542
- }
11543
- });
11544
-
11545
11707
  // ../runner/src/sandbox-probe.ts
11546
11708
  import { execFileSync } from "node:child_process";
11547
11709
  import { accessSync, constants } from "node:fs";
@@ -11654,6 +11816,9 @@ var init_src2 = __esm({
11654
11816
  init_codex_settings();
11655
11817
  init_agent_cli();
11656
11818
  init_custom_agents();
11819
+ init_agent_readonly();
11820
+ init_codex_agent_config();
11821
+ init_custom_agent_eval();
11657
11822
  init_verify_scope();
11658
11823
  init_code_style();
11659
11824
  init_paths();
@@ -12039,7 +12204,8 @@ var init_start = __esm({
12039
12204
  var doctor_exports = {};
12040
12205
  __export(doctor_exports, {
12041
12206
  default: () => doctor,
12042
- doctorReport: () => doctorReport
12207
+ doctorReport: () => doctorReport,
12208
+ resolveDoctorCodexBin: () => resolveDoctorCodexBin
12043
12209
  });
12044
12210
  import { execFileSync as execFileSync4 } from "node:child_process";
12045
12211
  import { accessSync as accessSync2, constants as constants2, existsSync as existsSync4 } from "node:fs";
@@ -12051,19 +12217,22 @@ function doctorReport(p3) {
12051
12217
  { mark: p3.git ? "\u2713" : "\u2717", text: p3.git ?? "git \u2014 TAK ADA (wajib: worktree per sesi)", fatal: !p3.git },
12052
12218
  { mark: p3.tmux ? "\u2713" : "\u2717", text: p3.tmux ?? "tmux \u2014 TAK ADA (wajib: sesi agen hidup di tmux)", fatal: !p3.tmux },
12053
12219
  { mark: p3.claude ? "\u2713" : "\xB7", text: p3.claude ? `claude ${p3.claude}` : "claude \u2014 tak ada", fatal: false },
12054
- { mark: p3.codex ? "\u2713" : "\xB7", text: p3.codex ? `codex ${p3.codex}` : "codex \u2014 tak ada", fatal: false },
12220
+ {
12221
+ mark: p3.codex ? codexNativeAgentsSupported(p3.codex) ? "\u2713" : "!" : "\xB7",
12222
+ text: p3.codex ? `codex ${p3.codex} \u2014 custom agent native ${codexNativeAgentsSupported(p3.codex) ? "siap" : `butuh >= ${CODEX_NATIVE_AGENTS_MIN_CLIENT}; sesi parent tetap bisa berjalan`}` : "codex \u2014 tak ada",
12223
+ fatal: false
12224
+ },
12055
12225
  // SPEC-471 · ADR-0095 · `gh` opsional: tanpa dia tarik issue jatuh ke HTTPS + GITHUB_TOKEN.
12056
12226
  {
12057
12227
  mark: p3.gh ? "\u2713" : "\xB7",
12058
12228
  text: p3.gh ? `gh ${p3.gh}` : "gh \u2014 tak ada (tarik issue akan lewat HTTP + GITHUB_TOKEN)",
12059
12229
  fatal: false
12060
12230
  },
12061
- // SPEC-909 · ADR-0146 · NON-FATAL dan itu disengaja: tanpa `curl` hook tetap `exit 0` — ia tak
12062
- // pernah memblokir agen tapi hanoman-lead tak akan menerima SATU PUN pertanyaan sesi, dan tak
12063
- // ada error di mana pun yang akan mengatakannya. Kegagalan senyap justru yang layak dilaporkan.
12231
+ // SPEC-950 · jalur normal memakai spool file bahkan di sandbox. curl hanya fallback untuk hook
12232
+ // lama/tanpa HANOMAN_EVENT_DIR, jadi absennya tidak lagi berarti telemetry utama mati.
12064
12233
  {
12065
12234
  mark: p3.curl ? "\u2713" : "!",
12066
- text: p3.curl ? `curl ${p3.curl}` : "curl \u2014 TAK ADA: hanoman-lead tak akan menerima pertanyaan sesi (SPEC-909)",
12235
+ text: p3.curl ? `curl ${p3.curl}` : "curl \u2014 tak ada (fallback event tanpa spool tidak tersedia)",
12067
12236
  fatal: false
12068
12237
  },
12069
12238
  // Direktori turunan lahir saat dipakai, jadi izin tulis yang belum ada di situ adalah
@@ -12117,12 +12286,22 @@ function version(bin, args) {
12117
12286
  return null;
12118
12287
  }
12119
12288
  }
12289
+ async function resolveDoctorCodexBin(env, dbUrl, read = readRuntimeConfig) {
12290
+ try {
12291
+ const configured = (await read(dbUrl, "HANOMAN_CODEX_BIN"))?.trim();
12292
+ if (configured) return configured;
12293
+ } catch {
12294
+ }
12295
+ return env.HANOMAN_CODEX_BIN?.trim() || "codex";
12296
+ }
12120
12297
  async function doctor(_argv, ctx) {
12121
12298
  let layout;
12122
12299
  let db;
12300
+ let dbUrl;
12123
12301
  try {
12124
12302
  layout = resolveLayout(distDir(), existsSync4);
12125
- db = dbFilePath(resolveDbUrl(ctx.env, dirname5(layout.schema)));
12303
+ dbUrl = resolveDbUrl(ctx.env, dirname5(layout.schema));
12304
+ db = dbFilePath(dbUrl);
12126
12305
  } catch (e) {
12127
12306
  ctx.stderr(`${e.message}
12128
12307
  `);
@@ -12136,7 +12315,8 @@ async function doctor(_argv, ctx) {
12136
12315
  { label: "key SSH", path: d.sshKeys, writable: writable(d.sshKeys), fatal: false }
12137
12316
  ];
12138
12317
  const claude = version(ctx.env.HANOMAN_CLAUDE_BIN ?? "claude", ["--version"]);
12139
- const codex = version(ctx.env.HANOMAN_CODEX_BIN ?? "codex", ["--version"]);
12318
+ const codexProbe = codexNativeVersionProbe(ctx.env, await resolveDoctorCodexBin(ctx.env, dbUrl));
12319
+ const codex = version(codexProbe.bin, codexProbe.args);
12140
12320
  const method = METHODS[DEFAULT_METHOD];
12141
12321
  const methods = zAgent.options.filter((a) => (a === "codex" ? codex : claude) !== null).map((a) => {
12142
12322
  const s2 = scanAgentSkills(a, ctx.env);
@@ -12172,6 +12352,7 @@ ${notice}
12172
12352
  if (!r.ok) ctx.stderr("\nada prasyarat yang belum terpenuhi \u2014 hanoman tak akan bisa menjalankan sesi\n");
12173
12353
  return r.ok ? 0 : 1;
12174
12354
  }
12355
+ var readRuntimeConfig;
12175
12356
  var init_doctor = __esm({
12176
12357
  "src/commands/doctor.ts"() {
12177
12358
  "use strict";
@@ -12179,6 +12360,15 @@ var init_doctor = __esm({
12179
12360
  init_src();
12180
12361
  init_layout();
12181
12362
  init_start();
12363
+ readRuntimeConfig = async (dbUrl, key) => {
12364
+ const { PrismaClient } = await import("@prisma/client");
12365
+ const client = new PrismaClient({ datasourceUrl: dbUrl });
12366
+ try {
12367
+ return (await client.runtimeConfig.findUnique({ where: { key }, select: { value: true } }))?.value ?? null;
12368
+ } finally {
12369
+ await client.$disconnect();
12370
+ }
12371
+ };
12182
12372
  }
12183
12373
  });
12184
12374