@lannguyensi/harness 0.63.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 (702) hide show
  1. package/CHANGELOG.md +90 -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 -338
  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 -705
  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 -378
  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 +15 -2607
  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 -243
  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/register-gate-group.d.ts +5 -0
  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 +5 -0
  145. package/dist/cli/register-operator-lifecycle.js +20 -0
  146. package/dist/cli/register-operator-lifecycle.js.map +1 -0
  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-setup-group.d.ts +5 -0
  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.d.ts +5 -0
  160. package/dist/cli/remove/register.js +25 -0
  161. package/dist/cli/remove/register.js.map +1 -0
  162. package/dist/cli/shell-quote.d.ts +7 -0
  163. package/dist/cli/shell-quote.js +17 -0
  164. package/dist/cli/shell-quote.js.map +1 -0
  165. package/dist/cli/uninstall/index.d.ts +4 -2
  166. package/dist/cli/uninstall/index.js +15 -7
  167. package/dist/cli/uninstall/index.js.map +1 -1
  168. package/dist/cli/uninstall/register.d.ts +5 -0
  169. package/dist/cli/uninstall/register.js +171 -0
  170. package/dist/cli/uninstall/register.js.map +1 -0
  171. package/dist/cli/uninstall/snapshot.d.ts +15 -0
  172. package/dist/cli/uninstall/snapshot.js +38 -0
  173. package/dist/cli/uninstall/snapshot.js.map +1 -1
  174. package/dist/cli/validate/checks.d.ts +0 -77
  175. package/dist/cli/validate/checks.js +3 -1049
  176. package/dist/cli/validate/checks.js.map +1 -1
  177. package/dist/cli/validate/index.js +19 -15
  178. package/dist/cli/validate/index.js.map +1 -1
  179. package/dist/index.d.ts +0 -3
  180. package/dist/index.js +0 -3
  181. package/dist/index.js.map +1 -1
  182. package/dist/io/claude-mcp.d.ts +1 -1
  183. package/dist/io/claude-mcp.js +27 -14
  184. package/dist/io/claude-mcp.js.map +1 -1
  185. package/dist/io/harness-lock.js +2 -2
  186. package/dist/io/harness-lock.js.map +1 -1
  187. package/dist/io/lock.d.ts +0 -26
  188. package/dist/io/lock.js +10 -27
  189. package/dist/io/lock.js.map +1 -1
  190. package/dist/io/merge-settings.js +6 -4
  191. package/dist/io/merge-settings.js.map +1 -1
  192. package/dist/io/project-name.d.ts +22 -39
  193. package/dist/io/project-name.js +25 -42
  194. package/dist/io/project-name.js.map +1 -1
  195. package/dist/io/read-regular-file.d.ts +103 -40
  196. package/dist/io/read-regular-file.js +196 -35
  197. package/dist/io/read-regular-file.js.map +1 -1
  198. package/dist/io/restart-hints.js +3 -6
  199. package/dist/io/restart-hints.js.map +1 -1
  200. package/dist/io/version-compare.d.ts +1 -3
  201. package/dist/io/version-compare.js +1 -3
  202. package/dist/io/version-compare.js.map +1 -1
  203. package/dist/io/write-regular-file.d.ts +30 -0
  204. package/dist/io/write-regular-file.js +85 -0
  205. package/dist/io/write-regular-file.js.map +1 -0
  206. package/dist/overrides/index.d.ts +1 -0
  207. package/dist/overrides/index.js +1 -0
  208. package/dist/overrides/index.js.map +1 -1
  209. package/dist/overrides/merge-layers.d.ts +43 -0
  210. package/dist/overrides/merge-layers.js +76 -0
  211. package/dist/overrides/merge-layers.js.map +1 -0
  212. package/dist/overrides/merge.d.ts +16 -0
  213. package/dist/overrides/merge.js +21 -8
  214. package/dist/overrides/merge.js.map +1 -1
  215. package/dist/policy-packs/builtin/branch-protection-runtime.d.ts +4 -69
  216. package/dist/policy-packs/builtin/branch-protection-runtime.js +12 -84
  217. package/dist/policy-packs/builtin/branch-protection-runtime.js.map +1 -1
  218. package/dist/policy-packs/builtin/branch-protection.d.ts +7 -30
  219. package/dist/policy-packs/builtin/branch-protection.js +56 -89
  220. package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
  221. package/dist/policy-packs/config-check.d.ts +7 -5
  222. package/dist/policy-packs/config-check.js +2 -2
  223. package/dist/policy-packs/config-check.js.map +1 -1
  224. package/dist/policy-packs/expand.d.ts +1 -3
  225. package/dist/policy-packs/expand.js +5 -42
  226. package/dist/policy-packs/expand.js.map +1 -1
  227. package/dist/policy-packs/index.d.ts +3 -3
  228. package/dist/policy-packs/index.js +2 -2
  229. package/dist/policy-packs/index.js.map +1 -1
  230. package/dist/policy-packs/registry.d.ts +9 -38
  231. package/dist/policy-packs/registry.js +5 -65
  232. package/dist/policy-packs/registry.js.map +1 -1
  233. package/dist/policy-packs/runtime.d.ts +0 -4
  234. package/dist/policy-packs/runtime.js +9 -22
  235. package/dist/policy-packs/runtime.js.map +1 -1
  236. package/dist/policy-packs/types.d.ts +0 -19
  237. package/dist/policy-packs/ux-compare.d.ts +1 -11
  238. package/dist/policy-packs/ux-compare.js +6 -42
  239. package/dist/policy-packs/ux-compare.js.map +1 -1
  240. package/dist/policy-packs/ux-drift-check.d.ts +5 -5
  241. package/dist/policy-packs/ux-drift-check.js +17 -22
  242. package/dist/policy-packs/ux-drift-check.js.map +1 -1
  243. package/dist/policy-packs/version-check.d.ts +5 -26
  244. package/dist/policy-packs/version-check.js +19 -74
  245. package/dist/policy-packs/version-check.js.map +1 -1
  246. package/dist/probes/memory.d.ts +1 -1
  247. package/dist/probes/memory.js +1 -1
  248. package/dist/probes/memory.js.map +1 -1
  249. package/dist/runtime/agent-facing.d.ts +9 -0
  250. package/dist/runtime/agent-facing.js +28 -14
  251. package/dist/runtime/agent-facing.js.map +1 -1
  252. package/dist/runtime/bounded-text.d.ts +15 -0
  253. package/dist/runtime/bounded-text.js +27 -0
  254. package/dist/runtime/bounded-text.js.map +1 -0
  255. package/dist/runtime/git-branch.d.ts +125 -0
  256. package/dist/runtime/git-branch.js +381 -0
  257. package/dist/runtime/git-branch.js.map +1 -0
  258. package/dist/runtime/hermetic-spawn-guard.d.ts +2 -2
  259. package/dist/runtime/hermetic-spawn-guard.js +24 -34
  260. package/dist/runtime/hermetic-spawn-guard.js.map +1 -1
  261. package/dist/runtime/home-dir.js +3 -2
  262. package/dist/runtime/home-dir.js.map +1 -1
  263. package/dist/runtime/tool-name-aliases.d.ts +0 -6
  264. package/dist/runtime/tool-name-aliases.js +5 -18
  265. package/dist/runtime/tool-name-aliases.js.map +1 -1
  266. package/dist/schema/grounding.d.ts +7 -54
  267. package/dist/schema/grounding.js +10 -12
  268. package/dist/schema/grounding.js.map +1 -1
  269. package/dist/schema/hooks.d.ts +39 -136
  270. package/dist/schema/hooks.js +3 -6
  271. package/dist/schema/hooks.js.map +1 -1
  272. package/dist/schema/index.d.ts +114 -3828
  273. package/dist/schema/index.js +41 -80
  274. package/dist/schema/index.js.map +1 -1
  275. package/dist/schema/memory.d.ts +54 -131
  276. package/dist/schema/memory.js +2 -2
  277. package/dist/schema/memory.js.map +1 -1
  278. package/dist/schema/policy-packs.d.ts +3 -45
  279. package/dist/schema/policy-packs.js +7 -13
  280. package/dist/schema/policy-packs.js.map +1 -1
  281. package/dist/schema/policy-ux.d.ts +7 -0
  282. package/dist/schema/policy-ux.js +18 -0
  283. package/dist/schema/policy-ux.js.map +1 -0
  284. package/dist/schema/removed-keys.d.ts +185 -0
  285. package/dist/schema/removed-keys.js +531 -0
  286. package/dist/schema/removed-keys.js.map +1 -0
  287. package/dist/schema/tools.d.ts +30 -268
  288. package/dist/schema/tools.js +4 -4
  289. package/dist/schema/tools.js.map +1 -1
  290. package/package.json +5 -16
  291. package/dist/cli/approve/branch-protection.d.ts +0 -69
  292. package/dist/cli/approve/branch-protection.js +0 -144
  293. package/dist/cli/approve/branch-protection.js.map +0 -1
  294. package/dist/cli/approve/risk.d.ts +0 -112
  295. package/dist/cli/approve/risk.js +0 -197
  296. package/dist/cli/approve/risk.js.map +0 -1
  297. package/dist/cli/approve/stdin-report.d.ts +0 -57
  298. package/dist/cli/approve/stdin-report.js +0 -145
  299. package/dist/cli/approve/stdin-report.js.map +0 -1
  300. package/dist/cli/approve/understanding.d.ts +0 -410
  301. package/dist/cli/approve/understanding.js +0 -996
  302. package/dist/cli/approve/understanding.js.map +0 -1
  303. package/dist/cli/audit.d.ts +0 -80
  304. package/dist/cli/audit.js +0 -252
  305. package/dist/cli/audit.js.map +0 -1
  306. package/dist/cli/delegate/index.d.ts +0 -76
  307. package/dist/cli/delegate/index.js +0 -443
  308. package/dist/cli/delegate/index.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/rogue-ledger.d.ts +0 -53
  319. package/dist/cli/doctor/rogue-ledger.js +0 -153
  320. package/dist/cli/doctor/rogue-ledger.js.map +0 -1
  321. package/dist/cli/doctor/session-start-preflight-setup-version.d.ts +0 -83
  322. package/dist/cli/doctor/session-start-preflight-setup-version.js +0 -114
  323. package/dist/cli/doctor/session-start-preflight-setup-version.js.map +0 -1
  324. package/dist/cli/doctor/settings-drift.d.ts +0 -25
  325. package/dist/cli/doctor/settings-drift.js +0 -213
  326. package/dist/cli/doctor/settings-drift.js.map +0 -1
  327. package/dist/cli/doctor/toolchain-parity.d.ts +0 -63
  328. package/dist/cli/doctor/toolchain-parity.js +0 -154
  329. package/dist/cli/doctor/toolchain-parity.js.map +0 -1
  330. package/dist/cli/doctor/ug-auto-approvals.d.ts +0 -40
  331. package/dist/cli/doctor/ug-auto-approvals.js +0 -91
  332. package/dist/cli/doctor/ug-auto-approvals.js.map +0 -1
  333. package/dist/cli/doctor/ug-delegations.d.ts +0 -26
  334. package/dist/cli/doctor/ug-delegations.js +0 -121
  335. package/dist/cli/doctor/ug-delegations.js.map +0 -1
  336. package/dist/cli/doctor/ug-inflight.d.ts +0 -35
  337. package/dist/cli/doctor/ug-inflight.js +0 -57
  338. package/dist/cli/doctor/ug-inflight.js.map +0 -1
  339. package/dist/cli/doctor/understanding-mode-env.d.ts +0 -51
  340. package/dist/cli/doctor/understanding-mode-env.js +0 -98
  341. package/dist/cli/doctor/understanding-mode-env.js.map +0 -1
  342. package/dist/cli/dry-run.d.ts +0 -46
  343. package/dist/cli/dry-run.js +0 -250
  344. package/dist/cli/dry-run.js.map +0 -1
  345. package/dist/cli/event-input.d.ts +0 -28
  346. package/dist/cli/event-input.js +0 -73
  347. package/dist/cli/event-input.js.map +0 -1
  348. package/dist/cli/explain-action.d.ts +0 -20
  349. package/dist/cli/explain-action.js +0 -27
  350. package/dist/cli/explain-action.js.map +0 -1
  351. package/dist/cli/explain-policy.d.ts +0 -135
  352. package/dist/cli/explain-policy.js +0 -266
  353. package/dist/cli/explain-policy.js.map +0 -1
  354. package/dist/cli/explain.d.ts +0 -31
  355. package/dist/cli/explain.js +0 -159
  356. package/dist/cli/explain.js.map +0 -1
  357. package/dist/cli/gc/index.d.ts +0 -63
  358. package/dist/cli/gc/index.js +0 -566
  359. package/dist/cli/gc/index.js.map +0 -1
  360. package/dist/cli/pack/approve-escape.d.ts +0 -19
  361. package/dist/cli/pack/approve-escape.js +0 -190
  362. package/dist/cli/pack/approve-escape.js.map +0 -1
  363. package/dist/cli/pack/auto-approve-path.d.ts +0 -146
  364. package/dist/cli/pack/auto-approve-path.js +0 -462
  365. package/dist/cli/pack/auto-approve-path.js.map +0 -1
  366. package/dist/cli/pack/hook-codex-post-tool-use.d.ts +0 -25
  367. package/dist/cli/pack/hook-codex-post-tool-use.js +0 -160
  368. package/dist/cli/pack/hook-codex-post-tool-use.js.map +0 -1
  369. package/dist/cli/pack/hook-codex-pre-tool-use.d.ts +0 -48
  370. package/dist/cli/pack/hook-codex-pre-tool-use.js +0 -474
  371. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +0 -1
  372. package/dist/cli/pack/hook-codex-stop.d.ts +0 -35
  373. package/dist/cli/pack/hook-codex-stop.js +0 -329
  374. package/dist/cli/pack/hook-codex-stop.js.map +0 -1
  375. package/dist/cli/pack/hook-codex-user-prompt-submit.d.ts +0 -24
  376. package/dist/cli/pack/hook-codex-user-prompt-submit.js +0 -93
  377. package/dist/cli/pack/hook-codex-user-prompt-submit.js.map +0 -1
  378. package/dist/cli/pack/hook-post-merge-gate-record.d.ts +0 -37
  379. package/dist/cli/pack/hook-post-merge-gate-record.js +0 -190
  380. package/dist/cli/pack/hook-post-merge-gate-record.js.map +0 -1
  381. package/dist/cli/pack/hook-post-merge-gate.d.ts +0 -34
  382. package/dist/cli/pack/hook-post-merge-gate.js +0 -277
  383. package/dist/cli/pack/hook-post-merge-gate.js.map +0 -1
  384. package/dist/cli/pack/hook-post-tool-use.d.ts +0 -45
  385. package/dist/cli/pack/hook-post-tool-use.js +0 -137
  386. package/dist/cli/pack/hook-post-tool-use.js.map +0 -1
  387. package/dist/cli/pack/hook-pre-tool-use.d.ts +0 -92
  388. package/dist/cli/pack/hook-pre-tool-use.js +0 -1328
  389. package/dist/cli/pack/hook-pre-tool-use.js.map +0 -1
  390. package/dist/cli/pack/hook-runtime-reality.d.ts +0 -63
  391. package/dist/cli/pack/hook-runtime-reality.js +0 -210
  392. package/dist/cli/pack/hook-runtime-reality.js.map +0 -1
  393. package/dist/cli/pack/hook-solution-acceptance-writeguard.d.ts +0 -32
  394. package/dist/cli/pack/hook-solution-acceptance-writeguard.js +0 -337
  395. package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +0 -1
  396. package/dist/cli/pack/hook-solution-acceptance.d.ts +0 -157
  397. package/dist/cli/pack/hook-solution-acceptance.js +0 -530
  398. package/dist/cli/pack/hook-solution-acceptance.js.map +0 -1
  399. package/dist/cli/pack/hook-stay-in-scope.d.ts +0 -37
  400. package/dist/cli/pack/hook-stay-in-scope.js +0 -128
  401. package/dist/cli/pack/hook-stay-in-scope.js.map +0 -1
  402. package/dist/cli/pack/hook-subagent-start.d.ts +0 -23
  403. package/dist/cli/pack/hook-subagent-start.js +0 -88
  404. package/dist/cli/pack/hook-subagent-start.js.map +0 -1
  405. package/dist/cli/pack/hook-subagent-stop.d.ts +0 -19
  406. package/dist/cli/pack/hook-subagent-stop.js +0 -41
  407. package/dist/cli/pack/hook-subagent-stop.js.map +0 -1
  408. package/dist/cli/pack/hook-track-active-claim.d.ts +0 -27
  409. package/dist/cli/pack/hook-track-active-claim.js +0 -275
  410. package/dist/cli/pack/hook-track-active-claim.js.map +0 -1
  411. package/dist/cli/pack/transcript-report-scan.d.ts +0 -71
  412. package/dist/cli/pack/transcript-report-scan.js +0 -359
  413. package/dist/cli/pack/transcript-report-scan.js.map +0 -1
  414. package/dist/cli/pack/understanding-report-schema-hint.d.ts +0 -26
  415. package/dist/cli/pack/understanding-report-schema-hint.js +0 -120
  416. package/dist/cli/pack/understanding-report-schema-hint.js.map +0 -1
  417. package/dist/cli/pack/upgrade.d.ts +0 -32
  418. package/dist/cli/pack/upgrade.js +0 -273
  419. package/dist/cli/pack/upgrade.js.map +0 -1
  420. package/dist/cli/pause/index.d.ts +0 -113
  421. package/dist/cli/pause/index.js +0 -251
  422. package/dist/cli/pause/index.js.map +0 -1
  423. package/dist/cli/pause-check.d.ts +0 -22
  424. package/dist/cli/pause-check.js +0 -54
  425. package/dist/cli/pause-check.js.map +0 -1
  426. package/dist/cli/policy/intercept.d.ts +0 -233
  427. package/dist/cli/policy/intercept.js +0 -1020
  428. package/dist/cli/policy/intercept.js.map +0 -1
  429. package/dist/cli/policy/risk-envelope-enrichment.d.ts +0 -40
  430. package/dist/cli/policy/risk-envelope-enrichment.js +0 -108
  431. package/dist/cli/policy/risk-envelope-enrichment.js.map +0 -1
  432. package/dist/cli/record/index.d.ts +0 -81
  433. package/dist/cli/record/index.js +0 -348
  434. package/dist/cli/record/index.js.map +0 -1
  435. package/dist/cli/resolve-env.d.ts +0 -32
  436. package/dist/cli/resolve-env.js +0 -47
  437. package/dist/cli/resolve-env.js.map +0 -1
  438. package/dist/cli/session-export/index.d.ts +0 -46
  439. package/dist/cli/session-export/index.js +0 -169
  440. package/dist/cli/session-export/index.js.map +0 -1
  441. package/dist/cli/session-export/redact.d.ts +0 -22
  442. package/dist/cli/session-export/redact.js +0 -47
  443. package/dist/cli/session-export/redact.js.map +0 -1
  444. package/dist/cli/session-export/transcript.d.ts +0 -24
  445. package/dist/cli/session-export/transcript.js +0 -164
  446. package/dist/cli/session-export/transcript.js.map +0 -1
  447. package/dist/cli/session-start/branch-check.d.ts +0 -18
  448. package/dist/cli/session-start/branch-check.js +0 -140
  449. package/dist/cli/session-start/branch-check.js.map +0 -1
  450. package/dist/cli/session-start/index.d.ts +0 -121
  451. package/dist/cli/session-start/index.js +0 -591
  452. package/dist/cli/session-start/index.js.map +0 -1
  453. package/dist/cli/session-start/shared-options.d.ts +0 -103
  454. package/dist/cli/session-start/shared-options.js +0 -107
  455. package/dist/cli/session-start/shared-options.js.map +0 -1
  456. package/dist/cli/session-start/stale-base-check.d.ts +0 -46
  457. package/dist/cli/session-start/stale-base-check.js +0 -352
  458. package/dist/cli/session-start/stale-base-check.js.map +0 -1
  459. package/dist/cli/session-start/toolchain-parity.d.ts +0 -267
  460. package/dist/cli/session-start/toolchain-parity.js +0 -797
  461. package/dist/cli/session-start/toolchain-parity.js.map +0 -1
  462. package/dist/cli/smoke/assertions.d.ts +0 -46
  463. package/dist/cli/smoke/assertions.js +0 -170
  464. package/dist/cli/smoke/assertions.js.map +0 -1
  465. package/dist/cli/smoke/index.d.ts +0 -76
  466. package/dist/cli/smoke/index.js +0 -245
  467. package/dist/cli/smoke/index.js.map +0 -1
  468. package/dist/cli/smoke/runner.d.ts +0 -55
  469. package/dist/cli/smoke/runner.js +0 -134
  470. package/dist/cli/smoke/runner.js.map +0 -1
  471. package/dist/cli/smoke/stream-parser.d.ts +0 -65
  472. package/dist/cli/smoke/stream-parser.js +0 -115
  473. package/dist/cli/smoke/stream-parser.js.map +0 -1
  474. package/dist/cli/test-risk.d.ts +0 -26
  475. package/dist/cli/test-risk.js +0 -34
  476. package/dist/cli/test-risk.js.map +0 -1
  477. package/dist/io/display-path.d.ts +0 -11
  478. package/dist/io/display-path.js +0 -29
  479. package/dist/io/display-path.js.map +0 -1
  480. package/dist/io/duration.d.ts +0 -5
  481. package/dist/io/duration.js +0 -50
  482. package/dist/io/duration.js.map +0 -1
  483. package/dist/io/extract.d.ts +0 -79
  484. package/dist/io/extract.js +0 -226
  485. package/dist/io/extract.js.map +0 -1
  486. package/dist/io/ledger-record.d.ts +0 -83
  487. package/dist/io/ledger-record.js +0 -301
  488. package/dist/io/ledger-record.js.map +0 -1
  489. package/dist/io/read-json-dir-entries.d.ts +0 -36
  490. package/dist/io/read-json-dir-entries.js +0 -48
  491. package/dist/io/read-json-dir-entries.js.map +0 -1
  492. package/dist/io/safe-json-parse.d.ts +0 -5
  493. package/dist/io/safe-json-parse.js +0 -21
  494. package/dist/io/safe-json-parse.js.map +0 -1
  495. package/dist/policies/index.d.ts +0 -5
  496. package/dist/policies/index.js +0 -6
  497. package/dist/policies/index.js.map +0 -1
  498. package/dist/policies/ledger-client.d.ts +0 -113
  499. package/dist/policies/ledger-client.js +0 -500
  500. package/dist/policies/ledger-client.js.map +0 -1
  501. package/dist/policies/producers.d.ts +0 -12
  502. package/dist/policies/producers.js +0 -61
  503. package/dist/policies/producers.js.map +0 -1
  504. package/dist/policies/requires.d.ts +0 -75
  505. package/dist/policies/requires.js +0 -240
  506. package/dist/policies/requires.js.map +0 -1
  507. package/dist/policies/timestamp.d.ts +0 -14
  508. package/dist/policies/timestamp.js +0 -36
  509. package/dist/policies/timestamp.js.map +0 -1
  510. package/dist/policy-packs/builtin/permission-profiles.d.ts +0 -11
  511. package/dist/policy-packs/builtin/permission-profiles.js +0 -74
  512. package/dist/policy-packs/builtin/permission-profiles.js.map +0 -1
  513. package/dist/policy-packs/builtin/post-merge-gate-runtime.d.ts +0 -286
  514. package/dist/policy-packs/builtin/post-merge-gate-runtime.js +0 -397
  515. package/dist/policy-packs/builtin/post-merge-gate-runtime.js.map +0 -1
  516. package/dist/policy-packs/builtin/post-merge-gate.d.ts +0 -56
  517. package/dist/policy-packs/builtin/post-merge-gate.js +0 -347
  518. package/dist/policy-packs/builtin/post-merge-gate.js.map +0 -1
  519. package/dist/policy-packs/builtin/solution-acceptance-reconnect.d.ts +0 -50
  520. package/dist/policy-packs/builtin/solution-acceptance-reconnect.js +0 -151
  521. package/dist/policy-packs/builtin/solution-acceptance-reconnect.js.map +0 -1
  522. package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +0 -323
  523. package/dist/policy-packs/builtin/solution-acceptance-runtime.js +0 -672
  524. package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +0 -1
  525. package/dist/policy-packs/builtin/solution-acceptance.d.ts +0 -45
  526. package/dist/policy-packs/builtin/solution-acceptance.js +0 -226
  527. package/dist/policy-packs/builtin/solution-acceptance.js.map +0 -1
  528. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.d.ts +0 -25
  529. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js +0 -94
  530. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js.map +0 -1
  531. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.d.ts +0 -35
  532. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js +0 -86
  533. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js.map +0 -1
  534. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.d.ts +0 -210
  535. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js +0 -361
  536. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js.map +0 -1
  537. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +0 -304
  538. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +0 -734
  539. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +0 -1
  540. package/dist/policy-packs/builtin/understanding-before-execution/expiry-reason.d.ts +0 -36
  541. package/dist/policy-packs/builtin/understanding-before-execution/expiry-reason.js +0 -73
  542. package/dist/policy-packs/builtin/understanding-before-execution/expiry-reason.js.map +0 -1
  543. package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +0 -19
  544. package/dist/policy-packs/builtin/understanding-before-execution/index.js +0 -83
  545. package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +0 -1
  546. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.d.ts +0 -135
  547. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js +0 -618
  548. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js.map +0 -1
  549. package/dist/policy-packs/builtin/understanding-before-execution/ledger.d.ts +0 -25
  550. package/dist/policy-packs/builtin/understanding-before-execution/ledger.js +0 -56
  551. package/dist/policy-packs/builtin/understanding-before-execution/ledger.js.map +0 -1
  552. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.d.ts +0 -20
  553. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +0 -93
  554. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +0 -1
  555. package/dist/policy-packs/builtin/understanding-before-execution/markers.d.ts +0 -126
  556. package/dist/policy-packs/builtin/understanding-before-execution/markers.js +0 -245
  557. package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +0 -1
  558. package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.d.ts +0 -29
  559. package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.js +0 -82
  560. package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.js.map +0 -1
  561. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.d.ts +0 -71
  562. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +0 -175
  563. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +0 -1
  564. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +0 -620
  565. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +0 -1047
  566. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +0 -1
  567. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.d.ts +0 -128
  568. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +0 -213
  569. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +0 -1
  570. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.d.ts +0 -129
  571. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.js +0 -166
  572. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.js.map +0 -1
  573. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.d.ts +0 -164
  574. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js +0 -275
  575. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js.map +0 -1
  576. package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +0 -1
  577. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +0 -10
  578. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +0 -1
  579. package/dist/policy-packs/builtin/understanding-before-execution.d.ts +0 -526
  580. package/dist/policy-packs/builtin/understanding-before-execution.js +0 -1074
  581. package/dist/policy-packs/builtin/understanding-before-execution.js.map +0 -1
  582. package/dist/policy-packs/permission-translator.d.ts +0 -9
  583. package/dist/policy-packs/permission-translator.js +0 -76
  584. package/dist/policy-packs/permission-translator.js.map +0 -1
  585. package/dist/runtime/action-envelope.d.ts +0 -64
  586. package/dist/runtime/action-envelope.js +0 -46
  587. package/dist/runtime/action-envelope.js.map +0 -1
  588. package/dist/runtime/approval-signing.d.ts +0 -111
  589. package/dist/runtime/approval-signing.js +0 -384
  590. package/dist/runtime/approval-signing.js.map +0 -1
  591. package/dist/runtime/bash-match-facts.d.ts +0 -60
  592. package/dist/runtime/bash-match-facts.js +0 -196
  593. package/dist/runtime/bash-match-facts.js.map +0 -1
  594. package/dist/runtime/bash-match-registry.d.ts +0 -112
  595. package/dist/runtime/bash-match-registry.js +0 -406
  596. package/dist/runtime/bash-match-registry.js.map +0 -1
  597. package/dist/runtime/bash-prefix-parse.d.ts +0 -31
  598. package/dist/runtime/bash-prefix-parse.js +0 -576
  599. package/dist/runtime/bash-prefix-parse.js.map +0 -1
  600. package/dist/runtime/command-normalize.d.ts +0 -658
  601. package/dist/runtime/command-normalize.js +0 -2410
  602. package/dist/runtime/command-normalize.js.map +0 -1
  603. package/dist/runtime/deletion-target-resolve.d.ts +0 -28
  604. package/dist/runtime/deletion-target-resolve.js +0 -1078
  605. package/dist/runtime/deletion-target-resolve.js.map +0 -1
  606. package/dist/runtime/destructive-shell-floor.d.ts +0 -19
  607. package/dist/runtime/destructive-shell-floor.js +0 -611
  608. package/dist/runtime/destructive-shell-floor.js.map +0 -1
  609. package/dist/runtime/environment-resolver.d.ts +0 -36
  610. package/dist/runtime/environment-resolver.js +0 -138
  611. package/dist/runtime/environment-resolver.js.map +0 -1
  612. package/dist/runtime/git-context.d.ts +0 -201
  613. package/dist/runtime/git-context.js +0 -422
  614. package/dist/runtime/git-context.js.map +0 -1
  615. package/dist/runtime/index.d.ts +0 -12
  616. package/dist/runtime/index.js +0 -13
  617. package/dist/runtime/index.js.map +0 -1
  618. package/dist/runtime/intercept.d.ts +0 -494
  619. package/dist/runtime/intercept.js +0 -1313
  620. package/dist/runtime/intercept.js.map +0 -1
  621. package/dist/runtime/kube-context.d.ts +0 -16
  622. package/dist/runtime/kube-context.js +0 -63
  623. package/dist/runtime/kube-context.js.map +0 -1
  624. package/dist/runtime/kubectl-target-parse.d.ts +0 -31
  625. package/dist/runtime/kubectl-target-parse.js +0 -249
  626. package/dist/runtime/kubectl-target-parse.js.map +0 -1
  627. package/dist/runtime/ledger-add.d.ts +0 -16
  628. package/dist/runtime/ledger-add.js +0 -150
  629. package/dist/runtime/ledger-add.js.map +0 -1
  630. package/dist/runtime/ledger-writer.d.ts +0 -27
  631. package/dist/runtime/ledger-writer.js +0 -46
  632. package/dist/runtime/ledger-writer.js.map +0 -1
  633. package/dist/runtime/pause-sentinel.d.ts +0 -60
  634. package/dist/runtime/pause-sentinel.js +0 -156
  635. package/dist/runtime/pause-sentinel.js.map +0 -1
  636. package/dist/runtime/pending-approval.d.ts +0 -23
  637. package/dist/runtime/pending-approval.js +0 -67
  638. package/dist/runtime/pending-approval.js.map +0 -1
  639. package/dist/runtime/read-only-bash.d.ts +0 -141
  640. package/dist/runtime/read-only-bash.js +0 -2204
  641. package/dist/runtime/read-only-bash.js.map +0 -1
  642. package/dist/runtime/recovery-git-commit.d.ts +0 -22
  643. package/dist/runtime/recovery-git-commit.js +0 -284
  644. package/dist/runtime/recovery-git-commit.js.map +0 -1
  645. package/dist/runtime/reject-malformed-session-id.d.ts +0 -8
  646. package/dist/runtime/reject-malformed-session-id.js +0 -16
  647. package/dist/runtime/reject-malformed-session-id.js.map +0 -1
  648. package/dist/runtime/risk-classifier.d.ts +0 -38
  649. package/dist/runtime/risk-classifier.js +0 -311
  650. package/dist/runtime/risk-classifier.js.map +0 -1
  651. package/dist/runtime/session-id.d.ts +0 -113
  652. package/dist/runtime/session-id.js +0 -193
  653. package/dist/runtime/session-id.js.map +0 -1
  654. package/dist/runtime/shell-word.d.ts +0 -62
  655. package/dist/runtime/shell-word.js +0 -410
  656. package/dist/runtime/shell-word.js.map +0 -1
  657. package/dist/runtime/task-providers/agent-tasks.d.ts +0 -103
  658. package/dist/runtime/task-providers/agent-tasks.js +0 -245
  659. package/dist/runtime/task-providers/agent-tasks.js.map +0 -1
  660. package/dist/runtime/when-eval.d.ts +0 -62
  661. package/dist/runtime/when-eval.js +0 -212
  662. package/dist/runtime/when-eval.js.map +0 -1
  663. package/dist/runtime/workflow-policies.d.ts +0 -207
  664. package/dist/runtime/workflow-policies.js +0 -596
  665. package/dist/runtime/workflow-policies.js.map +0 -1
  666. package/dist/schema/audit.d.ts +0 -71
  667. package/dist/schema/audit.js +0 -32
  668. package/dist/schema/audit.js.map +0 -1
  669. package/dist/schema/doctor.d.ts +0 -25
  670. package/dist/schema/doctor.js +0 -23
  671. package/dist/schema/doctor.js.map +0 -1
  672. package/dist/schema/environments.d.ts +0 -215
  673. package/dist/schema/environments.js +0 -101
  674. package/dist/schema/environments.js.map +0 -1
  675. package/dist/schema/extract.d.ts +0 -9
  676. package/dist/schema/extract.js +0 -86
  677. package/dist/schema/extract.js.map +0 -1
  678. package/dist/schema/permission-profiles.d.ts +0 -2292
  679. package/dist/schema/permission-profiles.js +0 -60
  680. package/dist/schema/permission-profiles.js.map +0 -1
  681. package/dist/schema/policies.d.ts +0 -974
  682. package/dist/schema/policies.js +0 -298
  683. package/dist/schema/policies.js.map +0 -1
  684. package/dist/schema/requires.d.ts +0 -131
  685. package/dist/schema/requires.js +0 -88
  686. package/dist/schema/requires.js.map +0 -1
  687. package/dist/schema/risk.d.ts +0 -144
  688. package/dist/schema/risk.js +0 -194
  689. package/dist/schema/risk.js.map +0 -1
  690. package/dist/schema/session-start-preflight.d.ts +0 -10
  691. package/dist/schema/session-start-preflight.js +0 -106
  692. package/dist/schema/session-start-preflight.js.map +0 -1
  693. package/dist/schema/stale-base-check.d.ts +0 -18
  694. package/dist/schema/stale-base-check.js +0 -37
  695. package/dist/schema/stale-base-check.js.map +0 -1
  696. package/dist/schema/toolchain-parity.d.ts +0 -21
  697. package/dist/schema/toolchain-parity.js +0 -48
  698. package/dist/schema/toolchain-parity.js.map +0 -1
  699. package/dist/schema/workflows.d.ts +0 -519
  700. package/dist/schema/workflows.js +0 -81
  701. package/dist/schema/workflows.js.map +0 -1
  702. package/scripts/runtime-reality-docker-probe.mjs +0 -71
@@ -1,797 +0,0 @@
1
- // `harness session-start toolchain-parity` — SessionStart hook entrypoint,
2
- // third sibling of `harness session-start preflight` / `branch-check`.
3
- //
4
- // Anlass: the 2026-07-22 PATH-shim incident, where a machine ran an entire
5
- // session unnoticed on Node 22 + understanding-gate 0.4.6 instead of the
6
- // intended Node 26 + 0.4.9 — nothing on that machine ever compared its own
7
- // toolchain against any other machine's. This producer closes that gap,
8
- // purely advisorily:
9
- //
10
- // (a) writes a toolchain snapshot of THIS machine to
11
- // `<machineStateDir>/<ownProfile>.json` (owner-writes-only — every
12
- // OTHER `*.json` in that directory is read-only input, never
13
- // touched);
14
- // (b) compares the local LIVE toolchain state against every peer
15
- // snapshot file already in that directory;
16
- // (c) reports drift as stderr warning lines plus a single
17
- // `toolchain-parity:ok` / `toolchain-parity:drift:<n>` ledger fact,
18
- // with a `:unparseable-peer:<n>` suffix appended whenever one or more
19
- // peer files failed to parse as JSON (task 690fba7c, follow-up from
20
- // the agent-memory 06d09cde incident: an unparseable peer snapshot is
21
- // a real corruption/drift signal and must not vanish from `drift:N`
22
- // as if it had been a complete comparison).
23
- //
24
- // Transport of the snapshot files BETWEEN machines is entirely
25
- // agent-memory-sync's job (agent-memory PR #64, already shipped) — this
26
- // module only ever reads/writes the local directory.
27
- //
28
- // SessionStart contract, same as the two siblings: `blocking:false`. Every
29
- // failure path (not configured, mkdir/write/read failure, a broken peer
30
- // file, a spawn timeout, a ledger-write failure) logs one line to stderr
31
- // and exits 0. There is no "gate" this producer can leave closed — it is
32
- // purely advisory, so degrading silently (well, LOUDLY on stderr, just
33
- // never blocking) is always the right move.
34
- import { execFile } from "node:child_process";
35
- import * as fs from "node:fs";
36
- import * as os from "node:os";
37
- import * as path from "node:path";
38
- import { readTopLevelMcpServers, resolveClaudeUserRegistryPath, } from "../../io/claude-mcp.js";
39
- import { assertNoRealSpawnInTests } from "../../runtime/hermetic-spawn-guard.js";
40
- import { resolveManifestLedgerWriter } from "../../runtime/ledger-writer.js";
41
- import { resolveReadSessionId, } from "../../runtime/session-id.js";
42
- import { loadManifest } from "../loader.js";
43
- import { classifySessionSource, explicitSessionId, FALLBACK_SESSION, malformedEventReason, readSessionStartEvent, resolveEventCwd, } from "./shared-options.js";
44
- const LEDGER_SOURCE = "harness-session-start-toolchain-parity";
45
- const SNAPSHOT_SCHEMA_VERSION = 1;
46
- // Individual spawn ceilings, not the expected duration: `node --version`
47
- // and `npm ls -g --depth=0 --json` run IN PARALLEL (Promise.all in
48
- // collectLocalSnapshot below), so the wall-time contribution of the two
49
- // spawns together is max(nodeTimeoutMs, npmTimeoutMs), not their sum.
50
- // Combined with two near-instant fs reads (OW-Kit version, MCP registry)
51
- // and a SEQUENTIAL ledger write afterwards, the worst-case wall-time this
52
- // hook needs against its configured `budget_ms` (and how much headroom
53
- // that leaves) is analyzed once, at the hook's own `budget_ms`
54
- // declaration in src/cli/init/templates.ts — search "toolchain-parity"
55
- // there. That comment is the single place the number lives; this one
56
- // deliberately does not restate it (task c1b5ade5 R2, closing a drift
57
- // between the two comments a prior round introduced).
58
- const DEFAULT_NODE_TIMEOUT_MS = 2_000;
59
- const DEFAULT_NPM_GLOBALS_TIMEOUT_MS = 4_000;
60
- /**
61
- * Profile names land in a filename; strip anything not filename-safe.
62
- * Exported (task 13919613) so `harness doctor`'s toolchain-parity section
63
- * can resolve the SAME own-profile filename this producer would, without
64
- * re-implementing the sanitization rule. Pure export, no behaviour change
65
- * to any call site in this module.
66
- */
67
- export function sanitizeProfileName(value) {
68
- const cleaned = value.replace(/[^a-zA-Z0-9._-]/g, "-");
69
- return cleaned.length > 0 ? cleaned : "profile";
70
- }
71
- function isRecordOfStrings(x) {
72
- return (typeof x === "object" &&
73
- x !== null &&
74
- !Array.isArray(x) &&
75
- Object.values(x).every((v) => typeof v === "string"));
76
- }
77
- /**
78
- * Parse a stored snapshot JSON. Rejects (`ok:false`) only on the shapes
79
- * that make comparison meaningless — not valid JSON, not an object,
80
- * missing/invalid `profile` or `timestamp`. Every other field degrades
81
- * leniently (missing/malformed -> empty/absent) rather than rejecting the
82
- * whole file, since a peer snapshot written by a future schema version
83
- * should still compare on the fields this version understands.
84
- */
85
- export function parseSnapshotJson(raw) {
86
- let parsed;
87
- try {
88
- parsed = JSON.parse(raw);
89
- }
90
- catch (err) {
91
- return { ok: false, reason: `not valid JSON: ${err.message}` };
92
- }
93
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
94
- return { ok: false, reason: "not a JSON object" };
95
- }
96
- const p = parsed;
97
- if (typeof p["profile"] !== "string" || p["profile"].length === 0) {
98
- return { ok: false, reason: "missing or invalid `profile` field" };
99
- }
100
- if (typeof p["timestamp"] !== "string" || Number.isNaN(Date.parse(p["timestamp"]))) {
101
- return { ok: false, reason: "missing or invalid `timestamp` field" };
102
- }
103
- const snapshot = {
104
- schemaVersion: SNAPSHOT_SCHEMA_VERSION,
105
- profile: p["profile"],
106
- timestamp: p["timestamp"],
107
- npmGlobals: isRecordOfStrings(p["npmGlobals"]) ? p["npmGlobals"] : {},
108
- mcpServers: Array.isArray(p["mcpServers"]) && p["mcpServers"].every((v) => typeof v === "string")
109
- ? p["mcpServers"]
110
- : [],
111
- };
112
- if (typeof p["node"] === "string")
113
- snapshot.node = p["node"];
114
- if (typeof p["nodeError"] === "string")
115
- snapshot.nodeError = p["nodeError"];
116
- if (typeof p["npmGlobalsError"] === "string")
117
- snapshot.npmGlobalsError = p["npmGlobalsError"];
118
- if (typeof p["owKitVersion"] === "string")
119
- snapshot.owKitVersion = p["owKitVersion"];
120
- if (typeof p["owKitError"] === "string")
121
- snapshot.owKitError = p["owKitError"];
122
- if (typeof p["mcpServersError"] === "string")
123
- snapshot.mcpServersError = p["mcpServersError"];
124
- return { ok: true, snapshot };
125
- }
126
- /**
127
- * Hermetic guard (mirrors `realNpmExec` in src/cli/doctor/npm-bin-path.ts):
128
- * asserts BEFORE touching `child_process` that we are not running under
129
- * vitest without a test having injected `runNodeVersion`. This function has
130
- * no try/catch around the assertion, and its only caller,
131
- * `collectLocalSnapshot`, calls it (via the `runNodeVersion` param, defaulted
132
- * to this function in `runSessionStartToolchainParity`) with no surrounding
133
- * try/catch either — see src/runtime/hermetic-spawn-guard.ts for why that
134
- * matters (a thrown `HermeticSpawnViolationError` must propagate, not be
135
- * folded into an ordinary "collection failed" outcome).
136
- *
137
- * PATH-resolved deliberately: `execFile("node", ...)` does a PATH lookup for
138
- * the literal string "node", NOT `process.execPath` — the entire point of
139
- * this collector is capturing the PATH state of the *shell* the session
140
- * runs in (the PATH-shim incident this task follows up on was exactly a
141
- * PATH pointing at the wrong node), which `process.version` (the node
142
- * running THIS harness process) would silently paper over.
143
- *
144
- * Exported (task 13919613) so `harness doctor`'s toolchain-parity section
145
- * can default its own `runNodeVersion` injection knob to the SAME real
146
- * collector this producer uses — the whole point of the reuse is that
147
- * doctor never re-implements this spawn/parse logic. No behaviour change:
148
- * this is the exact function every existing call site already used.
149
- */
150
- export function realNodeVersionSpawn(timeoutMs) {
151
- assertNoRealSpawnInTests("node --version", "Inject a fake `runNodeVersion` (SessionStartToolchainParityOptions.runNodeVersion) instead of exercising the real spawn path.");
152
- return new Promise((resolve) => {
153
- execFile("node", ["--version"], { timeout: timeoutMs, encoding: "utf8" }, (err, stdout) => {
154
- const text = (stdout ?? "").trim();
155
- if (text.length > 0) {
156
- resolve({ ok: true, version: text });
157
- return;
158
- }
159
- if (err) {
160
- const e = err;
161
- if (e.code === "ENOENT") {
162
- resolve({ ok: false, reason: "`node` not on PATH" });
163
- return;
164
- }
165
- if (e.killed) {
166
- resolve({ ok: false, reason: `\`node --version\` timed out after ${timeoutMs}ms` });
167
- return;
168
- }
169
- resolve({ ok: false, reason: `\`node --version\` failed: ${e.message}` });
170
- return;
171
- }
172
- resolve({ ok: false, reason: "`node --version` produced no output" });
173
- });
174
- });
175
- }
176
- /**
177
- * Hermetic guard, same shape/rationale as {@link realNodeVersionSpawn} —
178
- * see that function's doc. Tolerant of a non-zero exit the same way
179
- * `spawnPreflight` (session-start/index.ts) is tolerant of `preflight run`'s
180
- * exit code: `npm ls -g` can exit non-zero on an unrelated extraneous/
181
- * invalid global entry while still emitting a perfectly parseable
182
- * `dependencies` JSON block, so a parseable stdout wins over the exit code.
183
- *
184
- * Exported (task 13919613) for the same reuse reason as
185
- * {@link realNodeVersionSpawn} — `harness doctor`'s toolchain-parity
186
- * section defaults to this real collector rather than duplicating it.
187
- */
188
- export function realNpmGlobalsSpawn(timeoutMs) {
189
- assertNoRealSpawnInTests("npm ls -g --depth=0 --json", "Inject a fake `runNpmGlobals` (SessionStartToolchainParityOptions.runNpmGlobals) instead of exercising the real spawn path.");
190
- return new Promise((resolve) => {
191
- execFile("npm", ["ls", "-g", "--depth=0", "--json"], { timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024, encoding: "utf8" }, (err, stdout) => {
192
- const text = (stdout ?? "").trim();
193
- if (text.length > 0) {
194
- try {
195
- const parsed = JSON.parse(text);
196
- const packages = {};
197
- for (const [name, entry] of Object.entries(parsed.dependencies ?? {})) {
198
- if (typeof entry?.version === "string")
199
- packages[name] = entry.version;
200
- }
201
- resolve({ ok: true, packages });
202
- return;
203
- }
204
- catch {
205
- /* fall through to the error path */
206
- }
207
- }
208
- if (err) {
209
- const e = err;
210
- if (e.code === "ENOENT") {
211
- resolve({ ok: false, reason: "`npm` not on PATH" });
212
- return;
213
- }
214
- if (e.killed) {
215
- resolve({
216
- ok: false,
217
- reason: `\`npm ls -g --depth=0 --json\` timed out after ${timeoutMs}ms`,
218
- });
219
- return;
220
- }
221
- resolve({ ok: false, reason: `\`npm ls -g\` failed: ${e.message}` });
222
- return;
223
- }
224
- resolve({ ok: false, reason: "`npm ls -g --depth=0 --json` produced no parseable JSON" });
225
- });
226
- });
227
- }
228
- /**
229
- * Read `<workspaceRoot>/.ai/workflow/manifest.json`'s `version` field
230
- * (the orchestrator-workflow kit's own version marker). A missing file is
231
- * the expected common case (most repos are not OW-kit-managed) and is
232
- * NOT an error — it degrades silently to `{}`. Malformed JSON or a
233
- * non-string `version` field on a manifest that DOES exist is noteworthy
234
- * (something is there but broken), so that degrades to `{ error }`.
235
- *
236
- * Exported (task 13919613) for the same reuse reason as
237
- * {@link realNodeVersionSpawn}.
238
- */
239
- export function realReadOwKitVersion(workspaceRoot) {
240
- const manifestPath = path.join(workspaceRoot, ".ai", "workflow", "manifest.json");
241
- let raw;
242
- try {
243
- raw = fs.readFileSync(manifestPath, "utf8");
244
- }
245
- catch (err) {
246
- const e = err;
247
- if (e.code === "ENOENT")
248
- return {};
249
- return { error: `cannot read ${manifestPath}: ${e.message}` };
250
- }
251
- let parsed;
252
- try {
253
- parsed = JSON.parse(raw);
254
- }
255
- catch (err) {
256
- return { error: `${manifestPath} is not valid JSON: ${err.message}` };
257
- }
258
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
259
- return { error: `${manifestPath} is not a JSON object` };
260
- }
261
- const version = parsed["version"];
262
- if (typeof version !== "string" || version.length === 0) {
263
- return { error: `${manifestPath} has no string \`version\` field` };
264
- }
265
- return { version };
266
- }
267
- /**
268
- * MCP server names from Claude Code's user-scope registry — a pure FILE
269
- * READ (never a spawn), reusing the exact primitive `harness doctor`'s
270
- * claude-mcp check ultimately compares against
271
- * (`resolveClaudeUserRegistryPath` + `readTopLevelMcpServers` in
272
- * src/io/claude-mcp.ts — the same functions `adopt`/`detect` use for the
273
- * same "read the effective registration from outside io/claude-mcp.ts"
274
- * purpose). Deliberately NOT `claude mcp list` (a spawn): the brief calls
275
- * for a file-read source for the NAMES here, and this is it.
276
- *
277
- * Exported (task 13919613) for the same reuse reason as
278
- * {@link realNodeVersionSpawn}.
279
- */
280
- export function realReadMcpServerNames() {
281
- const registryPath = resolveClaudeUserRegistryPath();
282
- const { servers, error } = readTopLevelMcpServers(registryPath);
283
- if (error !== null)
284
- return { names: [], error };
285
- return { names: Object.keys(servers).sort() };
286
- }
287
- /**
288
- * Collect the local, LIVE toolchain snapshot. Never throws (every
289
- * collector above degrades to `{ok:false,reason}` / `{error}` instead of
290
- * rejecting/throwing) — a collection failure is reported both inline on
291
- * the returned snapshot (the matching `*Error` field) AND as a `notes`
292
- * line for the caller to log, but never stops the other collectors from
293
- * running. node/npm run in PARALLEL (see the timeout constants' comment
294
- * above for why).
295
- *
296
- * Exported (task 13919613): `harness doctor`'s toolchain-parity section
297
- * imports this directly as its Collector core — see that module's header
298
- * for the reuse rationale. No behaviour change to this function or any
299
- * existing call site.
300
- */
301
- export async function collectLocalSnapshot(opts) {
302
- const notes = [];
303
- const [nodeResult, npmResult] = await Promise.all([
304
- opts.runNodeVersion(opts.nodeTimeoutMs),
305
- opts.runNpmGlobals(opts.npmTimeoutMs),
306
- ]);
307
- const owKit = opts.readOwKitVersion(opts.workspaceRoot);
308
- const mcp = opts.readMcpServerNames();
309
- const snapshot = {
310
- schemaVersion: SNAPSHOT_SCHEMA_VERSION,
311
- profile: opts.profile,
312
- timestamp: opts.now.toISOString(),
313
- npmGlobals: {},
314
- mcpServers: [],
315
- };
316
- if (nodeResult.ok) {
317
- snapshot.node = nodeResult.version;
318
- }
319
- else {
320
- snapshot.nodeError = nodeResult.reason;
321
- notes.push(`node --version: ${nodeResult.reason}`);
322
- }
323
- if (npmResult.ok) {
324
- snapshot.npmGlobals = npmResult.packages;
325
- }
326
- else {
327
- snapshot.npmGlobalsError = npmResult.reason;
328
- notes.push(`npm ls -g: ${npmResult.reason}`);
329
- }
330
- if (owKit.version !== undefined)
331
- snapshot.owKitVersion = owKit.version;
332
- if (owKit.error !== undefined) {
333
- snapshot.owKitError = owKit.error;
334
- notes.push(`OW-Kit version: ${owKit.error}`);
335
- }
336
- if (mcp.error !== undefined) {
337
- snapshot.mcpServersError = mcp.error;
338
- notes.push(`MCP server names: ${mcp.error}`);
339
- }
340
- else {
341
- snapshot.mcpServers = mcp.names;
342
- }
343
- return { snapshot, notes };
344
- }
345
- // ---------------------------------------------------------------------
346
- // Snapshot write (own profile only) + peer comparison
347
- // ---------------------------------------------------------------------
348
- /**
349
- * Write ONLY `<machineStateDir>/<ownFileName>` (mkdir -p first). Never
350
- * touches any other file in the directory — the caller passes exactly
351
- * `sanitizeProfileName(profile) + ".json"`, so there is no path this
352
- * function could take that reaches a peer's file.
353
- */
354
- function writeOwnSnapshot(machineStateDir, ownFileName, snapshot) {
355
- const filePath = path.join(machineStateDir, ownFileName);
356
- try {
357
- fs.writeFileSync(filePath, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8");
358
- return { ok: true, path: filePath };
359
- }
360
- catch (err) {
361
- return { ok: false, reason: err.message };
362
- }
363
- }
364
- /** `"just now"` / `"<n>m"` / `"<n>h"` / `"<n>d"` — coarse, human-scannable. */
365
- export function formatSnapshotAge(ageMs) {
366
- if (ageMs < 60_000)
367
- return "just now";
368
- const minutes = Math.floor(ageMs / 60_000);
369
- if (minutes < 60)
370
- return `${minutes}m`;
371
- const hours = Math.floor(minutes / 60);
372
- if (hours < 48)
373
- return `${hours}h`;
374
- return `${Math.floor(hours / 24)}d`;
375
- }
376
- /**
377
- * Compare the local LIVE snapshot against one peer's stored snapshot.
378
- * Node / OW-Kit drift only fires when BOTH sides collected that field
379
- * successfully (an unavailable field reads as "unknown", not "match" or
380
- * "drift"). The npm-globals and MCP-name comparisons additionally gate on
381
- * the WHOLE field having collected without error on both sides — an
382
- * `npmGlobalsError`/`mcpServersError` on either side means that side's map
383
- * is "unknown", not "empty", and treating it as empty would fabricate a
384
- * wall of "missing on N packages" drift instead of the real gap (which the
385
- * caller's own note()s already surface separately, from collectLocalSnapshot's
386
- * `notes` and the peer-unreadable path).
387
- */
388
- export function compareToPeer(local, peer, now) {
389
- const drift = [];
390
- const peerProfile = peer.profile;
391
- if (local.node !== undefined && peer.node !== undefined && local.node !== peer.node) {
392
- drift.push({
393
- kind: "node",
394
- message: `node version: local ${local.node} vs peer ${peerProfile} ${peer.node}`,
395
- });
396
- }
397
- if (local.owKitVersion !== undefined &&
398
- peer.owKitVersion !== undefined &&
399
- local.owKitVersion !== peer.owKitVersion) {
400
- drift.push({
401
- kind: "ow_kit",
402
- message: `orchestrator-workflow kit version: local ${local.owKitVersion} vs peer ${peerProfile} ${peer.owKitVersion}`,
403
- });
404
- }
405
- if (local.npmGlobalsError === undefined && peer.npmGlobalsError === undefined) {
406
- const names = [...new Set([...Object.keys(local.npmGlobals), ...Object.keys(peer.npmGlobals)])].sort();
407
- for (const name of names) {
408
- const lv = local.npmGlobals[name];
409
- const pv = peer.npmGlobals[name];
410
- if (lv !== undefined && pv !== undefined) {
411
- if (lv !== pv) {
412
- drift.push({
413
- kind: "npm_package_version",
414
- message: `npm global \`${name}\`: local ${lv} vs peer ${peerProfile} ${pv}`,
415
- });
416
- }
417
- }
418
- else if (lv !== undefined) {
419
- drift.push({
420
- kind: "npm_package_missing_peer",
421
- message: `npm global \`${name}\`: local ${lv}, missing on peer ${peerProfile}`,
422
- });
423
- }
424
- else if (pv !== undefined) {
425
- drift.push({
426
- kind: "npm_package_missing_local",
427
- message: `npm global \`${name}\`: missing locally, peer ${peerProfile} has ${pv}`,
428
- });
429
- }
430
- }
431
- }
432
- if (local.mcpServersError === undefined && peer.mcpServersError === undefined) {
433
- const localSet = new Set(local.mcpServers);
434
- const peerSet = new Set(peer.mcpServers);
435
- const names = [...new Set([...localSet, ...peerSet])].sort();
436
- for (const name of names) {
437
- if (localSet.has(name) && !peerSet.has(name)) {
438
- drift.push({
439
- kind: "mcp_missing_peer",
440
- message: `mcp server \`${name}\`: registered locally, missing on peer ${peerProfile}`,
441
- });
442
- }
443
- else if (!localSet.has(name) && peerSet.has(name)) {
444
- drift.push({
445
- kind: "mcp_missing_local",
446
- message: `mcp server \`${name}\`: missing locally, registered on peer ${peerProfile}`,
447
- });
448
- }
449
- }
450
- }
451
- const peerTimeMs = Date.parse(peer.timestamp);
452
- const ageMs = Number.isFinite(peerTimeMs) ? Math.max(0, now.getTime() - peerTimeMs) : 0;
453
- return { peerProfile, ageMs, drift };
454
- }
455
- /**
456
- * Exported (task 13919613) so `harness doctor`'s toolchain-parity section
457
- * resolves the SAME default machine-state directory this producer writes
458
- * to when `toolchain_parity.machine_state_dir` is omitted from the
459
- * manifest — reusing the constant instead of re-deriving it.
460
- */
461
- export function defaultMachineStateDir() {
462
- return path.join(os.homedir(), ".harness", "machine-state");
463
- }
464
- export async function runSessionStartToolchainParity(opts = {}) {
465
- const stdin = opts.stdin ?? process.stdin;
466
- const stderr = opts.stderr ?? process.stderr;
467
- const now = opts.now ?? new Date();
468
- const nodeTimeoutMs = opts.nodeTimeoutMs ?? DEFAULT_NODE_TIMEOUT_MS;
469
- const npmTimeoutMs = opts.npmTimeoutMs ?? DEFAULT_NPM_GLOBALS_TIMEOUT_MS;
470
- const note = (msg) => {
471
- // CR/LF strip, ONCE, at this choke point (task c1b5ade5 R2b, closing
472
- // what R2's per-site sanitizeProfileName wraps left open): every
473
- // note() call writes exactly one stderr line prefixed with
474
- // "harness session-start toolchain-parity: ". Untrusted, cross-machine-
475
- // synced content reaches a note() argument through roughly ten call
476
- // sites below — a peer's `profile` field, a peer filename, and an
477
- // npm/mcp/node/owKitVersion value embedded in a compareToPeer drift
478
- // message — and R2 sanitized only 2 of them (the collision and stale
479
- // notes), leaving the rest (the "ok against"/"drift:N against" lines,
480
- // every individual drift message, and two of the three peer-file-read
481
- // notes) able to have a crafted `\n` forge a standalone fake parity
482
- // line. Stripping CR/LF here closes the whole class by construction,
483
- // regardless of which site the untrusted string flows through or
484
- // whether a future site remembers to sanitize its own argument.
485
- stderr.write(`harness session-start toolchain-parity: ${msg.replace(/[\r\n]/g, " ")}\n`);
486
- };
487
- const done = (wrote, profile, peersCompared, driftCount, sessionId, sessionSource, reason, unparseablePeerCount = 0) => ({
488
- exitCode: 0,
489
- wrote,
490
- profile,
491
- peersCompared,
492
- driftCount,
493
- unparseablePeerCount,
494
- sessionId,
495
- sessionSource,
496
- ...(reason !== undefined && { reason }),
497
- });
498
- let event;
499
- try {
500
- event = await readSessionStartEvent(stdin, {
501
- onTimeout: note,
502
- ...(opts.stdinIdleTimeoutMs !== undefined && { idleTimeoutMs: opts.stdinIdleTimeoutMs }),
503
- });
504
- }
505
- catch (err) {
506
- const reason = malformedEventReason(err);
507
- note(reason);
508
- return done(false, "", 0, 0, FALLBACK_SESSION, "default", reason);
509
- }
510
- const cwd = resolveEventCwd(opts.cwd, event);
511
- // Session id resolution: same precedence chain as the two siblings, so
512
- // all three producers stay symmetric.
513
- const explicit = explicitSessionId(opts.session, event);
514
- const resolveSession = opts.resolveSession ?? resolveReadSessionId;
515
- // Defensive (task c1b5ade5): a session resolver that throws (real or
516
- // injected) must degrade to the same FALLBACK_SESSION every OTHER "no
517
- // session known" path already uses, not crash a producer whose whole
518
- // contract is "never break the session loop". Unlike collectLocalSnapshot
519
- // below, this call is NOT part of the hermetic-spawn-guard contract, so
520
- // wrapping it here does not swallow a HermeticSpawnViolationError.
521
- let sessionId;
522
- // Task c1b5ade5 R2 (finding 3): a throw here always degrades sessionId to
523
- // FALLBACK_SESSION below, but sessionSource's ternary was computed purely
524
- // from `opts.session`/`event.session_id`/`sessionId` — so a stdin event
525
- // that DID carry a `session_id` still reported sessionSource "stdin" even
526
- // though the id actually recorded was "default" (the resolver never ran
527
- // to completion). That both misrepresents the ledger fact's provenance
528
- // and suppresses the AC5 "resolved to default" warning below, which only
529
- // fires when sessionSource === "default". `resolverThrew` short-circuits
530
- // sessionSource to "default" whenever the catch below fired, regardless
531
- // of what `explicit`/`event.session_id` looked like.
532
- let resolverThrew = false;
533
- try {
534
- sessionId = resolveSession(explicit, {});
535
- }
536
- catch (err) {
537
- note(`session id resolution failed: ${err.message}; falling back to session "${FALLBACK_SESSION}"`);
538
- sessionId = FALLBACK_SESSION;
539
- resolverThrew = true;
540
- }
541
- const sessionSource = classifySessionSource(opts.session, event, sessionId, resolverThrew);
542
- let manifest;
543
- if (opts.manifest) {
544
- manifest = opts.manifest;
545
- }
546
- else {
547
- try {
548
- manifest = loadManifest(opts).manifest;
549
- }
550
- catch (err) {
551
- const reason = `manifest load failed: ${err.message}`;
552
- note(reason);
553
- return done(false, "", 0, 0, sessionId, sessionSource, reason);
554
- }
555
- }
556
- const config = manifest.toolchain_parity;
557
- if (!config.enabled) {
558
- const reason = "not configured (add `toolchain_parity: { enabled: true }` to harness.yaml); skipping";
559
- note(reason);
560
- return done(false, "", 0, 0, sessionId, sessionSource, reason);
561
- }
562
- const machineStateDir = config.machine_state_dir ?? defaultMachineStateDir();
563
- const profile = config.profile ?? sanitizeProfileName(os.hostname());
564
- const workspaceRoot = config.workspace_root ?? cwd;
565
- const sanitizedProfile = sanitizeProfileName(profile);
566
- const ownFileName = `${sanitizedProfile}.json`;
567
- // Advisory staleness threshold (AC3, task c1b5ade5): undefined disables
568
- // the check entirely, matching the schema's default-off comment.
569
- const staleAfterMs = config.stale_after_days !== undefined ? config.stale_after_days * 24 * 60 * 60 * 1000 : undefined;
570
- try {
571
- fs.mkdirSync(machineStateDir, { recursive: true });
572
- }
573
- catch (err) {
574
- const reason = `cannot create/access machine-state dir ${machineStateDir}: ${err.message}`;
575
- note(reason);
576
- return done(false, profile, 0, 0, sessionId, sessionSource, reason);
577
- }
578
- // Lossy-sanitization advisory (task c1b5ade5): a `profile` configured
579
- // with characters that are not filename-safe silently loses information
580
- // when sanitizeProfileName strips/replaces them. That is invisible to an
581
- // operator unless flagged — worse, two DIFFERENT configured profiles can
582
- // sanitize to the SAME filename (e.g. "mac/mini" and "mac-mini" both land
583
- // on "mac-mini.json"), in which case this run's write is about to
584
- // silently overwrite what looked like a peer's snapshot. Both cases are
585
- // advisory-only: this producer still writes its own snapshot and
586
- // continues normally either way.
587
- if (sanitizedProfile !== profile) {
588
- note(`configured profile "${profile}" is not filename-safe; using sanitized "${sanitizedProfile}" for the snapshot file (${ownFileName})`);
589
- }
590
- // Collision check (task c1b5ade5 R2, finding 2): this must run on EVERY
591
- // invocation, not only when THIS machine's own profile happens to be
592
- // lossy — the collision is equally caused by a DIFFERENT (peer)
593
- // machine's lossy profile landing on the SAME sanitized filename as
594
- // this machine's own, already filename-safe, profile. Gating this read
595
- // on `sanitizedProfile !== profile` meant only the lossy side of such a
596
- // pair ever warned; the filename-safe side silently overwrote the lossy
597
- // peer's snapshot with no signal at all (and that peer would then never
598
- // show up in `peersCompared`). Only the "not filename-safe" note above
599
- // stays scoped to the lossy case — it is about THIS profile, not about
600
- // a collision.
601
- try {
602
- const existingRaw = fs.readFileSync(path.join(machineStateDir, ownFileName), "utf8");
603
- const existingParsed = parseSnapshotJson(existingRaw);
604
- if (existingParsed.ok && existingParsed.snapshot.profile !== profile) {
605
- // The existing snapshot's `profile` field is untrusted, cross-machine
606
- // synced content, shown RAW here (task c1b5ade5 R2b, reverting R2's
607
- // sanitizeProfileName wrap at this site): note() now strips CR/LF at
608
- // the choke point above, so a per-site sanitize is redundant AND
609
- // would mangle a legitimate non-ASCII profile name into a label that
610
- // no longer matches what actually collided. sanitizeProfileName
611
- // stays reserved for the FILENAME path (ownFileName, writeOwnSnapshot
612
- // below) — never for this stderr-display path.
613
- note(`WARNING: sanitized filename "${ownFileName}" collides with an existing snapshot for a DIFFERENT profile ("${existingParsed.snapshot.profile}"); this run's write is about to overwrite it — set a distinct \`toolchain_parity.profile\` on one of the machines`);
614
- }
615
- }
616
- catch (err) {
617
- // No existing file for this sanitized name yet (first run for this
618
- // profile/filename) is the expected common case — ENOENT stays a
619
- // silent skip; writeOwnSnapshot below reports its own write failures
620
- // separately. Anything else (EACCES, EISDIR, ...) means the collision
621
- // check silently did NOT run, which is a different, noteworthy outcome
622
- // from "no collision found" (task c1b5ade5 R2b, narrowing the
623
- // previously-bare catch): re-noted as one line rather than swallowed.
624
- const e = err;
625
- if (e.code !== "ENOENT") {
626
- note(`could not check for a profile-name collision on ${ownFileName}: ${e.message}`);
627
- }
628
- }
629
- const runNodeVersion = opts.runNodeVersion ?? realNodeVersionSpawn;
630
- const runNpmGlobals = opts.runNpmGlobals ?? realNpmGlobalsSpawn;
631
- const readOwKitVersion = opts.readOwKitVersion ?? realReadOwKitVersion;
632
- const readMcpServerNames = opts.readMcpServerNames ?? realReadMcpServerNames;
633
- const { snapshot: localSnapshot, notes } = await collectLocalSnapshot({
634
- profile,
635
- now,
636
- workspaceRoot,
637
- nodeTimeoutMs,
638
- npmTimeoutMs,
639
- runNodeVersion,
640
- runNpmGlobals,
641
- readOwKitVersion,
642
- readMcpServerNames,
643
- });
644
- for (const n of notes)
645
- note(n);
646
- const written = writeOwnSnapshot(machineStateDir, ownFileName, localSnapshot);
647
- if (!written.ok) {
648
- note(`could not write own snapshot: ${written.reason}`);
649
- }
650
- let entries;
651
- try {
652
- entries = fs.readdirSync(machineStateDir);
653
- }
654
- catch (err) {
655
- const reason = `cannot list machine-state dir ${machineStateDir}: ${err.message}`;
656
- note(reason);
657
- return done(false, profile, 0, 0, sessionId, sessionSource, reason);
658
- }
659
- const peerFiles = entries.filter((name) => name.endsWith(".json") && name !== ownFileName).sort();
660
- if (peerFiles.length === 0) {
661
- const reason = `no peer snapshots found in ${machineStateDir}; nothing to compare (normal for the first machine, or before any peer has run this companion)`;
662
- note(reason);
663
- return done(false, profile, 0, 0, sessionId, sessionSource, reason);
664
- }
665
- let peersCompared = 0;
666
- let driftTotal = 0;
667
- // Peer files that were present but failed to parse as valid JSON —
668
- // AC2 (task 690fba7c): these must not vanish silently from `drift:N`, so
669
- // their (file-derived) labels are surfaced both as a distinct stderr tag
670
- // and as a `:unparseable-peer:<n>` suffix on the ledger fact below.
671
- const unparseablePeers = [];
672
- for (const fileName of peerFiles) {
673
- const filePath = path.join(machineStateDir, fileName);
674
- let raw;
675
- try {
676
- raw = fs.readFileSync(filePath, "utf8");
677
- }
678
- catch (err) {
679
- note(`peer snapshot ${fileName} unreadable: ${err.message}`);
680
- continue;
681
- }
682
- const parsed = parseSnapshotJson(raw);
683
- if (!parsed.ok) {
684
- // The profile field itself failed to parse, so the filename (minus
685
- // its `.json` suffix) is the best available peer identifier. Files
686
- // this producer did NOT write are untrusted input (the machine-state
687
- // dir is populated cross-machine by sync), but note() strips CR/LF
688
- // at the choke point above (task c1b5ade5 R2b, reverting R2's
689
- // sanitizeProfileName wrap at this site), so the label is shown RAW
690
- // here — a crafted filename still cannot forge a standalone parity
691
- // line, and sanitizeProfileName stays reserved for the FILENAME
692
- // path, not this stderr-display one. The `|| fileName` keeps a
693
- // degenerate name like a literal `.json` from producing an empty
694
- // label.
695
- const peerLabel = fileName.replace(/\.json$/, "") || fileName;
696
- note(`peer snapshot ${fileName} is corrupt: ${parsed.reason}`);
697
- note(`parity:unparseable-peer:${peerLabel}`);
698
- unparseablePeers.push(peerLabel);
699
- continue;
700
- }
701
- const comparison = compareToPeer(localSnapshot, parsed.snapshot, now);
702
- peersCompared += 1;
703
- driftTotal += comparison.drift.length;
704
- const age = formatSnapshotAge(comparison.ageMs);
705
- if (comparison.drift.length === 0) {
706
- note(`ok against ${comparison.peerProfile} (snapshot age ${age})`);
707
- }
708
- else {
709
- note(`drift:${comparison.drift.length} against ${comparison.peerProfile} (snapshot age ${age})`);
710
- for (const item of comparison.drift)
711
- note(`drift — ${item.message}`);
712
- }
713
- // AC3 (task c1b5ade5): a stale peer is a trustworthiness CAVEAT on the
714
- // comparison above, not a drift finding — it never touches driftTotal.
715
- // Only fires when `stale_after_days` is explicitly configured.
716
- if (staleAfterMs !== undefined && comparison.ageMs > staleAfterMs) {
717
- // comparison.peerProfile is untrusted, cross-machine synced content,
718
- // shown RAW here for the same reason as the collision-warning note
719
- // above (task c1b5ade5 R2b, reverting R2's sanitizeProfileName wrap
720
- // at this site): note() strips CR/LF at the choke point, and this
721
- // also keeps the label identical to the "ok against"/"drift:N
722
- // against" line for the SAME peer just above, instead of showing two
723
- // different sanitized-vs-raw spellings of one peer's name in the
724
- // same run.
725
- note(`peer ${comparison.peerProfile} snapshot is stale (age ${age}, exceeds the configured ${config.stale_after_days}d threshold); its comparison may not reflect that machine's CURRENT toolchain`);
726
- }
727
- }
728
- if (peersCompared === 0) {
729
- const reason = `no comparable peer snapshots (all ${peerFiles.length} peer file(s) in ${machineStateDir} were unreadable/corrupt)`;
730
- note(reason);
731
- return done(false, profile, 0, 0, sessionId, sessionSource, reason, unparseablePeers.length);
732
- }
733
- // AC2 (task 690fba7c): a non-zero `unparseablePeers` count means the
734
- // comparison below is PARTIAL, so it is appended to the ledger fact
735
- // itself — the artifact an operator is most likely to read after the
736
- // fact via `harness audit` — not just to the stderr notes above. Absent
737
- // any unparseable peer, `content` is byte-identical to the pre-existing
738
- // format (AC3: no behaviour change for the all-valid-peers case).
739
- const unparseableSuffix = unparseablePeers.length > 0 ? `:unparseable-peer:${unparseablePeers.length}` : "";
740
- const content = (driftTotal === 0 ? "toolchain-parity:ok" : `toolchain-parity:drift:${driftTotal}`) + unparseableSuffix;
741
- let writeLedger = opts.writeLedger;
742
- if (!writeLedger) {
743
- // No try/catch here (task c1b5ade5 R2, finding 4 — orchestrator
744
- // decision): resolveManifestLedgerWriter is a pure function over an
745
- // already-validated `manifest` with no throw path (findGroundingMcp is
746
- // a plain array `.find`, mcpCommandList a plain string split) and no
747
- // I/O seam to fail — unlike `resolveSession` above and `writeLedger`
748
- // below, which DO wrap real I/O/spawn boundaries and stay caught. A
749
- // try/catch here had no exercisable throw path and was dead,
750
- // untested code (round-1 review finding); removed rather than built
751
- // out into a fake seam.
752
- const resolved = resolveManifestLedgerWriter(manifest, {
753
- ...(opts.ledgerTimeoutMs !== undefined ? { ledgerTimeoutMs: opts.ledgerTimeoutMs } : {}),
754
- });
755
- if (!resolved.ok) {
756
- const reason = `${resolved.reason}; cannot record ${content}`;
757
- note(reason);
758
- return done(false, profile, peersCompared, driftTotal, sessionId, sessionSource, reason, unparseablePeers.length);
759
- }
760
- writeLedger = resolved.write;
761
- }
762
- // Defensive (task c1b5ade5): an injected or real writeLedger that REJECTS
763
- // instead of resolving to `{ ok: false, reason }` must degrade the same
764
- // way the `!result.ok` branch below already does, not propagate as an
765
- // unhandled rejection out of a SessionStart hook.
766
- let result;
767
- try {
768
- result = await writeLedger({ sessionId, content, source: LEDGER_SOURCE });
769
- }
770
- catch (err) {
771
- const reason = `ledger write threw: ${err.message}`;
772
- note(reason);
773
- return done(false, profile, peersCompared, driftTotal, sessionId, sessionSource, reason, unparseablePeers.length);
774
- }
775
- if (!result.ok) {
776
- // `?? "unknown error"` is unreachable through any type-checked caller
777
- // (`AddLedgerFactResult`'s `!ok` arm types `reason` as a non-optional
778
- // string) — but unlike the try/catch removed around
779
- // resolveManifestLedgerWriter above (a private, non-injectable pure
780
- // function only ever called from this same typed module, so genuinely
781
- // dead), `writeLedger` IS an injectable public seam
782
- // (SessionStartToolchainParityOptions.writeLedger): a JS-level or
783
- // otherwise untyped caller can hand back `{ ok: false }` with no
784
- // `reason` at runtime, so this fallback stays (task c1b5ade5 R2b).
785
- const reason = `ledger write failed: ${result.reason ?? "unknown error"}`;
786
- note(reason);
787
- return done(false, profile, peersCompared, driftTotal, sessionId, sessionSource, reason, unparseablePeers.length);
788
- }
789
- note(`recorded ${content} for session ${sessionId}`);
790
- if (sessionSource === "default") {
791
- note("WARNING: session resolved to the literal \"default\". This tag is audit-only (no gate " +
792
- "consumes toolchain-parity: yet), but pipe SessionStart event JSON on stdin, export " +
793
- "$CLAUDE_SESSION_ID, or pass --session <id> for manual / scripted use to keep the audit trail useful.");
794
- }
795
- return done(true, profile, peersCompared, driftTotal, sessionId, sessionSource, undefined, unparseablePeers.length);
796
- }
797
- //# sourceMappingURL=toolchain-parity.js.map