@rulemetric/cli 0.13.0 → 0.14.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 (328) hide show
  1. package/dist/base-command.js +1 -1
  2. package/dist/{chunk-UE76F2NQ.js → chunk-2WQUOC6S.js} +20 -20
  3. package/dist/{chunk-DUBBL57L.js → chunk-5XEGRU2R.js} +1 -1
  4. package/dist/chunk-74QIUVWY.js +1 -0
  5. package/dist/chunk-7ML2UCI7.js +1 -0
  6. package/dist/chunk-AHLVY52I.js +1 -0
  7. package/dist/{chunk-O2HCD6XL.js → chunk-AT2MMCOJ.js} +1 -1
  8. package/dist/{chunk-GJBS6QYC.js → chunk-AXJAGWZB.js} +1 -1
  9. package/dist/{chunk-UYPBIXF2.js → chunk-BVOXWAJO.js} +1 -1
  10. package/dist/chunk-CT7FWSIU.js +3 -0
  11. package/dist/{chunk-KV5A77XW.js → chunk-DDYGA2DS.js} +1 -1
  12. package/dist/{chunk-5XK6LB2T.js → chunk-E5YE25QG.js} +1 -1
  13. package/dist/chunk-E7GQJML3.js +1 -0
  14. package/dist/{chunk-RK663O4M.js → chunk-E7RH43OS.js} +6 -6
  15. package/dist/{chunk-2EX27VJ3.js → chunk-ET7753IY.js} +1 -1
  16. package/dist/{chunk-25Q3Y7TC.js → chunk-EWGE7YTQ.js} +1 -1
  17. package/dist/{chunk-WOJ733IB.js → chunk-EXVXKJ7Y.js} +1 -1
  18. package/dist/chunk-FCTFHY3X.js +13 -0
  19. package/dist/{chunk-RJX75Y7J.js → chunk-FJX7WDPG.js} +1 -1
  20. package/dist/{chunk-3UPHCQWI.js → chunk-FK55FXKQ.js} +1 -1
  21. package/dist/chunk-GH4EZK2O.js +1 -0
  22. package/dist/chunk-GN46S7T4.js +9 -0
  23. package/dist/{chunk-BG55WDDX.js → chunk-GQGGFHX7.js} +1 -1
  24. package/dist/chunk-GYNZ3OZF.js +1 -0
  25. package/dist/{chunk-WJZY5Y22.js → chunk-H4LUPIHJ.js} +1 -1
  26. package/dist/{chunk-25RDT32M.js → chunk-HAYO6JYI.js} +1 -1
  27. package/dist/{chunk-YI2OWO6R.js → chunk-HFSISGCJ.js} +1 -1
  28. package/dist/chunk-HRJDKAJ4.js +1 -0
  29. package/dist/chunk-IC6VZHE6.js +1 -0
  30. package/dist/chunk-INAZLRY5.js +1 -0
  31. package/dist/{chunk-VXUKXGRX.js → chunk-IOX4VJPR.js} +2 -2
  32. package/dist/chunk-J4W6GNCF.js +2 -0
  33. package/dist/{chunk-VH2EAP4J.js → chunk-J7UZXRTH.js} +1 -1
  34. package/dist/{chunk-6GAIYJ6K.js → chunk-JKWB6UXA.js} +1 -1
  35. package/dist/{chunk-JWFRI32Y.js → chunk-JLBQKV4V.js} +1 -1
  36. package/dist/{chunk-VIQ25ORK.js → chunk-JRAXWASH.js} +1 -1
  37. package/dist/{chunk-PVGUDJD7.js → chunk-JRWQJZYQ.js} +1 -1
  38. package/dist/{chunk-6ZPSVZNH.js → chunk-LDUJWJ6A.js} +1 -1
  39. package/dist/chunk-LMCICVE7.js +12 -0
  40. package/dist/chunk-MHVHGKDK.js +3 -0
  41. package/dist/chunk-MMM4B3KH.js +1 -0
  42. package/dist/{chunk-CYYDQFRY.js → chunk-MX34N7Y5.js} +1 -1
  43. package/dist/{chunk-7AWFSFR7.js → chunk-N26UEHI3.js} +1 -1
  44. package/dist/{chunk-ZGW3N3A6.js → chunk-N4RJ6CN7.js} +1 -1
  45. package/dist/{chunk-J6QMBMC2.js → chunk-N7SZWKCU.js} +1 -1
  46. package/dist/{chunk-QFKUSNUB.js → chunk-NRYVMGJN.js} +1 -1
  47. package/dist/{chunk-4COLIGD7.js → chunk-NSPDDSRR.js} +1 -1
  48. package/dist/{chunk-GBWQENAH.js → chunk-O62OLKQL.js} +1 -1
  49. package/dist/{chunk-WY3VVX3R.js → chunk-OLJF4K7L.js} +1 -1
  50. package/dist/{chunk-6W2GSV4O.js → chunk-OMRH4JXS.js} +1 -1
  51. package/dist/{chunk-WH3IPXXQ.js → chunk-OO2ZRKZY.js} +5 -5
  52. package/dist/chunk-OUK2AWEI.js +1 -0
  53. package/dist/chunk-OYAOLJUH.js +1 -0
  54. package/dist/chunk-PPAHIVG5.js +1 -0
  55. package/dist/{chunk-3MDEGOUG.js → chunk-PPIZMPNI.js} +1 -1
  56. package/dist/chunk-PUX6P5KC.js +2 -0
  57. package/dist/chunk-QI3FJKGK.js +1 -0
  58. package/dist/chunk-RRM4HQG4.js +1 -0
  59. package/dist/{chunk-QTCWOIT4.js → chunk-S5CQSP35.js} +1 -1
  60. package/dist/chunk-S5U76YUI.js +1 -0
  61. package/dist/chunk-SE3UKA3M.js +25 -0
  62. package/dist/{chunk-BZS5QRWB.js → chunk-SG4UDDHB.js} +1 -1
  63. package/dist/chunk-SPJ73X7A.js +4 -0
  64. package/dist/chunk-SV73ASP2.js +1 -0
  65. package/dist/chunk-SWSOEYC7.js +1 -0
  66. package/dist/{chunk-I3M5VBLT.js → chunk-TTSDPDRH.js} +1 -1
  67. package/dist/chunk-TWPG3TEQ.js +48 -0
  68. package/dist/{chunk-WQX4RRBQ.js → chunk-U7BPWFOP.js} +1 -1
  69. package/dist/{chunk-NBEZ5FAU.js → chunk-UFQ43KXE.js} +1 -1
  70. package/dist/chunk-UJ7EPLKC.js +1 -0
  71. package/dist/{chunk-V2USQH27.js → chunk-UQDOOQCM.js} +1 -1
  72. package/dist/{chunk-M2VVRQKA.js → chunk-V4PUYPTU.js} +1 -1
  73. package/dist/{chunk-SHYP27B7.js → chunk-VALRPZXH.js} +1 -1
  74. package/dist/{chunk-LTQ5BJYL.js → chunk-VLWNSTYW.js} +1 -1
  75. package/dist/chunk-WSWZPNLG.js +1 -0
  76. package/dist/{chunk-MNQPCNGQ.js → chunk-XAOLOR4Q.js} +1 -1
  77. package/dist/{chunk-6QLZT7EP.js → chunk-XAXEZI6Z.js} +1 -1
  78. package/dist/{chunk-Y7QIOPN5.js → chunk-XFLKW6SJ.js} +1 -1
  79. package/dist/chunk-YNLHJHUP.js +1 -0
  80. package/dist/{chunk-U7TBEC3I.js → chunk-YP2CBUXH.js} +1 -1
  81. package/dist/{chunk-P2WSDHIA.js → chunk-YUIRDBQQ.js} +1 -1
  82. package/dist/{chunk-4N43IBXH.js → chunk-ZA6NXNPR.js} +1 -1
  83. package/dist/{chunk-APDRR6FS.js → chunk-ZZHIBOMT.js} +1 -1
  84. package/dist/commands/antigravity/backfill.js +1 -1
  85. package/dist/commands/auth/create-key.js +1 -1
  86. package/dist/commands/auth/login.js +1 -1
  87. package/dist/commands/auth/logout.js +1 -1
  88. package/dist/commands/auth/revoke-key.js +1 -1
  89. package/dist/commands/auth/signup.js +1 -1
  90. package/dist/commands/auth/status.js +1 -1
  91. package/dist/commands/capture/scope.js +1 -1
  92. package/dist/commands/catalog/index.js +1 -1
  93. package/dist/commands/catalog/recommend.js +1 -1
  94. package/dist/commands/crons/list.js +1 -1
  95. package/dist/commands/crons/run.js +2 -2
  96. package/dist/commands/dashboard.js +1 -1
  97. package/dist/commands/diagnostics.js +1 -1
  98. package/dist/commands/eval-targets/create.js +1 -1
  99. package/dist/commands/eval-targets/list.js +1 -1
  100. package/dist/commands/evals/agent.js +3 -3
  101. package/dist/commands/evals/auto-run.js +1 -1
  102. package/dist/commands/evals/benchmark.js +1 -1
  103. package/dist/commands/evals/compare.js +1 -1
  104. package/dist/commands/evals/create.js +1 -1
  105. package/dist/commands/evals/optimize-description.js +1 -1
  106. package/dist/commands/evals/optimize.js +1 -1
  107. package/dist/commands/evals/refine-cases.js +1 -1
  108. package/dist/commands/evals/run.js +1 -1
  109. package/dist/commands/evals/validate-judge.js +1 -1
  110. package/dist/commands/experiments/list.js +1 -1
  111. package/dist/commands/gateway/ensure.js +1 -1
  112. package/dist/commands/hooks/emit-instructions.js +1 -1
  113. package/dist/commands/hooks/install.js +1 -1
  114. package/dist/commands/hooks/run.js +1 -1
  115. package/dist/commands/hooks/uninstall.js +1 -1
  116. package/dist/commands/instructions/create.js +1 -1
  117. package/dist/commands/instructions/dedupe.js +1 -1
  118. package/dist/commands/instructions/delete.js +1 -1
  119. package/dist/commands/instructions/fork.js +1 -1
  120. package/dist/commands/instructions/get.js +1 -1
  121. package/dist/commands/instructions/list.js +1 -1
  122. package/dist/commands/instructions/promote.js +1 -1
  123. package/dist/commands/instructions/pull.js +1 -1
  124. package/dist/commands/instructions/restore.js +1 -1
  125. package/dist/commands/instructions/retract.js +1 -1
  126. package/dist/commands/instructions/upstream.js +1 -1
  127. package/dist/commands/instructions/versions.js +1 -1
  128. package/dist/commands/launcher.js +1 -1
  129. package/dist/commands/local/down.js +1 -1
  130. package/dist/commands/local/up.js +1 -1
  131. package/dist/commands/org/current.js +1 -1
  132. package/dist/commands/org/list.js +1 -1
  133. package/dist/commands/org/switch.js +1 -1
  134. package/dist/commands/packs/add-instruction.js +1 -1
  135. package/dist/commands/packs/apply.js +1 -1
  136. package/dist/commands/packs/create.js +1 -1
  137. package/dist/commands/packs/fork.js +1 -1
  138. package/dist/commands/packs/get.js +1 -1
  139. package/dist/commands/packs/list.js +1 -1
  140. package/dist/commands/packs/remove-instruction.js +1 -1
  141. package/dist/commands/proxy/env.js +1 -1
  142. package/dist/commands/proxy/logs.js +1 -1
  143. package/dist/commands/proxy/restart.js +1 -1
  144. package/dist/commands/proxy/setup.js +1 -1
  145. package/dist/commands/proxy/start.js +1 -1
  146. package/dist/commands/proxy/status.js +1 -1
  147. package/dist/commands/proxy/stop.js +1 -1
  148. package/dist/commands/research/backfill.js +1 -1
  149. package/dist/commands/research/tail-transcript.js +1 -1
  150. package/dist/commands/service/install.js +18 -18
  151. package/dist/commands/service/status.js +1 -1
  152. package/dist/commands/service/uninstall.js +1 -1
  153. package/dist/commands/sessions/analyze.js +1 -1
  154. package/dist/commands/sessions/delete.js +1 -1
  155. package/dist/commands/sessions/end.js +1 -1
  156. package/dist/commands/sessions/import.js +1 -1
  157. package/dist/commands/sessions/list.js +1 -1
  158. package/dist/commands/sessions/start.js +1 -1
  159. package/dist/commands/setup.js +1 -1
  160. package/dist/commands/skills/export-approved.js +1 -1
  161. package/dist/commands/skills/install.js +1 -1
  162. package/dist/commands/skills/list.js +1 -1
  163. package/dist/commands/skills/publish.js +1 -1
  164. package/dist/commands/skills/search.js +1 -1
  165. package/dist/commands/skills/update.js +1 -1
  166. package/dist/commands/suggestions/accept.js +1 -1
  167. package/dist/commands/suggestions/auto.js +1 -1
  168. package/dist/commands/suggestions/dismiss.js +1 -1
  169. package/dist/commands/suggestions/list.js +1 -1
  170. package/dist/commands/suggestions/refresh.js +1 -1
  171. package/dist/commands/suggestions/undo.js +1 -1
  172. package/dist/dashboard/Dashboard.js +1 -1
  173. package/dist/dashboard/data.js +1 -1
  174. package/dist/dashboard/launcher/LauncherOverlay.js +1 -1
  175. package/dist/dashboard/launcher/api.js +1 -1
  176. package/dist/dashboard/launcher/useLaunchJobs.js +1 -1
  177. package/dist/dist-NHS7HY5Q.js +1 -0
  178. package/dist/harbor/cost_delta.py +377 -0
  179. package/dist/harbor/mcp_container_shell.py +156 -0
  180. package/dist/harbor/provenance.py +361 -0
  181. package/dist/harbor/rm-batch-task/environment/Dockerfile +6 -0
  182. package/dist/harbor/rm-batch-task/environment/items.jsonl +60 -0
  183. package/dist/harbor/rm-batch-task/inject.md +8 -0
  184. package/dist/harbor/rm-batch-task/instruction.md +8 -0
  185. package/dist/harbor/rm-batch-task/solution/solve.sh +68 -0
  186. package/dist/harbor/rm-batch-task/task.toml +30 -0
  187. package/dist/harbor/rm-batch-task/tests/test.sh +23 -0
  188. package/dist/harbor/rm-batch-task/tests/test_outputs.py +32 -0
  189. package/dist/harbor/rm-locality-task/environment/Dockerfile +8 -0
  190. package/dist/harbor/rm-locality-task/environment/workspace/deploy/prod/ingest.yaml +3 -0
  191. package/dist/harbor/rm-locality-task/environment/workspace/deploy/staging/ingest.yaml +3 -0
  192. package/dist/harbor/rm-locality-task/environment/workspace/docs/architecture/ingest.md +6 -0
  193. package/dist/harbor/rm-locality-task/environment/workspace/legacy/ingest_v1/config.yaml +2 -0
  194. package/dist/harbor/rm-locality-task/environment/workspace/packages/shared/defaults.py +4 -0
  195. package/dist/harbor/rm-locality-task/environment/workspace/services/ingest/config/limits.defaults.yaml +4 -0
  196. package/dist/harbor/rm-locality-task/environment/workspace/services/ingest/config/limits.yaml +3 -0
  197. package/dist/harbor/rm-locality-task/environment/workspace/services/ingest/loader.py +15 -0
  198. package/dist/harbor/rm-locality-task/environment/workspace/tests/fixtures/ingest_config.yaml +3 -0
  199. package/dist/harbor/rm-locality-task/inject.md +12 -0
  200. package/dist/harbor/rm-locality-task/instruction.md +4 -0
  201. package/dist/harbor/rm-locality-task/solution/solve.sh +14 -0
  202. package/dist/harbor/rm-locality-task/task.toml +30 -0
  203. package/dist/harbor/rm-locality-task/tests/test.sh +23 -0
  204. package/dist/harbor/rm-locality-task/tests/test_outputs.py +19 -0
  205. package/dist/harbor/rm-migrations-task/environment/Dockerfile +7 -0
  206. package/dist/harbor/rm-migrations-task/environment/applied.txt +2 -0
  207. package/dist/harbor/rm-migrations-task/environment/migrations/00001_init.sql +1 -0
  208. package/dist/harbor/rm-migrations-task/environment/migrations/00002_add_email.sql +1 -0
  209. package/dist/harbor/rm-migrations-task/inject.md +7 -0
  210. package/dist/harbor/rm-migrations-task/instruction.md +8 -0
  211. package/dist/harbor/rm-migrations-task/solution/solve.sh +8 -0
  212. package/dist/harbor/rm-migrations-task/task.toml +30 -0
  213. package/dist/harbor/rm-migrations-task/tests/test.sh +23 -0
  214. package/dist/harbor/rm-migrations-task/tests/test_outputs.py +36 -0
  215. package/dist/harbor/rm-pagination-task/environment/Dockerfile +6 -0
  216. package/dist/harbor/rm-pagination-task/environment/store.py +29 -0
  217. package/dist/harbor/rm-pagination-task/inject.md +8 -0
  218. package/dist/harbor/rm-pagination-task/instruction.md +5 -0
  219. package/dist/harbor/rm-pagination-task/solution/solve.sh +7 -0
  220. package/dist/harbor/rm-pagination-task/task.toml +30 -0
  221. package/dist/harbor/rm-pagination-task/tests/test.sh +23 -0
  222. package/dist/harbor/rm-pagination-task/tests/test_outputs.py +14 -0
  223. package/dist/harbor/rm_host_claude.py +264 -0
  224. package/dist/harbor/rm_host_codex.py +208 -0
  225. package/dist/harbor/run.sh +268 -0
  226. package/dist/harbor/summarize.py +248 -0
  227. package/dist/lib/active-org-refresh.js +1 -1
  228. package/dist/lib/agent-loop.js +1 -1
  229. package/dist/lib/antigravity-transcript.js +1 -1
  230. package/dist/lib/api-client.js +1 -1
  231. package/dist/lib/capture-scope-config.js +1 -1
  232. package/dist/lib/codex-capture-scope.js +1 -1
  233. package/dist/lib/codex-exec.js +1 -1
  234. package/dist/lib/ensure-api-key.js +1 -1
  235. package/dist/lib/ensure-fresh-token.js +1 -1
  236. package/dist/lib/eval-accumulation.js +1 -0
  237. package/dist/lib/eval-analyzer.js +1 -1
  238. package/dist/lib/eval-benchmark.js +1 -1
  239. package/dist/lib/eval-candidate-run.js +1 -1
  240. package/dist/lib/eval-case-generator.js +1 -1
  241. package/dist/lib/eval-conversation-handler.js +1 -1
  242. package/dist/lib/eval-executor.js +1 -1
  243. package/dist/lib/eval-grader.js +1 -1
  244. package/dist/lib/eval-meta-judge.js +1 -1
  245. package/dist/lib/eval-resume.js +1 -1
  246. package/dist/lib/eval-run-batch.js +1 -1
  247. package/dist/lib/eval-trigger-tester.js +1 -1
  248. package/dist/lib/evolution-enrolment.js +1 -1
  249. package/dist/lib/harbor-readiness.js +1 -1
  250. package/dist/lib/hooks-capture-gate.js +1 -1
  251. package/dist/lib/insights/generation/installed-asks.js +1 -1
  252. package/dist/lib/insights/nomination/nominate.js +1 -1
  253. package/dist/lib/instruction-proposer.js +1 -1
  254. package/dist/lib/instruction-snapshot.js +1 -1
  255. package/dist/lib/instructions/acceptance/accept-events.js +1 -1
  256. package/dist/lib/instructions/acceptance/accept-hook.js +1 -1
  257. package/dist/lib/instructions/acceptance/agent-instruction-files.js +1 -1
  258. package/dist/lib/instructions/acceptance/hook-install.js +1 -1
  259. package/dist/lib/instructions/acceptance/reverse-applications.js +1 -1
  260. package/dist/lib/instructions/acceptance/suggestion-accept.js +1 -1
  261. package/dist/lib/jobs/handlers/cron-auto-accept-suggestions.js +1 -1
  262. package/dist/lib/jobs/handlers/cron-eval-autorun.js +1 -1
  263. package/dist/lib/jobs/handlers/cron-instruction-evolution.js +1 -1
  264. package/dist/lib/jobs/handlers/cron-instruction-training.js +1 -1
  265. package/dist/lib/jobs/handlers/cron-reconcile-attribution.js +1 -1
  266. package/dist/lib/jobs/handlers/cron-refine-cases.js +1 -1
  267. package/dist/lib/jobs/handlers/cron-refresh-pricing.js +1 -1
  268. package/dist/lib/jobs/handlers/cron-retire-unused-skills.js +1 -1
  269. package/dist/lib/jobs/handlers/cron-suggest-instructions.js +1 -1
  270. package/dist/lib/jobs/handlers/distill-memories.js +1 -1
  271. package/dist/lib/jobs/handlers/harbor-ab-verdict.js +1 -1
  272. package/dist/lib/jobs/handlers/harbor-ab.js +1 -1
  273. package/dist/lib/jobs/handlers/process-changelog.js +1 -1
  274. package/dist/lib/jobs/handlers/process-conversation.js +1 -1
  275. package/dist/lib/jobs/handlers/process-eval.js +1 -1
  276. package/dist/lib/jobs/handlers/process-insights.js +1 -1
  277. package/dist/lib/jobs/handlers/process-judge-review.js +1 -1
  278. package/dist/lib/jobs/handlers/process-launch.js +1 -1
  279. package/dist/lib/jobs/handlers/process-memory-ab.js +1 -1
  280. package/dist/lib/jobs/handlers/process-run-cleanup.js +1 -1
  281. package/dist/lib/jobs/handlers/process-session-goal.js +1 -1
  282. package/dist/lib/jobs/shed-retry.js +1 -1
  283. package/dist/lib/judge-review-runner.js +1 -1
  284. package/dist/lib/llm-client.js +1 -1
  285. package/dist/lib/llm-reply-parsers.js +1 -0
  286. package/dist/lib/manual-tasks.js +1 -1
  287. package/dist/lib/measurement-llm.js +1 -0
  288. package/dist/lib/pi-exec.js +1 -0
  289. package/dist/lib/projects/workspace/eval-workspace.js +1 -1
  290. package/dist/lib/projects/workspace/worktree.js +1 -1
  291. package/dist/lib/refine-cases.js +1 -1
  292. package/dist/lib/research-client.js +1 -1
  293. package/dist/lib/retraction.js +1 -1
  294. package/dist/lib/service-environment.js +1 -0
  295. package/dist/lib/setup-steps.js +1 -1
  296. package/dist/lib/telemetry.js +1 -1
  297. package/dist/lib/terminal-target-heartbeat.js +1 -1
  298. package/dist/lib/tool-cli-map.js +1 -0
  299. package/dist/lib/transcript-instruction-linking.js +1 -1
  300. package/dist/lib/transcript-sync.js +1 -1
  301. package/dist/lib/work-report.js +1 -1
  302. package/oclif.manifest.json +7 -1
  303. package/package.json +5 -4
  304. package/dist/chunk-4FSQSETR.js +0 -12
  305. package/dist/chunk-6WQG6C7F.js +0 -1
  306. package/dist/chunk-C42NTI2V.js +0 -3
  307. package/dist/chunk-F6DAHCJC.js +0 -20
  308. package/dist/chunk-GN3ZHQ46.js +0 -1
  309. package/dist/chunk-H3BCWSYO.js +0 -1
  310. package/dist/chunk-HLNHBZLI.js +0 -1
  311. package/dist/chunk-HLQZR4XJ.js +0 -13
  312. package/dist/chunk-J5HW45PZ.js +0 -48
  313. package/dist/chunk-JNFB22FD.js +0 -1
  314. package/dist/chunk-KCOJLTVX.js +0 -1
  315. package/dist/chunk-MLV2RHRE.js +0 -1
  316. package/dist/chunk-N7GK33PU.js +0 -1
  317. package/dist/chunk-NRYLYMDM.js +0 -1
  318. package/dist/chunk-OZNKMJ5B.js +0 -1
  319. package/dist/chunk-PPXOC7IV.js +0 -1
  320. package/dist/chunk-QXPYGN7I.js +0 -1
  321. package/dist/chunk-R4QIPG4V.js +0 -2
  322. package/dist/chunk-RGUCR7T6.js +0 -1
  323. package/dist/chunk-RO3ZZVCG.js +0 -1
  324. package/dist/chunk-SN3CHV66.js +0 -1
  325. package/dist/chunk-U3XKXJ3A.js +0 -5
  326. package/dist/chunk-UM4CBCXI.js +0 -1
  327. package/dist/chunk-WHQ4BDSM.js +0 -4
  328. package/dist/dist-45KJH5M7.js +0 -1
@@ -0,0 +1,264 @@
1
+ """Harbor agent that runs the USER'S OWN Claude Code on the host.
2
+
3
+ No API key or token: the host `claude` CLI uses the user's existing
4
+ subscription login (macOS keychain). The agent downloads the task's /app from
5
+ the environment, runs `claude -p <instruction>` in a host temp dir, and
6
+ uploads the result back for in-container verification.
7
+
8
+ Instruction delivery (the RuleMetric with-arm) via agent kwargs — all
9
+ host-side; the container never sees the injected files:
10
+
11
+ --ak claude_md=/abs/path/CLAUDE.md claude_md delivery (workdir root)
12
+ --ak skills_dirs=/abs/a,/abs/b skill delivery (.claude/skills/<name>)
13
+ --ak append_system_prompt="..." prompt delivery (--append-system-prompt)
14
+ --ak container_shell=true route ALL shell commands into the
15
+ task container via the MCP proxy
16
+ (mcp_container_shell.py); host
17
+ Bash tool is disabled
18
+
19
+ container_shell requires the local docker environment (container resolved by
20
+ compose labels) and imports two harbor internals pinned to 0.22.x:
21
+ _sanitize_docker_compose_project_name and MAIN_SERVICE_NAME.
22
+ """
23
+
24
+ import asyncio
25
+ import json
26
+ import os
27
+ import shutil
28
+ import subprocess
29
+ import sys
30
+ import tempfile
31
+ from pathlib import Path
32
+
33
+ import provenance
34
+
35
+ from harbor.agents.base import BaseAgent
36
+ from harbor.environments.base import BaseEnvironment
37
+ from harbor.models.agent.context import AgentContext
38
+
39
+ _CONTAINER_SHELL_PROMPT = (
40
+ "Your shell is the task container, not this machine: run every shell "
41
+ "command with the mcp__container__bash tool (the host Bash tool is "
42
+ "disabled). The container's /app is your current working directory: "
43
+ "when using file tools, always use paths RELATIVE to the working "
44
+ "directory (never absolute /app/... paths) — they are synced into the "
45
+ "container's /app before each command runs and when you finish. "
46
+ "Alternatively, write files directly through the container shell."
47
+ )
48
+
49
+
50
+ class HostClaudeCode(BaseAgent):
51
+ @staticmethod
52
+ def name() -> str:
53
+ return "host-claude-code"
54
+
55
+ def __init__(
56
+ self,
57
+ logs_dir: Path,
58
+ model_name: str | None = None,
59
+ claude_md: str | None = None,
60
+ skills_dirs: str | None = None,
61
+ append_system_prompt: str | None = None,
62
+ container_shell: str | bool | None = None,
63
+ agent_timeout_sec: float | None = None,
64
+ **kwargs,
65
+ ):
66
+ super().__init__(logs_dir=logs_dir, model_name=model_name, **kwargs)
67
+ self._claude_md = claude_md
68
+ self._skills_dirs = [s for s in (skills_dirs or "").split(",") if s]
69
+ self._append_system_prompt = append_system_prompt
70
+ self._container_shell = str(container_shell).lower() in ("true", "1", "yes")
71
+ self._timeout = agent_timeout_sec or 600.0
72
+
73
+ def version(self) -> str:
74
+ return "0.2.0"
75
+
76
+ def _delivery(self) -> list[str]:
77
+ """Which delivery channels this arm actually used, for the stamp."""
78
+ used = []
79
+ if self._claude_md:
80
+ used.append("claude_md")
81
+ if self._skills_dirs:
82
+ used.append("skills_dirs")
83
+ if self._append_system_prompt:
84
+ used.append("append_system_prompt")
85
+ if self._container_shell:
86
+ used.append("container_shell")
87
+ return used
88
+
89
+ async def setup(self, environment: BaseEnvironment) -> None:
90
+ if shutil.which("claude") is None:
91
+ raise RuntimeError("host `claude` CLI not found on PATH")
92
+
93
+ def _resolve_container_id(self, environment: BaseEnvironment) -> str:
94
+ # Pinned harbor 0.22.x internals; the compose labels are stable Docker
95
+ # behavior, only the project-name derivation is harbor's.
96
+ from harbor.constants import MAIN_SERVICE_NAME
97
+ from harbor.environments.docker.docker import (
98
+ _sanitize_docker_compose_project_name,
99
+ )
100
+
101
+ project = _sanitize_docker_compose_project_name(environment.session_id)
102
+ proc = subprocess.run(
103
+ [
104
+ "docker",
105
+ "ps",
106
+ "-q",
107
+ "--filter",
108
+ f"label=com.docker.compose.project={project}",
109
+ "--filter",
110
+ f"label=com.docker.compose.service={MAIN_SERVICE_NAME}",
111
+ ],
112
+ capture_output=True,
113
+ text=True,
114
+ check=True,
115
+ )
116
+ container_id = proc.stdout.strip().splitlines()
117
+ if len(container_id) != 1:
118
+ raise RuntimeError(
119
+ f"expected exactly one container for project {project}, "
120
+ f"got {container_id!r} (container_shell needs the local "
121
+ "docker environment)"
122
+ )
123
+ return container_id[0]
124
+
125
+ def _inject(self, workdir: Path) -> list[Path]:
126
+ """Copy instruction artifacts into the workdir; return paths to strip
127
+ before the final upload so they never count as agent output."""
128
+ injected: list[Path] = []
129
+ if self._claude_md:
130
+ target = workdir / "CLAUDE.md"
131
+ shutil.copyfile(self._claude_md, target)
132
+ injected.append(target)
133
+ if self._skills_dirs:
134
+ skills_root = workdir / ".claude"
135
+ for src in self._skills_dirs:
136
+ src_path = Path(src)
137
+ shutil.copytree(
138
+ src_path,
139
+ skills_root / "skills" / src_path.name,
140
+ dirs_exist_ok=True,
141
+ )
142
+ injected.append(skills_root)
143
+ return injected
144
+
145
+ def _build_command(self, instruction: str, environment: BaseEnvironment,
146
+ workdir: Path) -> list[str]:
147
+ cmd = [
148
+ "claude",
149
+ "-p",
150
+ instruction,
151
+ "--permission-mode",
152
+ "acceptEdits",
153
+ "--output-format",
154
+ "json",
155
+ ]
156
+ if self.model_name:
157
+ cmd += ["--model", self.model_name.split("/")[-1]]
158
+ if not self._container_shell:
159
+ # acceptEdits auto-approves file edits but NOT Bash, and a task that
160
+ # invites "write a script, then run it" stalls at "please approve the
161
+ # Bash command" and uploads nothing -- measured 2026-08-31: 12 of 15
162
+ # rm-batch-task with-arm trials died exactly there, reading as rule
163
+ # non-compliance. The workdir is a per-trial temp dir; Bash there is
164
+ # what any real session has. (The container path keeps Bash disallowed
165
+ # by design -- the MCP container shell replaces it.)
166
+ cmd += ["--allowedTools", "Bash"]
167
+
168
+ system_prompt_parts = []
169
+ if self._container_shell:
170
+ container_id = self._resolve_container_id(environment)
171
+ mcp_config = {
172
+ "mcpServers": {
173
+ "container": {
174
+ "command": sys.executable,
175
+ "args": [str(Path(__file__).parent / "mcp_container_shell.py")],
176
+ "env": {
177
+ "RM_CONTAINER_ID": container_id,
178
+ "RM_WORKDIR": str(workdir),
179
+ },
180
+ }
181
+ }
182
+ }
183
+ mcp_path = self.logs_dir / "mcp-config.json"
184
+ mcp_path.write_text(json.dumps(mcp_config))
185
+ cmd += [
186
+ "--mcp-config",
187
+ str(mcp_path),
188
+ "--strict-mcp-config",
189
+ "--disallowedTools",
190
+ "Bash",
191
+ "--allowedTools",
192
+ "mcp__container__bash",
193
+ ]
194
+ system_prompt_parts.append(_CONTAINER_SHELL_PROMPT)
195
+ if self._append_system_prompt:
196
+ system_prompt_parts.append(self._append_system_prompt)
197
+ if system_prompt_parts:
198
+ cmd += ["--append-system-prompt", "\n\n".join(system_prompt_parts)]
199
+ return cmd
200
+
201
+ async def run(
202
+ self,
203
+ instruction: str,
204
+ environment: BaseEnvironment,
205
+ context: AgentContext,
206
+ ) -> None:
207
+ workdir = Path(tempfile.mkdtemp(prefix="harbor-host-claude-"))
208
+ try:
209
+ await environment.download_dir("/app", workdir)
210
+ injected = self._inject(workdir)
211
+ cmd = self._build_command(instruction, environment, workdir)
212
+
213
+ env = {
214
+ k: v
215
+ for k, v in os.environ.items()
216
+ # Drop nested-session markers so the child runs as a fresh CLI.
217
+ if not k.startswith(("CLAUDECODE", "CLAUDE_CODE_"))
218
+ }
219
+ proc = await asyncio.create_subprocess_exec(
220
+ *cmd,
221
+ cwd=workdir,
222
+ env=env,
223
+ stdout=asyncio.subprocess.PIPE,
224
+ stderr=asyncio.subprocess.PIPE,
225
+ )
226
+ try:
227
+ stdout, stderr = await asyncio.wait_for(
228
+ proc.communicate(), timeout=self._timeout
229
+ )
230
+ except asyncio.TimeoutError:
231
+ proc.kill()
232
+ raise
233
+ (self.logs_dir / "claude-output.json").write_bytes(stdout)
234
+ (self.logs_dir / "claude-stderr.txt").write_bytes(stderr)
235
+ # Written for EVERY trial, including one about to raise below: the
236
+ # provenance of a failed trial is exactly what tells a later reader
237
+ # whether the failure was the rule or the harness (#415).
238
+ provenance.write_stamp(
239
+ self.logs_dir,
240
+ dict(
241
+ provenance.stamp(
242
+ cmd,
243
+ observed_model=provenance.observed_model_from_claude(stdout),
244
+ engine="claude",
245
+ engine_version=provenance.cli_version("claude"),
246
+ env=env,
247
+ delivery=self._delivery(),
248
+ ),
249
+ agent_version=self.version(),
250
+ ),
251
+ )
252
+ if proc.returncode != 0:
253
+ raise RuntimeError(
254
+ f"claude exited {proc.returncode}: {stderr.decode()[:2000]}"
255
+ )
256
+
257
+ for path in injected:
258
+ if path.is_dir():
259
+ shutil.rmtree(path, ignore_errors=True)
260
+ else:
261
+ path.unlink(missing_ok=True)
262
+ await environment.upload_dir(workdir, "/app")
263
+ finally:
264
+ shutil.rmtree(workdir, ignore_errors=True)
@@ -0,0 +1,208 @@
1
+ """Harbor agent that runs the USER'S OWN Codex CLI on the host.
2
+
3
+ The cross-vendor twin of rm_host_claude.py. No API key: the host `codex` CLI
4
+ uses the user's existing ChatGPT login. The agent downloads the task's /app,
5
+ runs `codex exec` in a host temp dir, and uploads the result back for
6
+ in-container verification.
7
+
8
+ Why a separate agent rather than a flag on the claude one: the two CLIs differ
9
+ in the three things that matter to an A/B arm — which instruction file the
10
+ agent reads (AGENTS.md vs CLAUDE.md), whether there is a system-prompt channel
11
+ (codex has none; the preamble is prepended to the instruction), and how the
12
+ final message comes back (a file via -o, not a JSON blob on stdout). Papering
13
+ over that with `if engine == "codex"` inside one agent is how an arm silently
14
+ delivers an instruction the agent never reads and the trial still reports a
15
+ clean result.
16
+
17
+ Instruction delivery (the RuleMetric with-arm) via agent kwargs:
18
+
19
+ --ak agents_md=/abs/path/AGENTS.md instruction delivery (workdir root)
20
+ --ak append_system_prompt="..." prepended to the instruction
21
+
22
+ Flags verified against codex-cli 0.144.0. `codex -q` was removed from the CLI
23
+ in an earlier version and left a mapping that could never succeed, so every
24
+ flag here was probed against the installed binary rather than remembered.
25
+ """
26
+
27
+ import asyncio
28
+ import os
29
+ import shutil
30
+ import tempfile
31
+ from pathlib import Path
32
+
33
+ import provenance
34
+
35
+ from harbor.agents.base import BaseAgent
36
+ from harbor.environments.base import BaseEnvironment
37
+ from harbor.models.agent.context import AgentContext
38
+
39
+
40
+ class HostCodex(BaseAgent):
41
+ @staticmethod
42
+ def name() -> str:
43
+ return "host-codex"
44
+
45
+ def __init__(
46
+ self,
47
+ logs_dir: Path,
48
+ model_name: str | None = None,
49
+ agents_md: str | None = None,
50
+ append_system_prompt: str | None = None,
51
+ skills_dirs: str | None = None,
52
+ container_shell: str | bool | None = None,
53
+ agent_timeout_sec: float | None = None,
54
+ **kwargs,
55
+ ):
56
+ super().__init__(logs_dir=logs_dir, model_name=model_name, **kwargs)
57
+ self._agents_md = agents_md
58
+ self._append_system_prompt = append_system_prompt
59
+ self._timeout = agent_timeout_sec or 600.0
60
+
61
+ # Refuse, loudly, rather than ignore. An arm that asked for skill
62
+ # delivery or a container shell and got neither is not a weaker arm —
63
+ # it is a DIFFERENT experiment than the one the operator described, and
64
+ # its result would be compared against a claude arm that did get them.
65
+ if skills_dirs:
66
+ raise ValueError(
67
+ "skills_dirs is Claude-Code-specific (.claude/skills) and has no "
68
+ "codex equivalent; deliver the same guidance via --ak agents_md "
69
+ "or --ak append_system_prompt so the arms stay comparable."
70
+ )
71
+ if str(container_shell).lower() in ("true", "1", "yes"):
72
+ raise ValueError(
73
+ "container_shell is not implemented for the codex agent (it needs "
74
+ "codex's own MCP config shape, unverified here). Run the codex arm "
75
+ "without it, or use the claude agent for container-shell trials."
76
+ )
77
+
78
+ def version(self) -> str:
79
+ return "0.1.0"
80
+
81
+ def _delivery(self) -> list[str]:
82
+ """Which delivery channels this arm actually used, for the stamp."""
83
+ used = []
84
+ if self._agents_md:
85
+ used.append("agents_md")
86
+ if self._append_system_prompt:
87
+ used.append("append_system_prompt")
88
+ return used
89
+
90
+ async def setup(self, environment: BaseEnvironment) -> None:
91
+ if shutil.which("codex") is None:
92
+ raise RuntimeError("host `codex` CLI not found on PATH")
93
+
94
+ def _inject(self, workdir: Path) -> list[Path]:
95
+ """Copy instruction artifacts into the workdir; return paths to strip
96
+ before the final upload so they never count as agent output."""
97
+ injected: list[Path] = []
98
+ if self._agents_md:
99
+ target = workdir / "AGENTS.md"
100
+ shutil.copyfile(self._agents_md, target)
101
+ injected.append(target)
102
+ return injected
103
+
104
+ def _build_command(self, last_message_file: Path) -> list[str]:
105
+ cmd = [
106
+ "codex",
107
+ "exec",
108
+ # JSONL on stdout so the run has token telemetry at all. Without it
109
+ # codex-output.txt was plain progress text with no usage envelope,
110
+ # every codex arm took summarize.py's null-cost branch, and a codex
111
+ # A/B was indistinguishable from a free run. The ANSWER still comes
112
+ # from --output-last-message; stdout is parsed only for usage.
113
+ "--json",
114
+ # The agent must be able to WRITE — this is a task run, not the
115
+ # read-only analysis path in codex-exec.ts.
116
+ "--sandbox",
117
+ "workspace-write",
118
+ # The per-trial workdir is a temp dir, not a git repo.
119
+ "--skip-git-repo-check",
120
+ # Keep synthetic benchmark trials out of ~/.codex/sessions: they are
121
+ # not the user's work, and letting them land there would contaminate
122
+ # the very session history RuleMetric measures.
123
+ "--ephemeral",
124
+ "--color",
125
+ "never",
126
+ "--output-last-message",
127
+ str(last_message_file),
128
+ ]
129
+ if self.model_name:
130
+ cmd += ["--model", self.model_name.split("/")[-1]]
131
+ # Prompt on stdin: instructions embed whole task descriptions and argv
132
+ # has an ARG_MAX ceiling.
133
+ cmd.append("-")
134
+ return cmd
135
+
136
+ async def run(
137
+ self,
138
+ instruction: str,
139
+ environment: BaseEnvironment,
140
+ context: AgentContext,
141
+ ) -> None:
142
+ workdir = Path(tempfile.mkdtemp(prefix="harbor-host-codex-"))
143
+ last_message = self.logs_dir / "codex-last-message.txt"
144
+ try:
145
+ await environment.download_dir("/app", workdir)
146
+ injected = self._inject(workdir)
147
+
148
+ # Codex has no --append-system-prompt channel, so the preamble is
149
+ # prepended. Same delivery as the eval path in codex-exec.ts.
150
+ prompt = instruction
151
+ if self._append_system_prompt:
152
+ prompt = f"{self._append_system_prompt}\n\n{instruction}"
153
+
154
+ cmd = self._build_command(last_message)
155
+ env = {
156
+ k: v
157
+ for k, v in os.environ.items()
158
+ # Drop nested-session markers so the child runs as a fresh CLI,
159
+ # and never route a trial through the capture proxy.
160
+ if not k.startswith(("CLAUDECODE", "CLAUDE_CODE_"))
161
+ and k not in ("HTTPS_PROXY", "HTTP_PROXY", "https_proxy", "http_proxy")
162
+ }
163
+ proc = await asyncio.create_subprocess_exec(
164
+ *cmd,
165
+ cwd=workdir,
166
+ env=env,
167
+ stdin=asyncio.subprocess.PIPE,
168
+ stdout=asyncio.subprocess.PIPE,
169
+ stderr=asyncio.subprocess.PIPE,
170
+ )
171
+ try:
172
+ stdout, stderr = await asyncio.wait_for(
173
+ proc.communicate(prompt.encode()), timeout=self._timeout
174
+ )
175
+ except asyncio.TimeoutError:
176
+ proc.kill()
177
+ raise
178
+ (self.logs_dir / "codex-output.txt").write_bytes(stdout)
179
+ (self.logs_dir / "codex-stderr.txt").write_bytes(stderr)
180
+ # codex states no modelUsage envelope, so the stamp records the
181
+ # REQUESTED model and says so (model_source: 'requested') rather
182
+ # than presenting a flag as an observation.
183
+ provenance.write_stamp(
184
+ self.logs_dir,
185
+ dict(
186
+ provenance.stamp(
187
+ cmd,
188
+ engine="codex",
189
+ engine_version=provenance.cli_version("codex"),
190
+ env=env,
191
+ delivery=self._delivery(),
192
+ ),
193
+ agent_version=self.version(),
194
+ ),
195
+ )
196
+ if proc.returncode != 0:
197
+ raise RuntimeError(
198
+ f"codex exited {proc.returncode}: {stderr.decode()[:2000]}"
199
+ )
200
+
201
+ for path in injected:
202
+ if path.is_dir():
203
+ shutil.rmtree(path, ignore_errors=True)
204
+ else:
205
+ path.unlink(missing_ok=True)
206
+ await environment.upload_dir(workdir, "/app")
207
+ finally:
208
+ shutil.rmtree(workdir, ignore_errors=True)