harnery 0.36.0 → 0.38.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 (432) hide show
  1. package/dist/commander.d.ts +9 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +10 -1
  4. package/dist/commands/admission.d.ts +21 -0
  5. package/dist/commands/admission.d.ts.map +1 -0
  6. package/dist/commands/admission.js +565 -0
  7. package/dist/commands/agents.d.ts +7 -0
  8. package/dist/commands/agents.d.ts.map +1 -1
  9. package/dist/commands/agents.js +148 -47
  10. package/dist/commands/artifacts.d.ts.map +1 -1
  11. package/dist/commands/artifacts.js +73 -2
  12. package/dist/commands/browse-ai.d.ts +2 -2
  13. package/dist/commands/browse-ai.d.ts.map +1 -1
  14. package/dist/commands/browse-ai.js +6 -4
  15. package/dist/commands/browse.d.ts.map +1 -1
  16. package/dist/commands/browse.js +360 -21
  17. package/dist/commands/diagnostics.d.ts +4 -0
  18. package/dist/commands/diagnostics.d.ts.map +1 -0
  19. package/dist/commands/diagnostics.js +229 -0
  20. package/dist/commands/fetch.js +1 -0
  21. package/dist/commands/files.d.ts +12 -0
  22. package/dist/commands/files.d.ts.map +1 -1
  23. package/dist/commands/files.js +54 -1
  24. package/dist/commands/governor.d.ts.map +1 -1
  25. package/dist/commands/governor.js +4 -0
  26. package/dist/commands/ledger-v3.d.ts.map +1 -1
  27. package/dist/commands/ledger-v3.js +71 -2
  28. package/dist/commands/qa-record.d.ts +144 -0
  29. package/dist/commands/qa-record.d.ts.map +1 -0
  30. package/dist/commands/qa-record.js +0 -0
  31. package/dist/commands/qa-run.d.ts +8 -0
  32. package/dist/commands/qa-run.d.ts.map +1 -0
  33. package/dist/commands/qa-run.js +396 -0
  34. package/dist/commands/qa-status.d.ts +71 -0
  35. package/dist/commands/qa-status.d.ts.map +1 -0
  36. package/dist/commands/qa-status.js +490 -0
  37. package/dist/commands/qa-verify.d.ts +40 -0
  38. package/dist/commands/qa-verify.d.ts.map +1 -0
  39. package/dist/commands/qa-verify.js +180 -0
  40. package/dist/commands/resources.d.ts +4 -0
  41. package/dist/commands/resources.d.ts.map +1 -0
  42. package/dist/commands/resources.js +29 -0
  43. package/dist/commands/review-pack.d.ts +4 -0
  44. package/dist/commands/review-pack.d.ts.map +1 -0
  45. package/dist/commands/review-pack.js +1001 -0
  46. package/dist/commands/supervisor.d.ts +4 -0
  47. package/dist/commands/supervisor.d.ts.map +1 -0
  48. package/dist/commands/supervisor.js +101 -0
  49. package/dist/commands/web.d.ts.map +1 -1
  50. package/dist/commands/web.js +17 -0
  51. package/dist/commands/workflow.d.ts.map +1 -1
  52. package/dist/commands/workflow.js +4 -0
  53. package/dist/core/agents/cli.js +7 -3
  54. package/dist/core/agents/health.d.ts +17 -0
  55. package/dist/core/agents/health.d.ts.map +1 -0
  56. package/dist/core/agents/health.js +109 -0
  57. package/dist/core/agents/mailbox.d.ts +92 -0
  58. package/dist/core/agents/mailbox.d.ts.map +1 -0
  59. package/dist/core/agents/mailbox.js +325 -0
  60. package/dist/core/agents/qa-signal.d.ts +111 -0
  61. package/dist/core/agents/qa-signal.d.ts.map +1 -0
  62. package/dist/core/agents/qa-signal.js +231 -0
  63. package/dist/core/agents/reconcile-coordination-v3.d.ts +44 -0
  64. package/dist/core/agents/reconcile-coordination-v3.d.ts.map +1 -0
  65. package/dist/core/agents/reconcile-coordination-v3.js +58 -0
  66. package/dist/core/agents/render/prompt-context.d.ts +5 -0
  67. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  68. package/dist/core/agents/render/prompt-context.js +34 -13
  69. package/dist/core/agents/render/session-context.d.ts +8 -0
  70. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  71. package/dist/core/agents/render/session-context.js +34 -3
  72. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  73. package/dist/core/agents/rules/commit-conflict.js +50 -1
  74. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  75. package/dist/core/agents/rules/stop-hook.js +6 -10
  76. package/dist/core/agents/session-finalizer-v3.js +4 -1
  77. package/dist/core/agents/session-name-display.d.ts +20 -5
  78. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  79. package/dist/core/agents/session-name-display.js +67 -7
  80. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  81. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  82. package/dist/core/agents/state/heartbeat-writer.d.ts +11 -0
  83. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  84. package/dist/core/agents/state/heartbeat-writer.js +17 -0
  85. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  86. package/dist/core/agents/state/live-coordination-view.js +1 -0
  87. package/dist/core/agents/state/live-coordination-writer.js +5 -0
  88. package/dist/core/artifacts/constants.d.ts +1 -1
  89. package/dist/core/artifacts/constants.js +1 -1
  90. package/dist/core/artifacts/index.d.ts +82 -6
  91. package/dist/core/artifacts/index.d.ts.map +1 -1
  92. package/dist/core/artifacts/index.js +410 -15
  93. package/dist/core/config.d.ts +20 -0
  94. package/dist/core/config.d.ts.map +1 -1
  95. package/dist/core/config.js +59 -0
  96. package/dist/core/diagnostics/advice.d.ts +9 -0
  97. package/dist/core/diagnostics/advice.d.ts.map +1 -0
  98. package/dist/core/diagnostics/advice.js +112 -0
  99. package/dist/core/diagnostics/bundle.d.ts +51 -0
  100. package/dist/core/diagnostics/bundle.d.ts.map +1 -0
  101. package/dist/core/diagnostics/bundle.js +968 -0
  102. package/dist/core/diagnostics/comparison.d.ts +5 -0
  103. package/dist/core/diagnostics/comparison.d.ts.map +1 -0
  104. package/dist/core/diagnostics/comparison.js +444 -0
  105. package/dist/core/diagnostics/contract.d.ts +245 -0
  106. package/dist/core/diagnostics/contract.d.ts.map +1 -0
  107. package/dist/core/diagnostics/contract.js +18 -0
  108. package/dist/core/diagnostics/identity.d.ts +3 -0
  109. package/dist/core/diagnostics/identity.d.ts.map +1 -0
  110. package/dist/core/diagnostics/identity.js +10 -0
  111. package/dist/core/diagnostics/index.d.ts +8 -0
  112. package/dist/core/diagnostics/index.d.ts.map +1 -0
  113. package/dist/core/diagnostics/index.js +7 -0
  114. package/dist/core/diagnostics/replay.d.ts +8 -0
  115. package/dist/core/diagnostics/replay.d.ts.map +1 -0
  116. package/dist/core/diagnostics/replay.js +213 -0
  117. package/dist/core/diagnostics/sanitize.d.ts +9 -0
  118. package/dist/core/diagnostics/sanitize.d.ts.map +1 -0
  119. package/dist/core/diagnostics/sanitize.js +84 -0
  120. package/dist/core/events/legacy-storage/compression.d.ts +13 -0
  121. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -0
  122. package/dist/core/events/legacy-storage/compression.js +107 -0
  123. package/dist/core/events/legacy-storage/index.d.ts +1 -0
  124. package/dist/core/events/legacy-storage/index.d.ts.map +1 -1
  125. package/dist/core/events/legacy-storage/index.js +1 -0
  126. package/dist/core/events/v3/archive-retention.d.ts +33 -0
  127. package/dist/core/events/v3/archive-retention.d.ts.map +1 -0
  128. package/dist/core/events/v3/archive-retention.js +195 -0
  129. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  130. package/dist/core/events/v3/bootstrap.js +10 -0
  131. package/dist/core/events/v3/capabilities.d.ts +1 -1
  132. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  133. package/dist/core/events/v3/capabilities.js +1 -0
  134. package/dist/core/events/v3/coordination-view.d.ts +3 -0
  135. package/dist/core/events/v3/coordination-view.d.ts.map +1 -1
  136. package/dist/core/events/v3/coordination-view.js +69 -8
  137. package/dist/core/events/v3/index.d.ts +1 -0
  138. package/dist/core/events/v3/index.d.ts.map +1 -1
  139. package/dist/core/events/v3/index.js +1 -0
  140. package/dist/core/events/v3/live-routing.d.ts.map +1 -1
  141. package/dist/core/events/v3/live-routing.js +1 -0
  142. package/dist/core/events/v3/producers/hook-base.d.ts +1 -1
  143. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  144. package/dist/core/events/v3/producers/hook-base.js +13 -2
  145. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  146. package/dist/core/events/v3/producers/intake.js +10 -2
  147. package/dist/core/events/v3/producers/recorder.d.ts +39 -0
  148. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  149. package/dist/core/events/v3/producers/recorder.js +350 -17
  150. package/dist/core/hooks/adapter/output.d.ts +8 -0
  151. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  152. package/dist/core/hooks/adapter/output.js +11 -1
  153. package/dist/core/hooks/cli.js +303 -219
  154. package/dist/core/hooks/health.d.ts +59 -0
  155. package/dist/core/hooks/health.d.ts.map +1 -0
  156. package/dist/core/hooks/health.js +84 -0
  157. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  158. package/dist/core/hooks/resolve/transcript.js +10 -3
  159. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  160. package/dist/core/hooks/session-name-presence.js +4 -1
  161. package/dist/core/qa-artifacts.d.ts +20 -0
  162. package/dist/core/qa-artifacts.d.ts.map +1 -0
  163. package/dist/core/qa-artifacts.js +110 -0
  164. package/dist/core/resources/contract.d.ts +96 -0
  165. package/dist/core/resources/contract.d.ts.map +1 -0
  166. package/dist/core/resources/contract.js +2 -0
  167. package/dist/core/resources/index.d.ts +6 -0
  168. package/dist/core/resources/index.d.ts.map +1 -0
  169. package/dist/core/resources/index.js +5 -0
  170. package/dist/core/resources/sampler.d.ts +34 -0
  171. package/dist/core/resources/sampler.d.ts.map +1 -0
  172. package/dist/core/resources/sampler.js +458 -0
  173. package/dist/core/resources/service-status.d.ts +3 -0
  174. package/dist/core/resources/service-status.d.ts.map +1 -0
  175. package/dist/core/resources/service-status.js +48 -0
  176. package/dist/core/resources/service.d.ts +26 -0
  177. package/dist/core/resources/service.d.ts.map +1 -0
  178. package/dist/core/resources/service.js +292 -0
  179. package/dist/core/resources/storage.d.ts +13 -0
  180. package/dist/core/resources/storage.d.ts.map +1 -0
  181. package/dist/core/resources/storage.js +30 -0
  182. package/dist/core/storage/atomic-json.d.ts +3 -0
  183. package/dist/core/storage/atomic-json.d.ts.map +1 -0
  184. package/dist/core/storage/atomic-json.js +15 -0
  185. package/dist/core/storage/builtins.d.ts.map +1 -1
  186. package/dist/core/storage/builtins.js +45 -6
  187. package/dist/core/storage/logger.d.ts +2 -0
  188. package/dist/core/storage/logger.d.ts.map +1 -1
  189. package/dist/core/storage/logger.js +2 -0
  190. package/dist/core/storage/query.d.ts +14 -0
  191. package/dist/core/storage/query.d.ts.map +1 -1
  192. package/dist/core/storage/query.js +51 -0
  193. package/dist/core/supervisor/activity.d.ts +4 -0
  194. package/dist/core/supervisor/activity.d.ts.map +1 -0
  195. package/dist/core/supervisor/activity.js +141 -0
  196. package/dist/core/supervisor/contract.d.ts +268 -0
  197. package/dist/core/supervisor/contract.d.ts.map +1 -0
  198. package/dist/core/supervisor/contract.js +35 -0
  199. package/dist/core/supervisor/explanations.d.ts +4 -0
  200. package/dist/core/supervisor/explanations.d.ts.map +1 -0
  201. package/dist/core/supervisor/explanations.js +71 -0
  202. package/dist/core/supervisor/findings.d.ts +20 -0
  203. package/dist/core/supervisor/findings.d.ts.map +1 -0
  204. package/dist/core/supervisor/findings.js +399 -0
  205. package/dist/core/supervisor/history.d.ts +9 -0
  206. package/dist/core/supervisor/history.d.ts.map +1 -0
  207. package/dist/core/supervisor/history.js +46 -0
  208. package/dist/core/supervisor/hook-health-alerts.d.ts +14 -0
  209. package/dist/core/supervisor/hook-health-alerts.d.ts.map +1 -0
  210. package/dist/core/supervisor/hook-health-alerts.js +70 -0
  211. package/dist/core/supervisor/hook-health-storage.d.ts +5 -0
  212. package/dist/core/supervisor/hook-health-storage.d.ts.map +1 -0
  213. package/dist/core/supervisor/hook-health-storage.js +24 -0
  214. package/dist/core/supervisor/hook-health.d.ts +72 -0
  215. package/dist/core/supervisor/hook-health.d.ts.map +1 -0
  216. package/dist/core/supervisor/hook-health.js +203 -0
  217. package/dist/core/supervisor/hooks.d.ts +10 -0
  218. package/dist/core/supervisor/hooks.d.ts.map +1 -0
  219. package/dist/core/supervisor/hooks.js +28 -0
  220. package/dist/core/supervisor/index.d.ts +15 -0
  221. package/dist/core/supervisor/index.d.ts.map +1 -0
  222. package/dist/core/supervisor/index.js +14 -0
  223. package/dist/core/supervisor/log-feed.d.ts +9 -0
  224. package/dist/core/supervisor/log-feed.d.ts.map +1 -0
  225. package/dist/core/supervisor/log-feed.js +103 -0
  226. package/dist/core/supervisor/service.d.ts +29 -0
  227. package/dist/core/supervisor/service.d.ts.map +1 -0
  228. package/dist/core/supervisor/service.js +450 -0
  229. package/dist/core/supervisor/services.d.ts +11 -0
  230. package/dist/core/supervisor/services.d.ts.map +1 -0
  231. package/dist/core/supervisor/services.js +155 -0
  232. package/dist/core/supervisor/status.d.ts +3 -0
  233. package/dist/core/supervisor/status.d.ts.map +1 -0
  234. package/dist/core/supervisor/status.js +48 -0
  235. package/dist/core/supervisor/storage.d.ts +30 -0
  236. package/dist/core/supervisor/storage.d.ts.map +1 -0
  237. package/dist/core/supervisor/storage.js +73 -0
  238. package/dist/core/supervisor/timeline.d.ts +3 -0
  239. package/dist/core/supervisor/timeline.d.ts.map +1 -0
  240. package/dist/core/supervisor/timeline.js +87 -0
  241. package/dist/core/workflow/admission.d.ts +23 -0
  242. package/dist/core/workflow/admission.d.ts.map +1 -0
  243. package/dist/core/workflow/admission.js +136 -0
  244. package/dist/core/workflow/engine.d.ts.map +1 -1
  245. package/dist/core/workflow/engine.js +77 -2
  246. package/dist/core/workflow/index.d.ts +3 -2
  247. package/dist/core/workflow/index.d.ts.map +1 -1
  248. package/dist/core/workflow/index.js +2 -1
  249. package/dist/core/workflow/proof.d.ts +2 -1
  250. package/dist/core/workflow/proof.d.ts.map +1 -1
  251. package/dist/core/workflow/proof.js +39 -1
  252. package/dist/core/workflow/run-state.d.ts +3 -2
  253. package/dist/core/workflow/run-state.d.ts.map +1 -1
  254. package/dist/core/workflow/run-state.js +8 -1
  255. package/dist/core/workflow/types.d.ts +33 -1
  256. package/dist/core/workflow/types.d.ts.map +1 -1
  257. package/dist/core/workflow/types.js +2 -1
  258. package/dist/lib/admission.d.ts +71 -0
  259. package/dist/lib/admission.d.ts.map +1 -0
  260. package/dist/lib/admission.js +264 -0
  261. package/dist/lib/agent-browser/client.d.ts +1 -1
  262. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  263. package/dist/lib/agent-browser/client.js +1 -5
  264. package/dist/lib/browser/capture-fidelity.d.ts +39 -0
  265. package/dist/lib/browser/capture-fidelity.d.ts.map +1 -0
  266. package/dist/lib/browser/capture-fidelity.js +84 -0
  267. package/dist/lib/browser/client.d.ts +46 -1
  268. package/dist/lib/browser/client.d.ts.map +1 -1
  269. package/dist/lib/browser/client.js +168 -9
  270. package/dist/lib/browser/critique.d.ts +38 -1
  271. package/dist/lib/browser/critique.d.ts.map +1 -1
  272. package/dist/lib/browser/critique.js +34 -6
  273. package/dist/lib/browser/index.d.ts +5 -1
  274. package/dist/lib/browser/index.d.ts.map +1 -1
  275. package/dist/lib/browser/index.js +4 -0
  276. package/dist/lib/browser/page-review-judge.d.ts +64 -0
  277. package/dist/lib/browser/page-review-judge.d.ts.map +1 -0
  278. package/dist/lib/browser/page-review-judge.js +270 -0
  279. package/dist/lib/browser/page-review-pack.d.ts +613 -0
  280. package/dist/lib/browser/page-review-pack.d.ts.map +1 -0
  281. package/dist/lib/browser/page-review-pack.js +1751 -0
  282. package/dist/lib/browser/qa-run-contracts.d.ts +377 -0
  283. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -0
  284. package/dist/lib/browser/qa-run-contracts.js +361 -0
  285. package/dist/lib/browser/qa-run.d.ts +141 -0
  286. package/dist/lib/browser/qa-run.d.ts.map +1 -0
  287. package/dist/lib/browser/qa-run.js +1283 -0
  288. package/dist/lib/browser/request-diagnostics.d.ts +13 -0
  289. package/dist/lib/browser/request-diagnostics.d.ts.map +1 -0
  290. package/dist/lib/browser/request-diagnostics.js +18 -0
  291. package/dist/lib/browser/tiling.d.ts +19 -0
  292. package/dist/lib/browser/tiling.d.ts.map +1 -1
  293. package/dist/lib/browser/tiling.js +28 -0
  294. package/dist/lib/cookies/client.d.ts +9 -0
  295. package/dist/lib/cookies/client.d.ts.map +1 -1
  296. package/dist/lib/cookies/client.js +197 -44
  297. package/dist/lib/cookies/extra.d.ts +18 -0
  298. package/dist/lib/cookies/extra.d.ts.map +1 -0
  299. package/dist/lib/cookies/extra.js +14 -0
  300. package/dist/lib/cookies/index.d.ts +2 -1
  301. package/dist/lib/cookies/index.d.ts.map +1 -1
  302. package/dist/lib/cookies/index.js +2 -1
  303. package/dist/lib/coord-root-id.d.ts +9 -0
  304. package/dist/lib/coord-root-id.d.ts.map +1 -0
  305. package/dist/lib/coord-root-id.js +17 -0
  306. package/dist/lib/durable-job.d.ts +124 -0
  307. package/dist/lib/durable-job.d.ts.map +1 -0
  308. package/dist/lib/durable-job.js +296 -0
  309. package/dist/lib/http/client.d.ts +7 -1
  310. package/dist/lib/http/client.d.ts.map +1 -1
  311. package/dist/lib/http/client.js +2 -0
  312. package/dist/lib/instructions/apply.d.ts +3 -1
  313. package/dist/lib/instructions/apply.d.ts.map +1 -1
  314. package/dist/lib/instructions/apply.js +74 -3
  315. package/dist/lib/instructions/templates.d.ts.map +1 -1
  316. package/dist/lib/instructions/templates.js +11 -2
  317. package/package.json +19 -2
  318. package/src/commander.ts +75 -1
  319. package/src/commands/admission.ts +699 -0
  320. package/src/commands/agents.ts +189 -50
  321. package/src/commands/artifacts.ts +132 -18
  322. package/src/commands/browse-ai.ts +10 -5
  323. package/src/commands/browse.ts +496 -20
  324. package/src/commands/diagnostics.ts +271 -0
  325. package/src/commands/fetch.ts +1 -0
  326. package/src/commands/files.ts +102 -2
  327. package/src/commands/governor.ts +8 -0
  328. package/src/commands/ledger-v3.ts +82 -0
  329. package/src/commands/qa-record.ts +682 -0
  330. package/src/commands/qa-run.ts +503 -0
  331. package/src/commands/qa-status.ts +608 -0
  332. package/src/commands/qa-verify.ts +238 -0
  333. package/src/commands/resources.ts +38 -0
  334. package/src/commands/review-pack.ts +1281 -0
  335. package/src/commands/supervisor.ts +141 -0
  336. package/src/commands/web.ts +19 -0
  337. package/src/commands/workflow.ts +8 -0
  338. package/src/core/agents/cli.ts +7 -3
  339. package/src/core/agents/health.ts +142 -0
  340. package/src/core/agents/mailbox.ts +373 -0
  341. package/src/core/agents/qa-signal.ts +261 -0
  342. package/src/core/agents/reconcile-coordination-v3.ts +77 -0
  343. package/src/core/agents/render/prompt-context.ts +42 -12
  344. package/src/core/agents/render/session-context.ts +37 -3
  345. package/src/core/agents/rules/commit-conflict.ts +58 -1
  346. package/src/core/agents/rules/stop-hook.ts +6 -12
  347. package/src/core/agents/session-finalizer-v3.ts +4 -1
  348. package/src/core/agents/session-name-display.ts +78 -7
  349. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  350. package/src/core/agents/state/heartbeat-writer.ts +23 -0
  351. package/src/core/agents/state/live-coordination-view.ts +1 -0
  352. package/src/core/agents/state/live-coordination-writer.ts +5 -0
  353. package/src/core/artifacts/constants.ts +1 -1
  354. package/src/core/artifacts/index.ts +565 -30
  355. package/src/core/config.ts +134 -2
  356. package/src/core/diagnostics/advice.ts +147 -0
  357. package/src/core/diagnostics/bundle.ts +1250 -0
  358. package/src/core/diagnostics/comparison.ts +565 -0
  359. package/src/core/diagnostics/contract.ts +311 -0
  360. package/src/core/diagnostics/identity.ts +11 -0
  361. package/src/core/diagnostics/index.ts +7 -0
  362. package/src/core/diagnostics/replay.ts +314 -0
  363. package/src/core/diagnostics/sanitize.ts +97 -0
  364. package/src/core/events/legacy-storage/compression.ts +130 -0
  365. package/src/core/events/legacy-storage/index.ts +1 -0
  366. package/src/core/events/v3/archive-retention.ts +251 -0
  367. package/src/core/events/v3/bootstrap.ts +10 -0
  368. package/src/core/events/v3/capabilities.ts +2 -0
  369. package/src/core/events/v3/coordination-view.ts +100 -11
  370. package/src/core/events/v3/index.ts +9 -0
  371. package/src/core/events/v3/live-routing.ts +1 -0
  372. package/src/core/events/v3/producers/hook-base.ts +14 -2
  373. package/src/core/events/v3/producers/intake.ts +10 -2
  374. package/src/core/events/v3/producers/recorder.ts +420 -17
  375. package/src/core/hooks/adapter/output.ts +12 -1
  376. package/src/core/hooks/cli.ts +332 -247
  377. package/src/core/hooks/health.ts +146 -0
  378. package/src/core/hooks/resolve/transcript.ts +10 -3
  379. package/src/core/hooks/session-name-presence.ts +6 -1
  380. package/src/core/qa-artifacts.ts +126 -0
  381. package/src/core/resources/contract.ts +105 -0
  382. package/src/core/resources/index.ts +5 -0
  383. package/src/core/resources/sampler.ts +590 -0
  384. package/src/core/resources/service-status.ts +62 -0
  385. package/src/core/resources/service.ts +337 -0
  386. package/src/core/resources/storage.ts +44 -0
  387. package/src/core/storage/atomic-json.ts +15 -0
  388. package/src/core/storage/builtins.ts +62 -6
  389. package/src/core/storage/logger.ts +2 -0
  390. package/src/core/storage/query.ts +58 -0
  391. package/src/core/supervisor/activity.ts +193 -0
  392. package/src/core/supervisor/contract.ts +312 -0
  393. package/src/core/supervisor/explanations.ts +104 -0
  394. package/src/core/supervisor/findings.ts +788 -0
  395. package/src/core/supervisor/history.ts +61 -0
  396. package/src/core/supervisor/hook-health-alerts.ts +108 -0
  397. package/src/core/supervisor/hook-health-storage.ts +25 -0
  398. package/src/core/supervisor/hook-health.ts +301 -0
  399. package/src/core/supervisor/hooks.ts +35 -0
  400. package/src/core/supervisor/index.ts +14 -0
  401. package/src/core/supervisor/log-feed.ts +131 -0
  402. package/src/core/supervisor/service.ts +511 -0
  403. package/src/core/supervisor/services.ts +203 -0
  404. package/src/core/supervisor/status.ts +59 -0
  405. package/src/core/supervisor/storage.ts +126 -0
  406. package/src/core/supervisor/timeline.ts +133 -0
  407. package/src/core/workflow/admission.ts +215 -0
  408. package/src/core/workflow/engine.ts +93 -1
  409. package/src/core/workflow/index.ts +10 -0
  410. package/src/core/workflow/proof.ts +54 -1
  411. package/src/core/workflow/run-state.ts +13 -1
  412. package/src/core/workflow/types.ts +39 -1
  413. package/src/lib/admission.ts +347 -0
  414. package/src/lib/agent-browser/client.ts +2 -10
  415. package/src/lib/browser/capture-fidelity.ts +98 -0
  416. package/src/lib/browser/client.ts +212 -10
  417. package/src/lib/browser/critique.ts +62 -7
  418. package/src/lib/browser/index.ts +65 -0
  419. package/src/lib/browser/page-review-judge.ts +360 -0
  420. package/src/lib/browser/page-review-pack.ts +2384 -0
  421. package/src/lib/browser/qa-run-contracts.ts +743 -0
  422. package/src/lib/browser/qa-run.ts +1462 -0
  423. package/src/lib/browser/request-diagnostics.ts +27 -0
  424. package/src/lib/browser/tiling.ts +32 -0
  425. package/src/lib/cookies/client.ts +228 -42
  426. package/src/lib/cookies/extra.ts +28 -0
  427. package/src/lib/cookies/index.ts +2 -0
  428. package/src/lib/coord-root-id.ts +23 -0
  429. package/src/lib/durable-job.ts +407 -0
  430. package/src/lib/http/client.ts +13 -1
  431. package/src/lib/instructions/apply.ts +81 -2
  432. package/src/lib/instructions/templates.ts +11 -2
@@ -21,15 +21,15 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
21
21
  import { dirname, join } from "node:path";
22
22
  import { coordEnv } from "../../lib/env.js";
23
23
  import { buildInstructionBundle } from "../../lib/instructions/bundle.js";
24
- import { coordBinPath } from "../agents/coord-bin.js";
25
24
  import { resolveCoordRoot } from "../agents/coord-client.js";
26
25
  import { classifyWriteClaimFinalization, formatWriteClaimFinalizationDenial, } from "../agents/finalization.js";
27
- import { restoreLiveCoordinationStateAfterEpochV3 } from "../agents/live-authority-v3.js";
26
+ import { recordLiveClaimChangeV3, restoreLiveCoordinationStateAfterEpochV3, } from "../agents/live-authority-v3.js";
28
27
  import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.js";
29
- import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, sessionNameDisplayInstruction, sessionNameDisplayPending, sessionNameDisplayRecoveryInstruction, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
30
- import { readHeartbeat, stampSessionNameSeen } from "../agents/state/heartbeat-writer.js";
28
+ import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, matchSessionNameDisplay, sessionNameDisplayAcceptedNames, sessionNameDisplayInstruction, sessionNameDisplayPending, sessionNameDisplayRecoveryInstruction, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
29
+ import { readHeartbeat, setAssignedNameCache, stampSessionNameRequested, stampSessionNameSeen, } from "../agents/state/heartbeat-writer.js";
31
30
  import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
32
31
  import { ensureLiveCoordinationHeartbeat } from "../agents/state/live-coordination-writer.js";
32
+ import { assignName } from "../agents/state/names.js";
33
33
  import { writePidmapRow } from "../agents/state/pidmap.js";
34
34
  import { agentsRequireGitFinalization, resolveBinName } from "../config.js";
35
35
  import { checkpointContext, completeContextRecovery, extractContextSample, markContextCompactionCompleted, prepareContextRecovery, readContextState, recordContextSample, } from "../context/index.js";
@@ -46,12 +46,14 @@ import { codexWslFileLinkTelemetry, inspectCodexWslBridge, isWslUncPath, renderC
46
46
  import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
47
47
  import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
48
48
  import { canonicalize } from "./guard-path.js";
49
+ import { beginHookHealth, observeHookDebug, observeHookError, writeHookHealthCompletion, } from "./health.js";
49
50
  import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.js";
50
51
  import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
51
52
  import { resolveOwner } from "./resolve/owner.js";
52
53
  import { inspectSessionNameDisplayImmediately, scanAssistantStatusBoxPresent, scanSessionNameDisplayedImmediately, scanStatusBoxPresent, scanTranscriptRuntime, } from "./resolve/transcript.js";
53
54
  import { sessionNamePresence } from "./session-name-presence.js";
54
55
  import { unsafeCrossShellReason } from "./unsafe-cross-shell.js";
56
+ let hookHealthState;
55
57
  function parseArgv(argv) {
56
58
  const out = { eventName: null, extra: [] };
57
59
  for (let i = 0; i < argv.length; i++) {
@@ -90,16 +92,15 @@ async function readStdin() {
90
92
  }
91
93
  }
92
94
  /**
93
- * Env for child agent-coord spawns, with the coord root pinned. The hook
94
- * process may be running with the session shell's cwd (inside a submodule or
95
- * journal dir), and agent-coord resolves its root from cwd unless overridden —
96
- * without the pin a child could write state to a different .harnery than the
97
- * one this hook resolved.
95
+ * Env for a detached hook retry worker, with the coord root pinned. The hook
96
+ * may run with the session shell's cwd (inside a submodule or journal dir), so
97
+ * the worker must retain the root this process already resolved.
98
98
  */
99
99
  function childEnv(coordRoot) {
100
100
  return { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot };
101
101
  }
102
102
  function appendDebug(coordRoot, entry) {
103
+ observeHookDebug(hookHealthState, entry);
103
104
  if (coordEnv("SHARED_LOGS") !== "0") {
104
105
  try {
105
106
  processLogger(coordRoot, "agent-hook").debug("agent_hook.diagnostic", legacyLogFields(entry));
@@ -119,6 +120,7 @@ function appendDebug(coordRoot, entry) {
119
120
  }
120
121
  }
121
122
  function logError(coordRoot, err, context) {
123
+ observeHookError(hookHealthState, context.phase);
122
124
  if (!coordRoot)
123
125
  return;
124
126
  if (coordEnv("SHARED_LOGS") !== "0") {
@@ -145,38 +147,41 @@ function logError(coordRoot, err, context) {
145
147
  }
146
148
  }
147
149
  /**
148
- * Spawn `agent-coord assign-name <owner> <kind>` to mint or recover the
149
- * hurricane-style name for this owner. Returns null on any failure so
150
- * session.started emission never breaks the adapter flow.
151
- *
152
- * Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
153
- * separation: we spawn rather than import.
150
+ * Mint or recover the hurricane-style name for this owner in-process. Returns
151
+ * null on any failure so session.started emission never breaks adapter flow.
154
152
  */
155
- function assignNameViaAgentCoord(coordRoot, instanceId, kind, forkedFrom) {
156
- const binary = coordBinPath("agent-coord", coordRoot) ?? "";
157
- if (!existsSync(binary))
158
- return null;
153
+ function assignNameInProcess(coordRoot, instanceId, kind, forkedFrom) {
159
154
  try {
160
- const args = ["assign-name", instanceId, kind];
161
- if (forkedFrom)
162
- args.push("--forked-from", forkedFrom);
163
- const result = spawnSync(binary, args, {
164
- encoding: "utf8",
165
- timeout: 2000,
166
- env: childEnv(coordRoot),
167
- });
168
- if (result.status !== 0 || !result.stdout)
169
- return null;
170
- const parsed = JSON.parse(result.stdout.trim());
171
- if (parsed.name && parsed.kind) {
172
- return { name: parsed.name, kind: parsed.kind };
173
- }
174
- return null;
155
+ const name = assignName(coordRoot, instanceId, kind, forkedFrom ? { forkedFrom } : undefined);
156
+ return { name, kind };
175
157
  }
176
158
  catch {
177
159
  return null;
178
160
  }
179
161
  }
162
+ /** Mid-flight sessions can emit tools before SessionStart assigns a pool name.
163
+ * Stamp the disposable cache so Codec does not render the 8-character id. */
164
+ function ensureSessionDisplayName(coordRoot, instanceId, sessionId, adapter) {
165
+ if (coordEnv("WORKFLOW_CHILD") === "1")
166
+ return;
167
+ const row = readLiveCoordinationRow(coordRoot, instanceId) ?? readHeartbeat(coordRoot, instanceId);
168
+ if (row?.kind === "subagent" || row?.kind === "transient" || row?.workflow_run_id)
169
+ return;
170
+ if (row?.name?.trim())
171
+ return;
172
+ const assigned = assignNameInProcess(coordRoot, instanceId, "session");
173
+ if (!assigned?.name)
174
+ return;
175
+ if (!readHeartbeat(coordRoot, instanceId)) {
176
+ try {
177
+ ensureLiveCoordinationHeartbeat(coordRoot, instanceId, sessionId, adapter);
178
+ }
179
+ catch {
180
+ /* cache materialization is best-effort; history still holds the name */
181
+ }
182
+ }
183
+ setAssignedNameCache(coordRoot, instanceId, assigned.name, "session");
184
+ }
180
185
  /**
181
186
  * Direct (in-process) pidmap write, avoiding the spawn overhead of going via
182
187
  * the agent-coord CLI for every session.started signal. Pid-map rows
@@ -227,9 +232,9 @@ function buildEventData(eventType, ctx) {
227
232
  // tool.requested heal path instead. The forkedFrom plumbing below stays
228
233
  // for adapters that DO report a parent at session start.
229
234
  const forkedFrom = undefined;
230
- // Assign (or recover) name + kind via agent-coord. Idempotent: resume
235
+ // Assign (or recover) name + kind in-process. Idempotent: resume
231
236
  // returns the original name; new owner consumes a counter slot.
232
- const assigned = assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
237
+ const assigned = assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
233
238
  // Write the adapter pid-map row so `harn agents whoami` ppid-walks find
234
239
  // this owner. Prefer the payload pid (the actual claude binary), then the
235
240
  // anchor walk (the `node` ancestor for Cursor, which has no payload pid),
@@ -288,12 +293,12 @@ function buildEventData(eventType, ctx) {
288
293
  clean_exit: p?.clean_exit ?? true,
289
294
  };
290
295
  case "turn.started": {
291
- // Cursor can deliver beforeSubmitPrompt before sessionStart. Ensure that
292
- // high-confidence prompt bootstrap also mints durable display identity;
293
- // a later SessionStart remains idempotent.
294
- if (ctx.adapter === "cursor" &&
295
- !readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
296
- assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session");
296
+ // Mid-flight onboarding (any adapter) can observe a turn before
297
+ // SessionStart. Mint durable display identity here so a later
298
+ // SessionStart remains idempotent and Codec is not stuck on the
299
+ // 8-character native id.
300
+ if (!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
301
+ assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session");
297
302
  }
298
303
  const prompt = p?.prompt ?? "";
299
304
  const { value, truncated } = clampString(prompt, 4000);
@@ -351,7 +356,7 @@ function buildEventData(eventType, ctx) {
351
356
  // (agent-coord/state/names.ts → kind=transient). Use the call ID as the
352
357
  // instance_id input; assignName falls through to transient.
353
358
  const childInstanceId = subagentCallId ?? ctx.instanceId;
354
- const assigned = assignNameViaAgentCoord(ctx.coordRoot, childInstanceId, "subagent");
359
+ const assigned = assignNameInProcess(ctx.coordRoot, childInstanceId, "subagent");
355
360
  return {
356
361
  agent_type: p?.raw.agent_type ??
357
362
  p?.raw.subagent_type ??
@@ -466,10 +471,18 @@ function numberField(value) {
466
471
  }
467
472
  async function main() {
468
473
  const hookStartedAt = performance.now();
474
+ const hookStartedRss = process.memoryUsage().rss;
469
475
  const hookClock = captureSpanClockV3();
470
476
  const { eventName, extra } = parseArgv(process.argv.slice(2));
471
477
  const adapter = detectAdapter(process.argv.slice(2));
472
478
  const raw = await readStdin();
479
+ hookHealthState = beginHookHealth({
480
+ started_at_ms: hookStartedAt,
481
+ started_rss_bytes: hookStartedRss,
482
+ event_name: eventName,
483
+ adapter,
484
+ payload_bytes: raw.length,
485
+ });
473
486
  // Cursor executes the Claude Code project hooks too on hosts wired for both
474
487
  // adapters, piping them its own payload (identifiable by the top-level
475
488
  // cursor_version envelope field). That stray `--adapter claude-code`
@@ -480,6 +493,7 @@ async function main() {
480
493
  if (coordEnv("AGENT_COORD_OFF") !== "1") {
481
494
  const coordRoot = resolveCoordRoot(process.cwd());
482
495
  if (coordRoot) {
496
+ hookHealthState.coord_root = coordRoot;
483
497
  appendDebug(coordRoot, {
484
498
  ts: new Date().toISOString(),
485
499
  event_name: eventName,
@@ -532,6 +546,7 @@ async function main() {
532
546
  const coordRoot = resolveCoordRoot(process.cwd());
533
547
  if (!coordRoot)
534
548
  return 0;
549
+ hookHealthState.coord_root = coordRoot;
535
550
  if (eventName === "runtime-context-retry") {
536
551
  return runRuntimeContextRetryWorker(coordRoot, adapter, extra);
537
552
  }
@@ -557,8 +572,9 @@ async function main() {
557
572
  // that tool. It is not a transcript and therefore cannot prove that an
558
573
  // earlier assistant response omitted the required session-name block.
559
574
  // afterAgentResponse is the one native boundary that carries the completed
560
- // assistant text, so stamp a positive sighting here without retaining or
561
- // emitting the response body into V3.
575
+ // assistant text. Preserve only privacy-safe ritual booleans in the V3
576
+ // producer's open-turn state; Stop consumes them into the authoritative
577
+ // turn.completed event. The response body never enters V3 or heartbeat.
562
578
  if (adapter === "cursor" && eventName === "after-agent-response") {
563
579
  const payload = parsePayload(raw, adapter);
564
580
  const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
@@ -566,15 +582,82 @@ async function main() {
566
582
  appendDebug(coordRoot, { ...debugBase, skipped: "no-owner-resolved" });
567
583
  return 0;
568
584
  }
569
- const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, owner.instance_id));
585
+ const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
586
+ const name = sessionNameDisplayPending(row);
570
587
  const text = typeof payload?.raw.text === "string" ? payload.raw.text : "";
571
- if (name && assistantTextStartsWithSessionNameBlock(text, name)) {
572
- stampSessionNameSeen(coordRoot, owner.instance_id, name);
573
- appendDebug(coordRoot, { ...debugBase, effect: "session-name-display-stamped" });
588
+ const sighting = matchSessionNameDisplay(row, text);
589
+ if (sighting) {
590
+ stampSessionNameSeen(coordRoot, owner.instance_id, sighting.pending);
591
+ appendDebug(coordRoot, {
592
+ ...debugBase,
593
+ effect: "session-name-display-stamped",
594
+ session_name_pending: sighting.pending,
595
+ ...(sighting.displayed === sighting.pending
596
+ ? {}
597
+ : { session_name_displayed: sighting.displayed, session_name_drift: true }),
598
+ });
599
+ }
600
+ else if (!name) {
601
+ // Nothing was owed. Distinct from a miss: conflating the two is what made
602
+ // a latched Cursor session undiagnosable from this log.
603
+ appendDebug(coordRoot, { ...debugBase, skipped: "no-pending-session-name" });
574
604
  }
575
605
  else {
576
- appendDebug(coordRoot, { ...debugBase, skipped: "no-session-name-sighting" });
606
+ appendDebug(coordRoot, {
607
+ ...debugBase,
608
+ skipped: "session-name-block-absent",
609
+ session_name_pending: name,
610
+ reply_bytes: text.length,
611
+ reply_leads_with_fence: /^\s*`{3,}/.test(text),
612
+ });
613
+ }
614
+ if (ledgerRoute.state === "blocked") {
615
+ appendDebug(coordRoot, {
616
+ ...debugBase,
617
+ skipped: "v3-control-blocked",
618
+ reason: ledgerRoute.reason,
619
+ owner_source: owner.source,
620
+ });
621
+ return 0;
577
622
  }
623
+ const statusBoxPresentStrict = scanAssistantStatusBoxPresent(undefined, text);
624
+ // The hook parser needs the completed reply long enough to scan it, but
625
+ // durable hook intake must never retain that body. Keep every other native
626
+ // field so turn correlation remains available to the producer.
627
+ const { text: _completedReply, ...privacySafeRaw } = payload?.raw ?? {};
628
+ const privacySafePayload = payload ? { ...payload, raw: privacySafeRaw } : null;
629
+ const v3Result = recordLiveHookSignalV3({
630
+ coordRoot,
631
+ route: ledgerRoute,
632
+ eventName,
633
+ payload: privacySafePayload,
634
+ adapter,
635
+ instanceId: owner.instance_id,
636
+ hook_name: eventName,
637
+ hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
638
+ monotonic_ns: hookClock.monotonic_ns,
639
+ turn_ritual: {
640
+ status_box_present: statusBoxPresentStrict,
641
+ status_box_present_strict: statusBoxPresentStrict,
642
+ session_name_required: name !== undefined,
643
+ session_name_present: name ? assistantTextStartsWithSessionNameBlock(text, name) : false,
644
+ },
645
+ });
646
+ observeHookDebug(hookHealthState, { event_v3_state: v3Result.state });
647
+ appendDebug(coordRoot, {
648
+ ...debugBase,
649
+ effect: "cursor-response-ritual-observed",
650
+ owner_source: owner.source,
651
+ event_v3_state: v3Result.state,
652
+ ...(v3Result.state === "observed"
653
+ ? {
654
+ generation_id: v3Result.generation_id,
655
+ turn_id: v3Result.turn_id,
656
+ response_observed_at: v3Result.observed_at,
657
+ status_box_present_strict: statusBoxPresentStrict,
658
+ }
659
+ : {}),
660
+ });
578
661
  return 0;
579
662
  }
580
663
  const norm = normalizeEventName(eventName);
@@ -595,8 +678,17 @@ async function main() {
595
678
  });
596
679
  return 0;
597
680
  }
681
+ hookHealthState.owner_id = owner.instance_id;
598
682
  const sessionId = payload?.session_id ?? payload?.conversation_id ?? owner.instance_id;
599
683
  const priorCoordination = readHeartbeat(coordRoot, owner.instance_id);
684
+ if (norm.event_type !== "session.ended" &&
685
+ coordEnv("WORKFLOW_CHILD") !== "1" &&
686
+ priorCoordination?.kind !== "subagent" &&
687
+ priorCoordination?.kind !== "transient" &&
688
+ !priorCoordination?.workflow_run_id &&
689
+ !priorCoordination?.name?.trim()) {
690
+ assignNameInProcess(coordRoot, owner.instance_id, "session");
691
+ }
600
692
  const data = buildEventData(norm.event_type, {
601
693
  coordRoot,
602
694
  payload,
@@ -661,6 +753,9 @@ async function main() {
661
753
  ...(turnRitual ? { turn_ritual: turnRitual } : {}),
662
754
  ...(deferV3Drain ? { defer_drain: true } : {}),
663
755
  });
756
+ observeHookDebug(hookHealthState, {
757
+ ...(v3Result ? { event_v3_state: v3Result.state } : {}),
758
+ });
664
759
  const recordedGenerationId = v3Result && "event" in v3Result && "generation_id" in v3Result.event.scope
665
760
  ? v3Result.event.scope.generation_id
666
761
  : undefined;
@@ -679,6 +774,14 @@ async function main() {
679
774
  logError(coordRoot, error, { phase: "epoch-coordination-restore" });
680
775
  }
681
776
  }
777
+ if (norm.event_type !== "session.ended") {
778
+ try {
779
+ ensureSessionDisplayName(coordRoot, owner.instance_id, sessionId, adapter);
780
+ }
781
+ catch (error) {
782
+ logError(coordRoot, error, { phase: "session-display-name" });
783
+ }
784
+ }
682
785
  const v3EventId = v3Result && "event" in v3Result
683
786
  ? v3Result.event.event_id
684
787
  : v3Result && "event_id" in v3Result
@@ -806,6 +909,7 @@ async function main() {
806
909
  // Session start is the only caller of the artifact janitor and the storage
807
910
  // maintenance pass, so both load here instead of on every tool call.
808
911
  const { autoCleanArtifacts } = await import("../artifacts/index.js");
912
+ const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
809
913
  const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
810
914
  // Effect: throttled daily sweep of expired artifact workspaces (guarded,
811
915
  // managed-expired only; see autoCleanArtifacts). Best-effort like every
@@ -816,6 +920,14 @@ async function main() {
816
920
  catch (err) {
817
921
  logError(coordRoot, err, { phase: "artifact-auto-clean" });
818
922
  }
923
+ // Effect: apply the same daily throttle to closed V3 epochs. The newest
924
+ // recovery epochs remain protected by the archive retention policy.
925
+ try {
926
+ autoCleanEventV3Archives(coordRoot);
927
+ }
928
+ catch (err) {
929
+ logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
930
+ }
819
931
  // Plan one bounded, claim-first global slice from cached pressure only.
820
932
  // Existing owner janitors above remain the only active executors until
821
933
  // each provider receives a separate production activation.
@@ -874,7 +986,7 @@ async function main() {
874
986
  // agnostic since v0.5.0.
875
987
  if (norm.event_type === "session.ended") {
876
988
  try {
877
- cleanupSessionEnd(coordRoot, owner.instance_id, data.reason ?? "unknown");
989
+ cleanupSessionEnd(coordRoot, owner.instance_id);
878
990
  }
879
991
  catch (err) {
880
992
  logError(coordRoot, err, { phase: "session-end-cleanup" });
@@ -900,40 +1012,17 @@ async function main() {
900
1012
  if (norm.event_type === "agent.started") {
901
1013
  try {
902
1014
  const childInstanceId = data.agent_id ?? owner.instance_id;
903
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
904
- if (existsSync(agentCoordBin)) {
905
- spawnSync(agentCoordBin, ["project"], {
906
- encoding: "utf8",
907
- timeout: 3000,
908
- env: childEnv(coordRoot),
909
- });
910
- spawnSync(agentCoordBin, [
911
- "log",
912
- `SUBAGENT_START agent_type=${data.agent_type ?? "unknown"} agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
913
- "--instance",
914
- childInstanceId,
915
- ], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
916
- }
917
- emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
1015
+ await emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
918
1016
  }
919
1017
  catch (err) {
920
- logError(coordRoot, err, { phase: "subagent-start-project" });
1018
+ logError(coordRoot, err, { phase: "subagent-start-context" });
921
1019
  }
922
1020
  }
923
1021
  // Phase 8: SubagentStop: delete subagent heartbeat + log.
924
1022
  if (norm.event_type === "agent.completed") {
925
1023
  try {
926
1024
  const childInstanceId = payload?.subagent_id ?? payload?.agent_id ?? owner.instance_id;
927
- cleanupSessionEnd(coordRoot, childInstanceId, data.reason ?? "unknown");
928
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
929
- if (existsSync(agentCoordBin)) {
930
- spawnSync(agentCoordBin, [
931
- "log",
932
- `SUBAGENT_STOP agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
933
- "--instance",
934
- childInstanceId,
935
- ], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
936
- }
1025
+ cleanupSessionEnd(coordRoot, childInstanceId);
937
1026
  const { reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
938
1027
  reconcileSessionFinalizationV3(coordRoot, { archive_observations: [] });
939
1028
  }
@@ -1147,6 +1236,11 @@ async function main() {
1147
1236
  try {
1148
1237
  const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, owner.instance_id));
1149
1238
  if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
1239
+ // Record the title before asking for it. The suggestion can still
1240
+ // change afterwards (an assigned-name rewrite, a lifecycle re-mint, a
1241
+ // rebuilt cache), and the agent must not be stranded for displaying
1242
+ // exactly what it was handed.
1243
+ stampSessionNameRequested(coordRoot, owner.instance_id, name);
1150
1244
  const { emitContext } = await import("./adapter/output.js");
1151
1245
  emitContext(adapter, "PostToolUse", sessionNameDisplayInstruction(name));
1152
1246
  }
@@ -1211,21 +1305,7 @@ async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter,
1211
1305
  const command = extractBashCommand(payload?.tool_name, payload?.tool_input);
1212
1306
  if (isSessionNameRemediationCommand(command, resolveBinName(coordRoot)))
1213
1307
  return true;
1214
- const inspection = adapter === "cursor" && payload?.agent_message !== undefined
1215
- ? {
1216
- state: assistantTextStartsWithSessionNameBlock(payload.agent_message, name)
1217
- ? "present"
1218
- : "unavailable",
1219
- }
1220
- : inspectSessionNameDisplayImmediately(
1221
- // Codex hook payloads omit transcript_path on every event, which
1222
- // left this inspection permanently unavailable and the latch never
1223
- // stamped. Discover the rollout by session id; the scan only runs
1224
- // while a name is pending, so the latch closes after one success.
1225
- payload?.transcript_path ??
1226
- (adapter === "codex"
1227
- ? discoverCodexSessionTranscript(payload?.session_id ?? instanceId)
1228
- : undefined), name, assistantTextStartsWithSessionNameBlock);
1308
+ const inspection = inspectDisplayEvidence(instanceId, adapter, payload, coordination);
1229
1309
  if (inspection.state === "present") {
1230
1310
  stampSessionNameSeen(coordRoot, instanceId, name);
1231
1311
  return true;
@@ -1238,10 +1318,53 @@ async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter,
1238
1318
  // readable transcript can still verify it.
1239
1319
  return true;
1240
1320
  }
1321
+ // Record the refusal. Without this the gate was the one enforcement path
1322
+ // that left no trace, so a session reporting "the gate keeps denying me"
1323
+ // could not be confirmed or refuted from the hook log.
1324
+ appendDebug(coordRoot, {
1325
+ ts: new Date().toISOString(),
1326
+ event_name: "pre-tool-use",
1327
+ adapter,
1328
+ effect: "session-name-display-denied",
1329
+ session_name_pending: name,
1330
+ tool_name: payload?.tool_name,
1331
+ });
1241
1332
  const { emitDeny } = await import("./adapter/output.js");
1242
1333
  emitDeny(adapter, sessionNameDisplayRecoveryInstruction(name, resolveBinName(coordRoot)));
1243
1334
  return false;
1244
1335
  }
1336
+ /**
1337
+ * Evidence for the pending display, over every title whose display counts.
1338
+ *
1339
+ * Cursor supplies the current narration directly; Claude Code and Codex are
1340
+ * resolved from their JSONL transcripts. Absent is the only state that denies,
1341
+ * so a title is refused only when a readable surface positively shows some
1342
+ * other opening text for every accepted title.
1343
+ */
1344
+ function inspectDisplayEvidence(instanceId, adapter, payload, coordination) {
1345
+ if (adapter === "cursor" && payload?.agent_message !== undefined) {
1346
+ return matchSessionNameDisplay(coordination, payload.agent_message)
1347
+ ? { state: "present" }
1348
+ : { state: "unavailable", reason: "transcript_not_ready" };
1349
+ }
1350
+ // Codex hook payloads omit transcript_path on every event, which left this
1351
+ // inspection permanently unavailable and the latch never stamped. Discover
1352
+ // the rollout by session id; the scan only runs while a name is pending, so
1353
+ // the latch closes after one success.
1354
+ const transcriptPath = payload?.transcript_path ??
1355
+ (adapter === "codex"
1356
+ ? discoverCodexSessionTranscript(payload?.session_id ?? instanceId)
1357
+ : undefined);
1358
+ let verdict = { state: "absent" };
1359
+ for (const candidate of sessionNameDisplayAcceptedNames(coordination)) {
1360
+ const inspection = inspectSessionNameDisplayImmediately(transcriptPath, candidate, assistantTextStartsWithSessionNameBlock);
1361
+ if (inspection.state === "present")
1362
+ return inspection;
1363
+ if (inspection.state === "unavailable")
1364
+ verdict = inspection;
1365
+ }
1366
+ return verdict;
1367
+ }
1245
1368
  function generationBoundHeartbeat(heartbeat, generationId) {
1246
1369
  return generationId && heartbeat?.v3_generation_id === generationId ? heartbeat : null;
1247
1370
  }
@@ -1273,40 +1396,19 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
1273
1396
  }
1274
1397
  if (targets.length === 0)
1275
1398
  return true;
1276
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1277
- if (!existsSync(agentCoordBin)) {
1278
- if (agentsRequireGitFinalization(coordRoot)) {
1279
- const { emitDeny } = await import("./adapter/output.js");
1280
- emitDeny(adapter, "Harnery denied the write before mutation because the claim coordinator is unavailable; repair the project's Harnery installation and retry.");
1281
- return false;
1282
- }
1283
- return true;
1284
- }
1399
+ const { evaluateClaim } = await import("../agents/rules/claim-conflict.js");
1285
1400
  // For apply_patch (multi-file), collect siblings so the deny reason names
1286
1401
  // them. For single-file tools the array has one entry.
1287
1402
  for (const target of targets) {
1288
- const verdictReq = JSON.stringify({
1289
- rule: "claim",
1290
- instance_id: instanceId,
1291
- session_id: sessionId,
1292
- path: target.path,
1293
- });
1294
- const result = spawnSync(agentCoordBin, ["verdict"], {
1295
- input: verdictReq,
1296
- encoding: "utf8",
1297
- timeout: 3000,
1298
- env: childEnv(coordRoot),
1299
- });
1300
- if (result.status !== 0 || !result.stdout)
1301
- continue;
1302
- let parsed = {};
1303
1403
  try {
1304
- parsed = JSON.parse(result.stdout.trim());
1305
- }
1306
- catch {
1307
- continue;
1308
- }
1309
- if (parsed.allow === false) {
1404
+ const parsed = evaluateClaim(coordRoot, {
1405
+ rule: "claim",
1406
+ instance_id: instanceId,
1407
+ session_id: sessionId,
1408
+ path: target.path,
1409
+ });
1410
+ if (parsed.allow !== false)
1411
+ continue;
1310
1412
  let reason = parsed.reason ?? `Path ${target.path} is currently being edited by another agent.`;
1311
1413
  if (targets.length > 1) {
1312
1414
  const siblings = targets
@@ -1322,6 +1424,7 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
1322
1424
  emitDeny(adapter, reason);
1323
1425
  return false;
1324
1426
  }
1427
+ catch { }
1325
1428
  }
1326
1429
  return true;
1327
1430
  }
@@ -1520,16 +1623,23 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
1520
1623
  const row = readLiveCoordinationRow(coordRoot, instanceId);
1521
1624
  if (!row?.files_touched?.includes(canonical))
1522
1625
  return;
1523
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1524
- if (!existsSync(agentCoordBin))
1525
- return;
1526
- spawnSync(agentCoordBin, ["release-claim", instanceId, canonical], {
1527
- encoding: "utf8",
1528
- timeout: 2000,
1529
- env: childEnv(coordRoot),
1530
- });
1626
+ const before = readHeartbeat(coordRoot, instanceId);
1627
+ try {
1628
+ recordLiveClaimChangeV3({
1629
+ coordRoot,
1630
+ owner: instanceId,
1631
+ nativeSessionId: before?.session_id ?? instanceId,
1632
+ adapter: adapterFromPlatform(before?.platform),
1633
+ operation: "released",
1634
+ path: canonical,
1635
+ access: "write",
1636
+ });
1637
+ }
1638
+ catch {
1639
+ /* best-effort release must not break adapter flow */
1640
+ }
1531
1641
  }
1532
- function cleanupSessionEnd(coordRoot, instanceId, reason) {
1642
+ function cleanupSessionEnd(coordRoot, instanceId) {
1533
1643
  // Sweep pid-map entries pointing to this instance
1534
1644
  const pidmapDir = join(coordRoot, ".harnery", "pid-map");
1535
1645
  if (existsSync(pidmapDir)) {
@@ -1553,44 +1663,20 @@ function cleanupSessionEnd(coordRoot, instanceId, reason) {
1553
1663
  /* swallow */
1554
1664
  }
1555
1665
  }
1556
- // Activity log
1557
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1558
- if (existsSync(agentCoordBin)) {
1559
- spawnSync(agentCoordBin, ["log", `SESSION_END reason=${reason}`, "--instance", instanceId], { encoding: "utf8", timeout: 2000 });
1560
- }
1561
1666
  }
1562
1667
  async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "") {
1563
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1564
- let additionalContext = "";
1565
- if (existsSync(agentCoordBin)) {
1566
- const args = ["prompt-context", "--instance", instanceId, "--session", sessionId];
1567
- // Every human-facing adapter gets the first-session naming reminder from
1568
- // the shared hook path. Cursor + Codex additionally get ongoing set-task
1569
- // staleness nudges; Claude Code enforces those via its Stop transcript.
1570
- // Codex also gets the optional project-owned prompt reminder. Claude Code
1571
- // already has native output-style reminders, so duplicating it would add
1572
- // noise. Cursor's current beforeSubmitPrompt output can allow or block but
1573
- // cannot inject model context; an Always Apply rule is its durable carrier.
1574
- // Only the boolean route travels through argv, never the host's text.
1575
- if (adapter === "codex")
1576
- args.push("--host-prompt-reminder");
1577
- // Codex gets a fresh status-footer reminder on every prompt. Its Stop hook
1578
- // stays observe-only, so missing the footer never replaces the answer.
1579
- // Claude Code gets a fresh per-turn ritual reminder: satisfying the
1580
- // contract up front is far cheaper than the bounce-and-retry the Stop hook
1581
- // otherwise forces. Cursor's beforeSubmitPrompt cannot inject context, so
1582
- // its Stop follow-up remains the supported enforcement channel.
1583
- args.push("--session-name-nudge");
1584
- if (adapter === "cursor" || adapter === "codex")
1585
- args.push("--task-nudge");
1586
- if (adapter === "codex")
1587
- args.push("--status-footer-nudge");
1588
- if (adapter === "claude-code")
1589
- args.push("--turn-ritual-nudge", adapter);
1590
- const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1591
- if (result.status === 0 && result.stdout)
1592
- additionalContext = result.stdout.trim();
1593
- }
1668
+ const { renderPromptContext } = await import("../agents/render/prompt-context.js");
1669
+ let additionalContext = renderPromptContext({
1670
+ coordRoot,
1671
+ instanceId,
1672
+ sessionId,
1673
+ adapter,
1674
+ sessionNameNudge: true,
1675
+ taskNudge: adapter === "cursor" || adapter === "codex",
1676
+ hostPromptReminder: adapter === "codex",
1677
+ statusFooterNudge: adapter === "codex",
1678
+ turnRitualNudge: adapter === "claude-code" ? adapter : undefined,
1679
+ }).trim();
1594
1680
  if (adapter === "codex") {
1595
1681
  const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, workspaceCwd);
1596
1682
  if (fileLinkContext) {
@@ -1606,8 +1692,8 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
1606
1692
  emitContext(adapter, "UserPromptSubmit", additionalContext);
1607
1693
  return true;
1608
1694
  }
1609
- function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
1610
- // Look up the subagent's assigned name (just-written by agent-coord assignName
1695
+ async function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
1696
+ // Look up the subagent's assigned name (just-written by the in-process name
1611
1697
  // in session.started data) + the parent's short id for the "you are a subagent
1612
1698
  // of X" framing.
1613
1699
  const subagentName = data.name ?? "";
@@ -1619,69 +1705,55 @@ function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapte
1619
1705
  // Render peer table inline since the subagent might want to know who else
1620
1706
  // is around. Reuse prompt-context (which dedups against the per-owner hash);
1621
1707
  // first call will always emit.
1622
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1623
1708
  let combined = message;
1624
- if (existsSync(agentCoordBin)) {
1625
- const result = spawnSync(agentCoordBin, ["prompt-context", "--instance", instanceId, "--session", sessionId, "--name", subagentName], { encoding: "utf8", timeout: 3000 });
1626
- const ctx = (result.stdout ?? "").trim();
1627
- if (ctx)
1628
- combined = `${message}\n\n${ctx}`;
1629
- }
1709
+ const { renderPromptContext } = await import("../agents/render/prompt-context.js");
1710
+ const context = renderPromptContext({
1711
+ coordRoot,
1712
+ instanceId,
1713
+ sessionId,
1714
+ agentName: subagentName,
1715
+ }).trim();
1716
+ if (context)
1717
+ combined = `${message}\n\n${context}`;
1630
1718
  // Use SubagentStart event-name in CC's hookSpecificOutput shape; cursor's
1631
1719
  // flat `additional_context` works the same way.
1632
- void import("./adapter/output.js").then(({ emitContext }) => {
1633
- emitContext(adapter, "SubagentStart", combined);
1634
- });
1720
+ const { emitContext } = await import("./adapter/output.js");
1721
+ emitContext(adapter, "SubagentStart", combined);
1635
1722
  }
1636
1723
  function adapterPlatform(adapter) {
1637
1724
  if (adapter === "claude-code")
1638
1725
  return "claude-code";
1639
1726
  return adapter;
1640
1727
  }
1728
+ function adapterFromPlatform(platform) {
1729
+ if (platform === "cursor")
1730
+ return "cursor";
1731
+ if (platform === "codex")
1732
+ return "codex";
1733
+ return "claude-code";
1734
+ }
1641
1735
  async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, emittedData, adapter, recoveryBriefing = "") {
1642
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1643
1736
  const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
1644
1737
  let additionalContext = "";
1645
- if (existsSync(agentCoordBin)) {
1646
- // Sync-project so the heartbeat exists for downstream readers (peer table,
1647
- // wiring check, council invites).
1648
- spawnSync(agentCoordBin, ["project"], { encoding: "utf8", timeout: 3000 });
1649
- // Opportunistic reconciliation makes normal session starts a failsafe for
1650
- // archive, idle, cascade, and host lifecycle observations.
1651
- spawnSync(agentCoordBin, ["reconcile-finalization"], {
1652
- encoding: "utf8",
1653
- timeout: 5000,
1654
- env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot },
1655
- });
1656
- // SESSION_START activity log line, fired across all adapters.
1657
- const model = emittedData.model ?? "unknown";
1658
- const source = emittedData.source ?? "startup";
1659
- const platform = adapterPlatform(adapter);
1660
- spawnSync(agentCoordBin, [
1661
- "log",
1662
- `SESSION_START model=${model} source=${source} platform=${platform}`,
1663
- "--instance",
1738
+ // Opportunistic reconciliation makes normal session starts a failsafe for
1739
+ // archive, idle, cascade, and host lifecycle observations. It also runs the
1740
+ // stale cache sweep through the shared ADR 0077 composition.
1741
+ const { reconcileCoordinationV3 } = await import("../agents/reconcile-coordination-v3.js");
1742
+ reconcileCoordinationV3(coordRoot);
1743
+ // Workflow children retain lifecycle/event capture but do not receive
1744
+ // operator-facing peer, council, or init-remediation context. Injecting
1745
+ // that context can make a bounded child follow housekeeping instructions
1746
+ // instead of its assigned prompt.
1747
+ if (!workflowChild) {
1748
+ const agentName = emittedData.name ?? "";
1749
+ const { renderSessionContext } = await import("../agents/render/session-context.js");
1750
+ additionalContext = renderSessionContext({
1751
+ coordRoot,
1664
1752
  instanceId,
1665
- ], { encoding: "utf8", timeout: 2000 });
1666
- // Workflow children retain lifecycle/event capture but do not receive
1667
- // operator-facing peer, council, or init-remediation context. Injecting
1668
- // that context can make a bounded child follow housekeeping instructions
1669
- // instead of its assigned prompt.
1670
- if (!workflowChild) {
1671
- const agentName = emittedData.name ?? "";
1672
- const args = ["session-context", "--instance", instanceId, "--session", sessionId];
1673
- if (agentName)
1674
- args.push("--name", agentName);
1675
- // The "You are agent-X." prefix in session-context renders unqualified by
1676
- // default (claude-code-style). For cursor/codex the bash dispatchers add
1677
- // a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
1678
- if (adapter !== "claude-code") {
1679
- args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
1680
- }
1681
- const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1682
- if (result.status === 0 && result.stdout)
1683
- additionalContext = result.stdout.trim();
1684
- }
1753
+ sessionId,
1754
+ agentName: agentName || undefined,
1755
+ platformLabel: adapter === "claude-code" ? undefined : adapter === "cursor" ? "Cursor" : "Codex",
1756
+ }).trim();
1685
1757
  }
1686
1758
  // Effect (claude-code): merge the journal recovery cue into the session-start
1687
1759
  // context. Was a standalone additionalContext emission from the previous
@@ -1722,6 +1794,11 @@ function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
1722
1794
  }
1723
1795
  main()
1724
1796
  .then(async (code) => {
1797
+ writeHookHealthCompletion(hookHealthState, {
1798
+ finished_at_ms: performance.now(),
1799
+ finished_rss_bytes: process.memoryUsage().rss,
1800
+ pid: process.pid,
1801
+ });
1725
1802
  await closeProcessLoggers();
1726
1803
  process.exit(code);
1727
1804
  })
@@ -1729,6 +1806,13 @@ main()
1729
1806
  logError(resolveCoordRoot(process.cwd()), err, {
1730
1807
  argv: process.argv.slice(2),
1731
1808
  pid: process.pid,
1809
+ phase: "top-level",
1810
+ });
1811
+ writeHookHealthCompletion(hookHealthState, {
1812
+ finished_at_ms: performance.now(),
1813
+ finished_rss_bytes: process.memoryUsage().rss,
1814
+ pid: process.pid,
1815
+ faulted: true,
1732
1816
  });
1733
1817
  await closeProcessLoggers();
1734
1818
  process.exit(0);