harnery 0.33.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 (657) hide show
  1. package/dist/commander.d.ts.map +1 -1
  2. package/dist/commander.js +4 -0
  3. package/dist/commands/adapter.js +2 -2
  4. package/dist/commands/agents.d.ts +144 -1
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +1220 -588
  7. package/dist/commands/artifacts.d.ts.map +1 -1
  8. package/dist/commands/artifacts.js +7 -6
  9. package/dist/commands/backup.d.ts +1 -1
  10. package/dist/commands/backup.d.ts.map +1 -1
  11. package/dist/commands/backup.js +29 -16
  12. package/dist/commands/browse.d.ts.map +1 -1
  13. package/dist/commands/browse.js +234 -12
  14. package/dist/commands/checkpoint.d.ts.map +1 -1
  15. package/dist/commands/checkpoint.js +3 -2
  16. package/dist/commands/claude-desktop.d.ts +1 -1
  17. package/dist/commands/claude-desktop.d.ts.map +1 -1
  18. package/dist/commands/claude-desktop.js +3 -74
  19. package/dist/commands/context.js +1 -6
  20. package/dist/commands/decision.d.ts.map +1 -1
  21. package/dist/commands/decision.js +22 -38
  22. package/dist/commands/docs.d.ts.map +1 -1
  23. package/dist/commands/docs.js +91 -28
  24. package/dist/commands/doctor.js +10 -1
  25. package/dist/commands/events.d.ts +5 -0
  26. package/dist/commands/events.d.ts.map +1 -0
  27. package/dist/commands/events.js +189 -0
  28. package/dist/commands/governor.d.ts.map +1 -1
  29. package/dist/commands/governor.js +13 -4
  30. package/dist/commands/init.d.ts +9 -2
  31. package/dist/commands/init.d.ts.map +1 -1
  32. package/dist/commands/init.js +223 -51
  33. package/dist/commands/journal.d.ts.map +1 -1
  34. package/dist/commands/journal.js +4 -13
  35. package/dist/commands/ledger-v3.d.ts +5 -0
  36. package/dist/commands/ledger-v3.d.ts.map +1 -0
  37. package/dist/commands/ledger-v3.js +91 -0
  38. package/dist/commands/presence.d.ts.map +1 -1
  39. package/dist/commands/presence.js +11 -5
  40. package/dist/commands/sync.d.ts +1 -1
  41. package/dist/commands/sync.js +1 -1
  42. package/dist/commands/web.d.ts +6 -0
  43. package/dist/commands/web.d.ts.map +1 -1
  44. package/dist/commands/web.js +67 -7
  45. package/dist/commands/work.d.ts.map +1 -1
  46. package/dist/commands/work.js +22 -5
  47. package/dist/core/adapter.d.ts +5 -0
  48. package/dist/core/adapter.d.ts.map +1 -0
  49. package/dist/core/adapter.js +1 -0
  50. package/dist/core/adapters/attest.d.ts +5 -2
  51. package/dist/core/adapters/attest.d.ts.map +1 -1
  52. package/dist/core/adapters/attest.js +8 -3
  53. package/dist/core/adapters/profiles.d.ts +6 -6
  54. package/dist/core/adapters/profiles.d.ts.map +1 -1
  55. package/dist/core/adapters/profiles.js +4 -4
  56. package/dist/core/agents/canonical-emit.d.ts +17 -15
  57. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  58. package/dist/core/agents/canonical-emit.js +22 -22
  59. package/dist/core/agents/cli.js +207 -234
  60. package/dist/core/agents/codex-archive-v3.d.ts +14 -0
  61. package/dist/core/agents/codex-archive-v3.d.ts.map +1 -0
  62. package/dist/core/agents/codex-archive-v3.js +110 -0
  63. package/dist/core/agents/coord-client.d.ts +19 -81
  64. package/dist/core/agents/coord-client.d.ts.map +1 -1
  65. package/dist/core/agents/coord-client.js +65 -155
  66. package/dist/core/agents/finalization.d.ts +4 -1
  67. package/dist/core/agents/finalization.d.ts.map +1 -1
  68. package/dist/core/agents/finalization.js +89 -32
  69. package/dist/core/agents/index.d.ts +4 -5
  70. package/dist/core/agents/index.d.ts.map +1 -1
  71. package/dist/core/agents/index.js +4 -5
  72. package/dist/core/agents/live-authority-v3.d.ts +59 -0
  73. package/dist/core/agents/live-authority-v3.d.ts.map +1 -0
  74. package/dist/core/agents/live-authority-v3.js +233 -0
  75. package/dist/core/agents/live-lifecycle-v3.d.ts +20 -0
  76. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -0
  77. package/dist/core/agents/live-lifecycle-v3.js +91 -0
  78. package/dist/core/agents/live-observation-v3.d.ts +25 -0
  79. package/dist/core/agents/live-observation-v3.d.ts.map +1 -0
  80. package/dist/core/agents/live-observation-v3.js +157 -0
  81. package/dist/core/agents/render/prompt-context.d.ts +4 -4
  82. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  83. package/dist/core/agents/render/prompt-context.js +23 -58
  84. package/dist/core/agents/render/session-context.d.ts +1 -1
  85. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  86. package/dist/core/agents/render/session-context.js +8 -32
  87. package/dist/core/agents/rules/claim-conflict.d.ts +3 -3
  88. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  89. package/dist/core/agents/rules/claim-conflict.js +79 -127
  90. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  91. package/dist/core/agents/rules/commit-conflict.js +3 -18
  92. package/dist/core/agents/rules/stop-hook.d.ts +9 -50
  93. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  94. package/dist/core/agents/rules/stop-hook.js +193 -236
  95. package/dist/core/agents/session-events.d.ts +5 -7
  96. package/dist/core/agents/session-events.d.ts.map +1 -1
  97. package/dist/core/agents/session-events.js +127 -95
  98. package/dist/core/agents/session-finalization-state-v3.d.ts +30 -0
  99. package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -0
  100. package/dist/core/agents/session-finalization-state-v3.js +40 -0
  101. package/dist/core/agents/session-finalizer-v3.d.ts +107 -0
  102. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -0
  103. package/dist/core/agents/session-finalizer-v3.js +734 -0
  104. package/dist/core/agents/session-name-display.d.ts +40 -0
  105. package/dist/core/agents/session-name-display.d.ts.map +1 -0
  106. package/dist/core/agents/session-name-display.js +119 -0
  107. package/dist/core/agents/state/activity-log.d.ts +3 -3
  108. package/dist/core/agents/state/activity-log.d.ts.map +1 -1
  109. package/dist/core/agents/state/activity-log.js +7 -16
  110. package/dist/core/agents/state/heartbeat-writer.d.ts +23 -55
  111. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  112. package/dist/core/agents/state/heartbeat-writer.js +56 -228
  113. package/dist/core/agents/state/live-coordination-view.d.ts +31 -0
  114. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -0
  115. package/dist/core/agents/state/live-coordination-view.js +209 -0
  116. package/dist/core/agents/state/session-state.d.ts +4 -3
  117. package/dist/core/agents/state/session-state.d.ts.map +1 -1
  118. package/dist/core/agents/state/session-state.js +18 -9
  119. package/dist/core/agents/state/stale-sweep.d.ts +1 -1
  120. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  121. package/dist/core/agents/state/stale-sweep.js +26 -15
  122. package/dist/core/artifacts/index.d.ts.map +1 -1
  123. package/dist/core/artifacts/index.js +5 -7
  124. package/dist/core/config.d.ts +33 -2
  125. package/dist/core/config.d.ts.map +1 -1
  126. package/dist/core/config.js +98 -2
  127. package/dist/core/context/index.d.ts +1 -4
  128. package/dist/core/context/index.d.ts.map +1 -1
  129. package/dist/core/context/index.js +9 -9
  130. package/dist/core/events/span-parent.d.ts +15 -0
  131. package/dist/core/events/span-parent.d.ts.map +1 -0
  132. package/dist/core/events/span-parent.js +34 -0
  133. package/dist/core/events/v3/advance.d.ts +14 -0
  134. package/dist/core/events/v3/advance.d.ts.map +1 -0
  135. package/dist/core/events/v3/advance.js +134 -0
  136. package/dist/core/events/v3/authority-outbox.d.ts +78 -0
  137. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -0
  138. package/dist/core/events/v3/authority-outbox.js +399 -0
  139. package/dist/core/events/v3/base-builder.d.ts +20 -0
  140. package/dist/core/events/v3/base-builder.d.ts.map +1 -0
  141. package/dist/core/events/v3/base-builder.js +29 -0
  142. package/dist/core/events/v3/base-contract.d.ts +7576 -0
  143. package/dist/core/events/v3/base-contract.d.ts.map +1 -0
  144. package/dist/core/events/v3/base-contract.js +647 -0
  145. package/dist/core/events/v3/bootstrap.d.ts +34 -0
  146. package/dist/core/events/v3/bootstrap.d.ts.map +1 -0
  147. package/dist/core/events/v3/bootstrap.js +175 -0
  148. package/dist/core/events/v3/builder.d.ts +20 -0
  149. package/dist/core/events/v3/builder.d.ts.map +1 -0
  150. package/dist/core/events/v3/builder.js +28 -0
  151. package/dist/core/events/v3/canonical.d.ts +31 -0
  152. package/dist/core/events/v3/canonical.d.ts.map +1 -0
  153. package/dist/core/events/v3/canonical.js +100 -0
  154. package/dist/core/events/v3/capabilities.d.ts +31 -0
  155. package/dist/core/events/v3/capabilities.d.ts.map +1 -0
  156. package/dist/core/events/v3/capabilities.js +107 -0
  157. package/dist/core/events/v3/capability-drift.d.ts +22 -0
  158. package/dist/core/events/v3/capability-drift.d.ts.map +1 -0
  159. package/dist/core/events/v3/capability-drift.js +134 -0
  160. package/dist/core/events/v3/catalog.d.ts +38 -0
  161. package/dist/core/events/v3/catalog.d.ts.map +1 -0
  162. package/dist/core/events/v3/catalog.js +135 -0
  163. package/dist/core/events/v3/clock-order.d.ts +34 -0
  164. package/dist/core/events/v3/clock-order.d.ts.map +1 -0
  165. package/dist/core/events/v3/clock-order.js +35 -0
  166. package/dist/core/events/v3/contract.d.ts +171 -0
  167. package/dist/core/events/v3/contract.d.ts.map +1 -0
  168. package/dist/core/events/v3/contract.js +180 -0
  169. package/dist/core/events/v3/control.d.ts +111 -0
  170. package/dist/core/events/v3/control.d.ts.map +1 -0
  171. package/dist/core/events/v3/control.js +419 -0
  172. package/dist/core/events/v3/coordination-view.d.ts +69 -0
  173. package/dist/core/events/v3/coordination-view.d.ts.map +1 -0
  174. package/dist/core/events/v3/coordination-view.js +119 -0
  175. package/dist/core/events/v3/economics.d.ts +77 -0
  176. package/dist/core/events/v3/economics.d.ts.map +1 -0
  177. package/dist/core/events/v3/economics.js +274 -0
  178. package/dist/core/events/v3/finalization-view.d.ts +26 -0
  179. package/dist/core/events/v3/finalization-view.d.ts.map +1 -0
  180. package/dist/core/events/v3/finalization-view.js +37 -0
  181. package/dist/core/events/v3/fingerprint-keys.d.ts +25 -0
  182. package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -0
  183. package/dist/core/events/v3/fingerprint-keys.js +217 -0
  184. package/dist/core/events/v3/generated.d.ts +4 -0
  185. package/dist/core/events/v3/generated.d.ts.map +1 -0
  186. package/dist/core/events/v3/generated.js +47 -0
  187. package/dist/core/events/v3/ids.d.ts +9 -0
  188. package/dist/core/events/v3/ids.d.ts.map +1 -0
  189. package/dist/core/events/v3/ids.js +25 -0
  190. package/dist/core/events/v3/index.d.ts +35 -0
  191. package/dist/core/events/v3/index.d.ts.map +1 -0
  192. package/dist/core/events/v3/index.js +33 -0
  193. package/dist/core/events/v3/latency.d.ts +63 -0
  194. package/dist/core/events/v3/latency.d.ts.map +1 -0
  195. package/dist/core/events/v3/latency.js +412 -0
  196. package/dist/core/events/v3/live-feed.d.ts +45 -0
  197. package/dist/core/events/v3/live-feed.d.ts.map +1 -0
  198. package/dist/core/events/v3/live-feed.js +246 -0
  199. package/dist/core/events/v3/live-observation.d.ts +35 -0
  200. package/dist/core/events/v3/live-observation.d.ts.map +1 -0
  201. package/dist/core/events/v3/live-observation.js +1 -0
  202. package/dist/core/events/v3/live-routing.d.ts +57 -0
  203. package/dist/core/events/v3/live-routing.d.ts.map +1 -0
  204. package/dist/core/events/v3/live-routing.js +107 -0
  205. package/dist/core/events/v3/producers/command-recorder.d.ts +67 -0
  206. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -0
  207. package/dist/core/events/v3/producers/command-recorder.js +477 -0
  208. package/dist/core/events/v3/producers/command.d.ts +50 -0
  209. package/dist/core/events/v3/producers/command.d.ts.map +1 -0
  210. package/dist/core/events/v3/producers/command.js +135 -0
  211. package/dist/core/events/v3/producers/coordination-recorder.d.ts +53 -0
  212. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -0
  213. package/dist/core/events/v3/producers/coordination-recorder.js +376 -0
  214. package/dist/core/events/v3/producers/coordination.d.ts +90 -0
  215. package/dist/core/events/v3/producers/coordination.d.ts.map +1 -0
  216. package/dist/core/events/v3/producers/coordination.js +229 -0
  217. package/dist/core/events/v3/producers/hook-base.d.ts +54 -0
  218. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -0
  219. package/dist/core/events/v3/producers/hook-base.js +424 -0
  220. package/dist/core/events/v3/producers/hook.d.ts +29 -0
  221. package/dist/core/events/v3/producers/hook.d.ts.map +1 -0
  222. package/dist/core/events/v3/producers/hook.js +71 -0
  223. package/dist/core/events/v3/producers/intake.d.ts +65 -0
  224. package/dist/core/events/v3/producers/intake.d.ts.map +1 -0
  225. package/dist/core/events/v3/producers/intake.js +244 -0
  226. package/dist/core/events/v3/producers/recorder.d.ts +240 -0
  227. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -0
  228. package/dist/core/events/v3/producers/recorder.js +2176 -0
  229. package/dist/core/events/v3/projection.d.ts +101 -0
  230. package/dist/core/events/v3/projection.d.ts.map +1 -0
  231. package/dist/core/events/v3/projection.js +479 -0
  232. package/dist/core/events/v3/reader.d.ts +75 -0
  233. package/dist/core/events/v3/reader.d.ts.map +1 -0
  234. package/dist/core/events/v3/reader.js +778 -0
  235. package/dist/core/events/v3/recovery-record.d.ts +47 -0
  236. package/dist/core/events/v3/recovery-record.d.ts.map +1 -0
  237. package/dist/core/events/v3/recovery-record.js +167 -0
  238. package/dist/core/events/v3/recovery.d.ts +22 -0
  239. package/dist/core/events/v3/recovery.d.ts.map +1 -0
  240. package/dist/core/events/v3/recovery.js +227 -0
  241. package/dist/core/events/v3/span-state.d.ts +52 -0
  242. package/dist/core/events/v3/span-state.d.ts.map +1 -0
  243. package/dist/core/events/v3/span-state.js +142 -0
  244. package/dist/core/events/v3/targets.d.ts +23 -0
  245. package/dist/core/events/v3/targets.d.ts.map +1 -0
  246. package/dist/core/events/v3/targets.js +134 -0
  247. package/dist/core/events/v3/turn-telemetry.d.ts +41 -0
  248. package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -0
  249. package/dist/core/events/v3/turn-telemetry.js +168 -0
  250. package/dist/core/events/v3/validate.d.ts +9 -0
  251. package/dist/core/events/v3/validate.d.ts.map +1 -0
  252. package/dist/core/events/v3/validate.js +281 -0
  253. package/dist/core/events/v3/writer.d.ts +56 -0
  254. package/dist/core/events/v3/writer.d.ts.map +1 -0
  255. package/dist/core/events/v3/writer.js +335 -0
  256. package/dist/core/governor/index.d.ts +1 -1
  257. package/dist/core/governor/index.d.ts.map +1 -1
  258. package/dist/core/governor/index.js +1 -1
  259. package/dist/core/governor/plan-api.d.ts +2 -1
  260. package/dist/core/governor/plan-api.d.ts.map +1 -1
  261. package/dist/core/governor/plan-api.js +6 -3
  262. package/dist/core/governor/read.d.ts +1 -1
  263. package/dist/core/governor/read.d.ts.map +1 -1
  264. package/dist/core/governor/read.js +1 -1
  265. package/dist/core/governor/state.d.ts +9 -0
  266. package/dist/core/governor/state.d.ts.map +1 -1
  267. package/dist/core/governor/state.js +40 -3
  268. package/dist/core/guard/config.d.ts +5 -0
  269. package/dist/core/guard/config.d.ts.map +1 -0
  270. package/dist/core/guard/config.js +157 -0
  271. package/dist/core/guard/coordinator.d.ts +14 -0
  272. package/dist/core/guard/coordinator.d.ts.map +1 -0
  273. package/dist/core/guard/coordinator.js +173 -0
  274. package/dist/core/guard/evaluator.d.ts +4 -0
  275. package/dist/core/guard/evaluator.d.ts.map +1 -0
  276. package/dist/core/guard/evaluator.js +242 -0
  277. package/dist/core/guard/evidence-v3.d.ts +15 -0
  278. package/dist/core/guard/evidence-v3.d.ts.map +1 -0
  279. package/dist/core/guard/evidence-v3.js +171 -0
  280. package/dist/core/guard/index.d.ts +10 -0
  281. package/dist/core/guard/index.d.ts.map +1 -0
  282. package/dist/core/guard/index.js +6 -0
  283. package/dist/core/guard/live-source-v3.d.ts +40 -0
  284. package/dist/core/guard/live-source-v3.d.ts.map +1 -0
  285. package/dist/core/guard/live-source-v3.js +128 -0
  286. package/dist/core/guard/storage.d.ts +27 -0
  287. package/dist/core/guard/storage.d.ts.map +1 -0
  288. package/dist/core/guard/storage.js +140 -0
  289. package/dist/core/guard/types.d.ts +119 -0
  290. package/dist/core/guard/types.d.ts.map +1 -0
  291. package/dist/core/guard/types.js +1 -0
  292. package/dist/core/hooks/adapter/detect.d.ts +18 -1
  293. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  294. package/dist/core/hooks/adapter/detect.js +29 -0
  295. package/dist/core/hooks/adapter/events.d.ts +7 -4
  296. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  297. package/dist/core/hooks/adapter/events.js +12 -4
  298. package/dist/core/hooks/adapter/output.d.ts +2 -2
  299. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  300. package/dist/core/hooks/adapter/parse.d.ts +7 -4
  301. package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
  302. package/dist/core/hooks/adapter/parse.js +63 -25
  303. package/dist/core/hooks/adapter/wiring.d.ts +30 -4
  304. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  305. package/dist/core/hooks/adapter/wiring.js +89 -11
  306. package/dist/core/hooks/cli.d.ts +1 -1
  307. package/dist/core/hooks/cli.js +305 -444
  308. package/dist/core/hooks/effects/image-capture.d.ts +20 -34
  309. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  310. package/dist/core/hooks/effects/image-capture.js +212 -221
  311. package/dist/core/hooks/effects/index.d.ts +1 -9
  312. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  313. package/dist/core/hooks/effects/index.js +1 -24
  314. package/dist/core/hooks/events/input-hash.d.ts +7 -0
  315. package/dist/core/hooks/events/input-hash.d.ts.map +1 -0
  316. package/dist/core/hooks/events/input-hash.js +43 -0
  317. package/dist/core/hooks/index.d.ts +4 -3
  318. package/dist/core/hooks/index.d.ts.map +1 -1
  319. package/dist/core/hooks/index.js +2 -3
  320. package/dist/core/hooks/resolve/coord-root.d.ts +1 -1
  321. package/dist/core/hooks/resolve/coord-root.js +1 -1
  322. package/dist/core/hooks/resolve/owner.d.ts +16 -15
  323. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  324. package/dist/core/hooks/resolve/owner.js +51 -42
  325. package/dist/core/hooks/resolve/transcript.d.ts +33 -3
  326. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  327. package/dist/core/hooks/resolve/transcript.js +155 -14
  328. package/dist/core/hooks/session-name-presence.d.ts +9 -10
  329. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  330. package/dist/core/hooks/session-name-presence.js +15 -15
  331. package/dist/core/hooks/unsafe-cross-shell.d.ts +1 -1
  332. package/dist/core/hooks/unsafe-cross-shell.d.ts.map +1 -1
  333. package/dist/core/journal/index.d.ts +2 -2
  334. package/dist/core/journal/index.d.ts.map +1 -1
  335. package/dist/core/journal/index.js +9 -22
  336. package/dist/core/presence/blob.d.ts +4 -3
  337. package/dist/core/presence/blob.d.ts.map +1 -1
  338. package/dist/core/presence/blob.js +45 -44
  339. package/dist/core/presence/codec-digest.d.ts +29 -0
  340. package/dist/core/presence/codec-digest.d.ts.map +1 -0
  341. package/dist/core/presence/codec-digest.js +170 -0
  342. package/dist/core/presence/relay-client.d.ts +1 -1
  343. package/dist/core/presence/relay-client.d.ts.map +1 -1
  344. package/dist/core/presence/relay-client.js +5 -5
  345. package/dist/core/work/index.d.ts +1 -1
  346. package/dist/core/work/index.d.ts.map +1 -1
  347. package/dist/core/work/index.js +1 -1
  348. package/dist/core/work/read.d.ts +1 -1
  349. package/dist/core/work/read.d.ts.map +1 -1
  350. package/dist/core/work/read.js +1 -1
  351. package/dist/core/work/runner.d.ts.map +1 -1
  352. package/dist/core/work/runner.js +6 -0
  353. package/dist/core/work/state.d.ts +9 -0
  354. package/dist/core/work/state.d.ts.map +1 -1
  355. package/dist/core/work/state.js +17 -3
  356. package/dist/core/workflow/engine.js +24 -25
  357. package/dist/core/workflow/live-session-v3.d.ts +19 -0
  358. package/dist/core/workflow/live-session-v3.d.ts.map +1 -0
  359. package/dist/core/workflow/live-session-v3.js +86 -0
  360. package/dist/core/workflow/scope-id.d.ts +3 -0
  361. package/dist/core/workflow/scope-id.d.ts.map +1 -0
  362. package/dist/core/workflow/scope-id.js +5 -0
  363. package/dist/core/workflow/workspaces/leases.d.ts +4 -0
  364. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  365. package/dist/core/workflow/workspaces/leases.js +53 -19
  366. package/dist/index.d.ts +1 -1
  367. package/dist/index.js +1 -1
  368. package/dist/lib/browser/client.d.ts +41 -0
  369. package/dist/lib/browser/client.d.ts.map +1 -1
  370. package/dist/lib/browser/client.js +203 -0
  371. package/dist/lib/browser/critique.d.ts +28 -2
  372. package/dist/lib/browser/critique.d.ts.map +1 -1
  373. package/dist/lib/browser/critique.js +74 -18
  374. package/dist/lib/browser/geometry.d.ts +1 -1
  375. package/dist/lib/browser/geometry.d.ts.map +1 -1
  376. package/dist/lib/browser/geometry.js +130 -6
  377. package/dist/lib/browser/index.d.ts +3 -0
  378. package/dist/lib/browser/index.d.ts.map +1 -1
  379. package/dist/lib/browser/index.js +3 -0
  380. package/dist/lib/browser/qa-plan.d.ts +174 -0
  381. package/dist/lib/browser/qa-plan.d.ts.map +1 -0
  382. package/dist/lib/browser/qa-plan.js +321 -0
  383. package/dist/lib/browser/qa-reuse.d.ts +99 -0
  384. package/dist/lib/browser/qa-reuse.d.ts.map +1 -0
  385. package/dist/lib/browser/qa-reuse.js +222 -0
  386. package/dist/lib/browser/qa-snapshot.d.ts +64 -0
  387. package/dist/lib/browser/qa-snapshot.d.ts.map +1 -0
  388. package/dist/lib/browser/qa-snapshot.js +171 -0
  389. package/dist/lib/browser/runts.d.ts.map +1 -1
  390. package/dist/lib/browser/runts.js +22 -1
  391. package/dist/lib/browser/tiling.d.ts +71 -0
  392. package/dist/lib/browser/tiling.d.ts.map +1 -0
  393. package/dist/lib/browser/tiling.js +118 -0
  394. package/dist/lib/claude-desktop.d.ts +0 -47
  395. package/dist/lib/claude-desktop.d.ts.map +1 -1
  396. package/dist/lib/claude-desktop.js +1 -169
  397. package/dist/lib/context/index.d.ts +0 -3
  398. package/dist/lib/context/index.d.ts.map +1 -1
  399. package/dist/lib/context/index.js +20 -42
  400. package/dist/lib/council/index.d.ts +2 -2
  401. package/dist/lib/council/index.d.ts.map +1 -1
  402. package/dist/lib/council/index.js +9 -20
  403. package/dist/lib/docs-frontmatter.d.ts +1 -6
  404. package/dist/lib/docs-frontmatter.d.ts.map +1 -1
  405. package/dist/lib/docs-frontmatter.js +12 -78
  406. package/dist/lib/docs-index.d.ts +9 -0
  407. package/dist/lib/docs-index.d.ts.map +1 -1
  408. package/dist/lib/docs-index.js +32 -3
  409. package/dist/lib/docs-lint.d.ts.map +1 -1
  410. package/dist/lib/docs-lint.js +14 -30
  411. package/dist/lib/docs-metadata-audit.d.ts +19 -0
  412. package/dist/lib/docs-metadata-audit.d.ts.map +1 -0
  413. package/dist/lib/docs-metadata-audit.js +132 -0
  414. package/dist/lib/docs-metadata-managed.d.ts +23 -0
  415. package/dist/lib/docs-metadata-managed.d.ts.map +1 -0
  416. package/dist/lib/docs-metadata-managed.js +78 -0
  417. package/dist/lib/docs-metadata-sync.d.ts +20 -0
  418. package/dist/lib/docs-metadata-sync.d.ts.map +1 -0
  419. package/dist/lib/docs-metadata-sync.js +222 -0
  420. package/dist/lib/docs-metadata-v2-schema.d.ts +273 -0
  421. package/dist/lib/docs-metadata-v2-schema.d.ts.map +1 -0
  422. package/dist/lib/docs-metadata-v2-schema.js +134 -0
  423. package/dist/lib/docs-metadata-v2.d.ts +21 -0
  424. package/dist/lib/docs-metadata-v2.d.ts.map +1 -0
  425. package/dist/lib/docs-metadata-v2.js +424 -0
  426. package/dist/lib/docs-new.d.ts +11 -0
  427. package/dist/lib/docs-new.d.ts.map +1 -0
  428. package/dist/lib/docs-new.js +52 -0
  429. package/dist/lib/docs-sweep.d.ts.map +1 -1
  430. package/dist/lib/docs-sweep.js +45 -29
  431. package/dist/lib/headless/index.d.ts +98 -0
  432. package/dist/lib/headless/index.d.ts.map +1 -0
  433. package/dist/lib/headless/index.js +211 -0
  434. package/dist/lib/identities/assume.d.ts +4 -4
  435. package/dist/lib/identities/assume.d.ts.map +1 -1
  436. package/dist/lib/identities/assume.js +52 -68
  437. package/dist/lib/instructions/apply.d.ts +5 -4
  438. package/dist/lib/instructions/apply.d.ts.map +1 -1
  439. package/dist/lib/instructions/apply.js +32 -20
  440. package/dist/lib/instructions/bundle.d.ts +1 -1
  441. package/dist/lib/instructions/bundle.d.ts.map +1 -1
  442. package/dist/lib/instructions/bundle.js +1 -1
  443. package/dist/lib/instructions/splice.d.ts +0 -1
  444. package/dist/lib/instructions/splice.d.ts.map +1 -1
  445. package/dist/lib/instructions/splice.js +22 -6
  446. package/dist/lib/instructions/templates.d.ts +7 -5
  447. package/dist/lib/instructions/templates.d.ts.map +1 -1
  448. package/dist/lib/instructions/templates.js +79 -9
  449. package/package.json +26 -5
  450. package/schemas/config.schema.json +120 -2
  451. package/schemas/docs-metadata-v2.schema.json +441 -0
  452. package/schemas/event-v3.schema.json +24472 -0
  453. package/src/commander.ts +4 -0
  454. package/src/commands/adapter.ts +2 -2
  455. package/src/commands/agents.ts +1512 -592
  456. package/src/commands/artifacts.ts +7 -6
  457. package/src/commands/backup.ts +29 -21
  458. package/src/commands/browse.ts +349 -8
  459. package/src/commands/checkpoint.ts +4 -3
  460. package/src/commands/claude-desktop.ts +2 -101
  461. package/src/commands/context.ts +1 -6
  462. package/src/commands/decision.ts +27 -44
  463. package/src/commands/docs.ts +100 -33
  464. package/src/commands/doctor.ts +14 -1
  465. package/src/commands/events.ts +256 -0
  466. package/src/commands/governor.ts +18 -4
  467. package/src/commands/init.ts +253 -57
  468. package/src/commands/instructions.ts +1 -1
  469. package/src/commands/journal.ts +4 -13
  470. package/src/commands/ledger-v3.ts +111 -0
  471. package/src/commands/presence.ts +11 -10
  472. package/src/commands/sync.ts +1 -1
  473. package/src/commands/web.ts +67 -8
  474. package/src/commands/work.ts +28 -5
  475. package/src/core/adapter.ts +5 -0
  476. package/src/core/adapters/attest.ts +8 -3
  477. package/src/core/adapters/profiles.ts +4 -6
  478. package/src/core/agents/canonical-emit.ts +34 -25
  479. package/src/core/agents/cli.ts +236 -262
  480. package/src/core/agents/codex-archive-v3.ts +138 -0
  481. package/src/core/agents/coord-client.ts +75 -188
  482. package/src/core/agents/finalization.ts +120 -38
  483. package/src/core/agents/index.ts +4 -5
  484. package/src/core/agents/live-authority-v3.ts +375 -0
  485. package/src/core/agents/live-lifecycle-v3.ts +140 -0
  486. package/src/core/agents/live-observation-v3.ts +216 -0
  487. package/src/core/agents/render/prompt-context.ts +29 -60
  488. package/src/core/agents/render/session-context.ts +10 -31
  489. package/src/core/agents/rules/claim-conflict.ts +85 -126
  490. package/src/core/agents/rules/commit-conflict.ts +3 -14
  491. package/src/core/agents/rules/stop-hook.ts +239 -293
  492. package/src/core/agents/session-events.ts +137 -104
  493. package/src/core/agents/session-finalization-state-v3.ts +90 -0
  494. package/src/core/agents/session-finalizer-v3.ts +953 -0
  495. package/src/core/agents/session-name-display.ts +149 -0
  496. package/src/core/agents/state/activity-log.ts +6 -14
  497. package/src/core/agents/state/heartbeat-writer.ts +82 -277
  498. package/src/core/agents/state/live-coordination-view.ts +290 -0
  499. package/src/core/agents/state/session-state.ts +24 -12
  500. package/src/core/agents/state/stale-sweep.ts +40 -17
  501. package/src/core/artifacts/index.ts +4 -5
  502. package/src/core/config.ts +148 -3
  503. package/src/core/context/index.ts +9 -17
  504. package/src/core/events/span-parent.ts +46 -0
  505. package/src/core/events/v3/advance.ts +168 -0
  506. package/src/core/events/v3/authority-outbox.ts +548 -0
  507. package/src/core/events/v3/base-builder.ts +60 -0
  508. package/src/core/events/v3/base-contract.ts +882 -0
  509. package/src/core/events/v3/bootstrap.ts +226 -0
  510. package/src/core/events/v3/builder.ts +59 -0
  511. package/src/core/events/v3/canonical.ts +153 -0
  512. package/src/core/events/v3/capabilities.ts +171 -0
  513. package/src/core/events/v3/capability-drift.ts +177 -0
  514. package/src/core/events/v3/catalog.ts +202 -0
  515. package/src/core/events/v3/clock-order.ts +41 -0
  516. package/src/core/events/v3/contract.ts +365 -0
  517. package/src/core/events/v3/control.ts +576 -0
  518. package/src/core/events/v3/coordination-view.ts +193 -0
  519. package/src/core/events/v3/economics.ts +384 -0
  520. package/src/core/events/v3/finalization-view.ts +67 -0
  521. package/src/core/events/v3/fingerprint-keys.ts +260 -0
  522. package/src/core/events/v3/generated.ts +48 -0
  523. package/src/core/events/v3/ids.ts +33 -0
  524. package/src/core/events/v3/index.ts +322 -0
  525. package/src/core/events/v3/latency.ts +551 -0
  526. package/src/core/events/v3/live-feed.ts +319 -0
  527. package/src/core/events/v3/live-observation.ts +39 -0
  528. package/src/core/events/v3/live-routing.ts +135 -0
  529. package/src/core/events/v3/producers/command-recorder.ts +631 -0
  530. package/src/core/events/v3/producers/command.ts +201 -0
  531. package/src/core/events/v3/producers/coordination-recorder.ts +538 -0
  532. package/src/core/events/v3/producers/coordination.ts +355 -0
  533. package/src/core/events/v3/producers/hook-base.ts +578 -0
  534. package/src/core/events/v3/producers/hook.ts +119 -0
  535. package/src/core/events/v3/producers/intake.ts +319 -0
  536. package/src/core/events/v3/producers/recorder.ts +2805 -0
  537. package/src/core/events/v3/projection.ts +653 -0
  538. package/src/core/events/v3/reader.ts +1127 -0
  539. package/src/core/events/v3/recovery-record.ts +244 -0
  540. package/src/core/events/v3/recovery.ts +271 -0
  541. package/src/core/events/v3/span-state.ts +187 -0
  542. package/src/core/events/v3/targets.ts +182 -0
  543. package/src/core/events/v3/turn-telemetry.ts +259 -0
  544. package/src/core/events/v3/validate.ts +361 -0
  545. package/src/core/events/v3/writer.ts +419 -0
  546. package/src/core/governor/index.ts +3 -0
  547. package/src/core/governor/plan-api.ts +10 -2
  548. package/src/core/governor/read.ts +3 -0
  549. package/src/core/governor/state.ts +52 -5
  550. package/src/core/guard/config.ts +206 -0
  551. package/src/core/guard/coordinator.ts +227 -0
  552. package/src/core/guard/evaluator.ts +343 -0
  553. package/src/core/guard/evidence-v3.ts +227 -0
  554. package/src/core/guard/index.ts +35 -0
  555. package/src/core/guard/live-source-v3.ts +204 -0
  556. package/src/core/guard/storage.ts +188 -0
  557. package/src/core/guard/types.ts +152 -0
  558. package/src/core/hooks/adapter/detect.ts +28 -1
  559. package/src/core/hooks/adapter/events.ts +14 -6
  560. package/src/core/hooks/adapter/output.ts +7 -2
  561. package/src/core/hooks/adapter/parse.ts +80 -38
  562. package/src/core/hooks/adapter/wiring.ts +119 -13
  563. package/src/core/hooks/cli.ts +351 -466
  564. package/src/core/hooks/effects/image-capture.ts +228 -238
  565. package/src/core/hooks/effects/index.ts +1 -36
  566. package/src/core/hooks/events/input-hash.ts +47 -0
  567. package/src/core/hooks/index.ts +4 -11
  568. package/src/core/hooks/resolve/coord-root.ts +1 -1
  569. package/src/core/hooks/resolve/owner.ts +52 -45
  570. package/src/core/hooks/resolve/transcript.ts +165 -14
  571. package/src/core/hooks/session-name-presence.ts +14 -15
  572. package/src/core/hooks/unsafe-cross-shell.ts +1 -1
  573. package/src/core/journal/index.ts +15 -22
  574. package/src/core/presence/blob.ts +48 -41
  575. package/src/core/presence/codec-digest.ts +215 -0
  576. package/src/core/presence/relay-client.ts +5 -5
  577. package/src/core/work/index.ts +3 -0
  578. package/src/core/work/read.ts +3 -0
  579. package/src/core/work/runner.ts +6 -0
  580. package/src/core/work/state.ts +27 -3
  581. package/src/core/workflow/engine.ts +24 -24
  582. package/src/core/workflow/live-session-v3.ts +126 -0
  583. package/src/core/workflow/scope-id.ts +6 -0
  584. package/src/core/workflow/workspaces/leases.ts +56 -19
  585. package/src/index.ts +1 -1
  586. package/src/lib/browser/client.ts +230 -0
  587. package/src/lib/browser/critique.ts +104 -27
  588. package/src/lib/browser/geometry.ts +143 -14
  589. package/src/lib/browser/index.ts +34 -0
  590. package/src/lib/browser/qa-plan.ts +501 -0
  591. package/src/lib/browser/qa-reuse.ts +306 -0
  592. package/src/lib/browser/qa-snapshot.ts +223 -0
  593. package/src/lib/browser/runts.ts +27 -2
  594. package/src/lib/browser/tiling.ts +164 -0
  595. package/src/lib/claude-desktop.ts +1 -232
  596. package/src/lib/context/index.ts +22 -46
  597. package/src/lib/council/index.ts +9 -21
  598. package/src/lib/docs-frontmatter.ts +8 -79
  599. package/src/lib/docs-index.ts +33 -3
  600. package/src/lib/docs-lint.ts +15 -29
  601. package/src/lib/docs-metadata-audit.ts +166 -0
  602. package/src/lib/docs-metadata-managed.ts +83 -0
  603. package/src/lib/docs-metadata-sync.ts +262 -0
  604. package/src/lib/docs-metadata-v2-schema.ts +138 -0
  605. package/src/lib/docs-metadata-v2.ts +646 -0
  606. package/src/lib/docs-new.ts +63 -0
  607. package/src/lib/docs-sweep.ts +42 -28
  608. package/src/lib/headless/index.ts +287 -0
  609. package/src/lib/identities/assume.ts +67 -86
  610. package/src/lib/instructions/apply.ts +33 -20
  611. package/src/lib/instructions/bundle.ts +2 -2
  612. package/src/lib/instructions/splice.ts +21 -7
  613. package/src/lib/instructions/templates.ts +85 -13
  614. package/dist/core/agents/cli-emit.d.ts +0 -27
  615. package/dist/core/agents/cli-emit.d.ts.map +0 -1
  616. package/dist/core/agents/cli-emit.js +0 -57
  617. package/dist/core/agents/codex-replay.d.ts +0 -29
  618. package/dist/core/agents/codex-replay.d.ts.map +0 -1
  619. package/dist/core/agents/codex-replay.js +0 -138
  620. package/dist/core/agents/events/consume.d.ts +0 -82
  621. package/dist/core/agents/events/consume.d.ts.map +0 -1
  622. package/dist/core/agents/events/consume.js +0 -195
  623. package/dist/core/agents/events/emit.d.ts +0 -42
  624. package/dist/core/agents/events/emit.d.ts.map +0 -1
  625. package/dist/core/agents/events/emit.js +0 -75
  626. package/dist/core/agents/events/ulid.d.ts +0 -11
  627. package/dist/core/agents/events/ulid.d.ts.map +0 -1
  628. package/dist/core/agents/events/ulid.js +0 -47
  629. package/dist/core/agents/state/heartbeat-projector.d.ts +0 -76
  630. package/dist/core/agents/state/heartbeat-projector.d.ts.map +0 -1
  631. package/dist/core/agents/state/heartbeat-projector.js +0 -581
  632. package/dist/core/hooks/events/emit.d.ts +0 -31
  633. package/dist/core/hooks/events/emit.d.ts.map +0 -1
  634. package/dist/core/hooks/events/emit.js +0 -93
  635. package/dist/core/hooks/events/rotate.d.ts +0 -43
  636. package/dist/core/hooks/events/rotate.d.ts.map +0 -1
  637. package/dist/core/hooks/events/rotate.js +0 -142
  638. package/dist/core/hooks/events/schema.d.ts +0 -341
  639. package/dist/core/hooks/events/schema.d.ts.map +0 -1
  640. package/dist/core/hooks/events/schema.js +0 -12
  641. package/dist/core/hooks/events/ulid.d.ts +0 -10
  642. package/dist/core/hooks/events/ulid.d.ts.map +0 -1
  643. package/dist/core/hooks/events/ulid.js +0 -47
  644. package/dist/lib/docs-frontmatter-migrate.d.ts +0 -33
  645. package/dist/lib/docs-frontmatter-migrate.d.ts.map +0 -1
  646. package/dist/lib/docs-frontmatter-migrate.js +0 -364
  647. package/src/core/agents/cli-emit.ts +0 -64
  648. package/src/core/agents/codex-replay.ts +0 -163
  649. package/src/core/agents/events/consume.ts +0 -254
  650. package/src/core/agents/events/emit.ts +0 -115
  651. package/src/core/agents/events/ulid.ts +0 -51
  652. package/src/core/agents/state/heartbeat-projector.ts +0 -645
  653. package/src/core/hooks/events/emit.ts +0 -125
  654. package/src/core/hooks/events/rotate.ts +0 -151
  655. package/src/core/hooks/events/schema.ts +0 -582
  656. package/src/core/hooks/events/ulid.ts +0 -51
  657. 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,29 +21,31 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
21
21
  import { dirname, join } from "node:path";
22
22
  import { coordEnv } from "../../lib/env.js";
23
23
  import { buildInstructionBundle } from "../../lib/instructions/bundle.js";
24
- import { replayCodexJsonl } from "../agents/codex-replay.js";
25
24
  import { coordBinPath } from "../agents/coord-bin.js";
26
- import { consumeSince, writeCursor } from "../agents/events/consume.js";
27
25
  import { classifyWriteClaimFinalization, formatWriteClaimFinalizationDenial, } from "../agents/finalization.js";
28
- import { evaluateStopHook } from "../agents/rules/stop-hook.js";
29
- import { projectHeartbeats } from "../agents/state/heartbeat-projector.js";
30
- import { stampSessionStateEvent } from "../agents/state/heartbeat-writer.js";
26
+ import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.js";
27
+ import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, sessionNameDisplayInstruction, sessionNameDisplayPending, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
28
+ import { stampSessionNameSeen } from "../agents/state/heartbeat-writer.js";
29
+ import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
31
30
  import { writePidmapRow } from "../agents/state/pidmap.js";
32
- import { shellMutationPaths } from "../agents/state/shell-mutation.js";
33
31
  import { agentsRequireGitFinalization, resolveBinName } from "../config.js";
34
32
  import { checkpointContext, completeContextRecovery, extractContextSample, markContextCompactionCompleted, prepareContextRecovery, readContextState, recordContextSample, } from "../context/index.js";
35
- import { ensureRelayDaemon, fetchPresence, publishPresence } from "../presence/index.js";
36
- import { detectAdapter } from "./adapter/detect.js";
33
+ import { tryWriteLiveDisplayV3 } from "../events/v3/live-feed.js";
34
+ import { recordLiveHookSignalV3, resolveLiveEventLedgerRouteV3, } from "../events/v3/live-routing.js";
35
+ import { captureSpanClockV3 } from "../events/v3/span-state.js";
36
+ import { fetchPresence } from "../presence/index.js";
37
+ import { stableScopeId } from "../workflow/scope-id.js";
38
+ import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.js";
37
39
  import { extractBashCommand, extractToolDescription, normalizeEventName, parsePayload, } from "./adapter/parse.js";
38
40
  import { codexWslFileLinkTelemetry, inspectCodexWslBridge, isWslUncPath, renderCodexWslFileLinkContext, } from "./codex-wsl-bridge.js";
39
- import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, resetSoundCounters, runSessionSyncExtension, runTurnSummary, soundForEvent, } from "./effects/index.js";
40
- import { emit } from "./events/emit.js";
41
+ import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
42
+ import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
41
43
  import { canonicalize } from "./guard-path.js";
42
44
  import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.js";
43
45
  import { findCoordRoot } from "./resolve/coord-root.js";
44
46
  import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
45
47
  import { resolveOwner } from "./resolve/owner.js";
46
- import { detectForkParent, scanAssistantTextIncludes, scanStatusBoxPresent, scanTranscriptModel, } from "./resolve/transcript.js";
48
+ import { inspectSessionNameDisplayImmediately, scanAssistantTextIncludes, scanSessionNameDisplayedImmediately, scanStatusBoxPresent, scanTranscriptModel, } from "./resolve/transcript.js";
47
49
  import { sessionNamePresence } from "./session-name-presence.js";
48
50
  import { unsafeCrossShellReason } from "./unsafe-cross-shell.js";
49
51
  function parseArgv(argv) {
@@ -119,7 +121,7 @@ function logError(coordRoot, err, context) {
119
121
  /**
120
122
  * Spawn `agent-coord assign-name <owner> <kind>` to mint or recover the
121
123
  * hurricane-style name for this owner. Returns null on any failure so
122
- * session.start emission never breaks the adapter flow.
124
+ * session.started emission never breaks the adapter flow.
123
125
  *
124
126
  * Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
125
127
  * separation: we spawn rather than import.
@@ -151,7 +153,7 @@ function assignNameViaAgentCoord(coordRoot, instanceId, kind, forkedFrom) {
151
153
  }
152
154
  /**
153
155
  * Direct (in-process) pidmap write, avoiding the spawn overhead of going via
154
- * the agent-coord CLI for every session.start / subagent.start. Pid-map rows
156
+ * the agent-coord CLI for every session.started signal. Pid-map rows
155
157
  * are essential for `harn agents whoami` ppid resolution.
156
158
  *
157
159
  * This used to inline its own copy of the write to keep this module's
@@ -185,18 +187,18 @@ function summarizeOutput(value, headTail = 500) {
185
187
  function buildEventData(eventType, ctx) {
186
188
  const p = ctx.payload;
187
189
  switch (eventType) {
188
- case "session.start": {
190
+ case "session.started": {
189
191
  const adapterPlatform = ctx.adapter === "claude-code"
190
192
  ? "claude-code"
191
193
  : ctx.adapter === "cursor"
192
194
  ? "cursor"
193
195
  : "codex";
194
196
  // Recorded fork lineage is NOT detected here. On claude-code a fork
195
- // never fires its own session.start — SessionStart fires under the
197
+ // never fires its own session.started — SessionStart fires under the
196
198
  // PARENT's session id (source=resume) before the fork id is minted
197
199
  // (verified 2026-08-05) — so detection at this point can only mislabel
198
200
  // the resumed parent. The fork's new instance is caught by the
199
- // tool.pre_use heal path instead. The forkedFrom plumbing below stays
201
+ // tool.requested heal path instead. The forkedFrom plumbing below stays
200
202
  // for adapters that DO report a parent at session start.
201
203
  const forkedFrom = undefined;
202
204
  // Assign (or recover) name + kind via agent-coord. Idempotent: resume
@@ -218,7 +220,7 @@ function buildEventData(eventType, ctx) {
218
220
  cwd: p?.cwd ?? process.cwd(),
219
221
  // Claude Code's SessionStart payload omits `model` (Codex + Cursor
220
222
  // supply it). Fall back to the transcript, populated on `resume`, and
221
- // backfilled later by `turn.stop` for a fresh `startup` session.
223
+ // backfilled later by `turn.completed` for a fresh startup session.
222
224
  model: p?.model ?? scanTranscriptModel(p?.transcript_path),
223
225
  pid: adapterPid,
224
226
  source: p?.source,
@@ -254,23 +256,30 @@ function buildEventData(eventType, ctx) {
254
256
  : {}),
255
257
  };
256
258
  }
257
- case "session.end":
259
+ case "session.ended":
258
260
  return {
259
261
  ended_at: new Date().toISOString(),
260
262
  clean_exit: p?.clean_exit ?? true,
261
263
  };
262
- case "user_prompt.submit": {
264
+ case "turn.started": {
265
+ // Cursor can deliver beforeSubmitPrompt before sessionStart. Ensure that
266
+ // high-confidence prompt bootstrap also mints durable display identity;
267
+ // a later SessionStart remains idempotent.
268
+ if (ctx.adapter === "cursor" &&
269
+ !readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
270
+ assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session");
271
+ }
263
272
  const prompt = p?.prompt ?? "";
264
273
  const { value, truncated } = clampString(prompt, 4000);
265
274
  return { prompt_text: value, ...(truncated ? { truncated: true } : {}) };
266
275
  }
267
- case "turn.stop": {
276
+ case "turn.completed": {
268
277
  const lastAssistantMessage = p?.raw.last_assistant_message ?? "";
269
278
  const fileLinkTelemetry = ctx.adapter === "codex"
270
279
  ? codexWslFileLinkTelemetry(ctx.coordRoot, p?.cwd, lastAssistantMessage)
271
280
  : null;
272
281
  return {
273
- // Backfill the model for adapters that omit it at session.start
282
+ // Backfill the model for adapters that omit it at session.started
274
283
  // (Claude Code). The transcript is populated with assistant turns by
275
284
  // Stop-hook time, so this resolves even for fresh `startup` sessions.
276
285
  model: p?.model ?? scanTranscriptModel(p?.transcript_path),
@@ -284,7 +293,7 @@ function buildEventData(eventType, ctx) {
284
293
  // Box present if the transcript scan finds it OR the final assistant
285
294
  // message carries the `┌─ agent-` prefix. The latter covers codex's
286
295
  // text-only stop (box in last_assistant_message, no transcript), which
287
- // the verdict now sees because agent-hook emits this turn.stop itself
296
+ // the verdict now sees because agent-hook emits this turn.completed itself
288
297
  // (the previous path passed those via the no-history fail-open).
289
298
  status_box_present: scanStatusBoxPresent(p?.transcript_path) || lastAssistantMessage.includes("┌─ agent-"),
290
299
  // Shadow measurement for the rule-2/3 detector: the loose scan above
@@ -302,12 +311,12 @@ function buildEventData(eventType, ctx) {
302
311
  // and which name that refers to. Reported on every stop while a name
303
312
  // exists (the stop-hook.session_name verdict reads it per turn); the
304
313
  // transcript scan itself stops once the name has been sighted.
305
- ...sessionNamePresence(ctx.coordRoot, ctx.instanceId, lastAssistantMessage, (name) => scanAssistantTextIncludes(p?.transcript_path, name)),
314
+ ...sessionNamePresence(ctx.coordRoot, ctx.instanceId, (name) => scanSessionNameDisplayedImmediately(p?.transcript_path, name, assistantTextStartsWithSessionNameBlock)),
306
315
  ...(fileLinkTelemetry ?? {}),
307
316
  stop_hook_active: p?.stop_hook_active,
308
317
  };
309
318
  }
310
- case "subagent.start": {
319
+ case "agent.started": {
311
320
  const subagentCallId = p?.raw.subagent_id ?? p?.raw.agent_id;
312
321
  // Subagents inherit parent's name via the resolve-name session_id path
313
322
  // (agent-coord/state/names.ts → kind=transient). Use the call ID as the
@@ -325,12 +334,12 @@ function buildEventData(eventType, ctx) {
325
334
  parent_session_id: p?.parent_session_id,
326
335
  };
327
336
  }
328
- case "subagent.stop": {
337
+ case "agent.completed": {
329
338
  const status = p?.exit_status;
330
339
  const normalized = status === "error" || status === "interrupted" ? status : "ok";
331
340
  return { exit_status: normalized, reason: p?.reason };
332
341
  }
333
- case "tool.pre_use": {
342
+ case "tool.requested": {
334
343
  const toolName = p?.tool_name ?? "unknown";
335
344
  const command = extractBashCommand(toolName, p?.tool_input);
336
345
  const description = extractToolDescription(p?.tool_input);
@@ -345,13 +354,15 @@ function buildEventData(eventType, ctx) {
345
354
  return {
346
355
  tool_name: toolName,
347
356
  tool_input: clamped.value,
357
+ input_hash: toolInputHash(toolName, p?.tool_input ?? null),
358
+ target_hash: toolTargetHash(toolName, p?.tool_input ?? null),
348
359
  intent,
349
360
  intent_source: source,
350
361
  tool_use_id: p?.tool_use_id,
351
362
  ...(clamped.truncated ? { truncated: true } : {}),
352
363
  };
353
364
  }
354
- case "interaction.input_requested": {
365
+ case "wait.started": {
355
366
  const description = extractToolDescription(p?.tool_input);
356
367
  const reason = description ? clampString(description, 500).value : undefined;
357
368
  return {
@@ -360,30 +371,27 @@ function buildEventData(eventType, ctx) {
360
371
  ...(reason ? { reason } : {}),
361
372
  };
362
373
  }
363
- case "tool.post_use": {
364
- const toolName = p?.tool_name ?? "unknown";
365
- const summary = summarizeOutput(p?.tool_response);
366
- return {
367
- tool_name: toolName,
368
- output_summary: summary.summary,
369
- exit_status: "ok",
370
- duration_ms: 0, // Phase 3 pairs pre/post via tool_use_id
371
- tool_use_id: p?.tool_use_id,
372
- ...(summary.truncated ? { truncated: true } : {}),
373
- };
374
- }
375
- case "tool.post_use_failure": {
374
+ case "tool.completed": {
376
375
  const toolName = p?.tool_name ?? "unknown";
377
376
  const summary = summarizeOutput(p?.tool_response);
378
- return {
379
- tool_name: toolName,
380
- error: summary.summary,
381
- duration_ms: 0,
382
- tool_use_id: p?.tool_use_id,
383
- ...(summary.truncated ? { truncated: true } : {}),
384
- };
385
- }
386
- case "context.compaction.started": {
377
+ return ctx.eventName === "post-tool-use-failure"
378
+ ? {
379
+ tool_name: toolName,
380
+ error: summary.summary,
381
+ duration_ms: 0,
382
+ tool_use_id: p?.tool_use_id,
383
+ ...(summary.truncated ? { truncated: true } : {}),
384
+ }
385
+ : {
386
+ tool_name: toolName,
387
+ output_summary: summary.summary,
388
+ exit_status: "ok",
389
+ duration_ms: 0,
390
+ tool_use_id: p?.tool_use_id,
391
+ ...(summary.truncated ? { truncated: true } : {}),
392
+ };
393
+ }
394
+ case "context.compaction_started": {
387
395
  const metadata = objectRecord(p?.raw.compact_metadata);
388
396
  return {
389
397
  trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
@@ -392,7 +400,7 @@ function buildEventData(eventType, ctx) {
392
400
  numberField(metadata?.pre_compact_tokens),
393
401
  };
394
402
  }
395
- case "context.compaction.completed": {
403
+ case "context.compaction_completed": {
396
404
  const metadata = objectRecord(p?.raw.compact_metadata);
397
405
  return {
398
406
  trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
@@ -427,9 +435,36 @@ function numberField(value) {
427
435
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
428
436
  }
429
437
  async function main() {
438
+ const hookStartedAt = performance.now();
439
+ const hookClock = captureSpanClockV3();
430
440
  const { eventName, extra } = parseArgv(process.argv.slice(2));
431
441
  const adapter = detectAdapter(process.argv.slice(2));
432
442
  const raw = await readStdin();
443
+ // Cursor executes the Claude Code project hooks too on hosts wired for both
444
+ // adapters, piping them its own payload (identifiable by the top-level
445
+ // cursor_version envelope field). That stray `--adapter claude-code`
446
+ // dispatch must not record (twin generation) or play Claude-Code-only
447
+ // sounds, so it bails here, before every effect. Cursor's own
448
+ // `--adapter cursor` dispatch of the same payload carries the session.
449
+ if (shouldSkipHookAdapter(adapter, raw)) {
450
+ if (coordEnv("AGENT_COORD_OFF") !== "1") {
451
+ const coordRoot = findCoordRoot(process.cwd());
452
+ if (coordRoot) {
453
+ appendDebug(coordRoot, {
454
+ ts: new Date().toISOString(),
455
+ event_name: eventName,
456
+ adapter,
457
+ extra_argv: extra,
458
+ payload_bytes: raw.length,
459
+ cwd: process.cwd(),
460
+ pid: process.pid,
461
+ ppid: process.ppid,
462
+ skipped: "cursor-payload-claude-adapter",
463
+ });
464
+ }
465
+ }
466
+ return 0;
467
+ }
433
468
  // Kill-switch-INDEPENDENT effects: notification sounds fire BEFORE the
434
469
  // HARNERY_AGENT_COORD_OFF gate so audible feedback survives incident-triage
435
470
  // bypass: sound playback happens before the kill-switch bailout.
@@ -460,6 +495,7 @@ async function main() {
460
495
  const coordRoot = findCoordRoot(process.cwd());
461
496
  if (!coordRoot)
462
497
  return 0;
498
+ const ledgerRoute = resolveLiveEventLedgerRouteV3(coordRoot);
463
499
  // Always log a breadcrumb, useful when an event_type maps to null or owner
464
500
  // resolution fails. Stays cheap (one append) and self-prunes via repo log
465
501
  // rotation policy.
@@ -483,7 +519,10 @@ async function main() {
483
519
  return 0;
484
520
  }
485
521
  const payload = parsePayload(raw, adapter);
486
- const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
522
+ const owner = resolveOwner({
523
+ payload: payload?.raw ?? null,
524
+ coordRoot,
525
+ });
487
526
  if (!owner) {
488
527
  appendDebug(coordRoot, {
489
528
  ...debugBase,
@@ -499,37 +538,98 @@ async function main() {
499
538
  raw,
500
539
  adapter,
501
540
  instanceId: owner.instance_id,
541
+ eventName,
502
542
  });
503
- const envelope = emit(coordRoot, {
504
- event_type: norm.event_type,
505
- instance_id: owner.instance_id,
506
- session_id: sessionId,
507
- parent_session_id: payload?.parent_session_id,
508
- turn_id: payload?.turn_id,
509
- parent_turn_id: payload?.parent_turn_id,
543
+ const turnRitual = norm.event_type === "turn.completed"
544
+ ? {
545
+ status_box_present: data.status_box_present === true,
546
+ status_box_present_strict: data.status_box_present_strict === true,
547
+ session_name_required: typeof data.session_name_present_for === "string",
548
+ session_name_present: data.session_name_present === true,
549
+ }
550
+ : undefined;
551
+ const stopRemediation = norm.event_type === "turn.started" &&
552
+ typeof data.prompt_text === "string" &&
553
+ data.prompt_text.includes(STOP_REMEDIATION_MARKER);
554
+ if (ledgerRoute.state === "blocked") {
555
+ appendDebug(coordRoot, {
556
+ ...debugBase,
557
+ skipped: "v3-control-blocked",
558
+ reason: ledgerRoute.reason,
559
+ event_type: norm.event_type,
560
+ owner_source: owner.source,
561
+ });
562
+ return 0;
563
+ }
564
+ let capturedImages = [];
565
+ if (norm.event_type === "tool.requested" ||
566
+ (norm.event_type === "tool.completed" && eventName === "post-tool-use")) {
567
+ try {
568
+ capturedImages = captureImages(coordRoot, norm.event_type, payload);
569
+ }
570
+ catch (err) {
571
+ logError(coordRoot, err, { phase: "image-capture" });
572
+ }
573
+ }
574
+ const v3Result = recordLiveHookSignalV3({
575
+ coordRoot,
576
+ route: ledgerRoute,
577
+ eventName,
578
+ payload,
510
579
  adapter,
511
- data,
512
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
513
- });
514
- appendDebug(coordRoot, {
515
- ...debugBase,
516
- event_type: norm.event_type,
517
- owner_source: owner.source,
518
- event_id: envelope.event_id,
580
+ instanceId: owner.instance_id,
581
+ ...(coordEnv("WORKFLOW_CHILD") === "1" && coordEnv("WORKFLOW_RUN_ID")
582
+ ? {
583
+ run_id: stableScopeId("run", coordEnv("WORKFLOW_RUN_ID")),
584
+ workflow_id: stableScopeId("wf", coordEnv("WORKFLOW_RUN_ID")),
585
+ ...(coordEnv("WORKFLOW_AGENT_ID")
586
+ ? { workflow_agent_id: coordEnv("WORKFLOW_AGENT_ID") }
587
+ : {}),
588
+ }
589
+ : {}),
590
+ ...(adapter === "codex" && isWslUncPath(payload?.cwd) ? { bridge: "codex-wsl" } : {}),
591
+ hook_name: eventName,
592
+ hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
593
+ monotonic_ns: hookClock.monotonic_ns,
594
+ ...(stopRemediation ? { stop_remediation: true } : {}),
595
+ ...(turnRitual ? { turn_ritual: turnRitual } : {}),
596
+ ...(coordEnv("EXPERIMENT_DEFER_V3_DRAIN") === "1" ? { defer_drain: true } : {}),
519
597
  });
520
- // Activity is only useful while it is current. Stamp prompt and permission
521
- // evidence directly from this envelope rather than draining the global
522
- // projector: a drain here could apply a tool event before its authorization
523
- // verdict. Tool progress is stamped later, after the guard allows it.
524
- if (norm.event_type === "user_prompt.submit" ||
525
- norm.event_type === "interaction.input_requested") {
598
+ const v3EventId = v3Result && "event" in v3Result
599
+ ? v3Result.event.event_id
600
+ : v3Result && "event_id" in v3Result
601
+ ? v3Result.event_id
602
+ : undefined;
603
+ if (v3Result?.state === "recorded" && capturedImages.length > 0) {
526
604
  try {
527
- stampSessionStateEvent(coordRoot, owner.instance_id, envelope);
605
+ recordImageArtifactsV3(coordRoot, v3Result.event, capturedImages);
528
606
  }
529
607
  catch (err) {
530
- logError(coordRoot, err, { phase: "activity-projection" });
608
+ logError(coordRoot, err, { phase: "image-artifact-observation" });
531
609
  }
532
610
  }
611
+ if (norm.event_type === "tool.requested" &&
612
+ v3Result &&
613
+ v3Result.state === "recorded" &&
614
+ "generation_id" in v3Result.event.scope) {
615
+ const intent = typeof data.intent === "string" ? data.intent : undefined;
616
+ if (intent && intent !== "(no intent)") {
617
+ tryWriteLiveDisplayV3(coordRoot, {
618
+ generation_id: v3Result.event.scope.generation_id,
619
+ event_id: v3Result.event.event_id,
620
+ ...(typeof data.tool_name === "string" ? { executable: data.tool_name } : {}),
621
+ intent_display: intent,
622
+ });
623
+ }
624
+ }
625
+ appendDebug(coordRoot, {
626
+ ...debugBase,
627
+ event_type: norm.event_type,
628
+ owner_source: owner.source,
629
+ ...(v3EventId ? { event_v3_id: v3EventId } : {}),
630
+ ...(v3Result ? { event_v3_state: v3Result.state } : {}),
631
+ ...(v3Result && "reason" in v3Result ? { event_v3_reason: v3Result.reason } : {}),
632
+ });
533
633
  // Context telemetry is opportunistic and truthful: only persist a sample
534
634
  // when the adapter payload actually exposes usage/window data. Identical
535
635
  // measurements are de-duplicated before they reach the canonical stream.
@@ -544,22 +644,7 @@ async function main() {
544
644
  });
545
645
  if (sample) {
546
646
  const recorded = recordContextSample(coordRoot, owner.instance_id, sample);
547
- if (recorded.changed) {
548
- emit(coordRoot, {
549
- event_type: "context.sampled",
550
- instance_id: owner.instance_id,
551
- session_id: sessionId,
552
- adapter,
553
- data: {
554
- model: sample.model,
555
- used_tokens: sample.used_tokens,
556
- window_tokens: sample.window_tokens,
557
- used_percent: sample.used_percent,
558
- telemetry_source: sample.source,
559
- confidence: sample.confidence,
560
- },
561
- });
562
- }
647
+ void recorded;
563
648
  }
564
649
  }
565
650
  catch (err) {
@@ -569,7 +654,7 @@ async function main() {
569
654
  // A native pre-compaction signal is the safest available point to capture
570
655
  // external work state. The operation is idempotent until recovery, so a
571
656
  // adapter retry cannot create a storm of near-identical capsules.
572
- if (norm.event_type === "context.compaction.started") {
657
+ if (norm.event_type === "context.compaction_started") {
573
658
  try {
574
659
  const checkpoint = checkpointContext(coordRoot, {
575
660
  sessionId,
@@ -579,25 +664,13 @@ async function main() {
579
664
  reason: "pre_compact",
580
665
  model: payload?.model,
581
666
  });
582
- emit(coordRoot, {
583
- event_type: "context.checkpoint.created",
584
- instance_id: owner.instance_id,
585
- session_id: sessionId,
586
- adapter,
587
- data: {
588
- capsule_id: checkpoint.capsule.capsule_id,
589
- generation: checkpoint.capsule.generation,
590
- path: checkpoint.state.latest_capsule ?? "",
591
- reason: checkpoint.capsule.reason,
592
- reused: checkpoint.reused,
593
- },
594
- });
667
+ void checkpoint;
595
668
  }
596
669
  catch (err) {
597
670
  logError(coordRoot, err, { phase: "context-checkpoint" });
598
671
  }
599
672
  }
600
- if (norm.event_type === "context.compaction.completed") {
673
+ if (norm.event_type === "context.compaction_completed") {
601
674
  try {
602
675
  markContextCompactionCompleted(coordRoot, {
603
676
  sessionId,
@@ -613,31 +686,16 @@ async function main() {
613
686
  // systemMessage JSON (peer table + wiring check + council invites).
614
687
  // Adapter-agnostic since v0.5.0; replaces the previous bash UX layer
615
688
  // and the equivalent per-adapter bash session_start handlers.
616
- if (norm.event_type === "session.start") {
689
+ if (norm.event_type === "session.started") {
617
690
  // Effect (claude-code): prune stale journal archives + sweep orphans.
618
691
  // The recovery-cue is merged into the
619
692
  // session-start additionalContext inside emitSessionStartSystemMessage.
620
693
  if (adapter === "claude-code")
621
694
  journalJanitor(coordRoot);
622
- // Image-feed retention sweep (size + age cap on .harnery/images/). Adapter-
623
- // agnostic, cheap (one readdir), fail-soft. Paired with journalJanitor as a
624
- // session-start "tidy the coord layer" step.
625
- try {
626
- imageJanitor(coordRoot);
627
- }
628
- catch (err) {
629
- logError(coordRoot, err, { phase: "session-start-image-janitor" });
630
- }
695
+ imageJanitor(coordRoot);
631
696
  let recovery = null;
632
697
  if (payload?.source === "compact") {
633
698
  try {
634
- emit(coordRoot, {
635
- event_type: "context.compaction.completed",
636
- instance_id: owner.instance_id,
637
- session_id: sessionId,
638
- adapter,
639
- data: { trigger: "auto" },
640
- });
641
699
  markContextCompactionCompleted(coordRoot, {
642
700
  sessionId,
643
701
  instanceId: owner.instance_id,
@@ -655,19 +713,16 @@ async function main() {
655
713
  try {
656
714
  const injected = await emitSessionStartSystemMessage(coordRoot, owner.instance_id, sessionId, data, adapter, recovery?.briefing ?? "");
657
715
  if (injected && recovery) {
658
- completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, adapter, recovery, "SessionStart");
716
+ completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
659
717
  }
660
718
  }
661
719
  catch (err) {
662
720
  logError(coordRoot, err, { phase: "session-start-systemMessage" });
663
721
  }
664
- // Cross-machine presence (ADR 0016): announce this machine's sessions and
665
- // pull peers'. Both are throttled + fail-silent; network runs detached.
666
- // When a relay is configured, also make sure the live-socket daemon runs.
722
+ // Pull advisory cross-machine presence. V3 never publishes from the
723
+ // disposable heartbeat cache.
667
724
  try {
668
- publishPresence(coordRoot);
669
725
  fetchPresence(coordRoot);
670
- ensureRelayDaemon(coordRoot);
671
726
  }
672
727
  catch (err) {
673
728
  logError(coordRoot, err, { phase: "session-start-presence" });
@@ -675,7 +730,7 @@ async function main() {
675
730
  }
676
731
  // Phase 8: SessionEnd cleanup: delete heartbeat + pid-map rows. Adapter-
677
732
  // agnostic since v0.5.0.
678
- if (norm.event_type === "session.end") {
733
+ if (norm.event_type === "session.ended") {
679
734
  try {
680
735
  cleanupSessionEnd(coordRoot, owner.instance_id, data.reason ?? "unknown");
681
736
  }
@@ -688,19 +743,11 @@ async function main() {
688
743
  journalArchive(coordRoot, owner.instance_id);
689
744
  runSessionSyncExtension(coordRoot, true);
690
745
  }
691
- // Presence: publish the post-cleanup state so peers see this session gone
692
- // promptly rather than waiting out the remote-stale window.
693
- try {
694
- publishPresence(coordRoot);
695
- }
696
- catch (err) {
697
- logError(coordRoot, err, { phase: "session-end-presence" });
698
- }
699
746
  }
700
747
  // Phase 8: SubagentStart: sync-project to create the subagent heartbeat,
701
748
  // log the lifecycle event, and emit a context message announcing the
702
749
  // subagent (claude-code + cursor; codex doesn't fan out subagents today).
703
- if (norm.event_type === "subagent.start") {
750
+ if (norm.event_type === "agent.started") {
704
751
  try {
705
752
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
706
753
  if (existsSync(agentCoordBin)) {
@@ -723,7 +770,7 @@ async function main() {
723
770
  }
724
771
  }
725
772
  // Phase 8: SubagentStop: delete subagent heartbeat + log.
726
- if (norm.event_type === "subagent.stop") {
773
+ if (norm.event_type === "agent.completed") {
727
774
  try {
728
775
  cleanupSessionEnd(coordRoot, owner.instance_id, data.reason ?? "unknown");
729
776
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
@@ -742,7 +789,7 @@ async function main() {
742
789
  }
743
790
  // Phase 8: UserPromptSubmit: render dedup'd peer table + council pending
744
791
  // and emit the adapter-shaped systemMessage JSON. Adapter-agnostic since v0.5.0.
745
- if (norm.event_type === "user_prompt.submit") {
792
+ if (norm.event_type === "turn.started") {
746
793
  // Effects (claude-code): reset per-turn sound rate-limit counters + run
747
794
  // presence detection on the prompt.
748
795
  if (adapter === "claude-code") {
@@ -777,74 +824,26 @@ async function main() {
777
824
  try {
778
825
  const injected = await emitUserPromptSubmitSystemMessage(coordRoot, owner.instance_id, sessionId, adapter, payload?.cwd, recovery?.briefing ?? "");
779
826
  if (injected && recovery) {
780
- completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, adapter, recovery, "UserPromptSubmit");
827
+ completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
781
828
  }
782
829
  }
783
830
  catch (err) {
784
831
  logError(coordRoot, err, { phase: "user-prompt-submit-systemMessage" });
785
832
  }
786
833
  }
787
- // turn.stop: telemetry + turn-summary effects, then the stop verdict. The
834
+ // turn.completed: telemetry, then the stop verdict. The
788
835
  // verdict + codex-replay previously lived in the per-adapter shell adapters;
789
836
  // agent-hook owns them now. Runs on the normal "stop" event only;
790
837
  // "stop-failure" (API error) gets no gate, matching the previous
791
838
  // stop vs stop-failure split.
792
- if (norm.event_type === "turn.stop" && eventName === "stop") {
793
- // Codex: replay the JSONL transcript → canonical events so the verdict has
794
- // the status_checked / task_set / status_box_present evidence (codex
795
- // doesn't emit those live; this re-emits turn.stop after agent-hook's own,
796
- // so the verdict reads the replay's box signal as the latest).
797
- if (adapter === "codex" && payload?.transcript_path && existsSync(payload.transcript_path)) {
798
- try {
799
- replayCodexJsonl({
800
- coordRoot,
801
- jsonlPath: payload.transcript_path,
802
- sessionId,
803
- instanceId: owner.instance_id,
804
- lastAssistantMessage: payload.raw.last_assistant_message ?? "",
805
- });
806
- }
807
- catch (err) {
808
- logError(coordRoot, err, { phase: "codex-replay" });
809
- }
810
- }
811
- // CC effects: rate-limited session-telemetry sync + turn-summary Haiku
812
- // auto-summary.
839
+ if (norm.event_type === "turn.completed" && eventName === "stop") {
840
+ // Claude Code session telemetry sync remains an independent side effect.
813
841
  if (adapter === "claude-code") {
814
842
  runSessionSyncExtension(coordRoot, false);
815
- runTurnSummary(coordRoot, owner.instance_id, sessionId, payload?.transcript_path);
816
- }
817
- // Master-state heartbeat projection. Drains events.ndjson since the last
818
- // cursor → per-owner heartbeats. Was a SECOND binary (`agent-coord project`)
819
- // pinned to Claude Code Stop only; folded in here so it (a) is one
820
- // entry per event like everything else and (b) fires on EVERY adapter's stop,
821
- // not just CC. Runs unconditionally before the verdict's possible exit-2 return
822
- // (the events are real regardless of whether the agent gets nagged), and after
823
- // codex-replay above so codex's replayed events are included in the drain.
824
- // Not an emitter (consumes + writes heartbeats), so no emitter/consumer conflict.
825
- try {
826
- const result = consumeSince(coordRoot);
827
- projectHeartbeats(coordRoot, result.events);
828
- if (result.lastEventId)
829
- writeCursor(coordRoot, result.lastEventId);
830
- }
831
- catch (err) {
832
- logError(coordRoot, err, { phase: "stop-projection" });
833
- }
834
- // Presence: publish AFTER the projection above so the blob carries this
835
- // turn's fresh task/turn_summary/files. Change-batched + keepalive'd;
836
- // the push itself runs detached. The relay daemon (when configured) is
837
- // re-ensured here too — it self-exits on idle, and the next turn revives it.
838
- try {
839
- publishPresence(coordRoot);
840
- ensureRelayDaemon(coordRoot);
841
843
  }
842
- catch (err) {
843
- logError(coordRoot, err, { phase: "stop-presence" });
844
- }
845
- // Stop verdict (status-box + set-task gate). Direct in-process call: the
846
- // rule lives in harnery. agent-hook already emitted this turn.stop (with
847
- // status_box_present) above, so the evidence is in the stream.
844
+ // Stop verdict (V3 ritual + task/status gate). Direct in-process call: the
845
+ // rule lives in Harnery. agent-hook already emitted this turn.completed
846
+ // with privacy-safe ritual observations, so the evidence is in the stream.
848
847
  const verdict = evaluateStopHook(coordRoot, {
849
848
  rule: "stop-hook",
850
849
  instance_id: owner.instance_id,
@@ -853,31 +852,27 @@ async function main() {
853
852
  bypass: coordEnv("AGENT_COORD_BYPASS_STOP") === "1",
854
853
  workflow_child: coordEnv("WORKFLOW_CHILD") === "1",
855
854
  });
856
- const enforcementMode = stopEnforcementMode(verdict.rule);
857
- emit(coordRoot, {
858
- event_type: "stop.verdict",
859
- instance_id: owner.instance_id,
860
- session_id: sessionId,
861
- turn_id: payload?.turn_id,
862
- parent_turn_id: payload?.parent_turn_id,
863
- adapter,
864
- data: {
865
- allow: verdict.allow,
866
- rule: verdict.rule,
867
- ...(verdict.reason ? { reason: verdict.reason } : {}),
868
- enforcement_mode: enforcementMode,
869
- eligible: enforcementMode === "enforced",
870
- nag_delivered: enforcementMode === "enforced" && !verdict.allow,
871
- },
872
- });
873
855
  if (!verdict.allow) {
874
- // Adapter-aware enforcement channel: Claude Code / Codex honor exit-2 +
875
- // stderr as a turn block; Cursor ignores exit codes (fail-open) and
876
- // re-prompts only via a `followup_message` it auto-submits. emitStopBlock
877
- // writes the right shape and returns the exit code to use.
856
+ // Adapter-aware enforcement channel: Claude Code honors exit-2 + stderr
857
+ // as a turn block; Cursor ignores exit codes and re-prompts only via a
858
+ // `followup_message` it auto-submits. Codex returned observe-only above.
859
+ // emitStopBlock writes the right shape and returns the exit code to use.
878
860
  const { emitStopBlock } = await import("./adapter/output.js");
879
861
  return emitStopBlock(adapter, verdict, coordRoot);
880
862
  }
863
+ // An explicit end requested from inside this turn cannot be authoritative
864
+ // until the adapter has committed turn.completed above. Reconcile only
865
+ // when such a request exists; any later real work cancels it in the
866
+ // finalizer instead of being terminated underneath the agent.
867
+ try {
868
+ const { hasPendingExplicitSessionEndV3, reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
869
+ if (hasPendingExplicitSessionEndV3(coordRoot)) {
870
+ reconcileSessionFinalizationV3(coordRoot);
871
+ }
872
+ }
873
+ catch (err) {
874
+ logError(coordRoot, err, { phase: "stop-explicit-session-finalization" });
875
+ }
881
876
  }
882
877
  // Phase 7: PreToolUse: heartbeat + pid-map self-heal on every tool call.
883
878
  // Adapter-agnostic: both writes have the same shape regardless of who fired.
@@ -888,40 +883,32 @@ async function main() {
888
883
  // side-by-side in the previous pre-tool-use adapter. The Phase 4-6 refactor
889
884
  // preserved the heartbeat half but dropped the pid-map half; the pid-map
890
885
  // call was restored here afterward.
891
- if (norm.event_type === "tool.pre_use") {
886
+ if (norm.event_type === "tool.requested") {
892
887
  try {
893
888
  // Recorded fork lineage, heal-path flavor. A forked CC conversation
894
- // never fires its own session.start (SessionStart fires under the
889
+ // never fires its own session.started (SessionStart fires under the
895
890
  // PARENT's session id with source=resume, before the fork id is
896
891
  // minted; verified 2026-08-05), so the fork's new instance first
897
892
  // materializes right here. Gate detection on "no heartbeat yet" so the
898
893
  // transcript scan runs once per instance lifetime, not per tool call.
899
- let forkedFrom;
900
- if (adapter === "claude-code" &&
901
- payload?.transcript_path &&
902
- !existsSync(join(coordRoot, ".harnery", "active", `${owner.instance_id}.json`))) {
903
- forkedFrom = detectForkParent(payload.transcript_path, owner.instance_id);
904
- }
905
- healHeartbeatViaCli(coordRoot, owner.instance_id, sessionId, adapter, forkedFrom);
906
894
  refreshPidmap(coordRoot, owner.instance_id, adapter, payload?.pid);
907
895
  }
908
896
  catch (err) {
909
897
  logError(coordRoot, err, { phase: "pre-tool-use-heal" });
910
898
  }
911
- // Image feed: a Read on an image file is the "agent viewed this" signal.
912
- // Capture the bytes (content-addressed, dedup'd) + emit image.captured.
899
+ // The suggested name is a pending display latch. The set-task call itself
900
+ // runs before a name exists; every later tool waits until the exact block
901
+ // is the first assistant text after the mint result. Cursor supplies
902
+ // agent_message directly; Claude Code and Codex are resolved from their
903
+ // JSONL transcripts. Later commentary must not erase an already-correct
904
+ // display, and an unreadable transcript must not deadlock every tool.
913
905
  try {
914
- captureImages(coordRoot, {
915
- eventType: "tool.pre_use",
916
- data,
917
- payload,
918
- instanceId: owner.instance_id,
919
- sessionId,
920
- adapter,
921
- });
906
+ const displayAllowed = await enforcePendingSessionNameDisplay(coordRoot, owner.instance_id, adapter, payload);
907
+ if (!displayAllowed)
908
+ return 0;
922
909
  }
923
910
  catch (err) {
924
- logError(coordRoot, err, { phase: "pre-tool-use-image-capture" });
911
+ logError(coordRoot, err, { phase: "pre-tool-use-session-name" });
925
912
  }
926
913
  // Windows-native Codex + WSL UNC only: block the one cross-shell shape
927
914
  // proven to corrupt argument boundaries. Normal WSL argv calls, literal
@@ -934,13 +921,6 @@ async function main() {
934
921
  toolInput: payload?.tool_input,
935
922
  });
936
923
  if (unsafeShellReason) {
937
- emit(coordRoot, {
938
- event_type: "decision.block",
939
- instance_id: owner.instance_id,
940
- session_id: sessionId,
941
- adapter,
942
- data: { rule: "unsafe_cross_shell", reason: unsafeShellReason },
943
- });
944
924
  const { emitDeny } = await import("./adapter/output.js");
945
925
  emitDeny(adapter, unsafeShellReason);
946
926
  return 0;
@@ -960,79 +940,29 @@ async function main() {
960
940
  catch (err) {
961
941
  logError(coordRoot, err, { phase: "pre-tool-use-guard" });
962
942
  }
963
- if (guardAllowed) {
964
- try {
965
- stampSessionStateEvent(coordRoot, owner.instance_id, envelope);
966
- }
967
- catch (err) {
968
- logError(coordRoot, err, { phase: "activity-projection" });
969
- }
970
- }
971
- // Shell-mutation warn (warn-only, never blocks). Was the cursor
972
- // beforeShellExecution + codex preToolUse-Bash shell-mutation-claim-log in
973
- // the per-adapter shell adapters. Cursor sends the command at payload.command;
974
- // codex Bash at tool_input.command. Emits a decision.warn per candidate-mutated
975
- // path so a peer sees the write in events.ndjson. (CC never did this,
976
- // preserved; it emits with its own hooks-side emitter per the
977
- // independent-emitter rule.)
978
- const shellCmd = eventName === "before-shell-execution"
979
- ? (payload?.raw.command ?? "")
980
- : adapter === "codex" && data.tool_name === "Bash"
981
- ? (payload?.raw.tool_input?.command ?? "")
982
- : "";
983
- if (shellCmd) {
984
- try {
985
- const paths = shellMutationPaths(shellCmd, coordRoot);
986
- const truncated = shellCmd.length > 80 ? shellCmd.slice(0, 80) : shellCmd;
987
- const platform = adapterPlatform(adapter);
988
- for (const p of paths) {
989
- emit(coordRoot, {
990
- event_type: "decision.warn",
991
- instance_id: owner.instance_id,
992
- session_id: sessionId,
993
- adapter,
994
- data: {
995
- rule: "shell_mutation_candidate",
996
- reason: `path=${p} cmd=${truncated} platform=${platform}`,
997
- },
998
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
999
- });
1000
- }
1001
- }
1002
- catch (err) {
1003
- logError(coordRoot, err, { phase: "shell-mutation-warn" });
1004
- }
1005
- }
943
+ if (!guardAllowed)
944
+ return 0;
1006
945
  }
1007
- // Phase 7: PostToolUse: stamp last_tool + last_tool_target on heartbeat.
1008
- // Adapter-agnostic for the same reason as tool.pre_use above.
1009
- if (norm.event_type === "tool.post_use") {
1010
- try {
1011
- stampToolActivity(coordRoot, owner.instance_id, data);
1012
- }
1013
- catch (err) {
1014
- logError(coordRoot, err, { phase: "post-tool-use-stamp" });
1015
- }
1016
- // Image feed: a Bash command that wrote an image (harn browse, harn image,
1017
- // --diff, …) is the "agent produced this" signal. Scan the command + its
1018
- // output for freshly-written image paths and capture them.
946
+ if (norm.event_type === "tool.completed" && eventName === "post-tool-use") {
947
+ // Inject only at the successful tool boundary that actually minted a name:
948
+ // the first non-empty set-task or the transition to lifecycle done. The
949
+ // coordination row intentionally remains pending until transcript evidence
950
+ // catches up; reading that latch alone here would re-inject after every
951
+ // later tool and make the agent print the same block repeatedly.
1019
952
  try {
1020
- captureImages(coordRoot, {
1021
- eventType: "tool.post_use",
1022
- data,
1023
- payload,
1024
- instanceId: owner.instance_id,
1025
- sessionId,
1026
- adapter,
1027
- });
953
+ const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, owner.instance_id));
954
+ if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
955
+ const { emitContext } = await import("./adapter/output.js");
956
+ emitContext(adapter, "PostToolUse", sessionNameDisplayInstruction(name));
957
+ }
1028
958
  }
1029
959
  catch (err) {
1030
- logError(coordRoot, err, { phase: "post-tool-use-image-capture" });
960
+ logError(coordRoot, err, { phase: "post-tool-use-session-name" });
1031
961
  }
1032
962
  }
1033
963
  // Phase 7: PostToolUseFailure: release claim on failed Edit (the file
1034
964
  // never landed; the claim is stale). Adapter-agnostic.
1035
- if (norm.event_type === "tool.post_use_failure") {
965
+ if (norm.event_type === "tool.completed" && eventName === "post-tool-use-failure") {
1036
966
  try {
1037
967
  releaseClaimOnFailure(coordRoot, owner.instance_id, data, payload?.raw);
1038
968
  }
@@ -1042,14 +972,32 @@ async function main() {
1042
972
  }
1043
973
  return 0;
1044
974
  }
1045
- function stopEnforcementMode(rule) {
1046
- if (rule === "stop-hook.codex_observe_only")
1047
- return "observe_only";
1048
- if (rule === "stop-hook.fail_open" || rule === "stop-hook.no_history")
1049
- return "fail_open";
1050
- if (rule === "stop-hook.bypass" || rule === "stop-hook.workflow_child")
1051
- return "exempt";
1052
- return "enforced";
975
+ async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter, payload) {
976
+ const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, instanceId));
977
+ if (!name)
978
+ return true;
979
+ const command = extractBashCommand(payload?.tool_name, payload?.tool_input);
980
+ if (isSessionNameRemediationCommand(command, resolveBinName(coordRoot)))
981
+ return true;
982
+ const inspection = payload?.agent_message !== undefined
983
+ ? {
984
+ state: assistantTextStartsWithSessionNameBlock(payload.agent_message, name)
985
+ ? "present"
986
+ : "absent",
987
+ }
988
+ : inspectSessionNameDisplayImmediately(payload?.transcript_path, name, assistantTextStartsWithSessionNameBlock);
989
+ if (inspection.state === "present") {
990
+ stampSessionNameSeen(coordRoot, instanceId, name);
991
+ return true;
992
+ }
993
+ if (inspection.state === "unavailable") {
994
+ const { emitContext } = await import("./adapter/output.js");
995
+ emitContext(adapter, "PreToolUse", "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.");
996
+ return true;
997
+ }
998
+ const { emitDeny } = await import("./adapter/output.js");
999
+ emitDeny(adapter, sessionNameDisplayInstruction(name));
1000
+ return false;
1053
1001
  }
1054
1002
  async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapter) {
1055
1003
  const toolName = data.tool_name ?? "";
@@ -1128,23 +1076,6 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
1128
1076
  emitDeny(adapter, reason);
1129
1077
  return false;
1130
1078
  }
1131
- // The verdict mutates the live heartbeat immediately, but that file is a
1132
- // projection and can be rebuilt at any later hook. Persist the acquisition
1133
- // in the canonical stream too. This matters most for Codex apply_patch:
1134
- // one tool call can claim several files, and tool.pre_use's clamped payload
1135
- // is not a durable ownership representation for those targets.
1136
- emit(coordRoot, {
1137
- event_type: "claim.acquire",
1138
- instance_id: instanceId,
1139
- session_id: sessionId,
1140
- adapter,
1141
- source: "agent-hooks",
1142
- data: {
1143
- path: target.path,
1144
- mode: "write",
1145
- ...(target.finalization ? { finalization: target.finalization } : {}),
1146
- },
1147
- });
1148
1079
  }
1149
1080
  return true;
1150
1081
  }
@@ -1204,28 +1135,10 @@ function parseApplyPatchPaths(data) {
1204
1135
  }
1205
1136
  return out;
1206
1137
  }
1207
- function healHeartbeatViaCli(coordRoot, instanceId, sessionId, adapter, forkedFrom) {
1208
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1209
- if (!existsSync(agentCoordBin))
1210
- return;
1211
- // Pass the detected adapter so a pruned Cursor/Codex heartbeat is recreated
1212
- // with the correct platform; without it, healHeartbeat defaults to
1213
- // claude-code and the dashboard mislabels the agent. See
1214
- // heartbeat-writer.healHeartbeat. --forked-from carries detected fork
1215
- // lineage for an instance that never session.started (the CC fork flow).
1216
- const args = ["heal-heartbeat", instanceId, sessionId, `--adapter=${adapter}`];
1217
- if (forkedFrom)
1218
- args.push(`--forked-from=${forkedFrom}`);
1219
- spawnSync(agentCoordBin, args, {
1220
- encoding: "utf8",
1221
- timeout: 2000,
1222
- env: childEnv(coordRoot),
1223
- });
1224
- }
1225
1138
  /**
1226
1139
  * Walk up the ppid chain on Linux/WSL looking for the adapter anchor PID,
1227
1140
  * the PID of the claude / cursor / codex binary. Finds the agent PID. Used by
1228
- * `tool.pre_use`'s pid-map self-heal so a re-parented adapter binary (the
1141
+ * `tool.requested` pid-map self-heal so a re-parented adapter binary (the
1229
1142
  * VS Code 2.1.x sibling-claude spawn case) gets its pid-map row rewritten on
1230
1143
  * the next tool call rather than going invisible until SessionStart fires
1231
1144
  * again, which it may never do.
@@ -1298,9 +1211,7 @@ function findAdapterAnchorPid(adapter) {
1298
1211
  return selectAnchorPid(chain, adapter);
1299
1212
  }
1300
1213
  /**
1301
- * Pid-map self-heal for `tool.pre_use`. Symmetric counterpart to
1302
- * `healHeartbeatViaCli`; the two were paired before the Phase 6 refactor split
1303
- * them apart, then restored together afterward.
1214
+ * Pid-map self-heal for `tool.requested`.
1304
1215
  *
1305
1216
  * The pid argument prefers the payload's `pid` (CC populates it on
1306
1217
  * SessionStart and may also send it on PreToolUse), then
@@ -1318,38 +1229,6 @@ function refreshPidmap(coordRoot, instanceId, adapter, payloadPid) {
1318
1229
  return;
1319
1230
  writePidmapViaAgentCoord(coordRoot, pid, instanceId, adapterPlatform(adapter));
1320
1231
  }
1321
- function stampToolActivity(coordRoot, instanceId, data) {
1322
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1323
- if (!existsSync(agentCoordBin))
1324
- return;
1325
- const toolName = data.tool_name ?? "";
1326
- // Extract a 1-line target from the tool_input blob (file path / command head).
1327
- const toolInputRaw = data.tool_input;
1328
- let target = "";
1329
- if (typeof toolInputRaw === "string") {
1330
- try {
1331
- const parsed = JSON.parse(toolInputRaw);
1332
- target =
1333
- parsed.file_path ??
1334
- parsed.path ??
1335
- parsed.notebook_path ??
1336
- parsed.command ??
1337
- parsed.url ??
1338
- parsed.pattern ??
1339
- "";
1340
- }
1341
- catch {
1342
- /* skip */
1343
- }
1344
- }
1345
- if (target.length > 200)
1346
- target = target.slice(0, 200);
1347
- spawnSync(agentCoordBin, ["stamp-tool-activity", instanceId, toolName, target], {
1348
- encoding: "utf8",
1349
- timeout: 2000,
1350
- env: childEnv(coordRoot),
1351
- });
1352
- }
1353
1232
  function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
1354
1233
  const toolName = data.tool_name ?? "";
1355
1234
  if (toolName !== "Edit" && toolName !== "Write" && toolName !== "NotebookEdit")
@@ -1389,6 +1268,12 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
1389
1268
  ? filePath.slice(coordRoot.length + 1)
1390
1269
  : filePath;
1391
1270
  }
1271
+ // In V3 the release is an authority event, not a heartbeat mutation. Avoid
1272
+ // creating a disposable cache for a failure that never acquired a claim,
1273
+ // but release the exact path when the validated projection says it is held.
1274
+ const row = readLiveCoordinationRow(coordRoot, instanceId);
1275
+ if (!row?.files_touched?.includes(canonical))
1276
+ return;
1392
1277
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1393
1278
  if (!existsSync(agentCoordBin))
1394
1279
  return;
@@ -1399,16 +1284,6 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
1399
1284
  });
1400
1285
  }
1401
1286
  function cleanupSessionEnd(coordRoot, instanceId, reason) {
1402
- // Remove heartbeat from the canonical .harnery/active/ dir.
1403
- const path = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
1404
- try {
1405
- if (existsSync(path)) {
1406
- require("node:fs").unlinkSync(path);
1407
- }
1408
- }
1409
- catch {
1410
- /* swallow */
1411
- }
1412
1287
  // Sweep pid-map entries pointing to this instance
1413
1288
  const pidmapDir = join(coordRoot, ".harnery", "pid-map");
1414
1289
  if (existsSync(pidmapDir)) {
@@ -1442,22 +1317,7 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
1442
1317
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1443
1318
  let additionalContext = "";
1444
1319
  if (existsSync(agentCoordBin)) {
1445
- // Look up the agent's name from its heartbeat (for council pending rendering).
1446
- let agentName = "";
1447
- try {
1448
- const fs = require("node:fs");
1449
- const hbPath = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
1450
- if (fs.existsSync(hbPath)) {
1451
- const hb = JSON.parse(fs.readFileSync(hbPath, "utf8"));
1452
- agentName = hb.name ?? "";
1453
- }
1454
- }
1455
- catch {
1456
- /* fall through with empty name; peer table still renders */
1457
- }
1458
1320
  const args = ["prompt-context", "--instance", instanceId, "--session", sessionId];
1459
- if (agentName)
1460
- args.push("--name", agentName);
1461
1321
  // Every human-facing adapter gets the first-session naming reminder from
1462
1322
  // the shared hook path. Cursor + Codex additionally get ongoing set-task
1463
1323
  // staleness nudges; Claude Code enforces those via its Stop transcript.
@@ -1494,7 +1354,7 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
1494
1354
  }
1495
1355
  function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
1496
1356
  // Look up the subagent's assigned name (just-written by agent-coord assignName
1497
- // in session.start data) + the parent's short id for the "you are a subagent
1357
+ // in session.started data) + the parent's short id for the "you are a subagent
1498
1358
  // of X" framing.
1499
1359
  const subagentName = data.name ?? "";
1500
1360
  if (!subagentName)
@@ -1526,13 +1386,19 @@ function adapterPlatform(adapter) {
1526
1386
  }
1527
1387
  async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, emittedData, adapter, recoveryBriefing = "") {
1528
1388
  const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1389
+ const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
1529
1390
  let additionalContext = "";
1530
1391
  if (existsSync(agentCoordBin)) {
1531
1392
  // Sync-project so the heartbeat exists for downstream readers (peer table,
1532
1393
  // wiring check, council invites).
1533
1394
  spawnSync(agentCoordBin, ["project"], { encoding: "utf8", timeout: 3000 });
1534
- // Stale-sweep dead peers before rendering peer table.
1535
- spawnSync(agentCoordBin, ["stale-sweep"], { encoding: "utf8", timeout: 3000 });
1395
+ // Opportunistic reconciliation makes normal session starts a failsafe for
1396
+ // archive, idle, cascade, and host lifecycle observations.
1397
+ spawnSync(agentCoordBin, ["reconcile-finalization"], {
1398
+ encoding: "utf8",
1399
+ timeout: 5000,
1400
+ env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot },
1401
+ });
1536
1402
  // SESSION_START activity log line, fired across all adapters.
1537
1403
  const model = emittedData.model ?? "unknown";
1538
1404
  const source = emittedData.source ?? "startup";
@@ -1543,34 +1409,39 @@ async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, e
1543
1409
  "--instance",
1544
1410
  instanceId,
1545
1411
  ], { encoding: "utf8", timeout: 2000 });
1546
- // Render the systemMessage via agent-coord.
1547
- const agentName = emittedData.name ?? "";
1548
- const args = ["session-context", "--instance", instanceId, "--session", sessionId];
1549
- if (agentName)
1550
- args.push("--name", agentName);
1551
- // The "You are agent-X." prefix in session-context renders unqualified by
1552
- // default (claude-code-style). For cursor/codex the bash dispatchers add
1553
- // a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
1554
- if (adapter !== "claude-code") {
1555
- args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
1412
+ // Workflow children retain lifecycle/event capture but do not receive
1413
+ // operator-facing peer, council, or init-remediation context. Injecting
1414
+ // that context can make a bounded child follow housekeeping instructions
1415
+ // instead of its assigned prompt.
1416
+ if (!workflowChild) {
1417
+ const agentName = emittedData.name ?? "";
1418
+ const args = ["session-context", "--instance", instanceId, "--session", sessionId];
1419
+ if (agentName)
1420
+ args.push("--name", agentName);
1421
+ // The "You are agent-X." prefix in session-context renders unqualified by
1422
+ // default (claude-code-style). For cursor/codex the bash dispatchers add
1423
+ // a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
1424
+ if (adapter !== "claude-code") {
1425
+ args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
1426
+ }
1427
+ const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1428
+ if (result.status === 0 && result.stdout)
1429
+ additionalContext = result.stdout.trim();
1556
1430
  }
1557
- const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1558
- if (result.status === 0 && result.stdout)
1559
- additionalContext = result.stdout.trim();
1560
1431
  }
1561
1432
  // Effect (claude-code): merge the journal recovery cue into the session-start
1562
1433
  // context. Was a standalone additionalContext emission from the previous
1563
1434
  // journal-on-start adapter; now that agent-hook is the single SessionStart
1564
1435
  // entry, it folds in here.
1565
- if (adapter === "claude-code") {
1436
+ if (adapter === "claude-code" && !workflowChild) {
1566
1437
  const cue = journalRecoveryCue(coordRoot);
1567
1438
  if (cue)
1568
1439
  additionalContext = [additionalContext, cue].filter(Boolean).join("\n\n");
1569
1440
  }
1570
- if (recoveryBriefing) {
1441
+ if (recoveryBriefing && !workflowChild) {
1571
1442
  additionalContext = [additionalContext, recoveryBriefing].filter(Boolean).join("\n\n");
1572
1443
  }
1573
- if (adapter === "codex" && isWslUncPath(emittedData.cwd)) {
1444
+ if (adapter === "codex" && !workflowChild && isWslUncPath(emittedData.cwd)) {
1574
1445
  const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, emittedData.cwd);
1575
1446
  if (fileLinkContext) {
1576
1447
  additionalContext = [additionalContext, fileLinkContext].filter(Boolean).join("\n\n");
@@ -1591,19 +1462,9 @@ async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, e
1591
1462
  emitContext(adapter, "SessionStart", additionalContext);
1592
1463
  return true;
1593
1464
  }
1594
- function completeRecoveryInjection(coordRoot, instanceId, sessionId, adapter, recovery, injectionEvent) {
1465
+ function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
1595
1466
  completeContextRecovery(coordRoot, { sessionId, instanceId });
1596
- emit(coordRoot, {
1597
- event_type: "context.recovery.injected",
1598
- instance_id: instanceId,
1599
- session_id: sessionId,
1600
- adapter,
1601
- data: {
1602
- capsule_id: recovery.capsule.capsule_id,
1603
- generation: recovery.capsule.generation,
1604
- injection_event: injectionEvent,
1605
- },
1606
- });
1467
+ void recovery;
1607
1468
  }
1608
1469
  main()
1609
1470
  .then((code) => process.exit(code))