@lannguyensi/harness 0.64.0 → 1.0.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 (710) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +46 -42
  3. package/dist/cli/add/index.js +17 -21
  4. package/dist/cli/add/index.js.map +1 -1
  5. package/dist/cli/adopt/derive.d.ts +2 -2
  6. package/dist/cli/adopt/derive.js +2 -19
  7. package/dist/cli/adopt/derive.js.map +1 -1
  8. package/dist/cli/adopt/index.d.ts +1 -1
  9. package/dist/cli/adopt/index.js +1 -12
  10. package/dist/cli/adopt/index.js.map +1 -1
  11. package/dist/cli/apply/apply.d.ts +20 -35
  12. package/dist/cli/apply/apply.js +61 -136
  13. package/dist/cli/apply/apply.js.map +1 -1
  14. package/dist/cli/apply/generate-codex-config.js +10 -58
  15. package/dist/cli/apply/generate-codex-config.js.map +1 -1
  16. package/dist/cli/apply/generate-memory-index.js +1 -2
  17. package/dist/cli/apply/generate-memory-index.js.map +1 -1
  18. package/dist/cli/apply/generate-opencode-config.d.ts +2 -10
  19. package/dist/cli/apply/generate-opencode-config.js +74 -113
  20. package/dist/cli/apply/generate-opencode-config.js.map +1 -1
  21. package/dist/cli/apply/generate-settings.d.ts +11 -64
  22. package/dist/cli/apply/generate-settings.js +27 -132
  23. package/dist/cli/apply/generate-settings.js.map +1 -1
  24. package/dist/cli/apply/index.d.ts +1 -1
  25. package/dist/cli/apply/index.js +1 -1
  26. package/dist/cli/apply/index.js.map +1 -1
  27. package/dist/cli/apply/install-codex-config.d.ts +153 -1
  28. package/dist/cli/apply/install-codex-config.js +445 -70
  29. package/dist/cli/apply/install-codex-config.js.map +1 -1
  30. package/dist/cli/apply/next-steps.d.ts +7 -0
  31. package/dist/cli/apply/next-steps.js +13 -1
  32. package/dist/cli/apply/next-steps.js.map +1 -1
  33. package/dist/cli/bounded-stdin.d.ts +0 -23
  34. package/dist/cli/bounded-stdin.js +2 -49
  35. package/dist/cli/bounded-stdin.js.map +1 -1
  36. package/dist/cli/describe.d.ts +1 -1
  37. package/dist/cli/describe.js +0 -3
  38. package/dist/cli/describe.js.map +1 -1
  39. package/dist/cli/diff/engine.d.ts +0 -11
  40. package/dist/cli/diff/engine.js +1 -17
  41. package/dist/cli/diff/engine.js.map +1 -1
  42. package/dist/cli/diff/index.js +14 -25
  43. package/dist/cli/diff/index.js.map +1 -1
  44. package/dist/cli/doctor/claude-mcp.d.ts +4 -19
  45. package/dist/cli/doctor/claude-mcp.js +11 -22
  46. package/dist/cli/doctor/claude-mcp.js.map +1 -1
  47. package/dist/cli/doctor/codex.d.ts +12 -14
  48. package/dist/cli/doctor/codex.js +31 -144
  49. package/dist/cli/doctor/codex.js.map +1 -1
  50. package/dist/cli/doctor/format.js +23 -345
  51. package/dist/cli/doctor/format.js.map +1 -1
  52. package/dist/cli/doctor/hook-liveness.d.ts +56 -0
  53. package/dist/cli/doctor/hook-liveness.js +455 -0
  54. package/dist/cli/doctor/hook-liveness.js.map +1 -0
  55. package/dist/cli/doctor/index.d.ts +13 -87
  56. package/dist/cli/doctor/index.js +57 -713
  57. package/dist/cli/doctor/index.js.map +1 -1
  58. package/dist/cli/doctor/opencode.d.ts +2 -1
  59. package/dist/cli/doctor/opencode.js.map +1 -1
  60. package/dist/cli/doctor/types.d.ts +21 -387
  61. package/dist/cli/doctor/types.js.map +1 -1
  62. package/dist/cli/drain-stdio.d.ts +31 -0
  63. package/dist/cli/drain-stdio.js +76 -0
  64. package/dist/cli/drain-stdio.js.map +1 -0
  65. package/dist/cli/export.d.ts +3 -1
  66. package/dist/cli/export.js +7 -16
  67. package/dist/cli/export.js.map +1 -1
  68. package/dist/cli/gate/codex-disable.d.ts +29 -0
  69. package/dist/cli/gate/codex-disable.js +195 -0
  70. package/dist/cli/gate/codex-disable.js.map +1 -0
  71. package/dist/cli/gate/codex-enable.d.ts +41 -0
  72. package/dist/cli/gate/codex-enable.js +127 -0
  73. package/dist/cli/gate/codex-enable.js.map +1 -0
  74. package/dist/cli/gate/codex-snapshot.d.ts +208 -0
  75. package/dist/cli/gate/codex-snapshot.js +402 -0
  76. package/dist/cli/gate/codex-snapshot.js.map +1 -0
  77. package/dist/cli/gate/disable.d.ts +9 -0
  78. package/dist/cli/gate/disable.js +263 -28
  79. package/dist/cli/gate/disable.js.map +1 -1
  80. package/dist/cli/gate/enable.d.ts +26 -6
  81. package/dist/cli/gate/enable.js +152 -80
  82. package/dist/cli/gate/enable.js.map +1 -1
  83. package/dist/cli/gate/link-chain.d.ts +53 -0
  84. package/dist/cli/gate/link-chain.js +122 -0
  85. package/dist/cli/gate/link-chain.js.map +1 -0
  86. package/dist/cli/gate/snapshot.d.ts +289 -12
  87. package/dist/cli/gate/snapshot.js +481 -22
  88. package/dist/cli/gate/snapshot.js.map +1 -1
  89. package/dist/cli/index.d.ts +0 -9
  90. package/dist/cli/index.js +9 -2131
  91. package/dist/cli/index.js.map +1 -1
  92. package/dist/cli/init/composer.d.ts +3 -6
  93. package/dist/cli/init/composer.js +6 -370
  94. package/dist/cli/init/composer.js.map +1 -1
  95. package/dist/cli/init/dependencies.d.ts +1 -2
  96. package/dist/cli/init/dependencies.js +4 -80
  97. package/dist/cli/init/dependencies.js.map +1 -1
  98. package/dist/cli/init/detect.js +5 -4
  99. package/dist/cli/init/detect.js.map +1 -1
  100. package/dist/cli/init/interactive.d.ts +1 -23
  101. package/dist/cli/init/interactive.js +33 -247
  102. package/dist/cli/init/interactive.js.map +1 -1
  103. package/dist/cli/init/profiles.d.ts +2 -2
  104. package/dist/cli/init/profiles.js +51 -245
  105. package/dist/cli/init/profiles.js.map +1 -1
  106. package/dist/cli/init/templates.d.ts +2 -37
  107. package/dist/cli/init/templates.js +25 -1237
  108. package/dist/cli/init/templates.js.map +1 -1
  109. package/dist/cli/list.d.ts +1 -1
  110. package/dist/cli/list.js +1 -38
  111. package/dist/cli/list.js.map +1 -1
  112. package/dist/cli/loader.d.ts +15 -1
  113. package/dist/cli/loader.js +56 -39
  114. package/dist/cli/loader.js.map +1 -1
  115. package/dist/cli/main.js +7 -2
  116. package/dist/cli/main.js.map +1 -1
  117. package/dist/cli/migrate-home/index.js +2 -2
  118. package/dist/cli/migrate-home/index.js.map +1 -1
  119. package/dist/cli/pack/add.js +1 -1
  120. package/dist/cli/pack/add.js.map +1 -1
  121. package/dist/cli/pack/hook-bootstrap.d.ts +5 -164
  122. package/dist/cli/pack/hook-bootstrap.js +15 -291
  123. package/dist/cli/pack/hook-bootstrap.js.map +1 -1
  124. package/dist/cli/pack/hook-branch-protection.d.ts +28 -21
  125. package/dist/cli/pack/hook-branch-protection.js +365 -340
  126. package/dist/cli/pack/hook-branch-protection.js.map +1 -1
  127. package/dist/cli/pack/index.d.ts +0 -1
  128. package/dist/cli/pack/index.js +0 -1
  129. package/dist/cli/pack/index.js.map +1 -1
  130. package/dist/cli/pack/list.js +1 -6
  131. package/dist/cli/pack/list.js.map +1 -1
  132. package/dist/cli/pack/mutate.d.ts +7 -9
  133. package/dist/cli/pack/mutate.js +6 -9
  134. package/dist/cli/pack/mutate.js.map +1 -1
  135. package/dist/cli/pack/reseed.d.ts +2 -2
  136. package/dist/cli/pack/reseed.js +16 -22
  137. package/dist/cli/pack/reseed.js.map +1 -1
  138. package/dist/cli/{delegate/register.d.ts → register-gate-group.d.ts} +1 -1
  139. package/dist/cli/register-gate-group.js +348 -0
  140. package/dist/cli/register-gate-group.js.map +1 -0
  141. package/dist/cli/register-inspect-group.d.ts +6 -0
  142. package/dist/cli/register-inspect-group.js +189 -0
  143. package/dist/cli/register-inspect-group.js.map +1 -0
  144. package/dist/cli/register-operator-lifecycle.d.ts +1 -1
  145. package/dist/cli/register-operator-lifecycle.js +3 -100
  146. package/dist/cli/register-operator-lifecycle.js.map +1 -1
  147. package/dist/cli/register-pack-group.d.ts +5 -0
  148. package/dist/cli/register-pack-group.js +134 -0
  149. package/dist/cli/register-pack-group.js.map +1 -0
  150. package/dist/cli/{register-audit-group.d.ts → register-setup-group.d.ts} +1 -1
  151. package/dist/cli/register-setup-group.js +495 -0
  152. package/dist/cli/register-setup-group.js.map +1 -0
  153. package/dist/cli/remove/index.d.ts +0 -14
  154. package/dist/cli/remove/index.js +2 -115
  155. package/dist/cli/remove/index.js.map +1 -1
  156. package/dist/cli/remove/mutate.d.ts +0 -2
  157. package/dist/cli/remove/mutate.js +1 -19
  158. package/dist/cli/remove/mutate.js.map +1 -1
  159. package/dist/cli/remove/register.js +2 -19
  160. package/dist/cli/remove/register.js.map +1 -1
  161. package/dist/cli/shell-quote.d.ts +7 -0
  162. package/dist/cli/shell-quote.js +17 -0
  163. package/dist/cli/shell-quote.js.map +1 -0
  164. package/dist/cli/uninstall/index.d.ts +4 -2
  165. package/dist/cli/uninstall/index.js +15 -7
  166. package/dist/cli/uninstall/index.js.map +1 -1
  167. package/dist/cli/uninstall/register.js +6 -4
  168. package/dist/cli/uninstall/register.js.map +1 -1
  169. package/dist/cli/uninstall/snapshot.d.ts +15 -0
  170. package/dist/cli/uninstall/snapshot.js +38 -0
  171. package/dist/cli/uninstall/snapshot.js.map +1 -1
  172. package/dist/cli/validate/checks.d.ts +0 -77
  173. package/dist/cli/validate/checks.js +3 -1049
  174. package/dist/cli/validate/checks.js.map +1 -1
  175. package/dist/cli/validate/index.js +19 -15
  176. package/dist/cli/validate/index.js.map +1 -1
  177. package/dist/index.d.ts +0 -3
  178. package/dist/index.js +0 -3
  179. package/dist/index.js.map +1 -1
  180. package/dist/io/claude-mcp.d.ts +1 -1
  181. package/dist/io/claude-mcp.js +27 -14
  182. package/dist/io/claude-mcp.js.map +1 -1
  183. package/dist/io/harness-lock.js +2 -2
  184. package/dist/io/harness-lock.js.map +1 -1
  185. package/dist/io/lock.d.ts +0 -26
  186. package/dist/io/lock.js +10 -27
  187. package/dist/io/lock.js.map +1 -1
  188. package/dist/io/merge-settings.js +6 -4
  189. package/dist/io/merge-settings.js.map +1 -1
  190. package/dist/io/project-name.d.ts +22 -39
  191. package/dist/io/project-name.js +25 -42
  192. package/dist/io/project-name.js.map +1 -1
  193. package/dist/io/read-regular-file.d.ts +103 -40
  194. package/dist/io/read-regular-file.js +196 -35
  195. package/dist/io/read-regular-file.js.map +1 -1
  196. package/dist/io/restart-hints.js +3 -6
  197. package/dist/io/restart-hints.js.map +1 -1
  198. package/dist/io/version-compare.d.ts +1 -3
  199. package/dist/io/version-compare.js +1 -3
  200. package/dist/io/version-compare.js.map +1 -1
  201. package/dist/io/write-regular-file.d.ts +30 -0
  202. package/dist/io/write-regular-file.js +85 -0
  203. package/dist/io/write-regular-file.js.map +1 -0
  204. package/dist/overrides/index.d.ts +1 -0
  205. package/dist/overrides/index.js +1 -0
  206. package/dist/overrides/index.js.map +1 -1
  207. package/dist/overrides/merge-layers.d.ts +43 -0
  208. package/dist/overrides/merge-layers.js +76 -0
  209. package/dist/overrides/merge-layers.js.map +1 -0
  210. package/dist/overrides/merge.d.ts +16 -0
  211. package/dist/overrides/merge.js +21 -8
  212. package/dist/overrides/merge.js.map +1 -1
  213. package/dist/policy-packs/builtin/branch-protection-runtime.d.ts +4 -69
  214. package/dist/policy-packs/builtin/branch-protection-runtime.js +12 -84
  215. package/dist/policy-packs/builtin/branch-protection-runtime.js.map +1 -1
  216. package/dist/policy-packs/builtin/branch-protection.d.ts +7 -30
  217. package/dist/policy-packs/builtin/branch-protection.js +56 -89
  218. package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
  219. package/dist/policy-packs/config-check.d.ts +7 -5
  220. package/dist/policy-packs/config-check.js +2 -2
  221. package/dist/policy-packs/config-check.js.map +1 -1
  222. package/dist/policy-packs/expand.d.ts +1 -3
  223. package/dist/policy-packs/expand.js +5 -42
  224. package/dist/policy-packs/expand.js.map +1 -1
  225. package/dist/policy-packs/index.d.ts +3 -3
  226. package/dist/policy-packs/index.js +2 -2
  227. package/dist/policy-packs/index.js.map +1 -1
  228. package/dist/policy-packs/registry.d.ts +9 -38
  229. package/dist/policy-packs/registry.js +5 -65
  230. package/dist/policy-packs/registry.js.map +1 -1
  231. package/dist/policy-packs/runtime.d.ts +0 -4
  232. package/dist/policy-packs/runtime.js +9 -22
  233. package/dist/policy-packs/runtime.js.map +1 -1
  234. package/dist/policy-packs/types.d.ts +0 -19
  235. package/dist/policy-packs/ux-compare.d.ts +1 -11
  236. package/dist/policy-packs/ux-compare.js +6 -42
  237. package/dist/policy-packs/ux-compare.js.map +1 -1
  238. package/dist/policy-packs/ux-drift-check.d.ts +5 -5
  239. package/dist/policy-packs/ux-drift-check.js +17 -22
  240. package/dist/policy-packs/ux-drift-check.js.map +1 -1
  241. package/dist/policy-packs/version-check.d.ts +5 -26
  242. package/dist/policy-packs/version-check.js +19 -74
  243. package/dist/policy-packs/version-check.js.map +1 -1
  244. package/dist/probes/memory.d.ts +1 -1
  245. package/dist/probes/memory.js +1 -1
  246. package/dist/probes/memory.js.map +1 -1
  247. package/dist/runtime/agent-facing.d.ts +9 -0
  248. package/dist/runtime/agent-facing.js +28 -14
  249. package/dist/runtime/agent-facing.js.map +1 -1
  250. package/dist/runtime/bounded-text.d.ts +15 -0
  251. package/dist/runtime/bounded-text.js +27 -0
  252. package/dist/runtime/bounded-text.js.map +1 -0
  253. package/dist/runtime/git-branch.d.ts +125 -0
  254. package/dist/runtime/git-branch.js +381 -0
  255. package/dist/runtime/git-branch.js.map +1 -0
  256. package/dist/runtime/hermetic-spawn-guard.d.ts +2 -2
  257. package/dist/runtime/hermetic-spawn-guard.js +24 -34
  258. package/dist/runtime/hermetic-spawn-guard.js.map +1 -1
  259. package/dist/runtime/home-dir.js +3 -2
  260. package/dist/runtime/home-dir.js.map +1 -1
  261. package/dist/runtime/tool-name-aliases.d.ts +0 -6
  262. package/dist/runtime/tool-name-aliases.js +5 -18
  263. package/dist/runtime/tool-name-aliases.js.map +1 -1
  264. package/dist/schema/grounding.d.ts +7 -54
  265. package/dist/schema/grounding.js +10 -12
  266. package/dist/schema/grounding.js.map +1 -1
  267. package/dist/schema/hooks.d.ts +39 -136
  268. package/dist/schema/hooks.js +3 -6
  269. package/dist/schema/hooks.js.map +1 -1
  270. package/dist/schema/index.d.ts +114 -3828
  271. package/dist/schema/index.js +41 -80
  272. package/dist/schema/index.js.map +1 -1
  273. package/dist/schema/memory.d.ts +54 -131
  274. package/dist/schema/memory.js +2 -2
  275. package/dist/schema/memory.js.map +1 -1
  276. package/dist/schema/policy-packs.d.ts +3 -45
  277. package/dist/schema/policy-packs.js +7 -13
  278. package/dist/schema/policy-packs.js.map +1 -1
  279. package/dist/schema/policy-ux.d.ts +7 -0
  280. package/dist/schema/policy-ux.js +18 -0
  281. package/dist/schema/policy-ux.js.map +1 -0
  282. package/dist/schema/removed-keys.d.ts +185 -0
  283. package/dist/schema/removed-keys.js +531 -0
  284. package/dist/schema/removed-keys.js.map +1 -0
  285. package/dist/schema/tools.d.ts +30 -268
  286. package/dist/schema/tools.js +4 -4
  287. package/dist/schema/tools.js.map +1 -1
  288. package/package.json +5 -16
  289. package/dist/cli/approve/branch-protection.d.ts +0 -69
  290. package/dist/cli/approve/branch-protection.js +0 -144
  291. package/dist/cli/approve/branch-protection.js.map +0 -1
  292. package/dist/cli/approve/risk.d.ts +0 -112
  293. package/dist/cli/approve/risk.js +0 -197
  294. package/dist/cli/approve/risk.js.map +0 -1
  295. package/dist/cli/approve/stdin-report.d.ts +0 -57
  296. package/dist/cli/approve/stdin-report.js +0 -145
  297. package/dist/cli/approve/stdin-report.js.map +0 -1
  298. package/dist/cli/approve/understanding.d.ts +0 -410
  299. package/dist/cli/approve/understanding.js +0 -996
  300. package/dist/cli/approve/understanding.js.map +0 -1
  301. package/dist/cli/audit.d.ts +0 -80
  302. package/dist/cli/audit.js +0 -252
  303. package/dist/cli/audit.js.map +0 -1
  304. package/dist/cli/delegate/index.d.ts +0 -76
  305. package/dist/cli/delegate/index.js +0 -444
  306. package/dist/cli/delegate/index.js.map +0 -1
  307. package/dist/cli/delegate/register.js +0 -90
  308. package/dist/cli/delegate/register.js.map +0 -1
  309. package/dist/cli/doctor/auto-approve-mode.d.ts +0 -37
  310. package/dist/cli/doctor/auto-approve-mode.js +0 -80
  311. package/dist/cli/doctor/auto-approve-mode.js.map +0 -1
  312. package/dist/cli/doctor/bypass-without-auto-approve.d.ts +0 -38
  313. package/dist/cli/doctor/bypass-without-auto-approve.js +0 -95
  314. package/dist/cli/doctor/bypass-without-auto-approve.js.map +0 -1
  315. package/dist/cli/doctor/codex-config-drift.d.ts +0 -28
  316. package/dist/cli/doctor/codex-config-drift.js +0 -240
  317. package/dist/cli/doctor/codex-config-drift.js.map +0 -1
  318. package/dist/cli/doctor/expire-on-tool-match.d.ts +0 -15
  319. package/dist/cli/doctor/expire-on-tool-match.js +0 -58
  320. package/dist/cli/doctor/expire-on-tool-match.js.map +0 -1
  321. package/dist/cli/doctor/rogue-ledger.d.ts +0 -53
  322. package/dist/cli/doctor/rogue-ledger.js +0 -153
  323. package/dist/cli/doctor/rogue-ledger.js.map +0 -1
  324. package/dist/cli/doctor/session-start-preflight-setup-version.d.ts +0 -83
  325. package/dist/cli/doctor/session-start-preflight-setup-version.js +0 -114
  326. package/dist/cli/doctor/session-start-preflight-setup-version.js.map +0 -1
  327. package/dist/cli/doctor/settings-drift.d.ts +0 -25
  328. package/dist/cli/doctor/settings-drift.js +0 -213
  329. package/dist/cli/doctor/settings-drift.js.map +0 -1
  330. package/dist/cli/doctor/toolchain-parity.d.ts +0 -63
  331. package/dist/cli/doctor/toolchain-parity.js +0 -154
  332. package/dist/cli/doctor/toolchain-parity.js.map +0 -1
  333. package/dist/cli/doctor/ug-auto-approvals.d.ts +0 -40
  334. package/dist/cli/doctor/ug-auto-approvals.js +0 -91
  335. package/dist/cli/doctor/ug-auto-approvals.js.map +0 -1
  336. package/dist/cli/doctor/ug-delegations.d.ts +0 -26
  337. package/dist/cli/doctor/ug-delegations.js +0 -121
  338. package/dist/cli/doctor/ug-delegations.js.map +0 -1
  339. package/dist/cli/doctor/ug-inflight.d.ts +0 -35
  340. package/dist/cli/doctor/ug-inflight.js +0 -57
  341. package/dist/cli/doctor/ug-inflight.js.map +0 -1
  342. package/dist/cli/doctor/understanding-mode-env.d.ts +0 -51
  343. package/dist/cli/doctor/understanding-mode-env.js +0 -98
  344. package/dist/cli/doctor/understanding-mode-env.js.map +0 -1
  345. package/dist/cli/dry-run.d.ts +0 -58
  346. package/dist/cli/dry-run.js +0 -283
  347. package/dist/cli/dry-run.js.map +0 -1
  348. package/dist/cli/enriched-event.d.ts +0 -35
  349. package/dist/cli/enriched-event.js +0 -60
  350. package/dist/cli/enriched-event.js.map +0 -1
  351. package/dist/cli/event-input.d.ts +0 -28
  352. package/dist/cli/event-input.js +0 -73
  353. package/dist/cli/event-input.js.map +0 -1
  354. package/dist/cli/explain-action.d.ts +0 -25
  355. package/dist/cli/explain-action.js +0 -57
  356. package/dist/cli/explain-action.js.map +0 -1
  357. package/dist/cli/explain-policy.d.ts +0 -147
  358. package/dist/cli/explain-policy.js +0 -248
  359. package/dist/cli/explain-policy.js.map +0 -1
  360. package/dist/cli/explain.d.ts +0 -31
  361. package/dist/cli/explain.js +0 -159
  362. package/dist/cli/explain.js.map +0 -1
  363. package/dist/cli/gc/index.d.ts +0 -63
  364. package/dist/cli/gc/index.js +0 -566
  365. package/dist/cli/gc/index.js.map +0 -1
  366. package/dist/cli/pack/approve-escape.d.ts +0 -19
  367. package/dist/cli/pack/approve-escape.js +0 -190
  368. package/dist/cli/pack/approve-escape.js.map +0 -1
  369. package/dist/cli/pack/auto-approve-path.d.ts +0 -146
  370. package/dist/cli/pack/auto-approve-path.js +0 -462
  371. package/dist/cli/pack/auto-approve-path.js.map +0 -1
  372. package/dist/cli/pack/hook-codex-post-tool-use.d.ts +0 -25
  373. package/dist/cli/pack/hook-codex-post-tool-use.js +0 -160
  374. package/dist/cli/pack/hook-codex-post-tool-use.js.map +0 -1
  375. package/dist/cli/pack/hook-codex-pre-tool-use.d.ts +0 -48
  376. package/dist/cli/pack/hook-codex-pre-tool-use.js +0 -474
  377. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +0 -1
  378. package/dist/cli/pack/hook-codex-stop.d.ts +0 -35
  379. package/dist/cli/pack/hook-codex-stop.js +0 -329
  380. package/dist/cli/pack/hook-codex-stop.js.map +0 -1
  381. package/dist/cli/pack/hook-codex-user-prompt-submit.d.ts +0 -24
  382. package/dist/cli/pack/hook-codex-user-prompt-submit.js +0 -93
  383. package/dist/cli/pack/hook-codex-user-prompt-submit.js.map +0 -1
  384. package/dist/cli/pack/hook-post-merge-gate-record.d.ts +0 -37
  385. package/dist/cli/pack/hook-post-merge-gate-record.js +0 -190
  386. package/dist/cli/pack/hook-post-merge-gate-record.js.map +0 -1
  387. package/dist/cli/pack/hook-post-merge-gate.d.ts +0 -34
  388. package/dist/cli/pack/hook-post-merge-gate.js +0 -277
  389. package/dist/cli/pack/hook-post-merge-gate.js.map +0 -1
  390. package/dist/cli/pack/hook-post-tool-use.d.ts +0 -45
  391. package/dist/cli/pack/hook-post-tool-use.js +0 -137
  392. package/dist/cli/pack/hook-post-tool-use.js.map +0 -1
  393. package/dist/cli/pack/hook-pre-tool-use.d.ts +0 -92
  394. package/dist/cli/pack/hook-pre-tool-use.js +0 -1328
  395. package/dist/cli/pack/hook-pre-tool-use.js.map +0 -1
  396. package/dist/cli/pack/hook-runtime-reality.d.ts +0 -63
  397. package/dist/cli/pack/hook-runtime-reality.js +0 -210
  398. package/dist/cli/pack/hook-runtime-reality.js.map +0 -1
  399. package/dist/cli/pack/hook-solution-acceptance-writeguard.d.ts +0 -32
  400. package/dist/cli/pack/hook-solution-acceptance-writeguard.js +0 -438
  401. package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +0 -1
  402. package/dist/cli/pack/hook-solution-acceptance.d.ts +0 -157
  403. package/dist/cli/pack/hook-solution-acceptance.js +0 -530
  404. package/dist/cli/pack/hook-solution-acceptance.js.map +0 -1
  405. package/dist/cli/pack/hook-stay-in-scope.d.ts +0 -37
  406. package/dist/cli/pack/hook-stay-in-scope.js +0 -128
  407. package/dist/cli/pack/hook-stay-in-scope.js.map +0 -1
  408. package/dist/cli/pack/hook-subagent-start.d.ts +0 -23
  409. package/dist/cli/pack/hook-subagent-start.js +0 -88
  410. package/dist/cli/pack/hook-subagent-start.js.map +0 -1
  411. package/dist/cli/pack/hook-subagent-stop.d.ts +0 -19
  412. package/dist/cli/pack/hook-subagent-stop.js +0 -41
  413. package/dist/cli/pack/hook-subagent-stop.js.map +0 -1
  414. package/dist/cli/pack/hook-track-active-claim.d.ts +0 -27
  415. package/dist/cli/pack/hook-track-active-claim.js +0 -275
  416. package/dist/cli/pack/hook-track-active-claim.js.map +0 -1
  417. package/dist/cli/pack/transcript-report-scan.d.ts +0 -71
  418. package/dist/cli/pack/transcript-report-scan.js +0 -359
  419. package/dist/cli/pack/transcript-report-scan.js.map +0 -1
  420. package/dist/cli/pack/understanding-report-schema-hint.d.ts +0 -26
  421. package/dist/cli/pack/understanding-report-schema-hint.js +0 -120
  422. package/dist/cli/pack/understanding-report-schema-hint.js.map +0 -1
  423. package/dist/cli/pack/upgrade.d.ts +0 -32
  424. package/dist/cli/pack/upgrade.js +0 -273
  425. package/dist/cli/pack/upgrade.js.map +0 -1
  426. package/dist/cli/pause/index.d.ts +0 -113
  427. package/dist/cli/pause/index.js +0 -251
  428. package/dist/cli/pause/index.js.map +0 -1
  429. package/dist/cli/pause-check.d.ts +0 -22
  430. package/dist/cli/pause-check.js +0 -54
  431. package/dist/cli/pause-check.js.map +0 -1
  432. package/dist/cli/policy/intercept.d.ts +0 -233
  433. package/dist/cli/policy/intercept.js +0 -1022
  434. package/dist/cli/policy/intercept.js.map +0 -1
  435. package/dist/cli/policy/risk-envelope-enrichment.d.ts +0 -40
  436. package/dist/cli/policy/risk-envelope-enrichment.js +0 -110
  437. package/dist/cli/policy/risk-envelope-enrichment.js.map +0 -1
  438. package/dist/cli/record/index.d.ts +0 -81
  439. package/dist/cli/record/index.js +0 -348
  440. package/dist/cli/record/index.js.map +0 -1
  441. package/dist/cli/register-audit-group.js +0 -91
  442. package/dist/cli/register-audit-group.js.map +0 -1
  443. package/dist/cli/resolve-env.d.ts +0 -32
  444. package/dist/cli/resolve-env.js +0 -46
  445. package/dist/cli/resolve-env.js.map +0 -1
  446. package/dist/cli/session-export/index.d.ts +0 -46
  447. package/dist/cli/session-export/index.js +0 -169
  448. package/dist/cli/session-export/index.js.map +0 -1
  449. package/dist/cli/session-export/redact.d.ts +0 -22
  450. package/dist/cli/session-export/redact.js +0 -47
  451. package/dist/cli/session-export/redact.js.map +0 -1
  452. package/dist/cli/session-export/transcript.d.ts +0 -24
  453. package/dist/cli/session-export/transcript.js +0 -164
  454. package/dist/cli/session-export/transcript.js.map +0 -1
  455. package/dist/cli/session-start/branch-check.d.ts +0 -18
  456. package/dist/cli/session-start/branch-check.js +0 -140
  457. package/dist/cli/session-start/branch-check.js.map +0 -1
  458. package/dist/cli/session-start/index.d.ts +0 -121
  459. package/dist/cli/session-start/index.js +0 -591
  460. package/dist/cli/session-start/index.js.map +0 -1
  461. package/dist/cli/session-start/shared-options.d.ts +0 -103
  462. package/dist/cli/session-start/shared-options.js +0 -107
  463. package/dist/cli/session-start/shared-options.js.map +0 -1
  464. package/dist/cli/session-start/stale-base-check.d.ts +0 -46
  465. package/dist/cli/session-start/stale-base-check.js +0 -352
  466. package/dist/cli/session-start/stale-base-check.js.map +0 -1
  467. package/dist/cli/session-start/toolchain-parity.d.ts +0 -267
  468. package/dist/cli/session-start/toolchain-parity.js +0 -797
  469. package/dist/cli/session-start/toolchain-parity.js.map +0 -1
  470. package/dist/cli/smoke/assertions.d.ts +0 -46
  471. package/dist/cli/smoke/assertions.js +0 -170
  472. package/dist/cli/smoke/assertions.js.map +0 -1
  473. package/dist/cli/smoke/index.d.ts +0 -76
  474. package/dist/cli/smoke/index.js +0 -245
  475. package/dist/cli/smoke/index.js.map +0 -1
  476. package/dist/cli/smoke/runner.d.ts +0 -55
  477. package/dist/cli/smoke/runner.js +0 -134
  478. package/dist/cli/smoke/runner.js.map +0 -1
  479. package/dist/cli/smoke/stream-parser.d.ts +0 -65
  480. package/dist/cli/smoke/stream-parser.js +0 -115
  481. package/dist/cli/smoke/stream-parser.js.map +0 -1
  482. package/dist/cli/test-risk.d.ts +0 -27
  483. package/dist/cli/test-risk.js +0 -38
  484. package/dist/cli/test-risk.js.map +0 -1
  485. package/dist/io/display-path.d.ts +0 -11
  486. package/dist/io/display-path.js +0 -29
  487. package/dist/io/display-path.js.map +0 -1
  488. package/dist/io/duration.d.ts +0 -5
  489. package/dist/io/duration.js +0 -50
  490. package/dist/io/duration.js.map +0 -1
  491. package/dist/io/extract.d.ts +0 -79
  492. package/dist/io/extract.js +0 -226
  493. package/dist/io/extract.js.map +0 -1
  494. package/dist/io/ledger-record.d.ts +0 -83
  495. package/dist/io/ledger-record.js +0 -301
  496. package/dist/io/ledger-record.js.map +0 -1
  497. package/dist/io/read-json-dir-entries.d.ts +0 -36
  498. package/dist/io/read-json-dir-entries.js +0 -48
  499. package/dist/io/read-json-dir-entries.js.map +0 -1
  500. package/dist/io/safe-json-parse.d.ts +0 -5
  501. package/dist/io/safe-json-parse.js +0 -21
  502. package/dist/io/safe-json-parse.js.map +0 -1
  503. package/dist/policies/index.d.ts +0 -5
  504. package/dist/policies/index.js +0 -6
  505. package/dist/policies/index.js.map +0 -1
  506. package/dist/policies/ledger-client.d.ts +0 -113
  507. package/dist/policies/ledger-client.js +0 -500
  508. package/dist/policies/ledger-client.js.map +0 -1
  509. package/dist/policies/producers.d.ts +0 -12
  510. package/dist/policies/producers.js +0 -61
  511. package/dist/policies/producers.js.map +0 -1
  512. package/dist/policies/requires.d.ts +0 -75
  513. package/dist/policies/requires.js +0 -240
  514. package/dist/policies/requires.js.map +0 -1
  515. package/dist/policies/timestamp.d.ts +0 -14
  516. package/dist/policies/timestamp.js +0 -36
  517. package/dist/policies/timestamp.js.map +0 -1
  518. package/dist/policy-packs/builtin/permission-profiles.d.ts +0 -11
  519. package/dist/policy-packs/builtin/permission-profiles.js +0 -74
  520. package/dist/policy-packs/builtin/permission-profiles.js.map +0 -1
  521. package/dist/policy-packs/builtin/post-merge-gate-runtime.d.ts +0 -286
  522. package/dist/policy-packs/builtin/post-merge-gate-runtime.js +0 -397
  523. package/dist/policy-packs/builtin/post-merge-gate-runtime.js.map +0 -1
  524. package/dist/policy-packs/builtin/post-merge-gate.d.ts +0 -56
  525. package/dist/policy-packs/builtin/post-merge-gate.js +0 -347
  526. package/dist/policy-packs/builtin/post-merge-gate.js.map +0 -1
  527. package/dist/policy-packs/builtin/solution-acceptance-reconnect.d.ts +0 -50
  528. package/dist/policy-packs/builtin/solution-acceptance-reconnect.js +0 -151
  529. package/dist/policy-packs/builtin/solution-acceptance-reconnect.js.map +0 -1
  530. package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +0 -323
  531. package/dist/policy-packs/builtin/solution-acceptance-runtime.js +0 -672
  532. package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +0 -1
  533. package/dist/policy-packs/builtin/solution-acceptance.d.ts +0 -45
  534. package/dist/policy-packs/builtin/solution-acceptance.js +0 -226
  535. package/dist/policy-packs/builtin/solution-acceptance.js.map +0 -1
  536. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.d.ts +0 -25
  537. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js +0 -94
  538. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js.map +0 -1
  539. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.d.ts +0 -35
  540. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js +0 -86
  541. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js.map +0 -1
  542. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.d.ts +0 -210
  543. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js +0 -361
  544. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js.map +0 -1
  545. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +0 -304
  546. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +0 -734
  547. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +0 -1
  548. package/dist/policy-packs/builtin/understanding-before-execution/expiry-reason.d.ts +0 -36
  549. package/dist/policy-packs/builtin/understanding-before-execution/expiry-reason.js +0 -73
  550. package/dist/policy-packs/builtin/understanding-before-execution/expiry-reason.js.map +0 -1
  551. package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +0 -19
  552. package/dist/policy-packs/builtin/understanding-before-execution/index.js +0 -83
  553. package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +0 -1
  554. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.d.ts +0 -135
  555. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js +0 -643
  556. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js.map +0 -1
  557. package/dist/policy-packs/builtin/understanding-before-execution/ledger.d.ts +0 -25
  558. package/dist/policy-packs/builtin/understanding-before-execution/ledger.js +0 -56
  559. package/dist/policy-packs/builtin/understanding-before-execution/ledger.js.map +0 -1
  560. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.d.ts +0 -20
  561. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +0 -106
  562. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +0 -1
  563. package/dist/policy-packs/builtin/understanding-before-execution/markers.d.ts +0 -126
  564. package/dist/policy-packs/builtin/understanding-before-execution/markers.js +0 -245
  565. package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +0 -1
  566. package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.d.ts +0 -29
  567. package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.js +0 -82
  568. package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.js.map +0 -1
  569. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.d.ts +0 -71
  570. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +0 -175
  571. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +0 -1
  572. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +0 -625
  573. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +0 -1076
  574. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +0 -1
  575. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.d.ts +0 -128
  576. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +0 -213
  577. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +0 -1
  578. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.d.ts +0 -129
  579. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.js +0 -166
  580. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.js.map +0 -1
  581. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.d.ts +0 -164
  582. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js +0 -275
  583. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js.map +0 -1
  584. package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +0 -1
  585. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +0 -10
  586. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +0 -1
  587. package/dist/policy-packs/builtin/understanding-before-execution.d.ts +0 -526
  588. package/dist/policy-packs/builtin/understanding-before-execution.js +0 -1078
  589. package/dist/policy-packs/builtin/understanding-before-execution.js.map +0 -1
  590. package/dist/policy-packs/permission-translator.d.ts +0 -9
  591. package/dist/policy-packs/permission-translator.js +0 -76
  592. package/dist/policy-packs/permission-translator.js.map +0 -1
  593. package/dist/runtime/action-envelope.d.ts +0 -64
  594. package/dist/runtime/action-envelope.js +0 -46
  595. package/dist/runtime/action-envelope.js.map +0 -1
  596. package/dist/runtime/approval-signing.d.ts +0 -111
  597. package/dist/runtime/approval-signing.js +0 -384
  598. package/dist/runtime/approval-signing.js.map +0 -1
  599. package/dist/runtime/bash-match-facts.d.ts +0 -60
  600. package/dist/runtime/bash-match-facts.js +0 -196
  601. package/dist/runtime/bash-match-facts.js.map +0 -1
  602. package/dist/runtime/bash-match-registry.d.ts +0 -112
  603. package/dist/runtime/bash-match-registry.js +0 -406
  604. package/dist/runtime/bash-match-registry.js.map +0 -1
  605. package/dist/runtime/bash-prefix-parse.d.ts +0 -31
  606. package/dist/runtime/bash-prefix-parse.js +0 -576
  607. package/dist/runtime/bash-prefix-parse.js.map +0 -1
  608. package/dist/runtime/command-normalize.d.ts +0 -658
  609. package/dist/runtime/command-normalize.js +0 -2410
  610. package/dist/runtime/command-normalize.js.map +0 -1
  611. package/dist/runtime/deletion-target-resolve.d.ts +0 -28
  612. package/dist/runtime/deletion-target-resolve.js +0 -1078
  613. package/dist/runtime/deletion-target-resolve.js.map +0 -1
  614. package/dist/runtime/destructive-shell-floor.d.ts +0 -19
  615. package/dist/runtime/destructive-shell-floor.js +0 -611
  616. package/dist/runtime/destructive-shell-floor.js.map +0 -1
  617. package/dist/runtime/environment-resolver.d.ts +0 -36
  618. package/dist/runtime/environment-resolver.js +0 -138
  619. package/dist/runtime/environment-resolver.js.map +0 -1
  620. package/dist/runtime/git-context.d.ts +0 -201
  621. package/dist/runtime/git-context.js +0 -422
  622. package/dist/runtime/git-context.js.map +0 -1
  623. package/dist/runtime/index.d.ts +0 -12
  624. package/dist/runtime/index.js +0 -13
  625. package/dist/runtime/index.js.map +0 -1
  626. package/dist/runtime/intercept.d.ts +0 -623
  627. package/dist/runtime/intercept.js +0 -1384
  628. package/dist/runtime/intercept.js.map +0 -1
  629. package/dist/runtime/kube-context.d.ts +0 -16
  630. package/dist/runtime/kube-context.js +0 -63
  631. package/dist/runtime/kube-context.js.map +0 -1
  632. package/dist/runtime/kubectl-target-parse.d.ts +0 -31
  633. package/dist/runtime/kubectl-target-parse.js +0 -249
  634. package/dist/runtime/kubectl-target-parse.js.map +0 -1
  635. package/dist/runtime/ledger-add.d.ts +0 -16
  636. package/dist/runtime/ledger-add.js +0 -150
  637. package/dist/runtime/ledger-add.js.map +0 -1
  638. package/dist/runtime/ledger-writer.d.ts +0 -27
  639. package/dist/runtime/ledger-writer.js +0 -46
  640. package/dist/runtime/ledger-writer.js.map +0 -1
  641. package/dist/runtime/pause-sentinel.d.ts +0 -60
  642. package/dist/runtime/pause-sentinel.js +0 -156
  643. package/dist/runtime/pause-sentinel.js.map +0 -1
  644. package/dist/runtime/pending-approval.d.ts +0 -23
  645. package/dist/runtime/pending-approval.js +0 -67
  646. package/dist/runtime/pending-approval.js.map +0 -1
  647. package/dist/runtime/read-only-bash.d.ts +0 -141
  648. package/dist/runtime/read-only-bash.js +0 -2204
  649. package/dist/runtime/read-only-bash.js.map +0 -1
  650. package/dist/runtime/recovery-git-commit.d.ts +0 -22
  651. package/dist/runtime/recovery-git-commit.js +0 -284
  652. package/dist/runtime/recovery-git-commit.js.map +0 -1
  653. package/dist/runtime/reject-malformed-session-id.d.ts +0 -8
  654. package/dist/runtime/reject-malformed-session-id.js +0 -16
  655. package/dist/runtime/reject-malformed-session-id.js.map +0 -1
  656. package/dist/runtime/risk-classifier.d.ts +0 -38
  657. package/dist/runtime/risk-classifier.js +0 -311
  658. package/dist/runtime/risk-classifier.js.map +0 -1
  659. package/dist/runtime/session-id.d.ts +0 -113
  660. package/dist/runtime/session-id.js +0 -193
  661. package/dist/runtime/session-id.js.map +0 -1
  662. package/dist/runtime/shell-word.d.ts +0 -62
  663. package/dist/runtime/shell-word.js +0 -410
  664. package/dist/runtime/shell-word.js.map +0 -1
  665. package/dist/runtime/task-providers/agent-tasks.d.ts +0 -105
  666. package/dist/runtime/task-providers/agent-tasks.js +0 -247
  667. package/dist/runtime/task-providers/agent-tasks.js.map +0 -1
  668. package/dist/runtime/when-eval.d.ts +0 -62
  669. package/dist/runtime/when-eval.js +0 -212
  670. package/dist/runtime/when-eval.js.map +0 -1
  671. package/dist/runtime/workflow-policies.d.ts +0 -207
  672. package/dist/runtime/workflow-policies.js +0 -596
  673. package/dist/runtime/workflow-policies.js.map +0 -1
  674. package/dist/schema/audit.d.ts +0 -71
  675. package/dist/schema/audit.js +0 -32
  676. package/dist/schema/audit.js.map +0 -1
  677. package/dist/schema/doctor.d.ts +0 -25
  678. package/dist/schema/doctor.js +0 -23
  679. package/dist/schema/doctor.js.map +0 -1
  680. package/dist/schema/environments.d.ts +0 -215
  681. package/dist/schema/environments.js +0 -101
  682. package/dist/schema/environments.js.map +0 -1
  683. package/dist/schema/extract.d.ts +0 -9
  684. package/dist/schema/extract.js +0 -86
  685. package/dist/schema/extract.js.map +0 -1
  686. package/dist/schema/permission-profiles.d.ts +0 -2292
  687. package/dist/schema/permission-profiles.js +0 -60
  688. package/dist/schema/permission-profiles.js.map +0 -1
  689. package/dist/schema/policies.d.ts +0 -974
  690. package/dist/schema/policies.js +0 -298
  691. package/dist/schema/policies.js.map +0 -1
  692. package/dist/schema/requires.d.ts +0 -131
  693. package/dist/schema/requires.js +0 -88
  694. package/dist/schema/requires.js.map +0 -1
  695. package/dist/schema/risk.d.ts +0 -144
  696. package/dist/schema/risk.js +0 -194
  697. package/dist/schema/risk.js.map +0 -1
  698. package/dist/schema/session-start-preflight.d.ts +0 -10
  699. package/dist/schema/session-start-preflight.js +0 -106
  700. package/dist/schema/session-start-preflight.js.map +0 -1
  701. package/dist/schema/stale-base-check.d.ts +0 -18
  702. package/dist/schema/stale-base-check.js +0 -37
  703. package/dist/schema/stale-base-check.js.map +0 -1
  704. package/dist/schema/toolchain-parity.d.ts +0 -21
  705. package/dist/schema/toolchain-parity.js +0 -48
  706. package/dist/schema/toolchain-parity.js.map +0 -1
  707. package/dist/schema/workflows.d.ts +0 -519
  708. package/dist/schema/workflows.js +0 -81
  709. package/dist/schema/workflows.js.map +0 -1
  710. package/scripts/runtime-reality-docker-probe.mjs +0 -71
@@ -1,1022 +0,0 @@
1
- // Phase 4 #5 — `harness policy intercept` CLI entrypoint.
2
- //
3
- // Wired by Claude Code's settings.json (regenerated by `harness apply`) as
4
- // the PreToolUse hook. Reads the event JSON from stdin, runs the runtime
5
- // interceptor, writes Claude Code's deny JSON to stdout on block.
6
- import { openLedgerSession, } from "../../policies/index.js";
7
- import * as fs from "node:fs";
8
- import * as os from "node:os";
9
- import { readStdinBounded, STDIN_IDLE_TIMEOUT_MS, stdinTimeoutNote, stdinTimeoutBlockJson, stdinTimeoutBlockReason } from "../bounded-stdin.js";
10
- import { buildActionEnvelope, intercept, isBlockingDecision, recordPolicyDecisionOnSession, resolveEnvironment, resolveGitContext, resolveKubeContext, sanitizeEnvelopeReason, } from "../../runtime/index.js";
11
- import { resolveGeneratedDir, writePendingApproval } from "../../runtime/pending-approval.js";
12
- import { ENV_RANK, resolveBashPrefixEnrichment } from "./risk-envelope-enrichment.js";
13
- import { parseKubectlTarget } from "../../runtime/kubectl-target-parse.js";
14
- import { MAX_NORMALIZE_LENGTH, normalizeCommand, normalizeCommandAmpAware, normalizeCommandQuoteAware, segmentViewOf, } from "../../runtime/command-normalize.js";
15
- import { extractShellCommand, SHELL_ALIASES } from "../../runtime/tool-name-aliases.js";
16
- import { loadManifest } from "../loader.js";
17
- import { checkPauseFromLoader } from "../pause-check.js";
18
- function findGroundingMcp(manifest) {
19
- return manifest.tools.mcp.find((m) => m.name === "grounding-mcp") ?? null;
20
- }
21
- /** Resolve an `os` fact, returning "" on the (rare) lookup failure. */
22
- function safeOs(fn) {
23
- try {
24
- return fn();
25
- }
26
- catch {
27
- return "";
28
- }
29
- }
30
- /**
31
- * Render the hook-identity suffix for a stderr diagnostic. Returns the
32
- * empty string when no `--hook` flag was supplied, so an operator
33
- * invoking `harness policy intercept` by hand (and tests that don't
34
- * inject hookName) see the original prefix verbatim.
35
- */
36
- function hookSuffix(hookName) {
37
- return hookName !== undefined && hookName.length > 0 ? ` [hook=${hookName}]` : "";
38
- }
39
- /**
40
- * Phase 5 #3 — render a deny / warn-degraded decision as a stderr
41
- * diagnostic block. Multiline, indented; each block is bounded by the
42
- * policy name so concurrent fires (rare but possible) stay readable.
43
- */
44
- function formatDecisionDiagnostic(decision, hookName) {
45
- // The deny-degraded suffix names the fail_open opt-out ON PURPOSE and
46
- // ONLY here: stderr is the operator's surface. The agent-facing deny
47
- // envelope deliberately omits it (see the deny-degraded branch in
48
- // `runtime/intercept.ts` — a deny that includes its own disable recipe
49
- // is not a gate).
50
- // Both degraded arms are cause-neutral: the degraded family has five
51
- // causes and only one of them is the ledger; the true cause follows
52
- // on the block's own `reason:` line (review 2026-08-08, rounds 3+5).
53
- const header = `harness policy intercept${hookSuffix(hookName)}: ${decision.policyName}: ${decision.outcome}${decision.outcome === "warn-degraded"
54
- ? " (evidence could not be evaluated; non-blocking per warn tier or fail_open opt-out)"
55
- : decision.outcome === "deny-degraded"
56
- ? " (evidence could not be evaluated; failing closed per enforcement tier; operator opt-out: risk.degraded_fail_posture: fail_open)"
57
- : ""}`;
58
- const lines = [header];
59
- lines.push(` ledger_tag: ${decision.ledgerTag}`);
60
- if (decision.requiresEval !== undefined) {
61
- lines.push(` matched: ${decision.requiresEval.matchedCount}`);
62
- }
63
- lines.push(` reason: ${decision.reason}`);
64
- const extractKeys = Object.keys(decision.extractValues);
65
- if (extractKeys.length > 0) {
66
- lines.push(" extract:");
67
- for (const k of extractKeys.sort()) {
68
- lines.push(` ${k}=${decision.extractValues[k]}`);
69
- }
70
- }
71
- return `${lines.join("\n")}\n`;
72
- }
73
- function isVerboseEnabled(opts) {
74
- if (opts.verbose === true)
75
- return true;
76
- if (opts.verbose === false)
77
- return false;
78
- const env = process.env.HARNESS_POLICY_VERBOSE;
79
- if (typeof env !== "string")
80
- return false;
81
- if (env.length === 0)
82
- return false;
83
- // Accept anything truthy except literal disable-words (case-insensitive).
84
- return !/^(0|false|no|off)$/i.test(env.trim());
85
- }
86
- // Derived from `SHELL_ALIASES` rather than hand-copying its members (fix
87
- // round 2): `tool-name-aliases.ts`'s own header warns against the copy,
88
- // and since fix round 1 the precompute-matches-matcher argument above
89
- // depends on this set and the alias expansion agreeing. Deriving it means
90
- // they cannot drift apart.
91
- function isCodexShellTool(toolName) {
92
- return typeof toolName === "string" && SHELL_ALIASES.includes(toolName);
93
- }
94
- function extractPerCallCwd(input) {
95
- if (!input || typeof input !== "object" || Array.isArray(input))
96
- return null;
97
- const args = input;
98
- for (const candidate of [args.workdir, args.cwd]) {
99
- if (typeof candidate === "string" && candidate.length > 0)
100
- return candidate;
101
- }
102
- return null;
103
- }
104
- function readCodexCommandCwdFromProc() {
105
- let argv;
106
- try {
107
- argv = fs.readFileSync("/proc/1/cmdline", "utf8").split("\0");
108
- }
109
- catch {
110
- return null;
111
- }
112
- const idx = argv.indexOf("--command-cwd");
113
- const cwd = idx >= 0 ? argv[idx + 1] : undefined;
114
- return typeof cwd === "string" && cwd.length > 0 ? cwd : null;
115
- }
116
- function resolvePolicyCwd(event, codexCommandCwd) {
117
- if (typeof event.cwd === "string" && event.cwd.length > 0)
118
- return event.cwd;
119
- if (isCodexShellTool(event.tool_name)) {
120
- for (const input of [event.raw_input, event.tool_input, event.input]) {
121
- const cwd = extractPerCallCwd(input);
122
- if (cwd)
123
- return cwd;
124
- }
125
- if (codexCommandCwd !== undefined && codexCommandCwd.length > 0) {
126
- return codexCommandCwd;
127
- }
128
- const procCwd = readCodexCommandCwdFromProc();
129
- if (procCwd)
130
- return procCwd;
131
- }
132
- return process.cwd();
133
- }
134
- /**
135
- * Explicit kubectl target, upgrade-only merge (task a7eb1a71): an
136
- * explicit --context/--namespace/-n on a kubectl command can only push
137
- * the resolved environment to something MORE dangerous than the
138
- * ambient kubeconfig alone already gave, never less, mirroring
139
- * applyBranchSwitchUpgrade exactly (both the ambient-only base and the
140
- * merged-candidate kube inputs run through the SAME resolveEnvironment
141
- * call with identical git/env inputs, so ENV_RANK picks the more
142
- * dangerous of the two). See the `CHANGELOG.md:#0.49.0` entry (task
143
- * a7eb1a71) for the measured downgrade this fixes.
144
- */
145
- function applyKubeTargetUpgrade(event, manifest, cwd, git, ambientKube, kubectlTarget, env, user, host, now) {
146
- if (kubectlTarget === null)
147
- return ambientKube;
148
- const candidateKube = {
149
- context: kubectlTarget.context ?? ambientKube.context,
150
- namespace: kubectlTarget.namespace ?? ambientKube.namespace,
151
- };
152
- if (candidateKube.context === ambientKube.context &&
153
- candidateKube.namespace === ambientKube.namespace) {
154
- return ambientKube;
155
- }
156
- const effectiveNow = now ?? new Date();
157
- const baseResolution = resolveEnvironment(buildActionEnvelope(event, { cwd, git, user, host, now: effectiveNow }), manifest.environments.resolvers, { env, kubeContext: ambientKube.context, kubeNamespace: ambientKube.namespace });
158
- const candidateResolution = resolveEnvironment(buildActionEnvelope(event, { cwd, git, user, host, now: effectiveNow }), manifest.environments.resolvers, { env, kubeContext: candidateKube.context, kubeNamespace: candidateKube.namespace });
159
- return ENV_RANK[candidateResolution.name] < ENV_RANK[baseResolution.name]
160
- ? candidateKube
161
- : ambientKube;
162
- }
163
- /**
164
- * Real grounding-mcp-backed ledger client. Exported for testing: the
165
- * `record` adapter's failure-surfacing path is exercised against a
166
- * bogus mcpCommand in tests/runtime/intercept-cli.test.ts.
167
- *
168
- * Connection discipline (2026-07-01 review, enforcement-subprocess-latency):
169
- * ONE lazily-opened grounding-mcp session serves every query and audit
170
- * record of the intercept invocation this client was constructed for,
171
- * instead of 2 fresh subprocess spawns per matching policy. On top,
172
- * `ledger_summary` returns the WHOLE session's entries (per-tag filtering
173
- * happens client-side in `intercept()`), so the K per-policy queries are
174
- * textually identical — they collapse into one cached round-trip per
175
- * sessionId. `runInterceptCli` calls `dispose()` when the intercept
176
- * completes; a caller constructing this client directly owns that call.
177
- */
178
- export function realLedgerClient(server, opts) {
179
- const command = Array.isArray(server.command)
180
- ? server.command
181
- : server.command.trim().split(/\s+/);
182
- const env = server.env ?? undefined;
183
- const timeoutMs = opts.ledgerTimeoutMs ?? server.health?.timeout_ms ?? 5_000;
184
- const stderr = opts.stderr ?? process.stderr;
185
- let session = null;
186
- const summaryCache = new Map();
187
- // One fresh-session retry for audit writes per client instance,
188
- // reserved for `deny-degraded` decisions (task f1aea826, AC4; review
189
- // 2026-08-08 tightened both the reservation and the budget). The
190
- // pooled session carries a session-wide timeout latch: a single slow
191
- // QUERY (the very condition that produces a blocking `deny-degraded`)
192
- // short-circuits every later call on the same session, including the
193
- // audit write for that decision — so the one row that most needs to
194
- // land was guaranteed to be lost. A fresh subprocess lands it whenever
195
- // grounding-mcp itself is healthy (the measured 2026-08-06 case: only
196
- // the artificial 1ms query budget latched; follow-up calls answered
197
- // fine).
198
- //
199
- // Bounds, stated precisely because the record runs on the deny path
200
- // BEFORE the envelope is emitted and any stall risks the outer
201
- // hook-timeout-is-allow contract: the retry is (a) reserved for
202
- // decisions whose outcome is `deny-degraded` — an allow/warn row
203
- // failing first must not consume it — (b) taken at most once per
204
- // client instance, and (c) run on a session whose OWN timeout is
205
- // `auditRetryTimeoutMs(timeoutMs)` = max(250ms, timeoutMs/4). Its two
206
- // calls (initialize + ledger_add) therefore add at most
207
- // 2 * max(250ms, timeoutMs/4) on top of the query's timeoutMs — that
208
- // is <=timeoutMs/2 (total <=1.5x) once timeoutMs >= 1s, while below
209
- // 1s the 250ms floor dominates and the ADD is bounded at 500ms
210
- // absolute (review 2026-08-08 round 2: at the audit's 1-100ms probe
211
- // settings that is a multiple of the query budget, but a trivial
212
- // absolute cost). The fresh session replaces the latched one, so
213
- // later audit writes in the same invocation inherit the working
214
- // transport (and its tighter budget). If the retry also fails, the
215
- // loud-stderr contract stands (audit degradation never blocks,
216
- // docs/okf matrix).
217
- const retryTimeoutMs = auditRetryTimeoutMs(timeoutMs);
218
- let recordRetryUsed = false;
219
- const getSession = () => (session ??= openLedgerSession({
220
- mcpCommand: command,
221
- ...(env && { mcpEnv: env }),
222
- timeoutMs,
223
- }));
224
- return {
225
- async query(_tag, sessionId) {
226
- // Cache the Promise (not the value) so concurrent queries for the
227
- // same session dedupe onto one in-flight round-trip. A degraded
228
- // result is cached too — deliberately: a first-query timeout
229
- // degrades ALL K policies off the one cached round-trip instead of
230
- // retrying per policy, which would reintroduce the fan-out. (What
231
- // a degraded evaluation MEANS is tier-dependent since task
232
- // f1aea826: warn policies stay non-blocking `warn-degraded`,
233
- // block/require_approval fail closed as `deny-degraded`.)
234
- // Staleness: our own
235
- // `record` rows are filtered out by the evaluator by design; an
236
- // EXTERNAL writer landing evidence mid-loop would make a later
237
- // policy deny where a fresh query would allow — a fail-closed,
238
- // ms-wide window resolved by the agent's retry (next intercept =
239
- // fresh session + fresh snapshot).
240
- let cached = summaryCache.get(sessionId);
241
- if (cached === undefined) {
242
- cached = getSession().querySummary({ sessionId });
243
- summaryCache.set(sessionId, cached);
244
- }
245
- return cached;
246
- },
247
- async record(decision, sessionId) {
248
- let result = await recordPolicyDecisionOnSession(getSession(), decision, sessionId);
249
- if (!result.ok &&
250
- decision.outcome === "deny-degraded" &&
251
- !recordRetryUsed) {
252
- // Latched-session recovery (bounds documented at retryTimeoutMs
253
- // above): drop the pooled session and retry ONCE over a fresh
254
- // subprocess with the tighter retry budget. Queries are
255
- // unaffected — their per-session summary promise is already
256
- // cached by the time any record runs.
257
- recordRetryUsed = true;
258
- session?.dispose();
259
- session = openLedgerSession({
260
- mcpCommand: command,
261
- ...(env && { mcpEnv: env }),
262
- timeoutMs: retryTimeoutMs,
263
- });
264
- result = await recordPolicyDecisionOnSession(session, decision, sessionId);
265
- }
266
- // A failed audit-write must not block the tool — the decision is
267
- // still applied — but it must not be silent either: an unrecorded
268
- // decision is invisible to `harness audit` / `explain --trace`.
269
- // Goes to stderr so Claude Code's stdout deny-JSON contract holds.
270
- if (!result.ok) {
271
- stderr.write(`harness policy intercept${hookSuffix(opts.hookName)}: audit-write failed for ` +
272
- `${decision.policyName}: ${result.reason ?? "unknown error"}\n`);
273
- }
274
- },
275
- dispose() {
276
- session?.dispose();
277
- },
278
- };
279
- }
280
- /**
281
- * Per-call budget of the audit-retry session (task f1aea826, review
282
- * round 2): a quarter of the ledger timeout with a 250ms floor (a fresh
283
- * node subprocess must cold-start + initialize within it). Exported as a
284
- * pure function so the FORMULA itself is pinned by a unit test — the
285
- * round-1 finding this bounds was precisely that an unbounded retry
286
- * reused the full budget, and nothing would go red if that regressed.
287
- */
288
- export function auditRetryTimeoutMs(timeoutMs) {
289
- return Math.max(250, Math.floor(timeoutMs / 4));
290
- }
291
- /**
292
- * Recognise a hook `command` string as an invocation of `harness policy
293
- * intercept`'s subcommand, regardless of how the leading interpreter /
294
- * binary token is spelled: a bare `harness policy intercept`, a node-path
295
- * form (`node /abs/path/dist/cli/index.js policy intercept`, e.g. a local
296
- * dev build), an `npx` form (`npx harness policy intercept`), a leading
297
- * inline env-var assignment (`FOO=bar harness policy intercept` — the
298
- * same shape `understanding-before-execution.ts`'s `wrap()` helper
299
- * prefixes pack-contributed commands with), or a trailing flag
300
- * (`harness policy intercept --hook <name>`, the tag
301
- * `generate-codex-config.ts#commandWithHookTag` appends).
302
- *
303
- * Every OTHER call site that needs to recognise this subcommand in this
304
- * codebase today (`generate-settings.ts#hookTimeoutSeconds`,
305
- * `generate-codex-config.ts#commandWithHookTag`) still compares
306
- * `command.trim() === "harness policy intercept"` VERBATIM — deliberately
307
- * left unchanged (task d20a7e0c scope: the hook-timeout projection
308
- * contract those two own is out of scope). For THEM a missed match only
309
- * changes a floor/flag on a command harness itself emits byte-for-byte,
310
- * so the single canonical spelling is enough. This matcher exists for a
311
- * caller (the validate/doctor hook-budget-vs-ledger-timeout guard, task
312
- * d20a7e0c) where under-recognition is a false NEGATIVE on a security
313
- * check against an OPERATOR-authored `manifest.hooks[]` entry, which is
314
- * under no obligation to spell the command exactly the way harness's own
315
- * generators do.
316
- *
317
- * Deliberately narrow, not a shell parser: matches the two literal words
318
- * `policy` and `intercept` as adjacent, whitespace-separated tokens,
319
- * preceded by start-of-string / whitespace / a path separator (so
320
- * `mypolicy intercept` inside one longer token cannot match) and followed
321
- * by whitespace, end-of-string, or a semicolon/quote character (`;`, `"`,
322
- * `'`) immediately abutting the word — the shape a chained shell command
323
- * (`harness policy intercept; echo done`) or an embedded `sh -c
324
- * "harness policy intercept"` wrapper produces, where nothing whitespace
325
- * separates `intercept` from the next token (review 2026-08-09, fix round
326
- * 1, finding 3). The quote arm accepts more than it strictly should: a
327
- * quote glued directly onto the word (`intercept'or'`) also matches, even
328
- * though a shell concatenates that into a different, longer identifier
329
- * (`interceptor`). This is a deliberate, harmless OVER-flag — at worst a
330
- * spurious validate/doctor diagnostic on an exotic command, never a missed
331
- * real hook — accepted because the same quote boundary is load-bearing for
332
- * the legitimate `sh -c "...intercept"` wrapper above. Still not
333
- * exhaustive: a trailing `)`, `|`, `&`, or
334
- * backtick immediately abutting the word (e.g. `$(harness policy
335
- * intercept)`) is a KNOWN, deliberately-unclosed gap — widening further
336
- * risks matching inside an unrelated longer token with no whitespace
337
- * boundary at all, and the shapes above already cover the realistic
338
- * manifest-authoring patterns this matcher exists for. A false negative
339
- * here degrades to the pre-existing verbatim behaviour (the hook is
340
- * simply not classified as ledger-consulting, so the margin check has
341
- * nothing to say about it) — not a runtime bypass; the dispatch this
342
- * check advises about is unaffected either way.
343
- */
344
- export function isPolicyInterceptCommand(command) {
345
- return /(?:^|[\s/\\])policy\s+intercept(?:\s|$|[;"'])/.test(command);
346
- }
347
- /**
348
- * Minimum `budget_ms` a blocking, ledger-consulting hook needs to clear
349
- * the worst-case wall time `realLedgerClient` (below) can spend before it
350
- * writes its own fail-closed decision — derived from that function's OWN
351
- * two round-trip shapes, not a measured-then-eyeballed constant. Both
352
- * halves are pinned against the real client in
353
- * tests/e2e/policy-intercept.test.ts ("hook-budget-ledger-margin
354
- * derivation (task d20a7e0c)").
355
- *
356
- * SCOPE, stated precisely (review 2026-08-09, fix round 1): this is the
357
- * worst case on the PURE-TIMEOUT HANG SHAPE — the shape where the
358
- * `query()` call that degrades does so by tripping `LedgerSession`'s own
359
- * session-wide `timedOut` latch (src/policies/ledger-client.ts), which is
360
- * what lets the retry sequence below start from an INSTANT first
361
- * `record()` attempt. A query() that instead degrades via a NON-timeout
362
- * path — `ledger_summary` coming back a genuine JSON-RPC error, an
363
- * unparseable payload, or a payload missing the `entries` shape
364
- * (`querySummary`'s non-timeout `degraded` branches in
365
- * src/policies/ledger-client.ts) — never sets that latch, so it falls
366
- * OUTSIDE this bound. See the KNOWN RESIDUAL paragraph at the end of this
367
- * comment; do not read this function, or the `checkHookBudgetLedgerMargin`
368
- * diagnostic it backs, as a blanket worst-case guarantee.
369
- *
370
- * `T` = the ledger's own per-call budget (`health.timeout_ms`, defaulted
371
- * to 5000 exactly like `realLedgerClient` itself). `R` =
372
- * `auditRetryTimeoutMs(T)`, the audit-retry session's own per-call budget.
373
- *
374
- * 1. query() — up to 2×T. `LedgerSession.querySummary`
375
- * (src/policies/ledger-client.ts) performs `ensureInitialized()`
376
- * (the `initialize` handshake, its own T-bounded wait) and, only
377
- * once THAT resolves, the actual `ledger_summary` `tools/call` (a
378
- * SEPARATE T-bounded wait). A ledger that is merely SLOW rather than
379
- * hard-down (the threat class docs/risk-gate.md and this file's own
380
- * `deny-degraded` diagnostics already name, distinct from an
381
- * instantly-refusing one) can answer `initialize` just under T and
382
- * still time out on the following `ledger_summary` — the case a
383
- * naive "1×T" assumption undercounts.
384
- *
385
- * 2. the deny-degraded audit retry — up to 3×R. On the pure-timeout
386
- * hang shape, the pooled session's `timedOut` latch (tripped by the
387
- * query()-side timeout itself) makes the FIRST `record()` attempt on
388
- * it instant (see `realLedgerClient`'s own doc comment above). The
389
- * ONE reserved retry then opens a FRESH session budgeted at R and
390
- * calls `recordPolicyDecisionOnSession` (src/runtime/ledger-
391
- * record.ts), which can spend up to three independent R-bounded
392
- * round-trips before it can report success or give up: the fresh
393
- * session's own `initialize` handshake, the first `ledger_add`
394
- * attempt at `type: "policy_decision"`, and — only reached when that
395
- * attempt comes back a genuine JSON-RPC error rather than a
396
- * transport timeout (an older/incompatible grounding-mcp rejecting
397
- * the newer type) — the legacy `type: "fact"` fallback `ledger_add`.
398
- *
399
- * query() must complete (or degrade) before any policy decision — and so
400
- * any record() — is computed (`intercept()`'s per-policy loop in
401
- * src/runtime/intercept.ts awaits `evaluateOnePolicy`, which calls
402
- * `ledger.query`, before it calls `ledger.record`), and the retry only
403
- * starts after the first record() attempt on the pooled session already
404
- * failed, so the two stages are SEQUENTIAL within one hook invocation:
405
- * worst case ON THE PURE-TIMEOUT HANG SHAPE = 2T + 3R.
406
- *
407
- * KNOWN RESIDUAL (review 2026-08-09, fix round 1): when `query()` instead
408
- * degrades via one of the NON-timeout branches named in the SCOPE
409
- * paragraph above, the pooled session's `timedOut` latch is never set, so
410
- * `record()`'s own first attempt is a REAL round-trip too — up to T for
411
- * the `type: "policy_decision"` `ledger_add` attempt, plus another up to
412
- * T for the legacy `type: "fact"` fallback if that attempt is itself
413
- * rejected non-timeout — BEFORE the 3R retry sequence even starts. Worst
414
- * case on this "error-then-hang" shape is therefore up to 4T + 3R, not
415
- * 2T + 3R: 23750ms at the shipped T=5000 default, against this
416
- * function's 13750ms. This formula is DELIBERATELY NOT raised to
417
- * 4T + 3R: doing so would fail `checkHookBudgetLedgerMargin` against the
418
- * shipped, reviewer-confirmed-clean fresh-init budget of 15000ms
419
- * (src/cli/init/templates.ts, task 7bf47554/#414 — out of scope here) at
420
- * the same T=5000 default, since 15000 < 23750. The guard this function
421
- * backs GUARANTEES delivery of the fail-closed verdict on the
422
- * pure-timeout hang shape, but NOT on this error-then-hang shape.
423
- * Unlike an earlier draft of this note, this residual is NOT confined to
424
- * one runtime projection: Claude Code and Codex both convert `budget_ms`
425
- * into the outer hook-kill seconds via the identical `hookTimeoutSeconds`
426
- * helper (src/cli/apply/generate-settings.ts, shared by
427
- * generate-codex-config.ts) since task 7bf47554/#414, so a manifest at
428
- * the shipped defaults is equally exposed on both — this was checked
429
- * against both generators' current source, not assumed.
430
- */
431
- export function requiredHookBudgetMs(ledgerTimeoutMs) {
432
- return 2 * ledgerTimeoutMs + 3 * auditRetryTimeoutMs(ledgerTimeoutMs);
433
- }
434
- function degradedLedgerClient(reason, stderr, hookName) {
435
- return {
436
- async query() {
437
- return { kind: "degraded", reason };
438
- },
439
- async record(decision) {
440
- // No transport exists, so no audit row can ever land. Stay silent
441
- // for non-blocking rows (a manifest without grounding-mcp would
442
- // otherwise emit one line per policy per tool event), but a
443
- // BLOCKING decision with no audit row must not also be invisible
444
- // (review 2026-08-08: previously a deny-degraded produced neither
445
- // a row nor an audit-write-failed line on this path). Blocking-ness
446
- // is the runtime's own isBlockingDecision — round 2 caught a third
447
- // hand-rolled copy of that predicate here.
448
- if (isBlockingDecision(decision)) {
449
- stderr.write(`harness policy intercept${hookSuffix(hookName)}: blocking decision for ` +
450
- `${decision.policyName} has NO audit row (${reason})\n`);
451
- }
452
- },
453
- };
454
- }
455
- /**
456
- * The hook event name a timed-out read carries when it names a hook event other
457
- * than PreToolUse, else `undefined`: the text is empty, truncated, not a JSON
458
- * object, names no event, or names PreToolUse. The caller keeps its pre-existing
459
- * continue behaviour only for an event this returns a name for.
460
- */
461
- function otherHookEventName(text) {
462
- let parsed;
463
- try {
464
- parsed = JSON.parse(text.trim());
465
- }
466
- catch {
467
- return undefined;
468
- }
469
- if (typeof parsed !== "object" || parsed === null)
470
- return undefined;
471
- const name = parsed.hook_event_name;
472
- return typeof name === "string" && name.length > 0 && name !== "PreToolUse" ? name : undefined;
473
- }
474
- export async function runInterceptCli(opts = {}) {
475
- const stdin = opts.stdin ?? process.stdin;
476
- const stdout = opts.stdout ?? process.stdout;
477
- const stderr = opts.stderr ?? process.stderr;
478
- const verbose = isVerboseEnabled(opts);
479
- const idleTimeoutMs = opts.stdinIdleTimeoutMs ?? STDIN_IDLE_TIMEOUT_MS;
480
- // Single pause-check options, shared by both checkPauseFromLoader calls.
481
- const pauseOpts = {
482
- loaderOpts: opts,
483
- hookLabel: "policy intercept",
484
- stderr,
485
- };
486
- if (opts.generatedDir !== undefined)
487
- pauseOpts.generatedDir = opts.generatedDir;
488
- if (opts.now !== undefined)
489
- pauseOpts.now = opts.now;
490
- const read = await readStdinBounded(stdin, idleTimeoutMs);
491
- if (read.timedOut && otherHookEventName(read.text) === undefined) {
492
- // A timed-out read leaves no event to judge, and this verb is the
493
- // PreToolUse gate entrypoint: continuing as an empty event turned a block
494
- // into an allow whenever the host stalled past the bound and then wrote a
495
- // complete gated event. The rendered hook command carries no event name, so
496
- // the event the read itself declares is the only signal there is: an event
497
- // naming another hook event keeps the continue path below, anything else
498
- // (nothing read, a truncated or unparseable prefix, a PreToolUse event, or
499
- // an object with no event name) is treated as the PreToolUse call this
500
- // verb is registered for. The operator pause still wins, and a paused run
501
- // allows exactly as it does after a read that closed.
502
- if (checkPauseFromLoader(pauseOpts).paused) {
503
- return { exitCode: 0, decisions: [], blocked: false };
504
- }
505
- const reason = stdinTimeoutBlockReason(idleTimeoutMs);
506
- stderr.write(`harness policy intercept${hookSuffix(opts.hookName)}: BLOCK: ${reason}\n`);
507
- stdout.write(`${stdinTimeoutBlockJson(reason)}\n`);
508
- return { exitCode: 0, decisions: [], blocked: true };
509
- }
510
- if (read.timedOut) {
511
- // Only an event that names another hook event gets here (see above), so the
512
- // text read is non-empty and parsed as that event below.
513
- stderr.write(`harness policy intercept${hookSuffix(opts.hookName)}: ${stdinTimeoutNote(read, idleTimeoutMs)}\n`);
514
- }
515
- const raw = read.text;
516
- let event;
517
- try {
518
- event = JSON.parse(raw.trim() || "{}");
519
- }
520
- catch (err) {
521
- stderr.write(`harness policy intercept${hookSuffix(opts.hookName)}: malformed event JSON: ${err.message}\n`);
522
- return { exitCode: 0, decisions: [], blocked: false };
523
- }
524
- // Pause sentinel — operator-only kill switch. Honoured BEFORE manifest
525
- // load so the lockout-recovery flow (where the manifest is exactly what
526
- // is broken) still respects an active pause. Mirrors the pack PreToolUse
527
- // hook: without this the policy gates kept firing while paused, i.e. the
528
- // very gates the kill switch most needs to silence (a wedged preflight /
529
- // grounding gate is the canonical reason an operator pauses at all).
530
- {
531
- if (checkPauseFromLoader(pauseOpts).paused) {
532
- return { exitCode: 0, decisions: [], blocked: false };
533
- }
534
- }
535
- let manifest;
536
- let manifestPath;
537
- try {
538
- if (opts.manifest) {
539
- manifest = opts.manifest;
540
- }
541
- else {
542
- const loaded = loadManifest(opts);
543
- manifest = loaded.manifest;
544
- manifestPath = loaded.resolved.base;
545
- }
546
- }
547
- catch (err) {
548
- stderr.write(`harness policy intercept${hookSuffix(opts.hookName)}: manifest load failed: ${err.message}\n`);
549
- return { exitCode: 0, decisions: [], blocked: false };
550
- }
551
- let ledger;
552
- if (opts.ledger) {
553
- ledger = opts.ledger;
554
- }
555
- else {
556
- const server = findGroundingMcp(manifest);
557
- ledger = server
558
- ? realLedgerClient(server, opts)
559
- : degradedLedgerClient("grounding-mcp not declared in manifest", stderr, opts.hookName);
560
- }
561
- // For the SESSION_ID builtin we keep the empty-string fallback rather
562
- // than routing through resolveSessionId (which lands on "default").
563
- // Routing through "default" would silently bake the literal string
564
- // into ledger_tag templates like `dogfood:${SESSION_ID}` →
565
- // `dogfood:default`, which would substring-match anything in the
566
- // shared "default" session — a worse failure mode than the current
567
- // empty substitution. The empty path produces tags like `dogfood:`
568
- // (substituteTemplate treats the registered "" value as a successful
569
- // substitution since the key is hasOwnProperty-present, so this is
570
- // NOT marked warn-degraded by the requires evaluator); the ledger
571
- // query for that fire also lands on the resolveSessionId-derived
572
- // session, so the asymmetry is acceptable in practice. Do NOT
573
- // "simplify" by routing this through resolveSessionId.
574
- // The env fallback is still applied so a Claude-Code-driven hook with
575
- // a stripped event can pick up the active session.
576
- const eventSessionId = typeof event.session_id === "string" ? event.session_id : undefined;
577
- // Env-var fallback chain: $CLAUDE_CODE_SESSION_ID first (the var Claude
578
- // Code actually exports), then legacy $CLAUDE_SESSION_ID. Matches the
579
- // resolver precedence in src/runtime/session-id.ts.
580
- const builtinSessionId = eventSessionId ??
581
- process.env.CLAUDE_CODE_SESSION_ID ??
582
- process.env.CLAUDE_SESSION_ID ??
583
- "";
584
- // REPO / BRANCH are derived from the tool event's cwd so that per-repo
585
- // and per-branch ledger tags (`preflight:${REPO}`, `preflight:${BRANCH}`)
586
- // actually namespace — they were previously read from HARNESS_REPO /
587
- // HARNESS_BRANCH env vars that nothing sets, collapsing every tag to the
588
- // literal `preflight:`. An explicit env var still wins: it is the
589
- // operator's deliberate override of the derived value.
590
- //
591
- // `cwdGitContext` below is the DEFAULT / FALLBACK builtins basis, not
592
- // the only one used anymore (task `98ad072f`, T-003). A command that
593
- // explicitly names a different repository (`git -C <B>`, `env -C <B>`,
594
- // a leading `cd <B> &&`) still resolves `builtins.REPO`/`.BRANCH` (and
595
- // `currentHeadSha`, below) from THIS cwd for the majority of policies —
596
- // any policy whose `requires:` doesn't reference `${REPO}`/`${BRANCH}`/
597
- // `at_head` stays exactly on this cwd-only path, as does any policy
598
- // whose own trigger-satisfying segment names no foreign target. Only a
599
- // MATCHED policy that (a) uses `${REPO}`/`${BRANCH}`/`at_head` and (b)
600
- // whose own `bash_match`-satisfying segment names a different,
601
- // resolvable git repository gets a per-policy override — resolved
602
- // lazily, inside `intercept()`, from the `commandSegments` view threaded
603
- // below, never from this `cwdGitContext`. A prior attempt (run
604
- // `2026-07-27-gate-target-repo-resolution`) tried a single global
605
- // `targetDir` per event instead and was removed before shipping: three
606
- // consecutive review rounds each found a different way one event-wide
607
- // target leaked into the wrong evaluation (Risk Gate risk
608
- // declassification; a push-gate multi-invocation fail-open; a non-git
609
- // gated verb sharing a chain with a targeted git call). This
610
- // `cwdGitContext` variable, and the `builtins` object built from it
611
- // below, remain exactly what they were before this task — the resolved
612
- // fallback every policy without a foreign attribution still uses,
613
- // including the Risk Gate's own `resolverGit` further down, which
614
- // this task does not touch at all.
615
- const cwd = resolvePolicyCwd(event, opts.codexCommandCwd);
616
- const cwdGitContext = resolveGitContext(cwd);
617
- // Computed once here (not per-policy) so `intercept()` below can thread
618
- // the SAME `NormalizedCommand` into every `bash_match` trigger check
619
- // instead of each one recomputing it.
620
- //
621
- // Fix round 1, finding F4: derive this from `extractShellCommand(event)`
622
- // — the SAME function `policyMatchesEvent`'s `bash_match` branch itself
623
- // calls (`src/runtime/tool-name-aliases.ts`) — instead of the narrower
624
- // `event.tool_name === "Bash"` + `readBashCommand(event.tool_input)` pair
625
- // this used before, which only ever looked at `tool_input.command`. A
626
- // Codex shell event (`tool_name: "shell"` / `"exec_command"` /
627
- // `"functions.exec_command"`, which `expandToolNameAliases` maps onto
628
- // `match: "Bash"` policies) commonly carries its command under
629
- // `raw_input`/`input` rather than `tool_input`, or under a `cmd` key —
630
- // `extractShellCommand` reads all of those, `readBashCommand` read none
631
- // of them. Before this fix such an event still reached the `bash_match`
632
- // branch (via alias expansion) but with no precomputed value, so
633
- // `policyMatchesEvent`'s per-policy fallback ran BOTH normalisation
634
- // passes once PER MATCHING POLICY instead of once per event.
635
- // `isCodexShellTool` scopes the precompute to exactly the tool-name set
636
- // `SHELL_ALIASES` expands (Bash plus the three Codex shell aliases), so
637
- // this does not start normalising unrelated tool calls that merely
638
- // happen to carry a `command`/`cmd`-named argument for some other
639
- // purpose.
640
- //
641
- // Fix round 2: that set is NOT, as this comment previously claimed, the
642
- // only way a `match: "Bash"` policy's `bash_match` branch can be
643
- // reached. `trigger.match` is a SUBSTRING test over the expanded alias
644
- // list, so `BashOutput` and `KillBash` — real Claude Code tool names —
645
- // satisfy `match: "Bash"` too and do reach that branch (measured: both
646
- // return `policyMatchesEvent === true`). They simply fall through to
647
- // the per-policy fallback here, exactly as they did before this change,
648
- // so nothing is gated differently; the precompute just does not
649
- // de-duplicate for them.
650
- //
651
- // This also makes the precomputed value and the matcher's own value
652
- // PROVABLY consistent for the common case: both are now the same
653
- // `extractShellCommand(event)` call, so the latent
654
- // `raw_input`/`input`/`cmd` divergence `InterceptOptions.normalizedCommand`'s
655
- // own JSDoc warns about (nothing verifies the threaded value came from
656
- // the event's real command) can no longer actually diverge for a
657
- // `tool_name` in `SHELL_ALIASES` — the one production call site
658
- // (`runInterceptCli`) derives both from the identical event via the
659
- // identical function. It remains an unenforced INVARIANT, not a checked
660
- // one: nothing stops a future second caller from threading a mismatched
661
- // value in, and `event.tool_input`/`raw_input`/`input` could still
662
- // change between the moment this is read and any later read of the same
663
- // event object, however unlikely in practice.
664
- //
665
- // TWO behavioural differences, both checked (fix round 2 corrects the
666
- // prior wording, which named only the first):
667
- //
668
- // 1. `readBashCommand` required a NON-EMPTY string; `extractShellCommand`
669
- // accepts an empty one. An empty command now becomes
670
- // `bashCommand = ""` instead of `null`, so `normalizedCommand`
671
- // becomes a defined `normalizeCommand("") = {normalized: "",
672
- // truncated: false, ...}` instead of staying `undefined` —
673
- // behaviourally identical either way (a pure function of the empty
674
- // string, now computed once instead of falling back per policy), and
675
- // `truncated` can never be `true` for an empty string, so the
676
- // `MAX_NORMALIZE_LENGTH` stderr line just below cannot fire from it.
677
- //
678
- // 2. That stderr line's SCOPE widened. Under the old ternary
679
- // `bashCommand` was unconditionally `null` for any non-`Bash`
680
- // `tool_name`, so `normalizedCommand` stayed `undefined` and the
681
- // guard could never fire for a Codex shell event, nor for a command
682
- // carried under `raw_input`/`input` or a `cmd` key. Measured at
683
- // HEAD: a `tool_name: "shell"` event with a 100,001-char
684
- // `raw_input.cmd` now emits the line. The direction is
685
- // safety-positive — a previously SILENT loss of normalised-form
686
- // coverage on those events is now reported — but it is a real change
687
- // in observable output, so it is named here rather than implied.
688
- const bashCommand = isCodexShellTool(event.tool_name) ? extractShellCommand(event) : null;
689
- const normalizedCommand = bashCommand === null ? undefined : normalizeCommand(bashCommand);
690
- // Per-segment view for `intercept()`'s per-policy attribution (task
691
- // `98ad072f`, T-003) — deferred behind a memoised thunk, mirroring
692
- // `ampNormalizedCommandThunk` below (D-015 fix round, run
693
- // 2026-08-02-per-repo-gate-scoping-redesign, correcting this comment's
694
- // own prior claim). CORRECTED: this is NOT "the same class as
695
- // `normalizeCommand`'s own eager call just above" — `segmentViewOf` runs
696
- // its OWN full segmentation walk over `bashCommand` (`command-
697
- // normalize.ts`'s `segmentAndCanonicalize`, a SEPARATE call from the one
698
- // `normalizeCommand` just made on the SAME string), and computing it
699
- // unconditionally, for every Bash event, regardless of whether any
700
- // policy in the manifest even uses `${REPO}`/`${BRANCH}`/`at_head`, was
701
- // measured as a real, avoidable second pass — +206% at
702
- // `MAX_NORMALIZE_LENGTH` (2.73 → 8.36 ms; see `segmentViewOf`'s own doc
703
- // comment in `command-normalize.ts`). Threading it as a self-memoising
704
- // thunk instead — the exact shape `ampNormalizedCommandThunk` below
705
- // already uses for the same reason — lets `intercept()`'s
706
- // `usesPerRepoBuiltins` gate decide whether the walk happens at all: a
707
- // manifest with none of those policies never calls `segmentViewOf`, and
708
- // a manifest that DOES still only pays the cost once per event, on
709
- // first use, not once per Bash event regardless of need.
710
- let commandSegmentsCache;
711
- const commandSegmentsThunk = bashCommand === null
712
- ? undefined
713
- : () => {
714
- if (commandSegmentsCache === undefined) {
715
- commandSegmentsCache = segmentViewOf(bashCommand);
716
- }
717
- return commandSegmentsCache;
718
- };
719
- // Memoised thunk for the ampersand-aware SECOND normalisation pass
720
- // (task aabbad63). Unlike `normalizedCommand` above — eagerly computed
721
- // once per Bash event, because `policyMatchesEvent`'s first two arms
722
- // may need it on every raw-miss — the amp-aware pass is consulted only
723
- // as a THIRD, additional arm, and only when a policy's regex has
724
- // already missed BOTH the raw command and `normalizedCommand`; most
725
- // events never reach it at all. Computing it eagerly here would pay
726
- // its segmentation cost on the common path for every single Bash call
727
- // inside the ~1000ms hook budget, whether or not any policy ends up
728
- // needing it. Wrapping it in a lazily-evaluated, self-memoising thunk
729
- // instead lets `policyMatchesEvent` call it from inside the manifest-
730
- // wide `matching` loop below (once per policy that still hasn't
731
- // matched) while the actual segmentation work happens AT MOST ONCE per
732
- // event — the same "computed once per event, not once per policy"
733
- // discipline `normalizedCommand` already has, just deferred until
734
- // first use instead of forced up front.
735
- let ampNormalizedCommandCache;
736
- const ampNormalizedCommandThunk = bashCommand === null
737
- ? undefined
738
- : () => (ampNormalizedCommandCache ??= normalizeCommandAmpAware(bashCommand));
739
- // Memoised thunk for the quote-aware THIRD normalisation pass (task
740
- // cf3dff51) — the SAME shape as `ampNormalizedCommandThunk` immediately
741
- // above, for the same reason: `policyMatchesEvent`'s fourth arm calls
742
- // this only when a policy's regex has already missed the raw command,
743
- // `normalizedCommand`, AND the amp-aware pass, so most events never
744
- // reach it at all; computing it eagerly here would pay its
745
- // segmentation cost on the common path for every Bash call regardless
746
- // of whether any policy ends up needing it.
747
- let quoteNormalizedCommandCache;
748
- const quoteNormalizedCommandThunk = bashCommand === null
749
- ? undefined
750
- : () => (quoteNormalizedCommandCache ??= normalizeCommandQuoteAware(bashCommand));
751
- // Above `MAX_NORMALIZE_LENGTH`, `normalizeCommand` skips normalisation
752
- // entirely and `truncated` comes back `true`. Raw matching still
753
- // applies regardless (`policyMatchesEvent`'s raw-OR-normalised-OR-amp-
754
- // normalised-OR-quote-normalised construction), so an oversized command
755
- // only loses the ADDITIONAL normalised-form coverage — but that skip
756
- // must not be silent, so one stderr line reports it here, keeping the
757
- // module itself pure and I/O-free. This one line also covers the
758
- // amp-aware SECOND pass (`normalizeCommandAmpAware`, task `aabbad63`,
759
- // fix round 1 finding F6) and the quote-aware THIRD pass
760
- // (`normalizeCommandQuoteAware`, task `cf3dff51`): both share the
761
- // identical `MAX_NORMALIZE_LENGTH` bound over the identical
762
- // `bashCommand`, so neither one's own `truncated` can ever disagree
763
- // with `normalizedCommand`'s for this call — no separate stderr line
764
- // names either pass's own skip, nor does one need to.
765
- if (normalizedCommand?.truncated === true) {
766
- stderr.write(`harness policy intercept${hookSuffix(opts.hookName)}: Bash command exceeds ${MAX_NORMALIZE_LENGTH} chars; normalised-form matching skipped for this call (raw match only)\n`);
767
- }
768
- const builtins = {
769
- SESSION_ID: builtinSessionId,
770
- REPO: process.env.HARNESS_REPO ?? cwdGitContext.repo,
771
- BRANCH: process.env.HARNESS_BRANCH ?? cwdGitContext.branch,
772
- TOOL_NAME: typeof event.tool_name === "string" ? event.tool_name : "",
773
- CWD: cwd,
774
- };
775
- // Risk Gate ambient context — resolved only when the manifest
776
- // declares a `when:`-bearing policy, so a pure Phase-4 manifest pays
777
- // no kube-config read. `intercept()` applies the same gate; this just
778
- // avoids the host I/O when nothing would consume it.
779
- let riskContext;
780
- if (manifest.policies.some((p) => p.when !== undefined)) {
781
- const kube = opts.kubeContext !== undefined || opts.kubeNamespace !== undefined
782
- ? {
783
- context: opts.kubeContext ?? "",
784
- namespace: opts.kubeNamespace ?? "",
785
- }
786
- : resolveKubeContext();
787
- // Three POSIX Bash idioms (`VAR=value command`, `cd <path> &&
788
- // command`, and — since task 341e024b — `git switch|checkout
789
- // <branch> && command`) were invisible to the resolver before: the
790
- // env resolver read `process.env` and the branch resolver read
791
- // `.git/HEAD` under the hook's starting cwd, so a prod signal
792
- // smuggled through any of the three idioms silently passed the
793
- // gate. Parse the leading prefix once from the Bash command and
794
- // merge the result into the resolver inputs only. ${CWD} always
795
- // names the hook's own cwd; ${REPO}/${BRANCH} (`cwdGitContext`
796
- // above, feeding `builtins`) are cwd-only too — see the comment
797
- // above `cwdGitContext`'s declaration. The Risk Gate resolver's git
798
- // base, `resolverGit` (risk-envelope-enrichment.ts), is its own narrow parse: it
799
- // recognises only a leading `cd`, never `git -C` / `--work-tree` /
800
- // `--git-dir` / `env -C`. Bottom line: `resolverGit` must never read
801
- // anything other than `cwdGitContext`, or the leading-`cd` target
802
- // within THIS same narrow parse — this feeds `when:`-clause risk
803
- // classification, not ledger-tag namespacing.
804
- //
805
- // G5 (review round 2, 2026-07-27): that leading-`cd` parse is itself
806
- // still a live declassification lever, PRE-EXISTING and NOT changed
807
- // by this run — identical on the shipped 0.42.0 control, so this is
808
- // documentation of a known asymmetry, not a new gap. From a repo on
809
- // `main`, `rm -rf /data` BLOCKS, but `cd <repo-on-feature/x> && rm -rf
810
- // /data` is ALLOWED: the `cd` target makes the resolver read
811
- // feature/x's branch instead of cwd's `main`, so `branch_patterns:
812
- // [main]` no longer matches. The asymmetry cuts both ways and neither
813
- // direction is fixed here: a leading `cd` can make a prod signal
814
- // VISIBLE that cwd alone would have missed (cwd on a feature branch,
815
- // `cd` into a repo actually on `main`) just as readily as it can make
816
- // one INVISIBLE (cwd on `main`, `cd` into a repo on a feature
817
- // branch, as in the example above) — `resolverGit` trusts whichever
818
- // of the two the command happens to name, with no independent check
819
- // that the `cd` target is the repo the destructive command actually
820
- // runs against. The `git switch`/`checkout` merge in that helper
821
- // (`gitForRisk`, `applyBranchSwitchUpgrade`, same module) deliberately does
822
- // NOT share this bidirectional-risk shape: it is a SEPARATE,
823
- // upgrade-only step layered on top of `resolverGit`, not a change to
824
- // `resolverGit` itself — a switch away from a production branch can
825
- // never downgrade what `resolverGit` (cwd- or cd-based) already
826
- // resolved.
827
- const riskUser = safeOs(() => os.userInfo().username);
828
- const riskHost = safeOs(() => os.hostname());
829
- // The leading-prefix parse and the three merges it feeds (inline
830
- // `VAR=value` env, leading `cd`, leading `git switch|checkout`)
831
- // live in `resolveBashPrefixEnrichment`, shared (via
832
- // `src/cli/enriched-event.ts`) with `harness explain-policy`,
833
- // `resolve-env`, `test-risk` and `explain-action` so the debug verbs
834
- // cannot drift from this hook.
835
- // The kubectl-target merge below stays hook-only.
836
- const { bashPrefix, riskBashCommand, git: gitForRisk, env: resolverEnv } = resolveBashPrefixEnrichment({
837
- event,
838
- manifest,
839
- cwd,
840
- cwdGitContext,
841
- env: opts.env ?? process.env,
842
- kubeContext: kube.context,
843
- kubeNamespace: kube.namespace,
844
- user: riskUser,
845
- host: riskHost,
846
- now: opts.now,
847
- });
848
- // Explicit `--context`/`--namespace`/`-n` on a `kubectl ...`
849
- // invocation (task a7eb1a71). Fed the REMAINDER after `bashPrefix`
850
- // consumed a leading `cd <path> &&` / `VAR=value` / `git switch
851
- // <branch> &&` prefix, not the raw command, so a wrapped invocation
852
- // like `cd /tmp && kubectl ... --context x` is still covered; see
853
- // `parseKubectlTarget`'s own doc comment for the narrow,
854
- // command-head-anchored scope this is otherwise kept to, and for
855
- // the forms still NOT unwrapped (sudo/time/env wrappers, a kubectl
856
- // that is not the first chained segment, a piped kubectl).
857
- const kubectlSubject = riskBashCommand === null ? null : riskBashCommand.slice(bashPrefix?.remainderStart ?? 0);
858
- const kubectlTarget = kubectlSubject === null ? null : parseKubectlTarget(kubectlSubject);
859
- // CONFLICT PRIORITY (task a7eb1a71): an explicit kubectl
860
- // --context/--namespace/-n is merged on top of the AMBIENT kube
861
- // state (`kube`), on top of `gitForRisk` above (the git context
862
- // AFTER the branch-switch upgrade), upgrade-only, exactly the same
863
- // asymmetric shape the branch-switch merge itself uses. Command
864
- // text can only raise the resolved environment toward production,
865
- // never lower an already-resolved production; see
866
- // `applyKubeTargetUpgrade`'s own doc comment for the measured
867
- // downgrade this fixes.
868
- const kubeForRisk = applyKubeTargetUpgrade(event, manifest, cwd, gitForRisk, { context: kube.context, namespace: kube.namespace }, kubectlTarget, resolverEnv, riskUser, riskHost, opts.now);
869
- riskContext = {
870
- git: gitForRisk,
871
- cwd,
872
- user: riskUser,
873
- host: riskHost,
874
- env: resolverEnv,
875
- kubeContext: kubeForRisk.context,
876
- kubeNamespace: kubeForRisk.namespace,
877
- };
878
- }
879
- let result;
880
- try {
881
- result = await intercept({
882
- manifest,
883
- event,
884
- ledger,
885
- builtins,
886
- stderr,
887
- ...(opts.ledgerTimeoutMs !== undefined && { ledgerTimeoutMs: opts.ledgerTimeoutMs }),
888
- ...(opts.now && { now: opts.now }),
889
- ...(cwdGitContext.sha.length > 0 && { currentHeadSha: cwdGitContext.sha }),
890
- ...(riskContext && { riskContext }),
891
- ...(normalizedCommand && { normalizedCommand }),
892
- ...(ampNormalizedCommandThunk && { ampNormalizedCommandThunk }),
893
- ...(quoteNormalizedCommandThunk && { quoteNormalizedCommandThunk }),
894
- ...(commandSegmentsThunk && { commandSegmentsThunk }),
895
- ...(process.env.HARNESS_REPO !== undefined && { repoOverridden: true }),
896
- ...(process.env.HARNESS_BRANCH !== undefined && { branchOverridden: true }),
897
- });
898
- }
899
- finally {
900
- // Tear down the pooled grounding-mcp session (no-op for injected test
901
- // doubles and the degraded client). All queries/records are awaited
902
- // inside intercept(), so nothing is in flight here.
903
- ledger.dispose?.();
904
- }
905
- // Stage the session id for a later arg-less `harness approve risk`
906
- // whenever the FIRST blocking decision is `require_approval`. Mirrors
907
- // the Understanding Gate hook (src/cli/pack/hook-pre-tool-use.ts):
908
- // the producer side knows the live session id (it just received it on
909
- // the hook event), but `harness approve risk` runs from the operator's
910
- // `!`-shell where `$CLAUDE_SESSION_ID` is unset, so it has to read the
911
- // marker. `deny` and `deny-degraded` decisions are deliberately not
912
- // staged: `harness approve risk` cannot unblock either (a deny is a
913
- // real verdict, a deny-degraded means the evidence source is
914
- // unreadable, so no approval tag could even be read), and writing a
915
- // marker the verb cannot act on would just lie about the
916
- // recoverability of the block. The "first blocking decision" check
917
- // (rather than `.some()`) matters when two `when:`-bearing policies
918
- // fire on the same event: the runtime's `intercept()` picks the first
919
- // blocking decision (its `isBlockingDecision`, imported here so the
920
- // two surfaces cannot drift — review 2026-08-08), so a blocking-first
921
- // / `require_approval`-second order produces an unrecoverable block;
922
- // we mustn't stage in that case either.
923
- //
924
- // Best-effort: a staging-write failure must never escalate a gate block
925
- // into a thrown hook error. `resolveGeneratedDir` is skipped (along
926
- // with the write) when no manifest path could be resolved — tests
927
- // exercise the policy logic with injected manifests and no on-disk
928
- // path, and the test path is also the only place an empty
929
- // `eventSessionId` is interesting.
930
- const firstBlocking = result.decisions.find(isBlockingDecision);
931
- if (result.blockJson &&
932
- eventSessionId !== undefined &&
933
- eventSessionId.length > 0 &&
934
- firstBlocking?.outcome === "require_approval" &&
935
- // An empty-identifier decision never reads the ledger, so no approval
936
- // tag could unblock it: staging a marker `harness approve risk` cannot
937
- // act on would misstate the recoverability of the block.
938
- firstBlocking.emptyIdentifier === undefined) {
939
- const generatedDir = opts.generatedDir
940
- ?? (manifestPath !== undefined
941
- ? resolveGeneratedDir({
942
- ...(opts.homeDir !== undefined ? { homeDir: opts.homeDir } : {}),
943
- manifestPath,
944
- })
945
- : undefined);
946
- if (generatedDir !== undefined) {
947
- try {
948
- writePendingApproval(generatedDir, eventSessionId);
949
- }
950
- catch {
951
- /* best-effort; the block below proceeds regardless */
952
- }
953
- }
954
- }
955
- if (result.blockJson) {
956
- stdout.write(`${JSON.stringify(result.blockJson)}\n`);
957
- }
958
- // No-match diagnostic. Always (not gated on --verbose) when the
959
- // manifest has at least one policy but none of them matched this
960
- // event. Catches the common debug footgun where an operator probes
961
- // `harness policy intercept` by hand and forgets `hook_event_name`,
962
- // making the engine return exit 0 + empty stdout. The probe then
963
- // looks like "policy did not load", when in fact the trigger filter
964
- // simply rejected the input. Emits to stderr so the Claude Code
965
- // hook contract on stdout is preserved.
966
- if (result.decisions.length === 0 && manifest.policies.length > 0) {
967
- stderr.write(formatNoMatchHint(event, manifest, opts.hookName));
968
- }
969
- // Degraded-deny operator hint. Default-verbosity, ONE line per event:
970
- // the envelope deliberately tells the agent only to involve the
971
- // operator, and the full diagnostic (which names the fail_open
972
- // opt-out) is verbose-only — without this line an operator whose
973
- // fleet wedges on a degraded ledger has no default-verbosity pointer
974
- // at all (review 2026-08-08, round 2). Two deliberate properties:
975
- // - It carries the decision's OWN reason (sanitised: it can embed
976
- // grounding-mcp subprocess output) instead of asserting a ledger
977
- // fault — deny-degraded has five causes and only one of them is
978
- // the ledger; a hardcoded "ledger unreadable" sent operators of
979
- // the other four down a false doctor-path (round 3, medium).
980
- // - It does NOT name the opt-out knob: hook stderr can be surfaced
981
- // to the model in some harness configurations, so the default
982
- // channel points at the operator docs and the verbose diagnostic
983
- // instead of carrying the disable recipe (deviation from round
984
- // 2's suggested fix, recorded in the run's 03-decisions.md).
985
- // Suppressed under --verbose, where formatDecisionDiagnostic already
986
- // prints the same reason with more context.
987
- const firstDegradedDeny = result.decisions.find((d) => d.outcome === "deny-degraded");
988
- if (firstDegradedDeny && !verbose) {
989
- stderr.write(`harness policy intercept${hookSuffix(opts.hookName)}: ${firstDegradedDeny.policyName}: ` +
990
- `deny-degraded (evidence could not be evaluated: ` +
991
- `${sanitizeEnvelopeReason(firstDegradedDeny.reason)}); failing closed. ` +
992
- `Operator recovery: check the reason above, then harness doctor if it names ` +
993
- `the ledger; details and the availability opt-out: docs/risk-gate.md ` +
994
- `("Degraded mode") or rerun with HARNESS_POLICY_VERBOSE=1\n`);
995
- }
996
- if (verbose) {
997
- for (const decision of result.decisions) {
998
- if (decision.outcome === "allow")
999
- continue;
1000
- stderr.write(formatDecisionDiagnostic(decision, opts.hookName));
1001
- }
1002
- }
1003
- return {
1004
- exitCode: 0,
1005
- decisions: result.decisions,
1006
- blocked: result.blockJson !== null,
1007
- };
1008
- }
1009
- function formatNoMatchHint(event, manifest, hookName) {
1010
- const observedEvent = typeof event.hook_event_name === "string" && event.hook_event_name.length > 0
1011
- ? `"${event.hook_event_name}"`
1012
- : "(missing)";
1013
- const observedTool = typeof event.tool_name === "string" && event.tool_name.length > 0
1014
- ? `"${event.tool_name}"`
1015
- : "(missing)";
1016
- const registeredEvents = Array.from(new Set(manifest.policies.map((p) => p.trigger.event))).sort();
1017
- return (`harness policy intercept${hookSuffix(hookName)}: no policy matched event ` +
1018
- `hook_event_name=${observedEvent} tool_name=${observedTool} ` +
1019
- `(registered policy events: ${registeredEvents.join(", ")}). ` +
1020
- `If probing by hand, ensure stdin includes hook_event_name (e.g. "PreToolUse" for tool gates).\n`);
1021
- }
1022
- //# sourceMappingURL=intercept.js.map