harnery 0.32.0 → 0.34.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 (665) hide show
  1. package/README.md +2 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +7 -1
  4. package/dist/commands/adapter.js +2 -2
  5. package/dist/commands/agents.d.ts +144 -1
  6. package/dist/commands/agents.d.ts.map +1 -1
  7. package/dist/commands/agents.js +1408 -565
  8. package/dist/commands/artifacts.d.ts.map +1 -1
  9. package/dist/commands/artifacts.js +7 -6
  10. package/dist/commands/backup.d.ts +1 -1
  11. package/dist/commands/backup.d.ts.map +1 -1
  12. package/dist/commands/backup.js +29 -16
  13. package/dist/commands/browse.d.ts.map +1 -1
  14. package/dist/commands/browse.js +234 -12
  15. package/dist/commands/checkpoint.d.ts.map +1 -1
  16. package/dist/commands/checkpoint.js +3 -2
  17. package/dist/commands/claude-desktop.d.ts +2 -2
  18. package/dist/commands/claude-desktop.d.ts.map +1 -1
  19. package/dist/commands/claude-desktop.js +2 -2
  20. package/dist/commands/context.js +1 -6
  21. package/dist/commands/decision.d.ts.map +1 -1
  22. package/dist/commands/decision.js +22 -38
  23. package/dist/commands/docs.d.ts.map +1 -1
  24. package/dist/commands/docs.js +91 -28
  25. package/dist/commands/doctor.d.ts +4 -2
  26. package/dist/commands/doctor.d.ts.map +1 -1
  27. package/dist/commands/doctor.js +55 -7
  28. package/dist/commands/events.d.ts +5 -0
  29. package/dist/commands/events.d.ts.map +1 -0
  30. package/dist/commands/events.js +189 -0
  31. package/dist/commands/governor.d.ts.map +1 -1
  32. package/dist/commands/governor.js +13 -4
  33. package/dist/commands/init.d.ts +10 -2
  34. package/dist/commands/init.d.ts.map +1 -1
  35. package/dist/commands/init.js +233 -51
  36. package/dist/commands/instructions.d.ts +4 -0
  37. package/dist/commands/instructions.d.ts.map +1 -0
  38. package/dist/commands/instructions.js +100 -0
  39. package/dist/commands/journal.d.ts.map +1 -1
  40. package/dist/commands/journal.js +4 -13
  41. package/dist/commands/ledger-v3.d.ts +5 -0
  42. package/dist/commands/ledger-v3.d.ts.map +1 -0
  43. package/dist/commands/ledger-v3.js +91 -0
  44. package/dist/commands/presence.d.ts.map +1 -1
  45. package/dist/commands/presence.js +15 -6
  46. package/dist/commands/sync.d.ts +1 -1
  47. package/dist/commands/sync.js +1 -1
  48. package/dist/commands/web.d.ts +6 -0
  49. package/dist/commands/web.d.ts.map +1 -1
  50. package/dist/commands/web.js +67 -7
  51. package/dist/commands/work.d.ts.map +1 -1
  52. package/dist/commands/work.js +22 -5
  53. package/dist/core/adapter.d.ts +5 -0
  54. package/dist/core/adapter.d.ts.map +1 -0
  55. package/dist/core/adapter.js +1 -0
  56. package/dist/core/adapters/attest.d.ts +5 -2
  57. package/dist/core/adapters/attest.d.ts.map +1 -1
  58. package/dist/core/adapters/attest.js +8 -3
  59. package/dist/core/adapters/profiles.d.ts +6 -6
  60. package/dist/core/adapters/profiles.d.ts.map +1 -1
  61. package/dist/core/adapters/profiles.js +4 -4
  62. package/dist/core/agents/canonical-emit.d.ts +17 -15
  63. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  64. package/dist/core/agents/canonical-emit.js +27 -25
  65. package/dist/core/agents/cli.js +212 -235
  66. package/dist/core/agents/codex-archive-v3.d.ts +14 -0
  67. package/dist/core/agents/codex-archive-v3.d.ts.map +1 -0
  68. package/dist/core/agents/codex-archive-v3.js +110 -0
  69. package/dist/core/agents/coord-client.d.ts +19 -70
  70. package/dist/core/agents/coord-client.d.ts.map +1 -1
  71. package/dist/core/agents/coord-client.js +65 -155
  72. package/dist/core/agents/finalization.d.ts +4 -1
  73. package/dist/core/agents/finalization.d.ts.map +1 -1
  74. package/dist/core/agents/finalization.js +89 -32
  75. package/dist/core/agents/index.d.ts +5 -5
  76. package/dist/core/agents/index.d.ts.map +1 -1
  77. package/dist/core/agents/index.js +5 -5
  78. package/dist/core/agents/live-authority-v3.d.ts +59 -0
  79. package/dist/core/agents/live-authority-v3.d.ts.map +1 -0
  80. package/dist/core/agents/live-authority-v3.js +233 -0
  81. package/dist/core/agents/live-lifecycle-v3.d.ts +20 -0
  82. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -0
  83. package/dist/core/agents/live-lifecycle-v3.js +91 -0
  84. package/dist/core/agents/live-observation-v3.d.ts +25 -0
  85. package/dist/core/agents/live-observation-v3.d.ts.map +1 -0
  86. package/dist/core/agents/live-observation-v3.js +157 -0
  87. package/dist/core/agents/render/prompt-context.d.ts +10 -4
  88. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  89. package/dist/core/agents/render/prompt-context.js +61 -55
  90. package/dist/core/agents/render/session-context.d.ts +1 -1
  91. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  92. package/dist/core/agents/render/session-context.js +15 -33
  93. package/dist/core/agents/rules/claim-conflict.d.ts +3 -3
  94. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  95. package/dist/core/agents/rules/claim-conflict.js +79 -127
  96. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  97. package/dist/core/agents/rules/commit-conflict.js +3 -18
  98. package/dist/core/agents/rules/stop-hook.d.ts +9 -50
  99. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  100. package/dist/core/agents/rules/stop-hook.js +193 -236
  101. package/dist/core/agents/session-events.d.ts +5 -7
  102. package/dist/core/agents/session-events.d.ts.map +1 -1
  103. package/dist/core/agents/session-events.js +127 -95
  104. package/dist/core/agents/session-finalization-state-v3.d.ts +30 -0
  105. package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -0
  106. package/dist/core/agents/session-finalization-state-v3.js +40 -0
  107. package/dist/core/agents/session-finalizer-v3.d.ts +107 -0
  108. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -0
  109. package/dist/core/agents/session-finalizer-v3.js +734 -0
  110. package/dist/core/agents/session-name-display.d.ts +40 -0
  111. package/dist/core/agents/session-name-display.d.ts.map +1 -0
  112. package/dist/core/agents/session-name-display.js +119 -0
  113. package/dist/core/agents/state/activity-log.d.ts +3 -3
  114. package/dist/core/agents/state/activity-log.d.ts.map +1 -1
  115. package/dist/core/agents/state/activity-log.js +7 -16
  116. package/dist/core/agents/state/heartbeat-writer.d.ts +29 -46
  117. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  118. package/dist/core/agents/state/heartbeat-writer.js +59 -199
  119. package/dist/core/agents/state/live-coordination-view.d.ts +31 -0
  120. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -0
  121. package/dist/core/agents/state/live-coordination-view.js +209 -0
  122. package/dist/core/agents/state/session-state.d.ts +45 -0
  123. package/dist/core/agents/state/session-state.d.ts.map +1 -0
  124. package/dist/core/agents/state/session-state.js +90 -0
  125. package/dist/core/agents/state/stale-sweep.d.ts +1 -1
  126. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  127. package/dist/core/agents/state/stale-sweep.js +26 -15
  128. package/dist/core/artifacts/index.d.ts.map +1 -1
  129. package/dist/core/artifacts/index.js +5 -7
  130. package/dist/core/config.d.ts +33 -2
  131. package/dist/core/config.d.ts.map +1 -1
  132. package/dist/core/config.js +98 -2
  133. package/dist/core/context/index.d.ts +1 -4
  134. package/dist/core/context/index.d.ts.map +1 -1
  135. package/dist/core/context/index.js +9 -9
  136. package/dist/core/events/span-parent.d.ts +15 -0
  137. package/dist/core/events/span-parent.d.ts.map +1 -0
  138. package/dist/core/events/span-parent.js +34 -0
  139. package/dist/core/events/v3/advance.d.ts +14 -0
  140. package/dist/core/events/v3/advance.d.ts.map +1 -0
  141. package/dist/core/events/v3/advance.js +134 -0
  142. package/dist/core/events/v3/authority-outbox.d.ts +78 -0
  143. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -0
  144. package/dist/core/events/v3/authority-outbox.js +399 -0
  145. package/dist/core/events/v3/base-builder.d.ts +20 -0
  146. package/dist/core/events/v3/base-builder.d.ts.map +1 -0
  147. package/dist/core/events/v3/base-builder.js +29 -0
  148. package/dist/core/events/v3/base-contract.d.ts +7576 -0
  149. package/dist/core/events/v3/base-contract.d.ts.map +1 -0
  150. package/dist/core/events/v3/base-contract.js +647 -0
  151. package/dist/core/events/v3/bootstrap.d.ts +34 -0
  152. package/dist/core/events/v3/bootstrap.d.ts.map +1 -0
  153. package/dist/core/events/v3/bootstrap.js +175 -0
  154. package/dist/core/events/v3/builder.d.ts +20 -0
  155. package/dist/core/events/v3/builder.d.ts.map +1 -0
  156. package/dist/core/events/v3/builder.js +28 -0
  157. package/dist/core/events/v3/canonical.d.ts +31 -0
  158. package/dist/core/events/v3/canonical.d.ts.map +1 -0
  159. package/dist/core/events/v3/canonical.js +100 -0
  160. package/dist/core/events/v3/capabilities.d.ts +31 -0
  161. package/dist/core/events/v3/capabilities.d.ts.map +1 -0
  162. package/dist/core/events/v3/capabilities.js +107 -0
  163. package/dist/core/events/v3/capability-drift.d.ts +22 -0
  164. package/dist/core/events/v3/capability-drift.d.ts.map +1 -0
  165. package/dist/core/events/v3/capability-drift.js +134 -0
  166. package/dist/core/events/v3/catalog.d.ts +38 -0
  167. package/dist/core/events/v3/catalog.d.ts.map +1 -0
  168. package/dist/core/events/v3/catalog.js +135 -0
  169. package/dist/core/events/v3/clock-order.d.ts +34 -0
  170. package/dist/core/events/v3/clock-order.d.ts.map +1 -0
  171. package/dist/core/events/v3/clock-order.js +35 -0
  172. package/dist/core/events/v3/contract.d.ts +171 -0
  173. package/dist/core/events/v3/contract.d.ts.map +1 -0
  174. package/dist/core/events/v3/contract.js +180 -0
  175. package/dist/core/events/v3/control.d.ts +111 -0
  176. package/dist/core/events/v3/control.d.ts.map +1 -0
  177. package/dist/core/events/v3/control.js +419 -0
  178. package/dist/core/events/v3/coordination-view.d.ts +69 -0
  179. package/dist/core/events/v3/coordination-view.d.ts.map +1 -0
  180. package/dist/core/events/v3/coordination-view.js +119 -0
  181. package/dist/core/events/v3/economics.d.ts +77 -0
  182. package/dist/core/events/v3/economics.d.ts.map +1 -0
  183. package/dist/core/events/v3/economics.js +274 -0
  184. package/dist/core/events/v3/finalization-view.d.ts +26 -0
  185. package/dist/core/events/v3/finalization-view.d.ts.map +1 -0
  186. package/dist/core/events/v3/finalization-view.js +37 -0
  187. package/dist/core/events/v3/fingerprint-keys.d.ts +25 -0
  188. package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -0
  189. package/dist/core/events/v3/fingerprint-keys.js +217 -0
  190. package/dist/core/events/v3/generated.d.ts +4 -0
  191. package/dist/core/events/v3/generated.d.ts.map +1 -0
  192. package/dist/core/events/v3/generated.js +47 -0
  193. package/dist/core/events/v3/ids.d.ts +9 -0
  194. package/dist/core/events/v3/ids.d.ts.map +1 -0
  195. package/dist/core/events/v3/ids.js +25 -0
  196. package/dist/core/events/v3/index.d.ts +35 -0
  197. package/dist/core/events/v3/index.d.ts.map +1 -0
  198. package/dist/core/events/v3/index.js +33 -0
  199. package/dist/core/events/v3/latency.d.ts +63 -0
  200. package/dist/core/events/v3/latency.d.ts.map +1 -0
  201. package/dist/core/events/v3/latency.js +412 -0
  202. package/dist/core/events/v3/live-feed.d.ts +45 -0
  203. package/dist/core/events/v3/live-feed.d.ts.map +1 -0
  204. package/dist/core/events/v3/live-feed.js +246 -0
  205. package/dist/core/events/v3/live-observation.d.ts +35 -0
  206. package/dist/core/events/v3/live-observation.d.ts.map +1 -0
  207. package/dist/core/events/v3/live-observation.js +1 -0
  208. package/dist/core/events/v3/live-routing.d.ts +57 -0
  209. package/dist/core/events/v3/live-routing.d.ts.map +1 -0
  210. package/dist/core/events/v3/live-routing.js +107 -0
  211. package/dist/core/events/v3/producers/command-recorder.d.ts +67 -0
  212. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -0
  213. package/dist/core/events/v3/producers/command-recorder.js +477 -0
  214. package/dist/core/events/v3/producers/command.d.ts +50 -0
  215. package/dist/core/events/v3/producers/command.d.ts.map +1 -0
  216. package/dist/core/events/v3/producers/command.js +135 -0
  217. package/dist/core/events/v3/producers/coordination-recorder.d.ts +53 -0
  218. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -0
  219. package/dist/core/events/v3/producers/coordination-recorder.js +376 -0
  220. package/dist/core/events/v3/producers/coordination.d.ts +90 -0
  221. package/dist/core/events/v3/producers/coordination.d.ts.map +1 -0
  222. package/dist/core/events/v3/producers/coordination.js +229 -0
  223. package/dist/core/events/v3/producers/hook-base.d.ts +54 -0
  224. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -0
  225. package/dist/core/events/v3/producers/hook-base.js +424 -0
  226. package/dist/core/events/v3/producers/hook.d.ts +29 -0
  227. package/dist/core/events/v3/producers/hook.d.ts.map +1 -0
  228. package/dist/core/events/v3/producers/hook.js +71 -0
  229. package/dist/core/events/v3/producers/intake.d.ts +65 -0
  230. package/dist/core/events/v3/producers/intake.d.ts.map +1 -0
  231. package/dist/core/events/v3/producers/intake.js +244 -0
  232. package/dist/core/events/v3/producers/recorder.d.ts +240 -0
  233. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -0
  234. package/dist/core/events/v3/producers/recorder.js +2176 -0
  235. package/dist/core/events/v3/projection.d.ts +101 -0
  236. package/dist/core/events/v3/projection.d.ts.map +1 -0
  237. package/dist/core/events/v3/projection.js +479 -0
  238. package/dist/core/events/v3/reader.d.ts +75 -0
  239. package/dist/core/events/v3/reader.d.ts.map +1 -0
  240. package/dist/core/events/v3/reader.js +778 -0
  241. package/dist/core/events/v3/recovery-record.d.ts +47 -0
  242. package/dist/core/events/v3/recovery-record.d.ts.map +1 -0
  243. package/dist/core/events/v3/recovery-record.js +167 -0
  244. package/dist/core/events/v3/recovery.d.ts +22 -0
  245. package/dist/core/events/v3/recovery.d.ts.map +1 -0
  246. package/dist/core/events/v3/recovery.js +227 -0
  247. package/dist/core/events/v3/span-state.d.ts +52 -0
  248. package/dist/core/events/v3/span-state.d.ts.map +1 -0
  249. package/dist/core/events/v3/span-state.js +142 -0
  250. package/dist/core/events/v3/targets.d.ts +23 -0
  251. package/dist/core/events/v3/targets.d.ts.map +1 -0
  252. package/dist/core/events/v3/targets.js +134 -0
  253. package/dist/core/events/v3/turn-telemetry.d.ts +41 -0
  254. package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -0
  255. package/dist/core/events/v3/turn-telemetry.js +168 -0
  256. package/dist/core/events/v3/validate.d.ts +9 -0
  257. package/dist/core/events/v3/validate.d.ts.map +1 -0
  258. package/dist/core/events/v3/validate.js +281 -0
  259. package/dist/core/events/v3/writer.d.ts +56 -0
  260. package/dist/core/events/v3/writer.d.ts.map +1 -0
  261. package/dist/core/events/v3/writer.js +335 -0
  262. package/dist/core/governor/index.d.ts +1 -1
  263. package/dist/core/governor/index.d.ts.map +1 -1
  264. package/dist/core/governor/index.js +1 -1
  265. package/dist/core/governor/plan-api.d.ts +2 -1
  266. package/dist/core/governor/plan-api.d.ts.map +1 -1
  267. package/dist/core/governor/plan-api.js +6 -3
  268. package/dist/core/governor/read.d.ts +1 -1
  269. package/dist/core/governor/read.d.ts.map +1 -1
  270. package/dist/core/governor/read.js +1 -1
  271. package/dist/core/governor/state.d.ts +9 -0
  272. package/dist/core/governor/state.d.ts.map +1 -1
  273. package/dist/core/governor/state.js +40 -3
  274. package/dist/core/guard/config.d.ts +5 -0
  275. package/dist/core/guard/config.d.ts.map +1 -0
  276. package/dist/core/guard/config.js +157 -0
  277. package/dist/core/guard/coordinator.d.ts +14 -0
  278. package/dist/core/guard/coordinator.d.ts.map +1 -0
  279. package/dist/core/guard/coordinator.js +173 -0
  280. package/dist/core/guard/evaluator.d.ts +4 -0
  281. package/dist/core/guard/evaluator.d.ts.map +1 -0
  282. package/dist/core/guard/evaluator.js +242 -0
  283. package/dist/core/guard/evidence-v3.d.ts +15 -0
  284. package/dist/core/guard/evidence-v3.d.ts.map +1 -0
  285. package/dist/core/guard/evidence-v3.js +171 -0
  286. package/dist/core/guard/index.d.ts +10 -0
  287. package/dist/core/guard/index.d.ts.map +1 -0
  288. package/dist/core/guard/index.js +6 -0
  289. package/dist/core/guard/live-source-v3.d.ts +40 -0
  290. package/dist/core/guard/live-source-v3.d.ts.map +1 -0
  291. package/dist/core/guard/live-source-v3.js +128 -0
  292. package/dist/core/guard/storage.d.ts +27 -0
  293. package/dist/core/guard/storage.d.ts.map +1 -0
  294. package/dist/core/guard/storage.js +140 -0
  295. package/dist/core/guard/types.d.ts +119 -0
  296. package/dist/core/guard/types.d.ts.map +1 -0
  297. package/dist/core/guard/types.js +1 -0
  298. package/dist/core/hooks/adapter/detect.d.ts +18 -1
  299. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  300. package/dist/core/hooks/adapter/detect.js +29 -0
  301. package/dist/core/hooks/adapter/events.d.ts +7 -4
  302. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  303. package/dist/core/hooks/adapter/events.js +13 -4
  304. package/dist/core/hooks/adapter/output.d.ts +2 -2
  305. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  306. package/dist/core/hooks/adapter/parse.d.ts +7 -4
  307. package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
  308. package/dist/core/hooks/adapter/parse.js +64 -24
  309. package/dist/core/hooks/adapter/wiring.d.ts +30 -4
  310. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  311. package/dist/core/hooks/adapter/wiring.js +89 -11
  312. package/dist/core/hooks/cli.d.ts +1 -1
  313. package/dist/core/hooks/cli.js +345 -400
  314. package/dist/core/hooks/codex-authorization.d.ts +30 -0
  315. package/dist/core/hooks/codex-authorization.d.ts.map +1 -0
  316. package/dist/core/hooks/codex-authorization.js +199 -0
  317. package/dist/core/hooks/effects/image-capture.d.ts +20 -34
  318. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  319. package/dist/core/hooks/effects/image-capture.js +212 -221
  320. package/dist/core/hooks/effects/index.d.ts +1 -9
  321. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  322. package/dist/core/hooks/effects/index.js +1 -24
  323. package/dist/core/hooks/events/input-hash.d.ts +7 -0
  324. package/dist/core/hooks/events/input-hash.d.ts.map +1 -0
  325. package/dist/core/hooks/events/input-hash.js +43 -0
  326. package/dist/core/hooks/index.d.ts +4 -3
  327. package/dist/core/hooks/index.d.ts.map +1 -1
  328. package/dist/core/hooks/index.js +2 -3
  329. package/dist/core/hooks/resolve/coord-root.d.ts +1 -1
  330. package/dist/core/hooks/resolve/coord-root.js +1 -1
  331. package/dist/core/hooks/resolve/owner.d.ts +16 -15
  332. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  333. package/dist/core/hooks/resolve/owner.js +51 -42
  334. package/dist/core/hooks/resolve/transcript.d.ts +33 -3
  335. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  336. package/dist/core/hooks/resolve/transcript.js +155 -14
  337. package/dist/core/hooks/session-name-presence.d.ts +9 -10
  338. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  339. package/dist/core/hooks/session-name-presence.js +15 -15
  340. package/dist/core/hooks/unsafe-cross-shell.d.ts +1 -1
  341. package/dist/core/hooks/unsafe-cross-shell.d.ts.map +1 -1
  342. package/dist/core/journal/index.d.ts +2 -2
  343. package/dist/core/journal/index.d.ts.map +1 -1
  344. package/dist/core/journal/index.js +9 -22
  345. package/dist/core/presence/blob.d.ts +8 -3
  346. package/dist/core/presence/blob.d.ts.map +1 -1
  347. package/dist/core/presence/blob.js +54 -41
  348. package/dist/core/presence/codec-digest.d.ts +29 -0
  349. package/dist/core/presence/codec-digest.d.ts.map +1 -0
  350. package/dist/core/presence/codec-digest.js +170 -0
  351. package/dist/core/presence/index.js +15 -1
  352. package/dist/core/presence/relay-client.d.ts +1 -1
  353. package/dist/core/presence/relay-client.d.ts.map +1 -1
  354. package/dist/core/presence/relay-client.js +5 -5
  355. package/dist/core/work/index.d.ts +1 -1
  356. package/dist/core/work/index.d.ts.map +1 -1
  357. package/dist/core/work/index.js +1 -1
  358. package/dist/core/work/read.d.ts +1 -1
  359. package/dist/core/work/read.d.ts.map +1 -1
  360. package/dist/core/work/read.js +1 -1
  361. package/dist/core/work/runner.d.ts.map +1 -1
  362. package/dist/core/work/runner.js +6 -0
  363. package/dist/core/work/state.d.ts +9 -0
  364. package/dist/core/work/state.d.ts.map +1 -1
  365. package/dist/core/work/state.js +17 -3
  366. package/dist/core/workflow/engine.js +24 -25
  367. package/dist/core/workflow/live-session-v3.d.ts +19 -0
  368. package/dist/core/workflow/live-session-v3.d.ts.map +1 -0
  369. package/dist/core/workflow/live-session-v3.js +86 -0
  370. package/dist/core/workflow/scope-id.d.ts +3 -0
  371. package/dist/core/workflow/scope-id.d.ts.map +1 -0
  372. package/dist/core/workflow/scope-id.js +5 -0
  373. package/dist/core/workflow/workspaces/leases.d.ts +4 -0
  374. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  375. package/dist/core/workflow/workspaces/leases.js +53 -19
  376. package/dist/index.d.ts +1 -1
  377. package/dist/index.js +1 -1
  378. package/dist/lib/browser/client.d.ts +41 -0
  379. package/dist/lib/browser/client.d.ts.map +1 -1
  380. package/dist/lib/browser/client.js +203 -0
  381. package/dist/lib/browser/critique.d.ts +28 -2
  382. package/dist/lib/browser/critique.d.ts.map +1 -1
  383. package/dist/lib/browser/critique.js +74 -18
  384. package/dist/lib/browser/geometry.d.ts +1 -1
  385. package/dist/lib/browser/geometry.d.ts.map +1 -1
  386. package/dist/lib/browser/geometry.js +130 -6
  387. package/dist/lib/browser/index.d.ts +3 -0
  388. package/dist/lib/browser/index.d.ts.map +1 -1
  389. package/dist/lib/browser/index.js +3 -0
  390. package/dist/lib/browser/qa-plan.d.ts +174 -0
  391. package/dist/lib/browser/qa-plan.d.ts.map +1 -0
  392. package/dist/lib/browser/qa-plan.js +321 -0
  393. package/dist/lib/browser/qa-reuse.d.ts +99 -0
  394. package/dist/lib/browser/qa-reuse.d.ts.map +1 -0
  395. package/dist/lib/browser/qa-reuse.js +222 -0
  396. package/dist/lib/browser/qa-snapshot.d.ts +64 -0
  397. package/dist/lib/browser/qa-snapshot.d.ts.map +1 -0
  398. package/dist/lib/browser/qa-snapshot.js +171 -0
  399. package/dist/lib/browser/runts.d.ts.map +1 -1
  400. package/dist/lib/browser/runts.js +22 -1
  401. package/dist/lib/browser/tiling.d.ts +71 -0
  402. package/dist/lib/browser/tiling.d.ts.map +1 -0
  403. package/dist/lib/browser/tiling.js +118 -0
  404. package/dist/lib/context/index.d.ts +0 -3
  405. package/dist/lib/context/index.d.ts.map +1 -1
  406. package/dist/lib/context/index.js +20 -42
  407. package/dist/lib/council/index.d.ts +2 -2
  408. package/dist/lib/council/index.d.ts.map +1 -1
  409. package/dist/lib/council/index.js +9 -20
  410. package/dist/lib/docs-frontmatter.d.ts +1 -6
  411. package/dist/lib/docs-frontmatter.d.ts.map +1 -1
  412. package/dist/lib/docs-frontmatter.js +12 -78
  413. package/dist/lib/docs-index.d.ts +9 -0
  414. package/dist/lib/docs-index.d.ts.map +1 -1
  415. package/dist/lib/docs-index.js +32 -3
  416. package/dist/lib/docs-lint.d.ts.map +1 -1
  417. package/dist/lib/docs-lint.js +14 -30
  418. package/dist/lib/docs-metadata-audit.d.ts +19 -0
  419. package/dist/lib/docs-metadata-audit.d.ts.map +1 -0
  420. package/dist/lib/docs-metadata-audit.js +132 -0
  421. package/dist/lib/docs-metadata-managed.d.ts +23 -0
  422. package/dist/lib/docs-metadata-managed.d.ts.map +1 -0
  423. package/dist/lib/docs-metadata-managed.js +78 -0
  424. package/dist/lib/docs-metadata-sync.d.ts +20 -0
  425. package/dist/lib/docs-metadata-sync.d.ts.map +1 -0
  426. package/dist/lib/docs-metadata-sync.js +222 -0
  427. package/dist/lib/docs-metadata-v2-schema.d.ts +273 -0
  428. package/dist/lib/docs-metadata-v2-schema.d.ts.map +1 -0
  429. package/dist/lib/docs-metadata-v2-schema.js +134 -0
  430. package/dist/lib/docs-metadata-v2.d.ts +21 -0
  431. package/dist/lib/docs-metadata-v2.d.ts.map +1 -0
  432. package/dist/lib/docs-metadata-v2.js +424 -0
  433. package/dist/lib/docs-new.d.ts +11 -0
  434. package/dist/lib/docs-new.d.ts.map +1 -0
  435. package/dist/lib/docs-new.js +52 -0
  436. package/dist/lib/docs-sweep.d.ts.map +1 -1
  437. package/dist/lib/docs-sweep.js +45 -29
  438. package/dist/lib/headless/index.d.ts +98 -0
  439. package/dist/lib/headless/index.d.ts.map +1 -0
  440. package/dist/lib/headless/index.js +211 -0
  441. package/dist/lib/identities/assume.d.ts +4 -4
  442. package/dist/lib/identities/assume.d.ts.map +1 -1
  443. package/dist/lib/identities/assume.js +52 -68
  444. package/dist/lib/instructions/apply.d.ts +5 -4
  445. package/dist/lib/instructions/apply.d.ts.map +1 -1
  446. package/dist/lib/instructions/apply.js +32 -20
  447. package/dist/lib/instructions/bundle.d.ts +33 -0
  448. package/dist/lib/instructions/bundle.d.ts.map +1 -0
  449. package/dist/lib/instructions/bundle.js +149 -0
  450. package/dist/lib/instructions/splice.d.ts +0 -1
  451. package/dist/lib/instructions/splice.d.ts.map +1 -1
  452. package/dist/lib/instructions/splice.js +22 -6
  453. package/dist/lib/instructions/templates.d.ts +7 -5
  454. package/dist/lib/instructions/templates.d.ts.map +1 -1
  455. package/dist/lib/instructions/templates.js +79 -9
  456. package/package.json +27 -6
  457. package/schemas/config.schema.json +120 -2
  458. package/schemas/docs-metadata-v2.schema.json +441 -0
  459. package/schemas/event-v3.schema.json +24472 -0
  460. package/src/commander.ts +7 -1
  461. package/src/commands/adapter.ts +2 -2
  462. package/src/commands/agents.ts +1737 -570
  463. package/src/commands/artifacts.ts +7 -6
  464. package/src/commands/backup.ts +29 -21
  465. package/src/commands/browse.ts +349 -8
  466. package/src/commands/checkpoint.ts +4 -3
  467. package/src/commands/claude-desktop.ts +7 -5
  468. package/src/commands/context.ts +1 -6
  469. package/src/commands/decision.ts +27 -44
  470. package/src/commands/docs.ts +100 -33
  471. package/src/commands/doctor.ts +73 -8
  472. package/src/commands/events.ts +256 -0
  473. package/src/commands/governor.ts +18 -4
  474. package/src/commands/init.ts +264 -57
  475. package/src/commands/instructions.ts +129 -0
  476. package/src/commands/journal.ts +4 -13
  477. package/src/commands/ledger-v3.ts +111 -0
  478. package/src/commands/presence.ts +18 -11
  479. package/src/commands/sync.ts +1 -1
  480. package/src/commands/web.ts +67 -8
  481. package/src/commands/work.ts +28 -5
  482. package/src/core/adapter.ts +5 -0
  483. package/src/core/adapters/attest.ts +8 -3
  484. package/src/core/adapters/profiles.ts +4 -6
  485. package/src/core/agents/canonical-emit.ts +39 -28
  486. package/src/core/agents/cli.ts +242 -263
  487. package/src/core/agents/codex-archive-v3.ts +138 -0
  488. package/src/core/agents/coord-client.ts +75 -177
  489. package/src/core/agents/finalization.ts +120 -38
  490. package/src/core/agents/index.ts +5 -5
  491. package/src/core/agents/live-authority-v3.ts +375 -0
  492. package/src/core/agents/live-lifecycle-v3.ts +140 -0
  493. package/src/core/agents/live-observation-v3.ts +216 -0
  494. package/src/core/agents/render/prompt-context.ts +89 -55
  495. package/src/core/agents/render/session-context.ts +23 -32
  496. package/src/core/agents/rules/claim-conflict.ts +85 -126
  497. package/src/core/agents/rules/commit-conflict.ts +3 -14
  498. package/src/core/agents/rules/stop-hook.ts +239 -293
  499. package/src/core/agents/session-events.ts +137 -104
  500. package/src/core/agents/session-finalization-state-v3.ts +90 -0
  501. package/src/core/agents/session-finalizer-v3.ts +953 -0
  502. package/src/core/agents/session-name-display.ts +149 -0
  503. package/src/core/agents/state/activity-log.ts +6 -14
  504. package/src/core/agents/state/heartbeat-writer.ts +98 -242
  505. package/src/core/agents/state/live-coordination-view.ts +290 -0
  506. package/src/core/agents/state/session-state.ts +143 -0
  507. package/src/core/agents/state/stale-sweep.ts +40 -17
  508. package/src/core/artifacts/index.ts +4 -5
  509. package/src/core/config.ts +148 -3
  510. package/src/core/context/index.ts +9 -17
  511. package/src/core/events/span-parent.ts +46 -0
  512. package/src/core/events/v3/advance.ts +168 -0
  513. package/src/core/events/v3/authority-outbox.ts +548 -0
  514. package/src/core/events/v3/base-builder.ts +60 -0
  515. package/src/core/events/v3/base-contract.ts +882 -0
  516. package/src/core/events/v3/bootstrap.ts +226 -0
  517. package/src/core/events/v3/builder.ts +59 -0
  518. package/src/core/events/v3/canonical.ts +153 -0
  519. package/src/core/events/v3/capabilities.ts +171 -0
  520. package/src/core/events/v3/capability-drift.ts +177 -0
  521. package/src/core/events/v3/catalog.ts +202 -0
  522. package/src/core/events/v3/clock-order.ts +41 -0
  523. package/src/core/events/v3/contract.ts +365 -0
  524. package/src/core/events/v3/control.ts +576 -0
  525. package/src/core/events/v3/coordination-view.ts +193 -0
  526. package/src/core/events/v3/economics.ts +384 -0
  527. package/src/core/events/v3/finalization-view.ts +67 -0
  528. package/src/core/events/v3/fingerprint-keys.ts +260 -0
  529. package/src/core/events/v3/generated.ts +48 -0
  530. package/src/core/events/v3/ids.ts +33 -0
  531. package/src/core/events/v3/index.ts +322 -0
  532. package/src/core/events/v3/latency.ts +551 -0
  533. package/src/core/events/v3/live-feed.ts +319 -0
  534. package/src/core/events/v3/live-observation.ts +39 -0
  535. package/src/core/events/v3/live-routing.ts +135 -0
  536. package/src/core/events/v3/producers/command-recorder.ts +631 -0
  537. package/src/core/events/v3/producers/command.ts +201 -0
  538. package/src/core/events/v3/producers/coordination-recorder.ts +538 -0
  539. package/src/core/events/v3/producers/coordination.ts +355 -0
  540. package/src/core/events/v3/producers/hook-base.ts +578 -0
  541. package/src/core/events/v3/producers/hook.ts +119 -0
  542. package/src/core/events/v3/producers/intake.ts +319 -0
  543. package/src/core/events/v3/producers/recorder.ts +2805 -0
  544. package/src/core/events/v3/projection.ts +653 -0
  545. package/src/core/events/v3/reader.ts +1127 -0
  546. package/src/core/events/v3/recovery-record.ts +244 -0
  547. package/src/core/events/v3/recovery.ts +271 -0
  548. package/src/core/events/v3/span-state.ts +187 -0
  549. package/src/core/events/v3/targets.ts +182 -0
  550. package/src/core/events/v3/turn-telemetry.ts +259 -0
  551. package/src/core/events/v3/validate.ts +361 -0
  552. package/src/core/events/v3/writer.ts +419 -0
  553. package/src/core/governor/index.ts +3 -0
  554. package/src/core/governor/plan-api.ts +10 -2
  555. package/src/core/governor/read.ts +3 -0
  556. package/src/core/governor/state.ts +52 -5
  557. package/src/core/guard/config.ts +206 -0
  558. package/src/core/guard/coordinator.ts +227 -0
  559. package/src/core/guard/evaluator.ts +343 -0
  560. package/src/core/guard/evidence-v3.ts +227 -0
  561. package/src/core/guard/index.ts +35 -0
  562. package/src/core/guard/live-source-v3.ts +204 -0
  563. package/src/core/guard/storage.ts +188 -0
  564. package/src/core/guard/types.ts +152 -0
  565. package/src/core/hooks/adapter/detect.ts +28 -1
  566. package/src/core/hooks/adapter/events.ts +15 -6
  567. package/src/core/hooks/adapter/output.ts +7 -2
  568. package/src/core/hooks/adapter/parse.ts +81 -36
  569. package/src/core/hooks/adapter/wiring.ts +119 -13
  570. package/src/core/hooks/cli.ts +398 -422
  571. package/src/core/hooks/codex-authorization.ts +253 -0
  572. package/src/core/hooks/effects/image-capture.ts +228 -238
  573. package/src/core/hooks/effects/index.ts +1 -36
  574. package/src/core/hooks/events/input-hash.ts +47 -0
  575. package/src/core/hooks/index.ts +4 -11
  576. package/src/core/hooks/resolve/coord-root.ts +1 -1
  577. package/src/core/hooks/resolve/owner.ts +52 -45
  578. package/src/core/hooks/resolve/transcript.ts +165 -14
  579. package/src/core/hooks/session-name-presence.ts +14 -15
  580. package/src/core/hooks/unsafe-cross-shell.ts +1 -1
  581. package/src/core/journal/index.ts +15 -22
  582. package/src/core/presence/blob.ts +63 -38
  583. package/src/core/presence/codec-digest.ts +215 -0
  584. package/src/core/presence/index.ts +18 -1
  585. package/src/core/presence/relay-client.ts +5 -5
  586. package/src/core/work/index.ts +3 -0
  587. package/src/core/work/read.ts +3 -0
  588. package/src/core/work/runner.ts +6 -0
  589. package/src/core/work/state.ts +27 -3
  590. package/src/core/workflow/engine.ts +24 -24
  591. package/src/core/workflow/live-session-v3.ts +126 -0
  592. package/src/core/workflow/scope-id.ts +6 -0
  593. package/src/core/workflow/workspaces/leases.ts +56 -19
  594. package/src/index.ts +1 -1
  595. package/src/lib/browser/client.ts +230 -0
  596. package/src/lib/browser/critique.ts +104 -27
  597. package/src/lib/browser/geometry.ts +143 -14
  598. package/src/lib/browser/index.ts +34 -0
  599. package/src/lib/browser/qa-plan.ts +501 -0
  600. package/src/lib/browser/qa-reuse.ts +306 -0
  601. package/src/lib/browser/qa-snapshot.ts +223 -0
  602. package/src/lib/browser/runts.ts +27 -2
  603. package/src/lib/browser/tiling.ts +164 -0
  604. package/src/lib/context/index.ts +22 -46
  605. package/src/lib/council/index.ts +9 -21
  606. package/src/lib/docs-frontmatter.ts +8 -79
  607. package/src/lib/docs-index.ts +33 -3
  608. package/src/lib/docs-lint.ts +15 -29
  609. package/src/lib/docs-metadata-audit.ts +166 -0
  610. package/src/lib/docs-metadata-managed.ts +83 -0
  611. package/src/lib/docs-metadata-sync.ts +262 -0
  612. package/src/lib/docs-metadata-v2-schema.ts +138 -0
  613. package/src/lib/docs-metadata-v2.ts +646 -0
  614. package/src/lib/docs-new.ts +63 -0
  615. package/src/lib/docs-sweep.ts +42 -28
  616. package/src/lib/headless/index.ts +287 -0
  617. package/src/lib/identities/assume.ts +67 -86
  618. package/src/lib/instructions/apply.ts +33 -20
  619. package/src/lib/instructions/bundle.ts +199 -0
  620. package/src/lib/instructions/splice.ts +21 -7
  621. package/src/lib/instructions/templates.ts +85 -13
  622. package/dist/core/agents/cli-emit.d.ts +0 -27
  623. package/dist/core/agents/cli-emit.d.ts.map +0 -1
  624. package/dist/core/agents/cli-emit.js +0 -57
  625. package/dist/core/agents/codex-replay.d.ts +0 -29
  626. package/dist/core/agents/codex-replay.d.ts.map +0 -1
  627. package/dist/core/agents/codex-replay.js +0 -138
  628. package/dist/core/agents/events/consume.d.ts +0 -82
  629. package/dist/core/agents/events/consume.d.ts.map +0 -1
  630. package/dist/core/agents/events/consume.js +0 -195
  631. package/dist/core/agents/events/emit.d.ts +0 -42
  632. package/dist/core/agents/events/emit.d.ts.map +0 -1
  633. package/dist/core/agents/events/emit.js +0 -75
  634. package/dist/core/agents/events/ulid.d.ts +0 -11
  635. package/dist/core/agents/events/ulid.d.ts.map +0 -1
  636. package/dist/core/agents/events/ulid.js +0 -47
  637. package/dist/core/agents/state/heartbeat-projector.d.ts +0 -65
  638. package/dist/core/agents/state/heartbeat-projector.d.ts.map +0 -1
  639. package/dist/core/agents/state/heartbeat-projector.js +0 -527
  640. package/dist/core/hooks/events/emit.d.ts +0 -31
  641. package/dist/core/hooks/events/emit.d.ts.map +0 -1
  642. package/dist/core/hooks/events/emit.js +0 -93
  643. package/dist/core/hooks/events/rotate.d.ts +0 -43
  644. package/dist/core/hooks/events/rotate.d.ts.map +0 -1
  645. package/dist/core/hooks/events/rotate.js +0 -142
  646. package/dist/core/hooks/events/schema.d.ts +0 -312
  647. package/dist/core/hooks/events/schema.d.ts.map +0 -1
  648. package/dist/core/hooks/events/schema.js +0 -12
  649. package/dist/core/hooks/events/ulid.d.ts +0 -10
  650. package/dist/core/hooks/events/ulid.d.ts.map +0 -1
  651. package/dist/core/hooks/events/ulid.js +0 -47
  652. package/dist/lib/docs-frontmatter-migrate.d.ts +0 -33
  653. package/dist/lib/docs-frontmatter-migrate.d.ts.map +0 -1
  654. package/dist/lib/docs-frontmatter-migrate.js +0 -364
  655. package/src/core/agents/cli-emit.ts +0 -64
  656. package/src/core/agents/codex-replay.ts +0 -163
  657. package/src/core/agents/events/consume.ts +0 -254
  658. package/src/core/agents/events/emit.ts +0 -115
  659. package/src/core/agents/events/ulid.ts +0 -51
  660. package/src/core/agents/state/heartbeat-projector.ts +0 -582
  661. package/src/core/hooks/events/emit.ts +0 -125
  662. package/src/core/hooks/events/rotate.ts +0 -151
  663. package/src/core/hooks/events/schema.ts +0 -538
  664. package/src/core/hooks/events/ulid.ts +0 -51
  665. package/src/lib/docs-frontmatter-migrate.ts +0 -427
@@ -8,7 +8,7 @@
8
8
  * 4. Resolve instance_id (env → payload → pid-map walk).
9
9
  * 5. Map event-name → canonical event_type.
10
10
  * 6. Build event data from payload + resolvers (intent, transcript scan).
11
- * 7. Append envelope to .harnery/events.ndjson via emit() under flock.
11
+ * 7. Record the normalized observation in the canonical V3 ledger.
12
12
  * 8. (Still also writes a debug breadcrumb to .harnery/debug/ for visibility.)
13
13
  *
14
14
  * Phase 2 ship criterion: confirms parser correctness across thousands of
@@ -21,19 +21,26 @@ import { spawnSync } from "node:child_process";
21
21
  import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
22
22
  import { dirname, join } from "node:path";
23
23
  import { coordEnv } from "../../lib/env.ts";
24
- import { replayCodexJsonl } from "../agents/codex-replay.ts";
24
+ import { buildInstructionBundle } from "../../lib/instructions/bundle.ts";
25
+ import type { Adapter } from "../adapter.ts";
25
26
  import { coordBinPath } from "../agents/coord-bin.ts";
26
- import { consumeSince, writeCursor } from "../agents/events/consume.ts";
27
27
  import {
28
28
  type ClaimFinalizationDecision,
29
29
  type ClaimFinalizationDescriptor,
30
30
  classifyWriteClaimFinalization,
31
31
  formatWriteClaimFinalizationDenial,
32
32
  } from "../agents/finalization.ts";
33
- import { evaluateStopHook } from "../agents/rules/stop-hook.ts";
34
- import { projectHeartbeats } from "../agents/state/heartbeat-projector.ts";
33
+ import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.ts";
34
+ import {
35
+ assistantTextStartsWithSessionNameBlock,
36
+ isSessionNameRemediationCommand,
37
+ sessionNameDisplayInstruction,
38
+ sessionNameDisplayPending,
39
+ toolResponseMintedSessionName,
40
+ } from "../agents/session-name-display.ts";
41
+ import { stampSessionNameSeen } from "../agents/state/heartbeat-writer.ts";
42
+ import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.ts";
35
43
  import { writePidmapRow } from "../agents/state/pidmap.ts";
36
- import { shellMutationPaths } from "../agents/state/shell-mutation.ts";
37
44
  import { agentsRequireGitFinalization, resolveBinName } from "../config.ts";
38
45
  import {
39
46
  checkpointContext,
@@ -45,8 +52,15 @@ import {
45
52
  readContextState,
46
53
  recordContextSample,
47
54
  } from "../context/index.ts";
48
- import { ensureRelayDaemon, fetchPresence, publishPresence } from "../presence/index.ts";
49
- import { detectAdapter } from "./adapter/detect.ts";
55
+ import { tryWriteLiveDisplayV3 } from "../events/v3/live-feed.ts";
56
+ import {
57
+ recordLiveHookSignalV3,
58
+ resolveLiveEventLedgerRouteV3,
59
+ } from "../events/v3/live-routing.ts";
60
+ import { captureSpanClockV3 } from "../events/v3/span-state.ts";
61
+ import { fetchPresence } from "../presence/index.ts";
62
+ import { stableScopeId } from "../workflow/scope-id.ts";
63
+ import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.ts";
50
64
  import {
51
65
  extractBashCommand,
52
66
  extractToolDescription,
@@ -69,21 +83,21 @@ import {
69
83
  journalJanitor,
70
84
  journalRecoveryCue,
71
85
  playSound,
86
+ recordImageArtifactsV3,
72
87
  resetSoundCounters,
73
88
  runSessionSyncExtension,
74
- runTurnSummary,
75
89
  soundForEvent,
76
90
  } from "./effects/index.ts";
77
- import { emit } from "./events/emit.ts";
78
- import type { Adapter } from "./events/schema.ts";
91
+ import { toolInputHash, toolTargetHash } from "./events/input-hash.ts";
79
92
  import { canonicalize } from "./guard-path.ts";
80
93
  import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.ts";
81
94
  import { findCoordRoot } from "./resolve/coord-root.ts";
82
95
  import { extractIntentComment, resolveIntent } from "./resolve/intent.ts";
83
96
  import { resolveOwner } from "./resolve/owner.ts";
84
97
  import {
85
- detectForkParent,
98
+ inspectSessionNameDisplayImmediately,
86
99
  scanAssistantTextIncludes,
100
+ scanSessionNameDisplayedImmediately,
87
101
  scanStatusBoxPresent,
88
102
  scanTranscriptModel,
89
103
  } from "./resolve/transcript.ts";
@@ -170,7 +184,7 @@ function logError(coordRoot: string | null, err: unknown, context: Record<string
170
184
  /**
171
185
  * Spawn `agent-coord assign-name <owner> <kind>` to mint or recover the
172
186
  * hurricane-style name for this owner. Returns null on any failure so
173
- * session.start emission never breaks the adapter flow.
187
+ * session.started emission never breaks the adapter flow.
174
188
  *
175
189
  * Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
176
190
  * separation: we spawn rather than import.
@@ -207,7 +221,7 @@ function assignNameViaAgentCoord(
207
221
 
208
222
  /**
209
223
  * Direct (in-process) pidmap write, avoiding the spawn overhead of going via
210
- * the agent-coord CLI for every session.start / subagent.start. Pid-map rows
224
+ * the agent-coord CLI for every session.started signal. Pid-map rows
211
225
  * are essential for `harn agents whoami` ppid resolution.
212
226
  *
213
227
  * This used to inline its own copy of the write to keep this module's
@@ -249,6 +263,7 @@ interface BuildContext {
249
263
  raw: string;
250
264
  adapter: Adapter;
251
265
  instanceId: string;
266
+ eventName: string;
252
267
  }
253
268
 
254
269
  function buildEventData(
@@ -257,7 +272,7 @@ function buildEventData(
257
272
  ): Record<string, unknown> {
258
273
  const p = ctx.payload;
259
274
  switch (eventType) {
260
- case "session.start": {
275
+ case "session.started": {
261
276
  const adapterPlatform =
262
277
  ctx.adapter === "claude-code"
263
278
  ? "claude-code"
@@ -265,11 +280,11 @@ function buildEventData(
265
280
  ? "cursor"
266
281
  : "codex";
267
282
  // Recorded fork lineage is NOT detected here. On claude-code a fork
268
- // never fires its own session.start — SessionStart fires under the
283
+ // never fires its own session.started — SessionStart fires under the
269
284
  // PARENT's session id (source=resume) before the fork id is minted
270
285
  // (verified 2026-08-05) — so detection at this point can only mislabel
271
286
  // the resumed parent. The fork's new instance is caught by the
272
- // tool.pre_use heal path instead. The forkedFrom plumbing below stays
287
+ // tool.requested heal path instead. The forkedFrom plumbing below stays
273
288
  // for adapters that DO report a parent at session start.
274
289
  const forkedFrom: string | undefined = undefined;
275
290
  // Assign (or recover) name + kind via agent-coord. Idempotent: resume
@@ -290,12 +305,13 @@ function buildEventData(
290
305
  if (adapterPid) {
291
306
  writePidmapViaAgentCoord(ctx.coordRoot, adapterPid, ctx.instanceId, adapterPlatform);
292
307
  }
308
+ const bundle = safeInstructionBundle(ctx.coordRoot, p?.cwd ?? process.cwd(), ctx.adapter);
293
309
  return {
294
310
  started_at: new Date().toISOString(),
295
311
  cwd: p?.cwd ?? process.cwd(),
296
312
  // Claude Code's SessionStart payload omits `model` (Codex + Cursor
297
313
  // supply it). Fall back to the transcript, populated on `resume`, and
298
- // backfilled later by `turn.stop` for a fresh `startup` session.
314
+ // backfilled later by `turn.completed` for a fresh startup session.
299
315
  model: p?.model ?? scanTranscriptModel(p?.transcript_path),
300
316
  pid: adapterPid,
301
317
  source: p?.source,
@@ -303,6 +319,14 @@ function buildEventData(
303
319
  name: assigned?.name,
304
320
  kind: "session",
305
321
  agent_id: ctx.instanceId,
322
+ ...(bundle
323
+ ? {
324
+ instruction_bundle_id: bundle.instruction_bundle_id,
325
+ instruction_source_id: bundle.canonical_source_id,
326
+ instruction_profile_root: bundle.profile_root,
327
+ instruction_component_count: bundle.components.length,
328
+ }
329
+ : {}),
306
330
  // Recorded fork lineage rides the canonical event too, so full replay
307
331
  // and derived readers converge on the same parent linkage that
308
332
  // .name-history carries (the ADR 0017 dual-write pattern).
@@ -324,26 +348,35 @@ function buildEventData(
324
348
  };
325
349
  }
326
350
 
327
- case "session.end":
351
+ case "session.ended":
328
352
  return {
329
353
  ended_at: new Date().toISOString(),
330
354
  clean_exit: p?.clean_exit ?? true,
331
355
  };
332
356
 
333
- case "user_prompt.submit": {
357
+ case "turn.started": {
358
+ // Cursor can deliver beforeSubmitPrompt before sessionStart. Ensure that
359
+ // high-confidence prompt bootstrap also mints durable display identity;
360
+ // a later SessionStart remains idempotent.
361
+ if (
362
+ ctx.adapter === "cursor" &&
363
+ !readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name
364
+ ) {
365
+ assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session");
366
+ }
334
367
  const prompt = p?.prompt ?? "";
335
368
  const { value, truncated } = clampString(prompt, 4000);
336
369
  return { prompt_text: value, ...(truncated ? { truncated: true } : {}) };
337
370
  }
338
371
 
339
- case "turn.stop": {
372
+ case "turn.completed": {
340
373
  const lastAssistantMessage = (p?.raw.last_assistant_message as string | undefined) ?? "";
341
374
  const fileLinkTelemetry =
342
375
  ctx.adapter === "codex"
343
376
  ? codexWslFileLinkTelemetry(ctx.coordRoot, p?.cwd, lastAssistantMessage)
344
377
  : null;
345
378
  return {
346
- // Backfill the model for adapters that omit it at session.start
379
+ // Backfill the model for adapters that omit it at session.started
347
380
  // (Claude Code). The transcript is populated with assistant turns by
348
381
  // Stop-hook time, so this resolves even for fresh `startup` sessions.
349
382
  model: p?.model ?? scanTranscriptModel(p?.transcript_path),
@@ -357,7 +390,7 @@ function buildEventData(
357
390
  // Box present if the transcript scan finds it OR the final assistant
358
391
  // message carries the `┌─ agent-` prefix. The latter covers codex's
359
392
  // text-only stop (box in last_assistant_message, no transcript), which
360
- // the verdict now sees because agent-hook emits this turn.stop itself
393
+ // the verdict now sees because agent-hook emits this turn.completed itself
361
394
  // (the previous path passed those via the no-history fail-open).
362
395
  status_box_present:
363
396
  scanStatusBoxPresent(p?.transcript_path) || lastAssistantMessage.includes("┌─ agent-"),
@@ -377,15 +410,19 @@ function buildEventData(
377
410
  // and which name that refers to. Reported on every stop while a name
378
411
  // exists (the stop-hook.session_name verdict reads it per turn); the
379
412
  // transcript scan itself stops once the name has been sighted.
380
- ...sessionNamePresence(ctx.coordRoot, ctx.instanceId, lastAssistantMessage, (name) =>
381
- scanAssistantTextIncludes(p?.transcript_path, name),
413
+ ...sessionNamePresence(ctx.coordRoot, ctx.instanceId, (name) =>
414
+ scanSessionNameDisplayedImmediately(
415
+ p?.transcript_path,
416
+ name,
417
+ assistantTextStartsWithSessionNameBlock,
418
+ ),
382
419
  ),
383
420
  ...(fileLinkTelemetry ?? {}),
384
421
  stop_hook_active: p?.stop_hook_active,
385
422
  };
386
423
  }
387
424
 
388
- case "subagent.start": {
425
+ case "agent.started": {
389
426
  const subagentCallId =
390
427
  (p?.raw.subagent_id as string | undefined) ?? (p?.raw.agent_id as string | undefined);
391
428
  // Subagents inherit parent's name via the resolve-name session_id path
@@ -406,14 +443,14 @@ function buildEventData(
406
443
  };
407
444
  }
408
445
 
409
- case "subagent.stop": {
446
+ case "agent.completed": {
410
447
  const status = p?.exit_status;
411
448
  const normalized: "ok" | "error" | "interrupted" =
412
449
  status === "error" || status === "interrupted" ? status : "ok";
413
450
  return { exit_status: normalized, reason: p?.reason };
414
451
  }
415
452
 
416
- case "tool.pre_use": {
453
+ case "tool.requested": {
417
454
  const toolName = p?.tool_name ?? "unknown";
418
455
  const command = extractBashCommand(toolName, p?.tool_input);
419
456
  const description = extractToolDescription(p?.tool_input);
@@ -428,6 +465,8 @@ function buildEventData(
428
465
  return {
429
466
  tool_name: toolName,
430
467
  tool_input: clamped.value,
468
+ input_hash: toolInputHash(toolName, p?.tool_input ?? null),
469
+ target_hash: toolTargetHash(toolName, p?.tool_input ?? null),
431
470
  intent,
432
471
  intent_source: source,
433
472
  tool_use_id: p?.tool_use_id,
@@ -435,32 +474,38 @@ function buildEventData(
435
474
  };
436
475
  }
437
476
 
438
- case "tool.post_use": {
439
- const toolName = p?.tool_name ?? "unknown";
440
- const summary = summarizeOutput(p?.tool_response);
477
+ case "wait.started": {
478
+ const description = extractToolDescription(p?.tool_input);
479
+ const reason = description ? clampString(description, 500).value : undefined;
441
480
  return {
442
- tool_name: toolName,
443
- output_summary: summary.summary,
444
- exit_status: "ok" as const,
445
- duration_ms: 0, // Phase 3 pairs pre/post via tool_use_id
446
- tool_use_id: p?.tool_use_id,
447
- ...(summary.truncated ? { truncated: true } : {}),
481
+ request_kind: "permission",
482
+ tool_name: p?.tool_name,
483
+ ...(reason ? { reason } : {}),
448
484
  };
449
485
  }
450
486
 
451
- case "tool.post_use_failure": {
487
+ case "tool.completed": {
452
488
  const toolName = p?.tool_name ?? "unknown";
453
489
  const summary = summarizeOutput(p?.tool_response);
454
- return {
455
- tool_name: toolName,
456
- error: summary.summary,
457
- duration_ms: 0,
458
- tool_use_id: p?.tool_use_id,
459
- ...(summary.truncated ? { truncated: true } : {}),
460
- };
490
+ return ctx.eventName === "post-tool-use-failure"
491
+ ? {
492
+ tool_name: toolName,
493
+ error: summary.summary,
494
+ duration_ms: 0,
495
+ tool_use_id: p?.tool_use_id,
496
+ ...(summary.truncated ? { truncated: true } : {}),
497
+ }
498
+ : {
499
+ tool_name: toolName,
500
+ output_summary: summary.summary,
501
+ exit_status: "ok" as const,
502
+ duration_ms: 0,
503
+ tool_use_id: p?.tool_use_id,
504
+ ...(summary.truncated ? { truncated: true } : {}),
505
+ };
461
506
  }
462
507
 
463
- case "context.compaction.started": {
508
+ case "context.compaction_started": {
464
509
  const metadata = objectRecord(p?.raw.compact_metadata);
465
510
  return {
466
511
  trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
@@ -471,7 +516,7 @@ function buildEventData(
471
516
  };
472
517
  }
473
518
 
474
- case "context.compaction.completed": {
519
+ case "context.compaction_completed": {
475
520
  const metadata = objectRecord(p?.raw.compact_metadata);
476
521
  return {
477
522
  trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
@@ -488,6 +533,15 @@ function buildEventData(
488
533
  }
489
534
  }
490
535
 
536
+ function safeInstructionBundle(coordRoot: string, cwd: string, adapter: Adapter) {
537
+ try {
538
+ return buildInstructionBundle({ coordRoot, cwd, adapter });
539
+ } catch (err) {
540
+ logError(coordRoot, err, { phase: "instruction-bundle-identity" });
541
+ return null;
542
+ }
543
+ }
544
+
491
545
  function objectRecord(value: unknown): Record<string, unknown> | undefined {
492
546
  return typeof value === "object" && value !== null && !Array.isArray(value)
493
547
  ? (value as Record<string, unknown>)
@@ -503,10 +557,38 @@ function numberField(value: unknown): number | undefined {
503
557
  }
504
558
 
505
559
  async function main(): Promise<number> {
560
+ const hookStartedAt = performance.now();
561
+ const hookClock = captureSpanClockV3();
506
562
  const { eventName, extra } = parseArgv(process.argv.slice(2));
507
563
  const adapter = detectAdapter(process.argv.slice(2));
508
564
  const raw = await readStdin();
509
565
 
566
+ // Cursor executes the Claude Code project hooks too on hosts wired for both
567
+ // adapters, piping them its own payload (identifiable by the top-level
568
+ // cursor_version envelope field). That stray `--adapter claude-code`
569
+ // dispatch must not record (twin generation) or play Claude-Code-only
570
+ // sounds, so it bails here, before every effect. Cursor's own
571
+ // `--adapter cursor` dispatch of the same payload carries the session.
572
+ if (shouldSkipHookAdapter(adapter, raw)) {
573
+ if (coordEnv("AGENT_COORD_OFF") !== "1") {
574
+ const coordRoot = findCoordRoot(process.cwd());
575
+ if (coordRoot) {
576
+ appendDebug(coordRoot, {
577
+ ts: new Date().toISOString(),
578
+ event_name: eventName,
579
+ adapter,
580
+ extra_argv: extra,
581
+ payload_bytes: raw.length,
582
+ cwd: process.cwd(),
583
+ pid: process.pid,
584
+ ppid: process.ppid,
585
+ skipped: "cursor-payload-claude-adapter",
586
+ });
587
+ }
588
+ }
589
+ return 0;
590
+ }
591
+
510
592
  // Kill-switch-INDEPENDENT effects: notification sounds fire BEFORE the
511
593
  // HARNERY_AGENT_COORD_OFF gate so audible feedback survives incident-triage
512
594
  // bypass: sound playback happens before the kill-switch bailout.
@@ -536,6 +618,7 @@ async function main(): Promise<number> {
536
618
 
537
619
  const coordRoot = findCoordRoot(process.cwd());
538
620
  if (!coordRoot) return 0;
621
+ const ledgerRoute = resolveLiveEventLedgerRouteV3(coordRoot);
539
622
 
540
623
  // Always log a breadcrumb, useful when an event_type maps to null or owner
541
624
  // resolution fails. Stays cheap (one append) and self-prunes via repo log
@@ -563,7 +646,10 @@ async function main(): Promise<number> {
563
646
  }
564
647
 
565
648
  const payload = parsePayload(raw, adapter);
566
- const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
649
+ const owner = resolveOwner({
650
+ payload: payload?.raw ?? null,
651
+ coordRoot,
652
+ });
567
653
  if (!owner) {
568
654
  appendDebug(coordRoot, {
569
655
  ...debugBase,
@@ -581,25 +667,108 @@ async function main(): Promise<number> {
581
667
  raw,
582
668
  adapter,
583
669
  instanceId: owner.instance_id,
670
+ eventName,
584
671
  });
672
+ const turnRitual =
673
+ norm.event_type === "turn.completed"
674
+ ? {
675
+ status_box_present: data.status_box_present === true,
676
+ status_box_present_strict: data.status_box_present_strict === true,
677
+ session_name_required: typeof data.session_name_present_for === "string",
678
+ session_name_present: data.session_name_present === true,
679
+ }
680
+ : undefined;
681
+ const stopRemediation =
682
+ norm.event_type === "turn.started" &&
683
+ typeof data.prompt_text === "string" &&
684
+ data.prompt_text.includes(STOP_REMEDIATION_MARKER);
585
685
 
586
- const envelope = emit(coordRoot, {
587
- event_type: norm.event_type,
588
- instance_id: owner.instance_id,
589
- session_id: sessionId,
590
- parent_session_id: payload?.parent_session_id,
591
- turn_id: payload?.turn_id,
592
- parent_turn_id: payload?.parent_turn_id,
686
+ if (ledgerRoute.state === "blocked") {
687
+ appendDebug(coordRoot, {
688
+ ...debugBase,
689
+ skipped: "v3-control-blocked",
690
+ reason: ledgerRoute.reason,
691
+ event_type: norm.event_type,
692
+ owner_source: owner.source,
693
+ });
694
+ return 0;
695
+ }
696
+
697
+ let capturedImages: ReturnType<typeof captureImages> = [];
698
+ if (
699
+ norm.event_type === "tool.requested" ||
700
+ (norm.event_type === "tool.completed" && eventName === "post-tool-use")
701
+ ) {
702
+ try {
703
+ capturedImages = captureImages(coordRoot, norm.event_type, payload);
704
+ } catch (err) {
705
+ logError(coordRoot, err, { phase: "image-capture" });
706
+ }
707
+ }
708
+
709
+ const v3Result = recordLiveHookSignalV3({
710
+ coordRoot,
711
+ route: ledgerRoute,
712
+ eventName,
713
+ payload,
593
714
  adapter,
594
- data,
595
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
596
- } as Parameters<typeof emit>[1]);
715
+ instanceId: owner.instance_id,
716
+ ...(coordEnv("WORKFLOW_CHILD") === "1" && coordEnv("WORKFLOW_RUN_ID")
717
+ ? {
718
+ run_id: stableScopeId("run", coordEnv("WORKFLOW_RUN_ID")!),
719
+ workflow_id: stableScopeId("wf", coordEnv("WORKFLOW_RUN_ID")!),
720
+ ...(coordEnv("WORKFLOW_AGENT_ID")
721
+ ? { workflow_agent_id: coordEnv("WORKFLOW_AGENT_ID") }
722
+ : {}),
723
+ }
724
+ : {}),
725
+ ...(adapter === "codex" && isWslUncPath(payload?.cwd) ? { bridge: "codex-wsl" as const } : {}),
726
+ hook_name: eventName,
727
+ hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
728
+ monotonic_ns: hookClock.monotonic_ns,
729
+ ...(stopRemediation ? { stop_remediation: true } : {}),
730
+ ...(turnRitual ? { turn_ritual: turnRitual } : {}),
731
+ ...(coordEnv("EXPERIMENT_DEFER_V3_DRAIN") === "1" ? { defer_drain: true } : {}),
732
+ });
733
+ const v3EventId =
734
+ v3Result && "event" in v3Result
735
+ ? v3Result.event.event_id
736
+ : v3Result && "event_id" in v3Result
737
+ ? v3Result.event_id
738
+ : undefined;
739
+
740
+ if (v3Result?.state === "recorded" && capturedImages.length > 0) {
741
+ try {
742
+ recordImageArtifactsV3(coordRoot, v3Result.event, capturedImages);
743
+ } catch (err) {
744
+ logError(coordRoot, err, { phase: "image-artifact-observation" });
745
+ }
746
+ }
747
+
748
+ if (
749
+ norm.event_type === "tool.requested" &&
750
+ v3Result &&
751
+ v3Result.state === "recorded" &&
752
+ "generation_id" in v3Result.event.scope
753
+ ) {
754
+ const intent = typeof data.intent === "string" ? data.intent : undefined;
755
+ if (intent && intent !== "(no intent)") {
756
+ tryWriteLiveDisplayV3(coordRoot, {
757
+ generation_id: v3Result.event.scope.generation_id,
758
+ event_id: v3Result.event.event_id,
759
+ ...(typeof data.tool_name === "string" ? { executable: data.tool_name } : {}),
760
+ intent_display: intent,
761
+ });
762
+ }
763
+ }
597
764
 
598
765
  appendDebug(coordRoot, {
599
766
  ...debugBase,
600
767
  event_type: norm.event_type,
601
768
  owner_source: owner.source,
602
- event_id: envelope.event_id,
769
+ ...(v3EventId ? { event_v3_id: v3EventId } : {}),
770
+ ...(v3Result ? { event_v3_state: v3Result.state } : {}),
771
+ ...(v3Result && "reason" in v3Result ? { event_v3_reason: v3Result.reason } : {}),
603
772
  });
604
773
 
605
774
  // Context telemetry is opportunistic and truthful: only persist a sample
@@ -616,22 +785,7 @@ async function main(): Promise<number> {
616
785
  });
617
786
  if (sample) {
618
787
  const recorded = recordContextSample(coordRoot, owner.instance_id, sample);
619
- if (recorded.changed) {
620
- emit(coordRoot, {
621
- event_type: "context.sampled",
622
- instance_id: owner.instance_id,
623
- session_id: sessionId,
624
- adapter,
625
- data: {
626
- model: sample.model,
627
- used_tokens: sample.used_tokens,
628
- window_tokens: sample.window_tokens,
629
- used_percent: sample.used_percent,
630
- telemetry_source: sample.source,
631
- confidence: sample.confidence,
632
- },
633
- });
634
- }
788
+ void recorded;
635
789
  }
636
790
  } catch (err) {
637
791
  logError(coordRoot, err, { phase: "context-sample" });
@@ -641,7 +795,7 @@ async function main(): Promise<number> {
641
795
  // A native pre-compaction signal is the safest available point to capture
642
796
  // external work state. The operation is idempotent until recovery, so a
643
797
  // adapter retry cannot create a storm of near-identical capsules.
644
- if (norm.event_type === "context.compaction.started") {
798
+ if (norm.event_type === "context.compaction_started") {
645
799
  try {
646
800
  const checkpoint = checkpointContext(coordRoot, {
647
801
  sessionId,
@@ -651,25 +805,13 @@ async function main(): Promise<number> {
651
805
  reason: "pre_compact",
652
806
  model: payload?.model,
653
807
  });
654
- emit(coordRoot, {
655
- event_type: "context.checkpoint.created",
656
- instance_id: owner.instance_id,
657
- session_id: sessionId,
658
- adapter,
659
- data: {
660
- capsule_id: checkpoint.capsule.capsule_id,
661
- generation: checkpoint.capsule.generation,
662
- path: checkpoint.state.latest_capsule ?? "",
663
- reason: checkpoint.capsule.reason,
664
- reused: checkpoint.reused,
665
- },
666
- });
808
+ void checkpoint;
667
809
  } catch (err) {
668
810
  logError(coordRoot, err, { phase: "context-checkpoint" });
669
811
  }
670
812
  }
671
813
 
672
- if (norm.event_type === "context.compaction.completed") {
814
+ if (norm.event_type === "context.compaction_completed") {
673
815
  try {
674
816
  markContextCompactionCompleted(coordRoot, {
675
817
  sessionId,
@@ -685,29 +827,15 @@ async function main(): Promise<number> {
685
827
  // systemMessage JSON (peer table + wiring check + council invites).
686
828
  // Adapter-agnostic since v0.5.0; replaces the previous bash UX layer
687
829
  // and the equivalent per-adapter bash session_start handlers.
688
- if (norm.event_type === "session.start") {
830
+ if (norm.event_type === "session.started") {
689
831
  // Effect (claude-code): prune stale journal archives + sweep orphans.
690
832
  // The recovery-cue is merged into the
691
833
  // session-start additionalContext inside emitSessionStartSystemMessage.
692
834
  if (adapter === "claude-code") journalJanitor(coordRoot);
693
- // Image-feed retention sweep (size + age cap on .harnery/images/). Adapter-
694
- // agnostic, cheap (one readdir), fail-soft. Paired with journalJanitor as a
695
- // session-start "tidy the coord layer" step.
696
- try {
697
- imageJanitor(coordRoot);
698
- } catch (err) {
699
- logError(coordRoot, err, { phase: "session-start-image-janitor" });
700
- }
835
+ imageJanitor(coordRoot);
701
836
  let recovery: PreparedContextRecovery | null = null;
702
837
  if (payload?.source === "compact") {
703
838
  try {
704
- emit(coordRoot, {
705
- event_type: "context.compaction.completed",
706
- instance_id: owner.instance_id,
707
- session_id: sessionId,
708
- adapter,
709
- data: { trigger: "auto" },
710
- });
711
839
  markContextCompactionCompleted(coordRoot, {
712
840
  sessionId,
713
841
  instanceId: owner.instance_id,
@@ -731,25 +859,15 @@ async function main(): Promise<number> {
731
859
  recovery?.briefing ?? "",
732
860
  );
733
861
  if (injected && recovery) {
734
- completeRecoveryInjection(
735
- coordRoot,
736
- owner.instance_id,
737
- sessionId,
738
- adapter,
739
- recovery,
740
- "SessionStart",
741
- );
862
+ completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
742
863
  }
743
864
  } catch (err) {
744
865
  logError(coordRoot, err, { phase: "session-start-systemMessage" });
745
866
  }
746
- // Cross-machine presence (ADR 0016): announce this machine's sessions and
747
- // pull peers'. Both are throttled + fail-silent; network runs detached.
748
- // When a relay is configured, also make sure the live-socket daemon runs.
867
+ // Pull advisory cross-machine presence. V3 never publishes from the
868
+ // disposable heartbeat cache.
749
869
  try {
750
- publishPresence(coordRoot);
751
870
  fetchPresence(coordRoot);
752
- ensureRelayDaemon(coordRoot);
753
871
  } catch (err) {
754
872
  logError(coordRoot, err, { phase: "session-start-presence" });
755
873
  }
@@ -757,7 +875,7 @@ async function main(): Promise<number> {
757
875
 
758
876
  // Phase 8: SessionEnd cleanup: delete heartbeat + pid-map rows. Adapter-
759
877
  // agnostic since v0.5.0.
760
- if (norm.event_type === "session.end") {
878
+ if (norm.event_type === "session.ended") {
761
879
  try {
762
880
  cleanupSessionEnd(coordRoot, owner.instance_id, (data.reason as string) ?? "unknown");
763
881
  } catch (err) {
@@ -769,19 +887,12 @@ async function main(): Promise<number> {
769
887
  journalArchive(coordRoot, owner.instance_id);
770
888
  runSessionSyncExtension(coordRoot, true);
771
889
  }
772
- // Presence: publish the post-cleanup state so peers see this session gone
773
- // promptly rather than waiting out the remote-stale window.
774
- try {
775
- publishPresence(coordRoot);
776
- } catch (err) {
777
- logError(coordRoot, err, { phase: "session-end-presence" });
778
- }
779
890
  }
780
891
 
781
892
  // Phase 8: SubagentStart: sync-project to create the subagent heartbeat,
782
893
  // log the lifecycle event, and emit a context message announcing the
783
894
  // subagent (claude-code + cursor; codex doesn't fan out subagents today).
784
- if (norm.event_type === "subagent.start") {
895
+ if (norm.event_type === "agent.started") {
785
896
  try {
786
897
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
787
898
  if (existsSync(agentCoordBin)) {
@@ -808,7 +919,7 @@ async function main(): Promise<number> {
808
919
  }
809
920
 
810
921
  // Phase 8: SubagentStop: delete subagent heartbeat + log.
811
- if (norm.event_type === "subagent.stop") {
922
+ if (norm.event_type === "agent.completed") {
812
923
  try {
813
924
  cleanupSessionEnd(coordRoot, owner.instance_id, (data.reason as string) ?? "unknown");
814
925
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
@@ -831,7 +942,7 @@ async function main(): Promise<number> {
831
942
 
832
943
  // Phase 8: UserPromptSubmit: render dedup'd peer table + council pending
833
944
  // and emit the adapter-shaped systemMessage JSON. Adapter-agnostic since v0.5.0.
834
- if (norm.event_type === "user_prompt.submit") {
945
+ if (norm.event_type === "turn.started") {
835
946
  // Effects (claude-code): reset per-turn sound rate-limit counters + run
836
947
  // presence detection on the prompt.
837
948
  if (adapter === "claude-code") {
@@ -872,81 +983,27 @@ async function main(): Promise<number> {
872
983
  recovery?.briefing ?? "",
873
984
  );
874
985
  if (injected && recovery) {
875
- completeRecoveryInjection(
876
- coordRoot,
877
- owner.instance_id,
878
- sessionId,
879
- adapter,
880
- recovery,
881
- "UserPromptSubmit",
882
- );
986
+ completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
883
987
  }
884
988
  } catch (err) {
885
989
  logError(coordRoot, err, { phase: "user-prompt-submit-systemMessage" });
886
990
  }
887
991
  }
888
992
 
889
- // turn.stop: telemetry + turn-summary effects, then the stop verdict. The
993
+ // turn.completed: telemetry, then the stop verdict. The
890
994
  // verdict + codex-replay previously lived in the per-adapter shell adapters;
891
995
  // agent-hook owns them now. Runs on the normal "stop" event only;
892
996
  // "stop-failure" (API error) gets no gate, matching the previous
893
997
  // stop vs stop-failure split.
894
- if (norm.event_type === "turn.stop" && eventName === "stop") {
895
- // Codex: replay the JSONL transcript → canonical events so the verdict has
896
- // the status_checked / task_set / status_box_present evidence (codex
897
- // doesn't emit those live; this re-emits turn.stop after agent-hook's own,
898
- // so the verdict reads the replay's box signal as the latest).
899
- if (adapter === "codex" && payload?.transcript_path && existsSync(payload.transcript_path)) {
900
- try {
901
- replayCodexJsonl({
902
- coordRoot,
903
- jsonlPath: payload.transcript_path,
904
- sessionId,
905
- instanceId: owner.instance_id,
906
- lastAssistantMessage: (payload.raw.last_assistant_message as string | undefined) ?? "",
907
- });
908
- } catch (err) {
909
- logError(coordRoot, err, { phase: "codex-replay" });
910
- }
911
- }
912
-
913
- // CC effects: rate-limited session-telemetry sync + turn-summary Haiku
914
- // auto-summary.
998
+ if (norm.event_type === "turn.completed" && eventName === "stop") {
999
+ // Claude Code session telemetry sync remains an independent side effect.
915
1000
  if (adapter === "claude-code") {
916
1001
  runSessionSyncExtension(coordRoot, false);
917
- runTurnSummary(coordRoot, owner.instance_id, sessionId, payload?.transcript_path);
918
- }
919
-
920
- // Master-state heartbeat projection. Drains events.ndjson since the last
921
- // cursor → per-owner heartbeats. Was a SECOND binary (`agent-coord project`)
922
- // pinned to Claude Code Stop only; folded in here so it (a) is one
923
- // entry per event like everything else and (b) fires on EVERY adapter's stop,
924
- // not just CC. Runs unconditionally before the verdict's possible exit-2 return
925
- // (the events are real regardless of whether the agent gets nagged), and after
926
- // codex-replay above so codex's replayed events are included in the drain.
927
- // Not an emitter (consumes + writes heartbeats), so no emitter/consumer conflict.
928
- try {
929
- const result = consumeSince(coordRoot);
930
- projectHeartbeats(coordRoot, result.events);
931
- if (result.lastEventId) writeCursor(coordRoot, result.lastEventId);
932
- } catch (err) {
933
- logError(coordRoot, err, { phase: "stop-projection" });
934
1002
  }
935
1003
 
936
- // Presence: publish AFTER the projection above so the blob carries this
937
- // turn's fresh task/turn_summary/files. Change-batched + keepalive'd;
938
- // the push itself runs detached. The relay daemon (when configured) is
939
- // re-ensured here too — it self-exits on idle, and the next turn revives it.
940
- try {
941
- publishPresence(coordRoot);
942
- ensureRelayDaemon(coordRoot);
943
- } catch (err) {
944
- logError(coordRoot, err, { phase: "stop-presence" });
945
- }
946
-
947
- // Stop verdict (status-box + set-task gate). Direct in-process call: the
948
- // rule lives in harnery. agent-hook already emitted this turn.stop (with
949
- // status_box_present) above, so the evidence is in the stream.
1004
+ // Stop verdict (V3 ritual + task/status gate). Direct in-process call: the
1005
+ // rule lives in Harnery. agent-hook already emitted this turn.completed
1006
+ // with privacy-safe ritual observations, so the evidence is in the stream.
950
1007
  const verdict = evaluateStopHook(coordRoot, {
951
1008
  rule: "stop-hook",
952
1009
  instance_id: owner.instance_id,
@@ -956,13 +1013,28 @@ async function main(): Promise<number> {
956
1013
  workflow_child: coordEnv("WORKFLOW_CHILD") === "1",
957
1014
  });
958
1015
  if (!verdict.allow) {
959
- // Adapter-aware enforcement channel: Claude Code / Codex honor exit-2 +
960
- // stderr as a turn block; Cursor ignores exit codes (fail-open) and
961
- // re-prompts only via a `followup_message` it auto-submits. emitStopBlock
962
- // writes the right shape and returns the exit code to use.
1016
+ // Adapter-aware enforcement channel: Claude Code honors exit-2 + stderr
1017
+ // as a turn block; Cursor ignores exit codes and re-prompts only via a
1018
+ // `followup_message` it auto-submits. Codex returned observe-only above.
1019
+ // emitStopBlock writes the right shape and returns the exit code to use.
963
1020
  const { emitStopBlock } = await import("./adapter/output.ts");
964
1021
  return emitStopBlock(adapter, verdict, coordRoot);
965
1022
  }
1023
+
1024
+ // An explicit end requested from inside this turn cannot be authoritative
1025
+ // until the adapter has committed turn.completed above. Reconcile only
1026
+ // when such a request exists; any later real work cancels it in the
1027
+ // finalizer instead of being terminated underneath the agent.
1028
+ try {
1029
+ const { hasPendingExplicitSessionEndV3, reconcileSessionFinalizationV3 } = await import(
1030
+ "../agents/session-finalizer-v3.ts"
1031
+ );
1032
+ if (hasPendingExplicitSessionEndV3(coordRoot)) {
1033
+ reconcileSessionFinalizationV3(coordRoot);
1034
+ }
1035
+ } catch (err) {
1036
+ logError(coordRoot, err, { phase: "stop-explicit-session-finalization" });
1037
+ }
966
1038
  }
967
1039
 
968
1040
  // Phase 7: PreToolUse: heartbeat + pid-map self-heal on every tool call.
@@ -974,41 +1046,35 @@ async function main(): Promise<number> {
974
1046
  // side-by-side in the previous pre-tool-use adapter. The Phase 4-6 refactor
975
1047
  // preserved the heartbeat half but dropped the pid-map half; the pid-map
976
1048
  // call was restored here afterward.
977
- if (norm.event_type === "tool.pre_use") {
1049
+ if (norm.event_type === "tool.requested") {
978
1050
  try {
979
1051
  // Recorded fork lineage, heal-path flavor. A forked CC conversation
980
- // never fires its own session.start (SessionStart fires under the
1052
+ // never fires its own session.started (SessionStart fires under the
981
1053
  // PARENT's session id with source=resume, before the fork id is
982
1054
  // minted; verified 2026-08-05), so the fork's new instance first
983
1055
  // materializes right here. Gate detection on "no heartbeat yet" so the
984
1056
  // transcript scan runs once per instance lifetime, not per tool call.
985
- let forkedFrom: string | undefined;
986
- if (
987
- adapter === "claude-code" &&
988
- payload?.transcript_path &&
989
- !existsSync(join(coordRoot, ".harnery", "active", `${owner.instance_id}.json`))
990
- ) {
991
- forkedFrom = detectForkParent(payload.transcript_path, owner.instance_id);
992
- }
993
- healHeartbeatViaCli(coordRoot, owner.instance_id, sessionId, adapter, forkedFrom);
994
1057
  refreshPidmap(coordRoot, owner.instance_id, adapter, payload?.pid);
995
1058
  } catch (err) {
996
1059
  logError(coordRoot, err, { phase: "pre-tool-use-heal" });
997
1060
  }
998
1061
 
999
- // Image feed: a Read on an image file is the "agent viewed this" signal.
1000
- // Capture the bytes (content-addressed, dedup'd) + emit image.captured.
1062
+ // The suggested name is a pending display latch. The set-task call itself
1063
+ // runs before a name exists; every later tool waits until the exact block
1064
+ // is the first assistant text after the mint result. Cursor supplies
1065
+ // agent_message directly; Claude Code and Codex are resolved from their
1066
+ // JSONL transcripts. Later commentary must not erase an already-correct
1067
+ // display, and an unreadable transcript must not deadlock every tool.
1001
1068
  try {
1002
- captureImages(coordRoot, {
1003
- eventType: "tool.pre_use",
1004
- data,
1005
- payload,
1006
- instanceId: owner.instance_id,
1007
- sessionId,
1069
+ const displayAllowed = await enforcePendingSessionNameDisplay(
1070
+ coordRoot,
1071
+ owner.instance_id,
1008
1072
  adapter,
1009
- });
1073
+ payload,
1074
+ );
1075
+ if (!displayAllowed) return 0;
1010
1076
  } catch (err) {
1011
- logError(coordRoot, err, { phase: "pre-tool-use-image-capture" });
1077
+ logError(coordRoot, err, { phase: "pre-tool-use-session-name" });
1012
1078
  }
1013
1079
 
1014
1080
  // Windows-native Codex + WSL UNC only: block the one cross-shell shape
@@ -1022,13 +1088,6 @@ async function main(): Promise<number> {
1022
1088
  toolInput: payload?.tool_input,
1023
1089
  });
1024
1090
  if (unsafeShellReason) {
1025
- emit(coordRoot, {
1026
- event_type: "decision.block",
1027
- instance_id: owner.instance_id,
1028
- session_id: sessionId,
1029
- adapter,
1030
- data: { rule: "unsafe_cross_shell", reason: unsafeShellReason },
1031
- });
1032
1091
  const { emitDeny } = await import("./adapter/output.ts");
1033
1092
  emitDeny(adapter, unsafeShellReason);
1034
1093
  return 0;
@@ -1042,80 +1101,41 @@ async function main(): Promise<number> {
1042
1101
  // apply_patch (codex) parses paths from the patch body and runs verdict
1043
1102
  // per-path; Edit/Write/NotebookEdit resolve a single target. Non-write tools
1044
1103
  // (incl. Agent) yield no targets and pass through with no deny.
1104
+ let guardAllowed = true;
1045
1105
  try {
1046
- await runPreToolUseGuard(coordRoot, owner.instance_id, sessionId, data, adapter);
1106
+ guardAllowed = await runPreToolUseGuard(
1107
+ coordRoot,
1108
+ owner.instance_id,
1109
+ sessionId,
1110
+ data,
1111
+ adapter,
1112
+ );
1047
1113
  } catch (err) {
1048
1114
  logError(coordRoot, err, { phase: "pre-tool-use-guard" });
1049
1115
  }
1050
-
1051
- // Shell-mutation warn (warn-only, never blocks). Was the cursor
1052
- // beforeShellExecution + codex preToolUse-Bash shell-mutation-claim-log in
1053
- // the per-adapter shell adapters. Cursor sends the command at payload.command;
1054
- // codex Bash at tool_input.command. Emits a decision.warn per candidate-mutated
1055
- // path so a peer sees the write in events.ndjson. (CC never did this,
1056
- // preserved; it emits with its own hooks-side emitter per the
1057
- // independent-emitter rule.)
1058
- const shellCmd =
1059
- eventName === "before-shell-execution"
1060
- ? ((payload?.raw.command as string | undefined) ?? "")
1061
- : adapter === "codex" && data.tool_name === "Bash"
1062
- ? (((payload?.raw.tool_input as Record<string, unknown> | undefined)?.command as
1063
- | string
1064
- | undefined) ?? "")
1065
- : "";
1066
- if (shellCmd) {
1067
- try {
1068
- const paths = shellMutationPaths(shellCmd, coordRoot);
1069
- const truncated = shellCmd.length > 80 ? shellCmd.slice(0, 80) : shellCmd;
1070
- const platform = adapterPlatform(adapter);
1071
- for (const p of paths) {
1072
- emit(coordRoot, {
1073
- event_type: "decision.warn",
1074
- instance_id: owner.instance_id,
1075
- session_id: sessionId,
1076
- adapter,
1077
- data: {
1078
- rule: "shell_mutation_candidate",
1079
- reason: `path=${p} cmd=${truncated} platform=${platform}`,
1080
- },
1081
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1082
- } as Parameters<typeof emit>[1]);
1083
- }
1084
- } catch (err) {
1085
- logError(coordRoot, err, { phase: "shell-mutation-warn" });
1086
- }
1087
- }
1116
+ if (!guardAllowed) return 0;
1088
1117
  }
1089
1118
 
1090
- // Phase 7: PostToolUse: stamp last_tool + last_tool_target on heartbeat.
1091
- // Adapter-agnostic for the same reason as tool.pre_use above.
1092
- if (norm.event_type === "tool.post_use") {
1119
+ if (norm.event_type === "tool.completed" && eventName === "post-tool-use") {
1120
+ // Inject only at the successful tool boundary that actually minted a name:
1121
+ // the first non-empty set-task or the transition to lifecycle done. The
1122
+ // coordination row intentionally remains pending until transcript evidence
1123
+ // catches up; reading that latch alone here would re-inject after every
1124
+ // later tool and make the agent print the same block repeatedly.
1093
1125
  try {
1094
- stampToolActivity(coordRoot, owner.instance_id, data);
1095
- } catch (err) {
1096
- logError(coordRoot, err, { phase: "post-tool-use-stamp" });
1097
- }
1098
-
1099
- // Image feed: a Bash command that wrote an image (harn browse, harn image,
1100
- // --diff, …) is the "agent produced this" signal. Scan the command + its
1101
- // output for freshly-written image paths and capture them.
1102
- try {
1103
- captureImages(coordRoot, {
1104
- eventType: "tool.post_use",
1105
- data,
1106
- payload,
1107
- instanceId: owner.instance_id,
1108
- sessionId,
1109
- adapter,
1110
- });
1126
+ const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, owner.instance_id));
1127
+ if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
1128
+ const { emitContext } = await import("./adapter/output.ts");
1129
+ emitContext(adapter, "PostToolUse", sessionNameDisplayInstruction(name));
1130
+ }
1111
1131
  } catch (err) {
1112
- logError(coordRoot, err, { phase: "post-tool-use-image-capture" });
1132
+ logError(coordRoot, err, { phase: "post-tool-use-session-name" });
1113
1133
  }
1114
1134
  }
1115
1135
 
1116
1136
  // Phase 7: PostToolUseFailure: release claim on failed Edit (the file
1117
1137
  // never landed; the claim is stale). Adapter-agnostic.
1118
- if (norm.event_type === "tool.post_use_failure") {
1138
+ if (norm.event_type === "tool.completed" && eventName === "post-tool-use-failure") {
1119
1139
  try {
1120
1140
  releaseClaimOnFailure(coordRoot, owner.instance_id, data, payload?.raw);
1121
1141
  } catch (err) {
@@ -1126,16 +1146,60 @@ async function main(): Promise<number> {
1126
1146
  return 0;
1127
1147
  }
1128
1148
 
1149
+ async function enforcePendingSessionNameDisplay(
1150
+ coordRoot: string,
1151
+ instanceId: string,
1152
+ adapter: Adapter,
1153
+ payload: ParsedPayload | null,
1154
+ ): Promise<boolean> {
1155
+ const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, instanceId));
1156
+ if (!name) return true;
1157
+
1158
+ const command = extractBashCommand(payload?.tool_name, payload?.tool_input);
1159
+ if (isSessionNameRemediationCommand(command, resolveBinName(coordRoot))) return true;
1160
+
1161
+ const inspection =
1162
+ payload?.agent_message !== undefined
1163
+ ? {
1164
+ state: assistantTextStartsWithSessionNameBlock(payload.agent_message, name)
1165
+ ? ("present" as const)
1166
+ : ("absent" as const),
1167
+ }
1168
+ : inspectSessionNameDisplayImmediately(
1169
+ payload?.transcript_path,
1170
+ name,
1171
+ assistantTextStartsWithSessionNameBlock,
1172
+ );
1173
+ if (inspection.state === "present") {
1174
+ stampSessionNameSeen(coordRoot, instanceId, name);
1175
+ return true;
1176
+ }
1177
+
1178
+ if (inspection.state === "unavailable") {
1179
+ const { emitContext } = await import("./adapter/output.ts");
1180
+ emitContext(
1181
+ adapter,
1182
+ "PreToolUse",
1183
+ "Harnery could not verify the pending session-name display because the adapter transcript is unavailable or not flushed yet. This tool is allowed so the session cannot deadlock; display the requested block before later work. Verification remains pending.",
1184
+ );
1185
+ return true;
1186
+ }
1187
+
1188
+ const { emitDeny } = await import("./adapter/output.ts");
1189
+ emitDeny(adapter, sessionNameDisplayInstruction(name));
1190
+ return false;
1191
+ }
1192
+
1129
1193
  async function runPreToolUseGuard(
1130
1194
  coordRoot: string,
1131
1195
  instanceId: string,
1132
1196
  sessionId: string,
1133
1197
  data: Record<string, unknown>,
1134
1198
  adapter: Adapter,
1135
- ): Promise<void> {
1199
+ ): Promise<boolean> {
1136
1200
  const toolName = (data.tool_name as string | undefined) ?? "";
1137
1201
  const rawTargets = collectGuardTargets(toolName, data);
1138
- if (rawTargets.length === 0) return;
1202
+ if (rawTargets.length === 0) return true;
1139
1203
 
1140
1204
  let targets: Array<{ path: string; finalization?: ClaimFinalizationDescriptor }>;
1141
1205
  if (agentsRequireGitFinalization(coordRoot)) {
@@ -1144,7 +1208,7 @@ async function runPreToolUseGuard(
1144
1208
  if (denied && !denied.allow) {
1145
1209
  const { emitDeny } = await import("./adapter/output.ts");
1146
1210
  emitDeny(adapter, formatWriteClaimFinalizationDenial(denied, resolveBinName(coordRoot)));
1147
- return;
1211
+ return false;
1148
1212
  }
1149
1213
  const allowed = decisions.filter(
1150
1214
  (decision): decision is Extract<ClaimFinalizationDecision, { allow: true }> => decision.allow,
@@ -1159,7 +1223,7 @@ async function runPreToolUseGuard(
1159
1223
  .filter((path): path is string => path !== null)
1160
1224
  .map((path) => ({ path }));
1161
1225
  }
1162
- if (targets.length === 0) return;
1226
+ if (targets.length === 0) return true;
1163
1227
 
1164
1228
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1165
1229
  if (!existsSync(agentCoordBin)) {
@@ -1169,8 +1233,9 @@ async function runPreToolUseGuard(
1169
1233
  adapter,
1170
1234
  "Harnery denied the write before mutation because the claim coordinator is unavailable; repair the project's Harnery installation and retry.",
1171
1235
  );
1236
+ return false;
1172
1237
  }
1173
- return;
1238
+ return true;
1174
1239
  }
1175
1240
 
1176
1241
  // For apply_patch (multi-file), collect siblings so the deny reason names
@@ -1210,27 +1275,10 @@ async function runPreToolUseGuard(
1210
1275
  }
1211
1276
  const { emitDeny } = await import("./adapter/output.ts");
1212
1277
  emitDeny(adapter, reason);
1213
- return;
1278
+ return false;
1214
1279
  }
1215
-
1216
- // The verdict mutates the live heartbeat immediately, but that file is a
1217
- // projection and can be rebuilt at any later hook. Persist the acquisition
1218
- // in the canonical stream too. This matters most for Codex apply_patch:
1219
- // one tool call can claim several files, and tool.pre_use's clamped payload
1220
- // is not a durable ownership representation for those targets.
1221
- emit(coordRoot, {
1222
- event_type: "claim.acquire",
1223
- instance_id: instanceId,
1224
- session_id: sessionId,
1225
- adapter,
1226
- source: "agent-hooks",
1227
- data: {
1228
- path: target.path,
1229
- mode: "write",
1230
- ...(target.finalization ? { finalization: target.finalization } : {}),
1231
- },
1232
- });
1233
1280
  }
1281
+ return true;
1234
1282
  }
1235
1283
 
1236
1284
  /** Canonicalize a path to monorepo-relative form. Absolute paths under
@@ -1289,33 +1337,10 @@ function parseApplyPatchPaths(data: Record<string, unknown>): string[] {
1289
1337
  return out;
1290
1338
  }
1291
1339
 
1292
- function healHeartbeatViaCli(
1293
- coordRoot: string,
1294
- instanceId: string,
1295
- sessionId: string,
1296
- adapter: string,
1297
- forkedFrom?: string,
1298
- ): void {
1299
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1300
- if (!existsSync(agentCoordBin)) return;
1301
- // Pass the detected adapter so a pruned Cursor/Codex heartbeat is recreated
1302
- // with the correct platform; without it, healHeartbeat defaults to
1303
- // claude-code and the dashboard mislabels the agent. See
1304
- // heartbeat-writer.healHeartbeat. --forked-from carries detected fork
1305
- // lineage for an instance that never session.started (the CC fork flow).
1306
- const args = ["heal-heartbeat", instanceId, sessionId, `--adapter=${adapter}`];
1307
- if (forkedFrom) args.push(`--forked-from=${forkedFrom}`);
1308
- spawnSync(agentCoordBin, args, {
1309
- encoding: "utf8",
1310
- timeout: 2000,
1311
- env: childEnv(coordRoot),
1312
- });
1313
- }
1314
-
1315
1340
  /**
1316
1341
  * Walk up the ppid chain on Linux/WSL looking for the adapter anchor PID,
1317
1342
  * the PID of the claude / cursor / codex binary. Finds the agent PID. Used by
1318
- * `tool.pre_use`'s pid-map self-heal so a re-parented adapter binary (the
1343
+ * `tool.requested` pid-map self-heal so a re-parented adapter binary (the
1319
1344
  * VS Code 2.1.x sibling-claude spawn case) gets its pid-map row rewritten on
1320
1345
  * the next tool call rather than going invisible until SessionStart fires
1321
1346
  * again, which it may never do.
@@ -1382,9 +1407,7 @@ function findAdapterAnchorPid(adapter?: Adapter): number | undefined {
1382
1407
  }
1383
1408
 
1384
1409
  /**
1385
- * Pid-map self-heal for `tool.pre_use`. Symmetric counterpart to
1386
- * `healHeartbeatViaCli`; the two were paired before the Phase 6 refactor split
1387
- * them apart, then restored together afterward.
1410
+ * Pid-map self-heal for `tool.requested`.
1388
1411
  *
1389
1412
  * The pid argument prefers the payload's `pid` (CC populates it on
1390
1413
  * SessionStart and may also send it on PreToolUse), then
@@ -1407,40 +1430,6 @@ function refreshPidmap(
1407
1430
  writePidmapViaAgentCoord(coordRoot, pid, instanceId, adapterPlatform(adapter));
1408
1431
  }
1409
1432
 
1410
- function stampToolActivity(
1411
- coordRoot: string,
1412
- instanceId: string,
1413
- data: Record<string, unknown>,
1414
- ): void {
1415
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1416
- if (!existsSync(agentCoordBin)) return;
1417
- const toolName = (data.tool_name as string | undefined) ?? "";
1418
- // Extract a 1-line target from the tool_input blob (file path / command head).
1419
- const toolInputRaw = data.tool_input;
1420
- let target = "";
1421
- if (typeof toolInputRaw === "string") {
1422
- try {
1423
- const parsed = JSON.parse(toolInputRaw) as Record<string, unknown>;
1424
- target =
1425
- (parsed.file_path as string | undefined) ??
1426
- (parsed.path as string | undefined) ??
1427
- (parsed.notebook_path as string | undefined) ??
1428
- (parsed.command as string | undefined) ??
1429
- (parsed.url as string | undefined) ??
1430
- (parsed.pattern as string | undefined) ??
1431
- "";
1432
- } catch {
1433
- /* skip */
1434
- }
1435
- }
1436
- if (target.length > 200) target = target.slice(0, 200);
1437
- spawnSync(agentCoordBin, ["stamp-tool-activity", instanceId, toolName, target], {
1438
- encoding: "utf8",
1439
- timeout: 2000,
1440
- env: childEnv(coordRoot),
1441
- });
1442
- }
1443
-
1444
1433
  function releaseClaimOnFailure(
1445
1434
  coordRoot: string,
1446
1435
  instanceId: string,
@@ -1484,6 +1473,12 @@ function releaseClaimOnFailure(
1484
1473
  : filePath;
1485
1474
  }
1486
1475
 
1476
+ // In V3 the release is an authority event, not a heartbeat mutation. Avoid
1477
+ // creating a disposable cache for a failure that never acquired a claim,
1478
+ // but release the exact path when the validated projection says it is held.
1479
+ const row = readLiveCoordinationRow(coordRoot, instanceId);
1480
+ if (!row?.files_touched?.includes(canonical)) return;
1481
+
1487
1482
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1488
1483
  if (!existsSync(agentCoordBin)) return;
1489
1484
  spawnSync(agentCoordBin, ["release-claim", instanceId, canonical], {
@@ -1494,15 +1489,6 @@ function releaseClaimOnFailure(
1494
1489
  }
1495
1490
 
1496
1491
  function cleanupSessionEnd(coordRoot: string, instanceId: string, reason: string): void {
1497
- // Remove heartbeat from the canonical .harnery/active/ dir.
1498
- const path = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
1499
- try {
1500
- if (existsSync(path)) {
1501
- require("node:fs").unlinkSync(path);
1502
- }
1503
- } catch {
1504
- /* swallow */
1505
- }
1506
1492
  // Sweep pid-map entries pointing to this instance
1507
1493
  const pidmapDir = join(coordRoot, ".harnery", "pid-map");
1508
1494
  if (existsSync(pidmapDir)) {
@@ -1547,29 +1533,20 @@ async function emitUserPromptSubmitSystemMessage(
1547
1533
  let additionalContext = "";
1548
1534
 
1549
1535
  if (existsSync(agentCoordBin)) {
1550
- // Look up the agent's name from its heartbeat (for council pending rendering).
1551
- let agentName = "";
1552
- try {
1553
- const fs = require("node:fs") as typeof import("node:fs");
1554
- const hbPath = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
1555
- if (fs.existsSync(hbPath)) {
1556
- const hb = JSON.parse(fs.readFileSync(hbPath, "utf8")) as { name?: string };
1557
- agentName = hb.name ?? "";
1558
- }
1559
- } catch {
1560
- /* fall through with empty name; peer table still renders */
1561
- }
1562
-
1563
1536
  const args = ["prompt-context", "--instance", instanceId, "--session", sessionId];
1564
- if (agentName) args.push("--name", agentName);
1565
1537
  // Every human-facing adapter gets the first-session naming reminder from
1566
1538
  // the shared hook path. Cursor + Codex additionally get ongoing set-task
1567
1539
  // staleness nudges; Claude Code enforces those via its Stop transcript.
1568
1540
  // Codex gets a fresh status-footer reminder on every prompt. Its Stop hook
1569
1541
  // stays observe-only, so missing the footer never replaces the answer.
1542
+ // Claude Code + Cursor (the Stop-ENFORCING adapters) get a fresh per-turn
1543
+ // ritual reminder: satisfying the contract up front is far cheaper than
1544
+ // the bounce-and-retry the Stop hook otherwise forces.
1570
1545
  args.push("--session-name-nudge");
1571
1546
  if (adapter === "cursor" || adapter === "codex") args.push("--task-nudge");
1572
1547
  if (adapter === "codex") args.push("--status-footer-nudge");
1548
+ if (adapter === "claude-code" || adapter === "cursor")
1549
+ args.push("--turn-ritual-nudge", adapter);
1573
1550
  const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1574
1551
  if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
1575
1552
  }
@@ -1597,7 +1574,7 @@ function emitSubagentStartContext(
1597
1574
  adapter: Adapter,
1598
1575
  ): void {
1599
1576
  // Look up the subagent's assigned name (just-written by agent-coord assignName
1600
- // in session.start data) + the parent's short id for the "you are a subagent
1577
+ // in session.started data) + the parent's short id for the "you are a subagent
1601
1578
  // of X" framing.
1602
1579
  const subagentName = (data.name as string | undefined) ?? "";
1603
1580
  if (!subagentName) return;
@@ -1642,13 +1619,19 @@ async function emitSessionStartSystemMessage(
1642
1619
  recoveryBriefing = "",
1643
1620
  ): Promise<boolean> {
1644
1621
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1622
+ const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
1645
1623
  let additionalContext = "";
1646
1624
  if (existsSync(agentCoordBin)) {
1647
1625
  // Sync-project so the heartbeat exists for downstream readers (peer table,
1648
1626
  // wiring check, council invites).
1649
1627
  spawnSync(agentCoordBin, ["project"], { encoding: "utf8", timeout: 3000 });
1650
- // Stale-sweep dead peers before rendering peer table.
1651
- spawnSync(agentCoordBin, ["stale-sweep"], { encoding: "utf8", timeout: 3000 });
1628
+ // Opportunistic reconciliation makes normal session starts a failsafe for
1629
+ // archive, idle, cascade, and host lifecycle observations.
1630
+ spawnSync(agentCoordBin, ["reconcile-finalization"], {
1631
+ encoding: "utf8",
1632
+ timeout: 5000,
1633
+ env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot },
1634
+ });
1652
1635
 
1653
1636
  // SESSION_START activity log line, fired across all adapters.
1654
1637
  const model = (emittedData.model as string | undefined) ?? "unknown";
@@ -1665,32 +1648,37 @@ async function emitSessionStartSystemMessage(
1665
1648
  { encoding: "utf8", timeout: 2000 },
1666
1649
  );
1667
1650
 
1668
- // Render the systemMessage via agent-coord.
1669
- const agentName = (emittedData.name as string | undefined) ?? "";
1670
- const args = ["session-context", "--instance", instanceId, "--session", sessionId];
1671
- if (agentName) args.push("--name", agentName);
1672
- // The "You are agent-X." prefix in session-context renders unqualified by
1673
- // default (claude-code-style). For cursor/codex the bash dispatchers add
1674
- // a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
1675
- if (adapter !== "claude-code") {
1676
- args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
1651
+ // Workflow children retain lifecycle/event capture but do not receive
1652
+ // operator-facing peer, council, or init-remediation context. Injecting
1653
+ // that context can make a bounded child follow housekeeping instructions
1654
+ // instead of its assigned prompt.
1655
+ if (!workflowChild) {
1656
+ const agentName = (emittedData.name as string | undefined) ?? "";
1657
+ const args = ["session-context", "--instance", instanceId, "--session", sessionId];
1658
+ if (agentName) args.push("--name", agentName);
1659
+ // The "You are agent-X." prefix in session-context renders unqualified by
1660
+ // default (claude-code-style). For cursor/codex the bash dispatchers add
1661
+ // a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
1662
+ if (adapter !== "claude-code") {
1663
+ args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
1664
+ }
1665
+ const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1666
+ if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
1677
1667
  }
1678
- const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1679
- if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
1680
1668
  }
1681
1669
 
1682
1670
  // Effect (claude-code): merge the journal recovery cue into the session-start
1683
1671
  // context. Was a standalone additionalContext emission from the previous
1684
1672
  // journal-on-start adapter; now that agent-hook is the single SessionStart
1685
1673
  // entry, it folds in here.
1686
- if (adapter === "claude-code") {
1674
+ if (adapter === "claude-code" && !workflowChild) {
1687
1675
  const cue = journalRecoveryCue(coordRoot);
1688
1676
  if (cue) additionalContext = [additionalContext, cue].filter(Boolean).join("\n\n");
1689
1677
  }
1690
- if (recoveryBriefing) {
1678
+ if (recoveryBriefing && !workflowChild) {
1691
1679
  additionalContext = [additionalContext, recoveryBriefing].filter(Boolean).join("\n\n");
1692
1680
  }
1693
- if (adapter === "codex" && isWslUncPath(emittedData.cwd)) {
1681
+ if (adapter === "codex" && !workflowChild && isWslUncPath(emittedData.cwd)) {
1694
1682
  const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, emittedData.cwd);
1695
1683
  if (fileLinkContext) {
1696
1684
  additionalContext = [additionalContext, fileLinkContext].filter(Boolean).join("\n\n");
@@ -1716,22 +1704,10 @@ function completeRecoveryInjection(
1716
1704
  coordRoot: string,
1717
1705
  instanceId: string,
1718
1706
  sessionId: string,
1719
- adapter: Adapter,
1720
1707
  recovery: PreparedContextRecovery,
1721
- injectionEvent: "SessionStart" | "UserPromptSubmit",
1722
1708
  ): void {
1723
1709
  completeContextRecovery(coordRoot, { sessionId, instanceId });
1724
- emit(coordRoot, {
1725
- event_type: "context.recovery.injected",
1726
- instance_id: instanceId,
1727
- session_id: sessionId,
1728
- adapter,
1729
- data: {
1730
- capsule_id: recovery.capsule.capsule_id,
1731
- generation: recovery.capsule.generation,
1732
- injection_event: injectionEvent,
1733
- },
1734
- });
1710
+ void recovery;
1735
1711
  }
1736
1712
 
1737
1713
  main()