harnery 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (665) hide show
  1. package/README.md +2 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +7 -1
  4. package/dist/commands/adapter.js +2 -2
  5. package/dist/commands/agents.d.ts +144 -1
  6. package/dist/commands/agents.d.ts.map +1 -1
  7. package/dist/commands/agents.js +1408 -565
  8. package/dist/commands/artifacts.d.ts.map +1 -1
  9. package/dist/commands/artifacts.js +7 -6
  10. package/dist/commands/backup.d.ts +1 -1
  11. package/dist/commands/backup.d.ts.map +1 -1
  12. package/dist/commands/backup.js +29 -16
  13. package/dist/commands/browse.d.ts.map +1 -1
  14. package/dist/commands/browse.js +234 -12
  15. package/dist/commands/checkpoint.d.ts.map +1 -1
  16. package/dist/commands/checkpoint.js +3 -2
  17. package/dist/commands/claude-desktop.d.ts +2 -2
  18. package/dist/commands/claude-desktop.d.ts.map +1 -1
  19. package/dist/commands/claude-desktop.js +2 -2
  20. package/dist/commands/context.js +1 -6
  21. package/dist/commands/decision.d.ts.map +1 -1
  22. package/dist/commands/decision.js +22 -38
  23. package/dist/commands/docs.d.ts.map +1 -1
  24. package/dist/commands/docs.js +91 -28
  25. package/dist/commands/doctor.d.ts +4 -2
  26. package/dist/commands/doctor.d.ts.map +1 -1
  27. package/dist/commands/doctor.js +55 -7
  28. package/dist/commands/events.d.ts +5 -0
  29. package/dist/commands/events.d.ts.map +1 -0
  30. package/dist/commands/events.js +189 -0
  31. package/dist/commands/governor.d.ts.map +1 -1
  32. package/dist/commands/governor.js +13 -4
  33. package/dist/commands/init.d.ts +10 -2
  34. package/dist/commands/init.d.ts.map +1 -1
  35. package/dist/commands/init.js +233 -51
  36. package/dist/commands/instructions.d.ts +4 -0
  37. package/dist/commands/instructions.d.ts.map +1 -0
  38. package/dist/commands/instructions.js +100 -0
  39. package/dist/commands/journal.d.ts.map +1 -1
  40. package/dist/commands/journal.js +4 -13
  41. package/dist/commands/ledger-v3.d.ts +5 -0
  42. package/dist/commands/ledger-v3.d.ts.map +1 -0
  43. package/dist/commands/ledger-v3.js +91 -0
  44. package/dist/commands/presence.d.ts.map +1 -1
  45. package/dist/commands/presence.js +15 -6
  46. package/dist/commands/sync.d.ts +1 -1
  47. package/dist/commands/sync.js +1 -1
  48. package/dist/commands/web.d.ts +6 -0
  49. package/dist/commands/web.d.ts.map +1 -1
  50. package/dist/commands/web.js +67 -7
  51. package/dist/commands/work.d.ts.map +1 -1
  52. package/dist/commands/work.js +22 -5
  53. package/dist/core/adapter.d.ts +5 -0
  54. package/dist/core/adapter.d.ts.map +1 -0
  55. package/dist/core/adapter.js +1 -0
  56. package/dist/core/adapters/attest.d.ts +5 -2
  57. package/dist/core/adapters/attest.d.ts.map +1 -1
  58. package/dist/core/adapters/attest.js +8 -3
  59. package/dist/core/adapters/profiles.d.ts +6 -6
  60. package/dist/core/adapters/profiles.d.ts.map +1 -1
  61. package/dist/core/adapters/profiles.js +4 -4
  62. package/dist/core/agents/canonical-emit.d.ts +17 -15
  63. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  64. package/dist/core/agents/canonical-emit.js +27 -25
  65. package/dist/core/agents/cli.js +212 -235
  66. package/dist/core/agents/codex-archive-v3.d.ts +14 -0
  67. package/dist/core/agents/codex-archive-v3.d.ts.map +1 -0
  68. package/dist/core/agents/codex-archive-v3.js +110 -0
  69. package/dist/core/agents/coord-client.d.ts +19 -70
  70. package/dist/core/agents/coord-client.d.ts.map +1 -1
  71. package/dist/core/agents/coord-client.js +65 -155
  72. package/dist/core/agents/finalization.d.ts +4 -1
  73. package/dist/core/agents/finalization.d.ts.map +1 -1
  74. package/dist/core/agents/finalization.js +89 -32
  75. package/dist/core/agents/index.d.ts +5 -5
  76. package/dist/core/agents/index.d.ts.map +1 -1
  77. package/dist/core/agents/index.js +5 -5
  78. package/dist/core/agents/live-authority-v3.d.ts +59 -0
  79. package/dist/core/agents/live-authority-v3.d.ts.map +1 -0
  80. package/dist/core/agents/live-authority-v3.js +233 -0
  81. package/dist/core/agents/live-lifecycle-v3.d.ts +20 -0
  82. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -0
  83. package/dist/core/agents/live-lifecycle-v3.js +91 -0
  84. package/dist/core/agents/live-observation-v3.d.ts +25 -0
  85. package/dist/core/agents/live-observation-v3.d.ts.map +1 -0
  86. package/dist/core/agents/live-observation-v3.js +157 -0
  87. package/dist/core/agents/render/prompt-context.d.ts +10 -4
  88. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  89. package/dist/core/agents/render/prompt-context.js +61 -55
  90. package/dist/core/agents/render/session-context.d.ts +1 -1
  91. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  92. package/dist/core/agents/render/session-context.js +15 -33
  93. package/dist/core/agents/rules/claim-conflict.d.ts +3 -3
  94. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  95. package/dist/core/agents/rules/claim-conflict.js +79 -127
  96. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  97. package/dist/core/agents/rules/commit-conflict.js +3 -18
  98. package/dist/core/agents/rules/stop-hook.d.ts +9 -50
  99. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  100. package/dist/core/agents/rules/stop-hook.js +193 -236
  101. package/dist/core/agents/session-events.d.ts +5 -7
  102. package/dist/core/agents/session-events.d.ts.map +1 -1
  103. package/dist/core/agents/session-events.js +127 -95
  104. package/dist/core/agents/session-finalization-state-v3.d.ts +30 -0
  105. package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -0
  106. package/dist/core/agents/session-finalization-state-v3.js +40 -0
  107. package/dist/core/agents/session-finalizer-v3.d.ts +107 -0
  108. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -0
  109. package/dist/core/agents/session-finalizer-v3.js +734 -0
  110. package/dist/core/agents/session-name-display.d.ts +40 -0
  111. package/dist/core/agents/session-name-display.d.ts.map +1 -0
  112. package/dist/core/agents/session-name-display.js +119 -0
  113. package/dist/core/agents/state/activity-log.d.ts +3 -3
  114. package/dist/core/agents/state/activity-log.d.ts.map +1 -1
  115. package/dist/core/agents/state/activity-log.js +7 -16
  116. package/dist/core/agents/state/heartbeat-writer.d.ts +29 -46
  117. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  118. package/dist/core/agents/state/heartbeat-writer.js +59 -199
  119. package/dist/core/agents/state/live-coordination-view.d.ts +31 -0
  120. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -0
  121. package/dist/core/agents/state/live-coordination-view.js +209 -0
  122. package/dist/core/agents/state/session-state.d.ts +45 -0
  123. package/dist/core/agents/state/session-state.d.ts.map +1 -0
  124. package/dist/core/agents/state/session-state.js +90 -0
  125. package/dist/core/agents/state/stale-sweep.d.ts +1 -1
  126. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  127. package/dist/core/agents/state/stale-sweep.js +26 -15
  128. package/dist/core/artifacts/index.d.ts.map +1 -1
  129. package/dist/core/artifacts/index.js +5 -7
  130. package/dist/core/config.d.ts +33 -2
  131. package/dist/core/config.d.ts.map +1 -1
  132. package/dist/core/config.js +98 -2
  133. package/dist/core/context/index.d.ts +1 -4
  134. package/dist/core/context/index.d.ts.map +1 -1
  135. package/dist/core/context/index.js +9 -9
  136. package/dist/core/events/span-parent.d.ts +15 -0
  137. package/dist/core/events/span-parent.d.ts.map +1 -0
  138. package/dist/core/events/span-parent.js +34 -0
  139. package/dist/core/events/v3/advance.d.ts +14 -0
  140. package/dist/core/events/v3/advance.d.ts.map +1 -0
  141. package/dist/core/events/v3/advance.js +134 -0
  142. package/dist/core/events/v3/authority-outbox.d.ts +78 -0
  143. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -0
  144. package/dist/core/events/v3/authority-outbox.js +399 -0
  145. package/dist/core/events/v3/base-builder.d.ts +20 -0
  146. package/dist/core/events/v3/base-builder.d.ts.map +1 -0
  147. package/dist/core/events/v3/base-builder.js +29 -0
  148. package/dist/core/events/v3/base-contract.d.ts +7576 -0
  149. package/dist/core/events/v3/base-contract.d.ts.map +1 -0
  150. package/dist/core/events/v3/base-contract.js +647 -0
  151. package/dist/core/events/v3/bootstrap.d.ts +34 -0
  152. package/dist/core/events/v3/bootstrap.d.ts.map +1 -0
  153. package/dist/core/events/v3/bootstrap.js +175 -0
  154. package/dist/core/events/v3/builder.d.ts +20 -0
  155. package/dist/core/events/v3/builder.d.ts.map +1 -0
  156. package/dist/core/events/v3/builder.js +28 -0
  157. package/dist/core/events/v3/canonical.d.ts +31 -0
  158. package/dist/core/events/v3/canonical.d.ts.map +1 -0
  159. package/dist/core/events/v3/canonical.js +100 -0
  160. package/dist/core/events/v3/capabilities.d.ts +31 -0
  161. package/dist/core/events/v3/capabilities.d.ts.map +1 -0
  162. package/dist/core/events/v3/capabilities.js +107 -0
  163. package/dist/core/events/v3/capability-drift.d.ts +22 -0
  164. package/dist/core/events/v3/capability-drift.d.ts.map +1 -0
  165. package/dist/core/events/v3/capability-drift.js +134 -0
  166. package/dist/core/events/v3/catalog.d.ts +38 -0
  167. package/dist/core/events/v3/catalog.d.ts.map +1 -0
  168. package/dist/core/events/v3/catalog.js +135 -0
  169. package/dist/core/events/v3/clock-order.d.ts +34 -0
  170. package/dist/core/events/v3/clock-order.d.ts.map +1 -0
  171. package/dist/core/events/v3/clock-order.js +35 -0
  172. package/dist/core/events/v3/contract.d.ts +171 -0
  173. package/dist/core/events/v3/contract.d.ts.map +1 -0
  174. package/dist/core/events/v3/contract.js +180 -0
  175. package/dist/core/events/v3/control.d.ts +111 -0
  176. package/dist/core/events/v3/control.d.ts.map +1 -0
  177. package/dist/core/events/v3/control.js +419 -0
  178. package/dist/core/events/v3/coordination-view.d.ts +69 -0
  179. package/dist/core/events/v3/coordination-view.d.ts.map +1 -0
  180. package/dist/core/events/v3/coordination-view.js +119 -0
  181. package/dist/core/events/v3/economics.d.ts +77 -0
  182. package/dist/core/events/v3/economics.d.ts.map +1 -0
  183. package/dist/core/events/v3/economics.js +274 -0
  184. package/dist/core/events/v3/finalization-view.d.ts +26 -0
  185. package/dist/core/events/v3/finalization-view.d.ts.map +1 -0
  186. package/dist/core/events/v3/finalization-view.js +37 -0
  187. package/dist/core/events/v3/fingerprint-keys.d.ts +25 -0
  188. package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -0
  189. package/dist/core/events/v3/fingerprint-keys.js +217 -0
  190. package/dist/core/events/v3/generated.d.ts +4 -0
  191. package/dist/core/events/v3/generated.d.ts.map +1 -0
  192. package/dist/core/events/v3/generated.js +47 -0
  193. package/dist/core/events/v3/ids.d.ts +9 -0
  194. package/dist/core/events/v3/ids.d.ts.map +1 -0
  195. package/dist/core/events/v3/ids.js +25 -0
  196. package/dist/core/events/v3/index.d.ts +35 -0
  197. package/dist/core/events/v3/index.d.ts.map +1 -0
  198. package/dist/core/events/v3/index.js +33 -0
  199. package/dist/core/events/v3/latency.d.ts +63 -0
  200. package/dist/core/events/v3/latency.d.ts.map +1 -0
  201. package/dist/core/events/v3/latency.js +412 -0
  202. package/dist/core/events/v3/live-feed.d.ts +45 -0
  203. package/dist/core/events/v3/live-feed.d.ts.map +1 -0
  204. package/dist/core/events/v3/live-feed.js +246 -0
  205. package/dist/core/events/v3/live-observation.d.ts +35 -0
  206. package/dist/core/events/v3/live-observation.d.ts.map +1 -0
  207. package/dist/core/events/v3/live-observation.js +1 -0
  208. package/dist/core/events/v3/live-routing.d.ts +57 -0
  209. package/dist/core/events/v3/live-routing.d.ts.map +1 -0
  210. package/dist/core/events/v3/live-routing.js +107 -0
  211. package/dist/core/events/v3/producers/command-recorder.d.ts +67 -0
  212. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -0
  213. package/dist/core/events/v3/producers/command-recorder.js +477 -0
  214. package/dist/core/events/v3/producers/command.d.ts +50 -0
  215. package/dist/core/events/v3/producers/command.d.ts.map +1 -0
  216. package/dist/core/events/v3/producers/command.js +135 -0
  217. package/dist/core/events/v3/producers/coordination-recorder.d.ts +53 -0
  218. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -0
  219. package/dist/core/events/v3/producers/coordination-recorder.js +376 -0
  220. package/dist/core/events/v3/producers/coordination.d.ts +90 -0
  221. package/dist/core/events/v3/producers/coordination.d.ts.map +1 -0
  222. package/dist/core/events/v3/producers/coordination.js +229 -0
  223. package/dist/core/events/v3/producers/hook-base.d.ts +54 -0
  224. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -0
  225. package/dist/core/events/v3/producers/hook-base.js +424 -0
  226. package/dist/core/events/v3/producers/hook.d.ts +29 -0
  227. package/dist/core/events/v3/producers/hook.d.ts.map +1 -0
  228. package/dist/core/events/v3/producers/hook.js +71 -0
  229. package/dist/core/events/v3/producers/intake.d.ts +65 -0
  230. package/dist/core/events/v3/producers/intake.d.ts.map +1 -0
  231. package/dist/core/events/v3/producers/intake.js +244 -0
  232. package/dist/core/events/v3/producers/recorder.d.ts +240 -0
  233. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -0
  234. package/dist/core/events/v3/producers/recorder.js +2176 -0
  235. package/dist/core/events/v3/projection.d.ts +101 -0
  236. package/dist/core/events/v3/projection.d.ts.map +1 -0
  237. package/dist/core/events/v3/projection.js +479 -0
  238. package/dist/core/events/v3/reader.d.ts +75 -0
  239. package/dist/core/events/v3/reader.d.ts.map +1 -0
  240. package/dist/core/events/v3/reader.js +778 -0
  241. package/dist/core/events/v3/recovery-record.d.ts +47 -0
  242. package/dist/core/events/v3/recovery-record.d.ts.map +1 -0
  243. package/dist/core/events/v3/recovery-record.js +167 -0
  244. package/dist/core/events/v3/recovery.d.ts +22 -0
  245. package/dist/core/events/v3/recovery.d.ts.map +1 -0
  246. package/dist/core/events/v3/recovery.js +227 -0
  247. package/dist/core/events/v3/span-state.d.ts +52 -0
  248. package/dist/core/events/v3/span-state.d.ts.map +1 -0
  249. package/dist/core/events/v3/span-state.js +142 -0
  250. package/dist/core/events/v3/targets.d.ts +23 -0
  251. package/dist/core/events/v3/targets.d.ts.map +1 -0
  252. package/dist/core/events/v3/targets.js +134 -0
  253. package/dist/core/events/v3/turn-telemetry.d.ts +41 -0
  254. package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -0
  255. package/dist/core/events/v3/turn-telemetry.js +168 -0
  256. package/dist/core/events/v3/validate.d.ts +9 -0
  257. package/dist/core/events/v3/validate.d.ts.map +1 -0
  258. package/dist/core/events/v3/validate.js +281 -0
  259. package/dist/core/events/v3/writer.d.ts +56 -0
  260. package/dist/core/events/v3/writer.d.ts.map +1 -0
  261. package/dist/core/events/v3/writer.js +335 -0
  262. package/dist/core/governor/index.d.ts +1 -1
  263. package/dist/core/governor/index.d.ts.map +1 -1
  264. package/dist/core/governor/index.js +1 -1
  265. package/dist/core/governor/plan-api.d.ts +2 -1
  266. package/dist/core/governor/plan-api.d.ts.map +1 -1
  267. package/dist/core/governor/plan-api.js +6 -3
  268. package/dist/core/governor/read.d.ts +1 -1
  269. package/dist/core/governor/read.d.ts.map +1 -1
  270. package/dist/core/governor/read.js +1 -1
  271. package/dist/core/governor/state.d.ts +9 -0
  272. package/dist/core/governor/state.d.ts.map +1 -1
  273. package/dist/core/governor/state.js +40 -3
  274. package/dist/core/guard/config.d.ts +5 -0
  275. package/dist/core/guard/config.d.ts.map +1 -0
  276. package/dist/core/guard/config.js +157 -0
  277. package/dist/core/guard/coordinator.d.ts +14 -0
  278. package/dist/core/guard/coordinator.d.ts.map +1 -0
  279. package/dist/core/guard/coordinator.js +173 -0
  280. package/dist/core/guard/evaluator.d.ts +4 -0
  281. package/dist/core/guard/evaluator.d.ts.map +1 -0
  282. package/dist/core/guard/evaluator.js +242 -0
  283. package/dist/core/guard/evidence-v3.d.ts +15 -0
  284. package/dist/core/guard/evidence-v3.d.ts.map +1 -0
  285. package/dist/core/guard/evidence-v3.js +171 -0
  286. package/dist/core/guard/index.d.ts +10 -0
  287. package/dist/core/guard/index.d.ts.map +1 -0
  288. package/dist/core/guard/index.js +6 -0
  289. package/dist/core/guard/live-source-v3.d.ts +40 -0
  290. package/dist/core/guard/live-source-v3.d.ts.map +1 -0
  291. package/dist/core/guard/live-source-v3.js +128 -0
  292. package/dist/core/guard/storage.d.ts +27 -0
  293. package/dist/core/guard/storage.d.ts.map +1 -0
  294. package/dist/core/guard/storage.js +140 -0
  295. package/dist/core/guard/types.d.ts +119 -0
  296. package/dist/core/guard/types.d.ts.map +1 -0
  297. package/dist/core/guard/types.js +1 -0
  298. package/dist/core/hooks/adapter/detect.d.ts +18 -1
  299. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  300. package/dist/core/hooks/adapter/detect.js +29 -0
  301. package/dist/core/hooks/adapter/events.d.ts +7 -4
  302. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  303. package/dist/core/hooks/adapter/events.js +13 -4
  304. package/dist/core/hooks/adapter/output.d.ts +2 -2
  305. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  306. package/dist/core/hooks/adapter/parse.d.ts +7 -4
  307. package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
  308. package/dist/core/hooks/adapter/parse.js +64 -24
  309. package/dist/core/hooks/adapter/wiring.d.ts +30 -4
  310. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  311. package/dist/core/hooks/adapter/wiring.js +89 -11
  312. package/dist/core/hooks/cli.d.ts +1 -1
  313. package/dist/core/hooks/cli.js +345 -400
  314. package/dist/core/hooks/codex-authorization.d.ts +30 -0
  315. package/dist/core/hooks/codex-authorization.d.ts.map +1 -0
  316. package/dist/core/hooks/codex-authorization.js +199 -0
  317. package/dist/core/hooks/effects/image-capture.d.ts +20 -34
  318. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  319. package/dist/core/hooks/effects/image-capture.js +212 -221
  320. package/dist/core/hooks/effects/index.d.ts +1 -9
  321. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  322. package/dist/core/hooks/effects/index.js +1 -24
  323. package/dist/core/hooks/events/input-hash.d.ts +7 -0
  324. package/dist/core/hooks/events/input-hash.d.ts.map +1 -0
  325. package/dist/core/hooks/events/input-hash.js +43 -0
  326. package/dist/core/hooks/index.d.ts +4 -3
  327. package/dist/core/hooks/index.d.ts.map +1 -1
  328. package/dist/core/hooks/index.js +2 -3
  329. package/dist/core/hooks/resolve/coord-root.d.ts +1 -1
  330. package/dist/core/hooks/resolve/coord-root.js +1 -1
  331. package/dist/core/hooks/resolve/owner.d.ts +16 -15
  332. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  333. package/dist/core/hooks/resolve/owner.js +51 -42
  334. package/dist/core/hooks/resolve/transcript.d.ts +33 -3
  335. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  336. package/dist/core/hooks/resolve/transcript.js +155 -14
  337. package/dist/core/hooks/session-name-presence.d.ts +9 -10
  338. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  339. package/dist/core/hooks/session-name-presence.js +15 -15
  340. package/dist/core/hooks/unsafe-cross-shell.d.ts +1 -1
  341. package/dist/core/hooks/unsafe-cross-shell.d.ts.map +1 -1
  342. package/dist/core/journal/index.d.ts +2 -2
  343. package/dist/core/journal/index.d.ts.map +1 -1
  344. package/dist/core/journal/index.js +9 -22
  345. package/dist/core/presence/blob.d.ts +8 -3
  346. package/dist/core/presence/blob.d.ts.map +1 -1
  347. package/dist/core/presence/blob.js +54 -41
  348. package/dist/core/presence/codec-digest.d.ts +29 -0
  349. package/dist/core/presence/codec-digest.d.ts.map +1 -0
  350. package/dist/core/presence/codec-digest.js +170 -0
  351. package/dist/core/presence/index.js +15 -1
  352. package/dist/core/presence/relay-client.d.ts +1 -1
  353. package/dist/core/presence/relay-client.d.ts.map +1 -1
  354. package/dist/core/presence/relay-client.js +5 -5
  355. package/dist/core/work/index.d.ts +1 -1
  356. package/dist/core/work/index.d.ts.map +1 -1
  357. package/dist/core/work/index.js +1 -1
  358. package/dist/core/work/read.d.ts +1 -1
  359. package/dist/core/work/read.d.ts.map +1 -1
  360. package/dist/core/work/read.js +1 -1
  361. package/dist/core/work/runner.d.ts.map +1 -1
  362. package/dist/core/work/runner.js +6 -0
  363. package/dist/core/work/state.d.ts +9 -0
  364. package/dist/core/work/state.d.ts.map +1 -1
  365. package/dist/core/work/state.js +17 -3
  366. package/dist/core/workflow/engine.js +24 -25
  367. package/dist/core/workflow/live-session-v3.d.ts +19 -0
  368. package/dist/core/workflow/live-session-v3.d.ts.map +1 -0
  369. package/dist/core/workflow/live-session-v3.js +86 -0
  370. package/dist/core/workflow/scope-id.d.ts +3 -0
  371. package/dist/core/workflow/scope-id.d.ts.map +1 -0
  372. package/dist/core/workflow/scope-id.js +5 -0
  373. package/dist/core/workflow/workspaces/leases.d.ts +4 -0
  374. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  375. package/dist/core/workflow/workspaces/leases.js +53 -19
  376. package/dist/index.d.ts +1 -1
  377. package/dist/index.js +1 -1
  378. package/dist/lib/browser/client.d.ts +41 -0
  379. package/dist/lib/browser/client.d.ts.map +1 -1
  380. package/dist/lib/browser/client.js +203 -0
  381. package/dist/lib/browser/critique.d.ts +28 -2
  382. package/dist/lib/browser/critique.d.ts.map +1 -1
  383. package/dist/lib/browser/critique.js +74 -18
  384. package/dist/lib/browser/geometry.d.ts +1 -1
  385. package/dist/lib/browser/geometry.d.ts.map +1 -1
  386. package/dist/lib/browser/geometry.js +130 -6
  387. package/dist/lib/browser/index.d.ts +3 -0
  388. package/dist/lib/browser/index.d.ts.map +1 -1
  389. package/dist/lib/browser/index.js +3 -0
  390. package/dist/lib/browser/qa-plan.d.ts +174 -0
  391. package/dist/lib/browser/qa-plan.d.ts.map +1 -0
  392. package/dist/lib/browser/qa-plan.js +321 -0
  393. package/dist/lib/browser/qa-reuse.d.ts +99 -0
  394. package/dist/lib/browser/qa-reuse.d.ts.map +1 -0
  395. package/dist/lib/browser/qa-reuse.js +222 -0
  396. package/dist/lib/browser/qa-snapshot.d.ts +64 -0
  397. package/dist/lib/browser/qa-snapshot.d.ts.map +1 -0
  398. package/dist/lib/browser/qa-snapshot.js +171 -0
  399. package/dist/lib/browser/runts.d.ts.map +1 -1
  400. package/dist/lib/browser/runts.js +22 -1
  401. package/dist/lib/browser/tiling.d.ts +71 -0
  402. package/dist/lib/browser/tiling.d.ts.map +1 -0
  403. package/dist/lib/browser/tiling.js +118 -0
  404. package/dist/lib/context/index.d.ts +0 -3
  405. package/dist/lib/context/index.d.ts.map +1 -1
  406. package/dist/lib/context/index.js +20 -42
  407. package/dist/lib/council/index.d.ts +2 -2
  408. package/dist/lib/council/index.d.ts.map +1 -1
  409. package/dist/lib/council/index.js +9 -20
  410. package/dist/lib/docs-frontmatter.d.ts +1 -6
  411. package/dist/lib/docs-frontmatter.d.ts.map +1 -1
  412. package/dist/lib/docs-frontmatter.js +12 -78
  413. package/dist/lib/docs-index.d.ts +9 -0
  414. package/dist/lib/docs-index.d.ts.map +1 -1
  415. package/dist/lib/docs-index.js +32 -3
  416. package/dist/lib/docs-lint.d.ts.map +1 -1
  417. package/dist/lib/docs-lint.js +14 -30
  418. package/dist/lib/docs-metadata-audit.d.ts +19 -0
  419. package/dist/lib/docs-metadata-audit.d.ts.map +1 -0
  420. package/dist/lib/docs-metadata-audit.js +132 -0
  421. package/dist/lib/docs-metadata-managed.d.ts +23 -0
  422. package/dist/lib/docs-metadata-managed.d.ts.map +1 -0
  423. package/dist/lib/docs-metadata-managed.js +78 -0
  424. package/dist/lib/docs-metadata-sync.d.ts +20 -0
  425. package/dist/lib/docs-metadata-sync.d.ts.map +1 -0
  426. package/dist/lib/docs-metadata-sync.js +222 -0
  427. package/dist/lib/docs-metadata-v2-schema.d.ts +273 -0
  428. package/dist/lib/docs-metadata-v2-schema.d.ts.map +1 -0
  429. package/dist/lib/docs-metadata-v2-schema.js +134 -0
  430. package/dist/lib/docs-metadata-v2.d.ts +21 -0
  431. package/dist/lib/docs-metadata-v2.d.ts.map +1 -0
  432. package/dist/lib/docs-metadata-v2.js +424 -0
  433. package/dist/lib/docs-new.d.ts +11 -0
  434. package/dist/lib/docs-new.d.ts.map +1 -0
  435. package/dist/lib/docs-new.js +52 -0
  436. package/dist/lib/docs-sweep.d.ts.map +1 -1
  437. package/dist/lib/docs-sweep.js +45 -29
  438. package/dist/lib/headless/index.d.ts +98 -0
  439. package/dist/lib/headless/index.d.ts.map +1 -0
  440. package/dist/lib/headless/index.js +211 -0
  441. package/dist/lib/identities/assume.d.ts +4 -4
  442. package/dist/lib/identities/assume.d.ts.map +1 -1
  443. package/dist/lib/identities/assume.js +52 -68
  444. package/dist/lib/instructions/apply.d.ts +5 -4
  445. package/dist/lib/instructions/apply.d.ts.map +1 -1
  446. package/dist/lib/instructions/apply.js +32 -20
  447. package/dist/lib/instructions/bundle.d.ts +33 -0
  448. package/dist/lib/instructions/bundle.d.ts.map +1 -0
  449. package/dist/lib/instructions/bundle.js +149 -0
  450. package/dist/lib/instructions/splice.d.ts +0 -1
  451. package/dist/lib/instructions/splice.d.ts.map +1 -1
  452. package/dist/lib/instructions/splice.js +22 -6
  453. package/dist/lib/instructions/templates.d.ts +7 -5
  454. package/dist/lib/instructions/templates.d.ts.map +1 -1
  455. package/dist/lib/instructions/templates.js +79 -9
  456. package/package.json +27 -6
  457. package/schemas/config.schema.json +120 -2
  458. package/schemas/docs-metadata-v2.schema.json +441 -0
  459. package/schemas/event-v3.schema.json +24472 -0
  460. package/src/commander.ts +7 -1
  461. package/src/commands/adapter.ts +2 -2
  462. package/src/commands/agents.ts +1737 -570
  463. package/src/commands/artifacts.ts +7 -6
  464. package/src/commands/backup.ts +29 -21
  465. package/src/commands/browse.ts +349 -8
  466. package/src/commands/checkpoint.ts +4 -3
  467. package/src/commands/claude-desktop.ts +7 -5
  468. package/src/commands/context.ts +1 -6
  469. package/src/commands/decision.ts +27 -44
  470. package/src/commands/docs.ts +100 -33
  471. package/src/commands/doctor.ts +73 -8
  472. package/src/commands/events.ts +256 -0
  473. package/src/commands/governor.ts +18 -4
  474. package/src/commands/init.ts +264 -57
  475. package/src/commands/instructions.ts +129 -0
  476. package/src/commands/journal.ts +4 -13
  477. package/src/commands/ledger-v3.ts +111 -0
  478. package/src/commands/presence.ts +18 -11
  479. package/src/commands/sync.ts +1 -1
  480. package/src/commands/web.ts +67 -8
  481. package/src/commands/work.ts +28 -5
  482. package/src/core/adapter.ts +5 -0
  483. package/src/core/adapters/attest.ts +8 -3
  484. package/src/core/adapters/profiles.ts +4 -6
  485. package/src/core/agents/canonical-emit.ts +39 -28
  486. package/src/core/agents/cli.ts +242 -263
  487. package/src/core/agents/codex-archive-v3.ts +138 -0
  488. package/src/core/agents/coord-client.ts +75 -177
  489. package/src/core/agents/finalization.ts +120 -38
  490. package/src/core/agents/index.ts +5 -5
  491. package/src/core/agents/live-authority-v3.ts +375 -0
  492. package/src/core/agents/live-lifecycle-v3.ts +140 -0
  493. package/src/core/agents/live-observation-v3.ts +216 -0
  494. package/src/core/agents/render/prompt-context.ts +89 -55
  495. package/src/core/agents/render/session-context.ts +23 -32
  496. package/src/core/agents/rules/claim-conflict.ts +85 -126
  497. package/src/core/agents/rules/commit-conflict.ts +3 -14
  498. package/src/core/agents/rules/stop-hook.ts +239 -293
  499. package/src/core/agents/session-events.ts +137 -104
  500. package/src/core/agents/session-finalization-state-v3.ts +90 -0
  501. package/src/core/agents/session-finalizer-v3.ts +953 -0
  502. package/src/core/agents/session-name-display.ts +149 -0
  503. package/src/core/agents/state/activity-log.ts +6 -14
  504. package/src/core/agents/state/heartbeat-writer.ts +98 -242
  505. package/src/core/agents/state/live-coordination-view.ts +290 -0
  506. package/src/core/agents/state/session-state.ts +143 -0
  507. package/src/core/agents/state/stale-sweep.ts +40 -17
  508. package/src/core/artifacts/index.ts +4 -5
  509. package/src/core/config.ts +148 -3
  510. package/src/core/context/index.ts +9 -17
  511. package/src/core/events/span-parent.ts +46 -0
  512. package/src/core/events/v3/advance.ts +168 -0
  513. package/src/core/events/v3/authority-outbox.ts +548 -0
  514. package/src/core/events/v3/base-builder.ts +60 -0
  515. package/src/core/events/v3/base-contract.ts +882 -0
  516. package/src/core/events/v3/bootstrap.ts +226 -0
  517. package/src/core/events/v3/builder.ts +59 -0
  518. package/src/core/events/v3/canonical.ts +153 -0
  519. package/src/core/events/v3/capabilities.ts +171 -0
  520. package/src/core/events/v3/capability-drift.ts +177 -0
  521. package/src/core/events/v3/catalog.ts +202 -0
  522. package/src/core/events/v3/clock-order.ts +41 -0
  523. package/src/core/events/v3/contract.ts +365 -0
  524. package/src/core/events/v3/control.ts +576 -0
  525. package/src/core/events/v3/coordination-view.ts +193 -0
  526. package/src/core/events/v3/economics.ts +384 -0
  527. package/src/core/events/v3/finalization-view.ts +67 -0
  528. package/src/core/events/v3/fingerprint-keys.ts +260 -0
  529. package/src/core/events/v3/generated.ts +48 -0
  530. package/src/core/events/v3/ids.ts +33 -0
  531. package/src/core/events/v3/index.ts +322 -0
  532. package/src/core/events/v3/latency.ts +551 -0
  533. package/src/core/events/v3/live-feed.ts +319 -0
  534. package/src/core/events/v3/live-observation.ts +39 -0
  535. package/src/core/events/v3/live-routing.ts +135 -0
  536. package/src/core/events/v3/producers/command-recorder.ts +631 -0
  537. package/src/core/events/v3/producers/command.ts +201 -0
  538. package/src/core/events/v3/producers/coordination-recorder.ts +538 -0
  539. package/src/core/events/v3/producers/coordination.ts +355 -0
  540. package/src/core/events/v3/producers/hook-base.ts +578 -0
  541. package/src/core/events/v3/producers/hook.ts +119 -0
  542. package/src/core/events/v3/producers/intake.ts +319 -0
  543. package/src/core/events/v3/producers/recorder.ts +2805 -0
  544. package/src/core/events/v3/projection.ts +653 -0
  545. package/src/core/events/v3/reader.ts +1127 -0
  546. package/src/core/events/v3/recovery-record.ts +244 -0
  547. package/src/core/events/v3/recovery.ts +271 -0
  548. package/src/core/events/v3/span-state.ts +187 -0
  549. package/src/core/events/v3/targets.ts +182 -0
  550. package/src/core/events/v3/turn-telemetry.ts +259 -0
  551. package/src/core/events/v3/validate.ts +361 -0
  552. package/src/core/events/v3/writer.ts +419 -0
  553. package/src/core/governor/index.ts +3 -0
  554. package/src/core/governor/plan-api.ts +10 -2
  555. package/src/core/governor/read.ts +3 -0
  556. package/src/core/governor/state.ts +52 -5
  557. package/src/core/guard/config.ts +206 -0
  558. package/src/core/guard/coordinator.ts +227 -0
  559. package/src/core/guard/evaluator.ts +343 -0
  560. package/src/core/guard/evidence-v3.ts +227 -0
  561. package/src/core/guard/index.ts +35 -0
  562. package/src/core/guard/live-source-v3.ts +204 -0
  563. package/src/core/guard/storage.ts +188 -0
  564. package/src/core/guard/types.ts +152 -0
  565. package/src/core/hooks/adapter/detect.ts +28 -1
  566. package/src/core/hooks/adapter/events.ts +15 -6
  567. package/src/core/hooks/adapter/output.ts +7 -2
  568. package/src/core/hooks/adapter/parse.ts +81 -36
  569. package/src/core/hooks/adapter/wiring.ts +119 -13
  570. package/src/core/hooks/cli.ts +398 -422
  571. package/src/core/hooks/codex-authorization.ts +253 -0
  572. package/src/core/hooks/effects/image-capture.ts +228 -238
  573. package/src/core/hooks/effects/index.ts +1 -36
  574. package/src/core/hooks/events/input-hash.ts +47 -0
  575. package/src/core/hooks/index.ts +4 -11
  576. package/src/core/hooks/resolve/coord-root.ts +1 -1
  577. package/src/core/hooks/resolve/owner.ts +52 -45
  578. package/src/core/hooks/resolve/transcript.ts +165 -14
  579. package/src/core/hooks/session-name-presence.ts +14 -15
  580. package/src/core/hooks/unsafe-cross-shell.ts +1 -1
  581. package/src/core/journal/index.ts +15 -22
  582. package/src/core/presence/blob.ts +63 -38
  583. package/src/core/presence/codec-digest.ts +215 -0
  584. package/src/core/presence/index.ts +18 -1
  585. package/src/core/presence/relay-client.ts +5 -5
  586. package/src/core/work/index.ts +3 -0
  587. package/src/core/work/read.ts +3 -0
  588. package/src/core/work/runner.ts +6 -0
  589. package/src/core/work/state.ts +27 -3
  590. package/src/core/workflow/engine.ts +24 -24
  591. package/src/core/workflow/live-session-v3.ts +126 -0
  592. package/src/core/workflow/scope-id.ts +6 -0
  593. package/src/core/workflow/workspaces/leases.ts +56 -19
  594. package/src/index.ts +1 -1
  595. package/src/lib/browser/client.ts +230 -0
  596. package/src/lib/browser/critique.ts +104 -27
  597. package/src/lib/browser/geometry.ts +143 -14
  598. package/src/lib/browser/index.ts +34 -0
  599. package/src/lib/browser/qa-plan.ts +501 -0
  600. package/src/lib/browser/qa-reuse.ts +306 -0
  601. package/src/lib/browser/qa-snapshot.ts +223 -0
  602. package/src/lib/browser/runts.ts +27 -2
  603. package/src/lib/browser/tiling.ts +164 -0
  604. package/src/lib/context/index.ts +22 -46
  605. package/src/lib/council/index.ts +9 -21
  606. package/src/lib/docs-frontmatter.ts +8 -79
  607. package/src/lib/docs-index.ts +33 -3
  608. package/src/lib/docs-lint.ts +15 -29
  609. package/src/lib/docs-metadata-audit.ts +166 -0
  610. package/src/lib/docs-metadata-managed.ts +83 -0
  611. package/src/lib/docs-metadata-sync.ts +262 -0
  612. package/src/lib/docs-metadata-v2-schema.ts +138 -0
  613. package/src/lib/docs-metadata-v2.ts +646 -0
  614. package/src/lib/docs-new.ts +63 -0
  615. package/src/lib/docs-sweep.ts +42 -28
  616. package/src/lib/headless/index.ts +287 -0
  617. package/src/lib/identities/assume.ts +67 -86
  618. package/src/lib/instructions/apply.ts +33 -20
  619. package/src/lib/instructions/bundle.ts +199 -0
  620. package/src/lib/instructions/splice.ts +21 -7
  621. package/src/lib/instructions/templates.ts +85 -13
  622. package/dist/core/agents/cli-emit.d.ts +0 -27
  623. package/dist/core/agents/cli-emit.d.ts.map +0 -1
  624. package/dist/core/agents/cli-emit.js +0 -57
  625. package/dist/core/agents/codex-replay.d.ts +0 -29
  626. package/dist/core/agents/codex-replay.d.ts.map +0 -1
  627. package/dist/core/agents/codex-replay.js +0 -138
  628. package/dist/core/agents/events/consume.d.ts +0 -82
  629. package/dist/core/agents/events/consume.d.ts.map +0 -1
  630. package/dist/core/agents/events/consume.js +0 -195
  631. package/dist/core/agents/events/emit.d.ts +0 -42
  632. package/dist/core/agents/events/emit.d.ts.map +0 -1
  633. package/dist/core/agents/events/emit.js +0 -75
  634. package/dist/core/agents/events/ulid.d.ts +0 -11
  635. package/dist/core/agents/events/ulid.d.ts.map +0 -1
  636. package/dist/core/agents/events/ulid.js +0 -47
  637. package/dist/core/agents/state/heartbeat-projector.d.ts +0 -65
  638. package/dist/core/agents/state/heartbeat-projector.d.ts.map +0 -1
  639. package/dist/core/agents/state/heartbeat-projector.js +0 -527
  640. package/dist/core/hooks/events/emit.d.ts +0 -31
  641. package/dist/core/hooks/events/emit.d.ts.map +0 -1
  642. package/dist/core/hooks/events/emit.js +0 -93
  643. package/dist/core/hooks/events/rotate.d.ts +0 -43
  644. package/dist/core/hooks/events/rotate.d.ts.map +0 -1
  645. package/dist/core/hooks/events/rotate.js +0 -142
  646. package/dist/core/hooks/events/schema.d.ts +0 -312
  647. package/dist/core/hooks/events/schema.d.ts.map +0 -1
  648. package/dist/core/hooks/events/schema.js +0 -12
  649. package/dist/core/hooks/events/ulid.d.ts +0 -10
  650. package/dist/core/hooks/events/ulid.d.ts.map +0 -1
  651. package/dist/core/hooks/events/ulid.js +0 -47
  652. package/dist/lib/docs-frontmatter-migrate.d.ts +0 -33
  653. package/dist/lib/docs-frontmatter-migrate.d.ts.map +0 -1
  654. package/dist/lib/docs-frontmatter-migrate.js +0 -364
  655. package/src/core/agents/cli-emit.ts +0 -64
  656. package/src/core/agents/codex-replay.ts +0 -163
  657. package/src/core/agents/events/consume.ts +0 -254
  658. package/src/core/agents/events/emit.ts +0 -115
  659. package/src/core/agents/events/ulid.ts +0 -51
  660. package/src/core/agents/state/heartbeat-projector.ts +0 -582
  661. package/src/core/hooks/events/emit.ts +0 -125
  662. package/src/core/hooks/events/rotate.ts +0 -151
  663. package/src/core/hooks/events/schema.ts +0 -538
  664. package/src/core/hooks/events/ulid.ts +0 -51
  665. package/src/lib/docs-frontmatter-migrate.ts +0 -427
@@ -0,0 +1,140 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { Adapter } from "../adapter.ts";
3
+ import { buildEventV3 } from "../events/v3/builder.ts";
4
+ import { normalizeNativeIdV3 } from "../events/v3/canonical.ts";
5
+ import type { EventV3 } from "../events/v3/contract.ts";
6
+ import { readEventV3ControlState } from "../events/v3/control.ts";
7
+ import { fingerprintContextV3 } from "../events/v3/fingerprint-keys.ts";
8
+ import {
9
+ liveInstanceIdV3,
10
+ livePlatformV3,
11
+ resolveLiveEventLedgerRouteV3,
12
+ } from "../events/v3/live-routing.ts";
13
+ import { readHookProducerStateV3 } from "../events/v3/producers/recorder.ts";
14
+ import { writeEventV3 } from "../events/v3/writer.ts";
15
+ import { LiveCoordinationAuthorityV3Error } from "./live-authority-v3.ts";
16
+ import { liveCoordinationAdapterV3 } from "./state/live-coordination-view.ts";
17
+
18
+ export type LiveLifecycleObservationV3Result = { state: "recorded"; event: EventV3 };
19
+
20
+ interface LiveLifecycleObservationBaseV3 {
21
+ coordRoot: string;
22
+ owner: string;
23
+ nativeSessionId: string;
24
+ adapter: Adapter;
25
+ observedAt?: string;
26
+ }
27
+
28
+ export function recordLiveSweepObservationV3(
29
+ input: LiveLifecycleObservationBaseV3 & {
30
+ observation: "stale_heartbeat" | "killed" | "unparseable_heartbeat" | "missing_timestamp";
31
+ ageMs: number;
32
+ },
33
+ ): LiveLifecycleObservationV3Result {
34
+ return recordObservation(input, "lifecycle.sweep_observed", {
35
+ subject_instance_id: liveInstanceIdV3(input.owner),
36
+ observation: input.observation,
37
+ provisional: true,
38
+ age_ms: Math.max(0, Math.floor(input.ageMs)),
39
+ });
40
+ }
41
+
42
+ export function recordLiveResumeObservationV3(
43
+ input: LiveLifecycleObservationBaseV3,
44
+ ): LiveLifecycleObservationV3Result {
45
+ const normalized = withAttestedAdapter(input);
46
+ const hook = requireHookState(normalized);
47
+ return recordObservation(normalized, "session.resumed", {
48
+ prior_generation_id: hook.generation_id,
49
+ continuity: "native",
50
+ evidence_reference: "heartbeat_recreated",
51
+ });
52
+ }
53
+
54
+ function recordObservation<T extends "lifecycle.sweep_observed" | "session.resumed">(
55
+ input: LiveLifecycleObservationBaseV3,
56
+ eventType: T,
57
+ payload: T extends "lifecycle.sweep_observed"
58
+ ? {
59
+ subject_instance_id: `inst_${string}`;
60
+ observation: string;
61
+ provisional: true;
62
+ age_ms: number;
63
+ }
64
+ : {
65
+ prior_generation_id: `gen_${string}`;
66
+ continuity: "native";
67
+ evidence_reference: string;
68
+ },
69
+ ): LiveLifecycleObservationV3Result {
70
+ const route = resolveLiveEventLedgerRouteV3(input.coordRoot);
71
+ if (route.state === "blocked") throw new LiveCoordinationAuthorityV3Error(route.reason);
72
+ input = withAttestedAdapter(input);
73
+ const control = readEventV3ControlState(input.coordRoot);
74
+ if (control.state !== route.mode) {
75
+ throw new LiveCoordinationAuthorityV3Error("control_state_changed");
76
+ }
77
+ const hook = requireHookState(input);
78
+ if (!hook.last_event_id) throw new LiveCoordinationAuthorityV3Error("generation_has_no_event");
79
+ const rootId = control.genesis.event.scope.root_id as `root_${string}`;
80
+ const context = fingerprintContextV3(
81
+ input.coordRoot,
82
+ rootId,
83
+ hook.generation_id,
84
+ control.genesis.profile.privacy_key_epoch,
85
+ );
86
+ const nativeObservationId = `${eventType}\0${input.owner}\0${input.observedAt ?? "now"}\0${JSON.stringify(payload)}`;
87
+ const common = {
88
+ producer: {
89
+ producer_id: "prd_agent-coord-lifecycle" as const,
90
+ boot_id: `boot_${randomUUID()}` as const,
91
+ sequence: 1,
92
+ component: "agent-coord" as const,
93
+ build_id: route.build_id,
94
+ platform: livePlatformV3(),
95
+ },
96
+ scope: {
97
+ root_id: rootId,
98
+ instance_id: hook.instance_id,
99
+ session_id: hook.session_id,
100
+ generation_id: hook.generation_id,
101
+ },
102
+ attestation_id: hook.attestation_id,
103
+ links: { caused_by: [hook.last_event_id] },
104
+ provenance: {
105
+ source_event: `agent-coord.${eventType}`,
106
+ attestation: "derived" as const,
107
+ confidence: "exact" as const,
108
+ source_record_id: normalizeNativeIdV3(
109
+ context,
110
+ `agent-coord.${eventType}`,
111
+ nativeObservationId,
112
+ ),
113
+ attribution: {
114
+ method: "heartbeat_match" as const,
115
+ state: "verified" as const,
116
+ observer_instance_id: hook.instance_id,
117
+ subject_instance_id: hook.instance_id,
118
+ },
119
+ },
120
+ observed_at: input.observedAt,
121
+ monotonic_ns: process.hrtime.bigint().toString(),
122
+ };
123
+ const event = buildEventV3(eventType, { ...common, payload } as never) as EventV3;
124
+ writeEventV3(input.coordRoot, event);
125
+ return { state: "recorded", event };
126
+ }
127
+
128
+ function withAttestedAdapter<T extends LiveLifecycleObservationBaseV3>(input: T): T {
129
+ const adapter = liveCoordinationAdapterV3(input.coordRoot, input.owner);
130
+ if (!adapter) return input;
131
+ return { ...input, adapter };
132
+ }
133
+
134
+ function requireHookState(input: LiveLifecycleObservationBaseV3) {
135
+ const hook = readHookProducerStateV3(input.coordRoot, input.adapter, input.nativeSessionId);
136
+ if (!hook || hook.instance_id !== liveInstanceIdV3(input.owner)) {
137
+ throw new LiveCoordinationAuthorityV3Error("hook_generation_not_joinable");
138
+ }
139
+ return hook;
140
+ }
@@ -0,0 +1,216 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { Adapter } from "../adapter.ts";
3
+ import { buildEventV3 } from "../events/v3/builder.ts";
4
+ import {
5
+ canonicalJsonV3,
6
+ fingerprintV3,
7
+ normalizeNativeIdV3,
8
+ sha256V3,
9
+ } from "../events/v3/canonical.ts";
10
+ import type { EventTypeV3, EventV3 } from "../events/v3/contract.ts";
11
+ import { readEventV3ControlState } from "../events/v3/control.ts";
12
+ import { readCoordinationViewV3 } from "../events/v3/coordination-view.ts";
13
+ import { fingerprintContextV3 } from "../events/v3/fingerprint-keys.ts";
14
+ import type { LiveCoordinationObservationV3 } from "../events/v3/live-observation.ts";
15
+ import {
16
+ liveInstanceIdV3,
17
+ livePlatformV3,
18
+ resolveLiveEventLedgerRouteV3,
19
+ } from "../events/v3/live-routing.ts";
20
+ import { readHookProducerStateV3 } from "../events/v3/producers/recorder.ts";
21
+ import { writeEventV3 } from "../events/v3/writer.ts";
22
+ import { LiveCoordinationAuthorityV3Error } from "./live-authority-v3.ts";
23
+ import { liveCoordinationAdapterV3 } from "./state/live-coordination-view.ts";
24
+
25
+ export type { LiveCoordinationObservationV3 } from "../events/v3/live-observation.ts";
26
+
27
+ export interface RecordLiveCoordinationObservationV3Input {
28
+ coordRoot: string;
29
+ owner: string;
30
+ nativeSessionId: string;
31
+ adapter: Adapter;
32
+ observation: LiveCoordinationObservationV3;
33
+ observationId?: string;
34
+ observedAt?: string;
35
+ }
36
+
37
+ export interface RecordLiveCoordinationObservationV3Result {
38
+ state: "recorded";
39
+ event: EventV3;
40
+ }
41
+
42
+ /**
43
+ * Append a non-authority coordination observation against one attested live
44
+ * generation. Raw message and record bodies are used only to derive keyed
45
+ * fingerprints or content digests; they never enter the ledger payload.
46
+ */
47
+ export function recordLiveCoordinationObservationV3(
48
+ input: RecordLiveCoordinationObservationV3Input,
49
+ ): RecordLiveCoordinationObservationV3Result {
50
+ const route = resolveLiveEventLedgerRouteV3(input.coordRoot);
51
+ if (route.state === "blocked") throw new LiveCoordinationAuthorityV3Error(route.reason);
52
+ const control = readEventV3ControlState(input.coordRoot);
53
+ if (control.state !== route.mode) {
54
+ throw new LiveCoordinationAuthorityV3Error("control_state_changed");
55
+ }
56
+ const adapter = liveCoordinationAdapterV3(input.coordRoot, input.owner) ?? input.adapter;
57
+ const hook = readHookProducerStateV3(input.coordRoot, adapter, input.nativeSessionId);
58
+ if (
59
+ !hook ||
60
+ hook.terminal ||
61
+ !hook.last_event_id ||
62
+ hook.instance_id !== liveInstanceIdV3(input.owner)
63
+ ) {
64
+ throw new LiveCoordinationAuthorityV3Error("hook_generation_not_joinable");
65
+ }
66
+
67
+ const rootId = control.genesis.event.scope.root_id as `root_${string}`;
68
+ const fingerprintContext = fingerprintContextV3(
69
+ input.coordRoot,
70
+ rootId,
71
+ hook.generation_id,
72
+ control.genesis.profile.privacy_key_epoch,
73
+ );
74
+ const observationId = input.observationId ?? `${input.observation.event_type}-${randomUUID()}`;
75
+ const subject =
76
+ "subject" in input.observation && input.observation.subject
77
+ ? liveInstanceIdV3(input.observation.subject)
78
+ : hook.instance_id;
79
+ const common = {
80
+ producer: {
81
+ producer_id: "prd_agent-coord-observation" as const,
82
+ boot_id: `boot_${randomUUID()}` as const,
83
+ sequence: 1,
84
+ component: "agent-coord" as const,
85
+ build_id: route.build_id,
86
+ platform: livePlatformV3(),
87
+ },
88
+ scope: {
89
+ root_id: rootId,
90
+ instance_id: hook.instance_id,
91
+ session_id: hook.session_id,
92
+ generation_id: hook.generation_id,
93
+ },
94
+ attestation_id: hook.attestation_id,
95
+ links: { caused_by: [hook.last_event_id] },
96
+ provenance: {
97
+ source_event: `agent-coord.${input.observation.event_type}`,
98
+ attestation: "derived" as const,
99
+ confidence: "exact" as const,
100
+ source_record_id: normalizeNativeIdV3(
101
+ fingerprintContext,
102
+ "agent-coord.observation",
103
+ observationId,
104
+ ),
105
+ attribution: {
106
+ method: "session_env" as const,
107
+ state: "verified" as const,
108
+ observer_instance_id: hook.instance_id,
109
+ subject_instance_id: subject,
110
+ },
111
+ },
112
+ observed_at: input.observedAt,
113
+ monotonic_ns: process.hrtime.bigint().toString(),
114
+ };
115
+
116
+ const observation = input.observation;
117
+ let event: EventV3;
118
+ switch (observation.event_type) {
119
+ case "coord.status_observed":
120
+ event = buildEventV3("coord.status_observed", {
121
+ ...common,
122
+ payload: {
123
+ observer_instance_id: hook.instance_id,
124
+ subject_instance_id: subject,
125
+ status: safeToken(observation.status),
126
+ },
127
+ });
128
+ break;
129
+ case "coord.presence_changed": {
130
+ // The ledger projection, not the independently versioned presence file,
131
+ // owns the canonical prior state. Omitting an unknown first prior keeps
132
+ // the transition honest and avoids manufacturing a history.
133
+ const projectedPrior = readCoordinationViewV3(input.coordRoot).instances[hook.instance_id]
134
+ ?.presence_state;
135
+ event = buildEventV3("coord.presence_changed", {
136
+ ...common,
137
+ payload: {
138
+ actor_instance_id: hook.instance_id,
139
+ subject_instance_id: hook.instance_id,
140
+ ...(projectedPrior ? { prior_state: safeToken(projectedPrior) } : {}),
141
+ new_state: safeToken(observation.new_state),
142
+ reason: reasonCode(observation.reason),
143
+ authority: {},
144
+ },
145
+ });
146
+ break;
147
+ }
148
+ case "coord.message_observed":
149
+ event = buildEventV3("coord.message_observed", {
150
+ ...common,
151
+ payload: {
152
+ message_id: safeToken(observation.message_id ?? `msg_${randomUUID()}`),
153
+ direction: observation.direction,
154
+ peer_instance_id: subject,
155
+ body_length: Buffer.byteLength(observation.body, "utf8"),
156
+ body_fingerprint: fingerprintV3(
157
+ fingerprintContext,
158
+ "coord.message.body",
159
+ observation.body,
160
+ "generation",
161
+ ),
162
+ },
163
+ });
164
+ break;
165
+ case "council.state_changed":
166
+ event = buildEventV3("council.state_changed", {
167
+ ...common,
168
+ payload: {
169
+ council_id: safeToken(observation.council_id),
170
+ ...(observation.prior_state ? { prior_state: safeToken(observation.prior_state) } : {}),
171
+ new_state: safeToken(observation.new_state),
172
+ record_digest: sha256V3(canonicalJsonV3(observation.record)),
173
+ },
174
+ });
175
+ break;
176
+ case "decision.state_changed": {
177
+ const projectedDecisionPrior = readCoordinationViewV3(input.coordRoot).decisions[
178
+ observation.decision_id
179
+ ];
180
+ event = buildEventV3("decision.state_changed", {
181
+ ...common,
182
+ payload: {
183
+ decision_id: safeToken(observation.decision_id),
184
+ ...(projectedDecisionPrior ? { prior_state: safeToken(projectedDecisionPrior) } : {}),
185
+ new_state: safeToken(observation.new_state),
186
+ record_digest: sha256V3(canonicalJsonV3(observation.record)),
187
+ authority: { record_id: safeToken(observation.decision_id) },
188
+ },
189
+ });
190
+ break;
191
+ }
192
+ }
193
+ writeEventV3(input.coordRoot, event);
194
+ return { state: "recorded", event };
195
+ }
196
+
197
+ function safeToken(value: string): string {
198
+ const normalized = value.normalize("NFC");
199
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,127}$/.test(normalized)) {
200
+ throw new LiveCoordinationAuthorityV3Error(`invalid_safe_token:${value}`);
201
+ }
202
+ return normalized;
203
+ }
204
+
205
+ function reasonCode(value: string): string {
206
+ const normalized = value.normalize("NFC");
207
+ if (!/^[a-z0-9][a-z0-9._-]{0,79}$/.test(normalized)) {
208
+ throw new LiveCoordinationAuthorityV3Error(`invalid_reason_code:${value}`);
209
+ }
210
+ return normalized;
211
+ }
212
+
213
+ export type LiveCoordinationObservationEventTypeV3 = Extract<
214
+ EventTypeV3,
215
+ LiveCoordinationObservationV3["event_type"]
216
+ >;
@@ -3,7 +3,7 @@
3
3
  * council-pending hash-dedup, the cross-adapter first-session naming nudge,
4
4
  * the Cursor/Codex set-task staleness nudge, and the Codex status-footer
5
5
  * reminder.
6
- * agent-hook's user_prompt.submit post-emit handler calls this
6
+ * agent-hook's turn.started post-emit handler calls this
7
7
  * and forwards the result as the adapter-shaped additionalContext payload.
8
8
  *
9
9
  * Four subsections combined into one additionalContext payload:
@@ -12,9 +12,9 @@
12
12
  * + sorted(files_touched) + platform, sorted by instance_id).
13
13
  * 2. Council pending: pending open-council IDs hashed; re-emits when the
14
14
  * ID set changes.
15
- * 3. Focus nudge. Until the session has produced its suggested name (first
16
- * non-empty set-task), tells every adapter — on every prompt, undeduped —
17
- * to reproduce set-task's suggested session name in a fenced code block.
15
+ * 3. Focus nudge. Until the session has displayed its current suggested
16
+ * name, tells every adapter on every prompt to send the exact fenced block
17
+ * before prose or another tool call.
18
18
  * Cursor/Codex additionally get the existing unset/stale-task reminder
19
19
  * because their Stop hooks do not enforce task declarations as reliably
20
20
  * as Claude Code's.
@@ -45,6 +45,12 @@ import { join } from "node:path";
45
45
  import { coordEnv } from "../../../lib/env.ts";
46
46
  import { endOfTurnStatusCommand, resolveBinName } from "../../config.ts";
47
47
  import { type RemoteMachine, readRemoteMachines } from "../../presence/index.ts";
48
+ import { sessionNameDisplayBlock, sessionNameDisplayPending } from "../session-name-display.ts";
49
+ import {
50
+ readLiveCoordinationRow,
51
+ readLiveCoordinationRows,
52
+ } from "../state/live-coordination-view.ts";
53
+ import type { AgentActivity, TaskState } from "../state/session-state.ts";
48
54
  import { formatPendingCouncils } from "./session-context.ts";
49
55
 
50
56
  interface HeartbeatRow {
@@ -54,14 +60,15 @@ interface HeartbeatRow {
54
60
  session_id?: string;
55
61
  started_at?: string;
56
62
  last_heartbeat?: string;
57
- last_tool?: string;
58
- last_tool_target?: string;
59
63
  files_touched?: string[];
60
64
  platform?: string;
61
65
  task?: string;
62
- task_updated_at?: string;
66
+ activity?: AgentActivity;
67
+ task_state?: TaskState;
68
+ task_state_reason?: string;
69
+ task_updated_at?: string | null;
63
70
  suggested_session_name?: string;
64
- turn_summary?: string;
71
+ session_name_seen_for?: string;
65
72
  workflow_run_id?: string;
66
73
  }
67
74
 
@@ -79,6 +86,12 @@ export interface PromptContextOpts {
79
86
  /** When true, append the non-deduplicated Codex status-footer reminder.
80
87
  * The caller enables this only for human-facing Codex sessions. */
81
88
  statusFooterNudge?: boolean;
89
+ /** Adapter id whose Stop hook ENFORCES the end-of-turn ritual (task_set +
90
+ * status check). When set, append a fresh per-prompt reminder of that
91
+ * contract so the model satisfies it before ending the turn instead of
92
+ * being bounced into a retry. Not hash-deduped, same rationale as the
93
+ * status footer. */
94
+ turnRitualNudge?: string;
82
95
  }
83
96
 
84
97
  /**
@@ -98,6 +111,7 @@ export function renderPromptContext(opts: PromptContextOpts): string {
98
111
  sessionNameNudge,
99
112
  taskNudge,
100
113
  statusFooterNudge,
114
+ turnRitualNudge,
101
115
  } = opts;
102
116
  const sections: string[] = [];
103
117
 
@@ -130,11 +144,43 @@ export function renderPromptContext(opts: PromptContextOpts): string {
130
144
  if (statusMsg) sections.push(statusMsg);
131
145
  }
132
146
 
147
+ // 5. Adapters whose Stop hook ENFORCES the turn ritual get a fresh reminder
148
+ // on every prompt. Reminding before the reply is drafted is far cheaper than
149
+ // the bounce-and-retry the Stop hook otherwise forces: enforcement-only
150
+ // compliance was measured missing on a double-digit share of turns. Not
151
+ // hash-deduped for the same reason as the Codex footer.
152
+ if (turnRitualNudge) {
153
+ const ritualMsg = renderTurnRitualReminder(coordRoot, instanceId, turnRitualNudge);
154
+ if (ritualMsg) sections.push(ritualMsg);
155
+ }
156
+
133
157
  return sections.join("\n\n");
134
158
  }
135
159
 
160
+ function renderTurnRitualReminder(
161
+ coordRoot: string,
162
+ selfInstanceId: string,
163
+ adapter: string,
164
+ ): string {
165
+ const hb = readLiveCoordinationRow(coordRoot, selfInstanceId);
166
+ if (!hb || hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id) return "";
167
+
168
+ const bin = resolveBinName(coordRoot);
169
+ const statusCommand = endOfTurnStatusCommand(coordRoot);
170
+ // Claude Code renders the status box only if the reply carries it; Cursor
171
+ // shows the box inline on its own, so it is never asked for a second copy.
172
+ const paste =
173
+ adapter === "claude-code"
174
+ ? " and paste its output verbatim in a fenced code block at the end of your reply"
175
+ : "";
176
+ return (
177
+ `Turn ritual (Stop-enforced): on any turn that uses tools, run \`${bin} agents set-task "<short focus>"\` at some point during the turn (required every turn, even when the focus is unchanged; pass an empty string when there is no meaningful focus), then make \`${statusCommand}\` your final tool call${paste}. ` +
178
+ "A turn that ends without these is bounced by the Stop hook and costs a retry."
179
+ );
180
+ }
181
+
136
182
  function renderStatusFooterReminder(coordRoot: string, selfInstanceId: string): string {
137
- const hb = readHeartbeat(join(coordRoot, ".harnery", "active", `${selfInstanceId}.json`));
183
+ const hb = readLiveCoordinationRow(coordRoot, selfInstanceId);
138
184
  if (!hb || hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id) return "";
139
185
 
140
186
  const statusCommand = endOfTurnStatusCommand(coordRoot);
@@ -156,14 +202,8 @@ function computeFocusNudgeIfChanged(
156
202
  selfInstanceId: string,
157
203
  opts: { sessionNameNudge: boolean; taskNudge: boolean },
158
204
  ): string {
159
- const hbPath = join(coordRoot, ".harnery", "active", `${selfInstanceId}.json`);
160
- if (!existsSync(hbPath)) return "";
161
- let hb: HeartbeatRow;
162
- try {
163
- hb = JSON.parse(readFileSync(hbPath, "utf8"));
164
- } catch {
165
- return "";
166
- }
205
+ const hb = readLiveCoordinationRow(coordRoot, selfInstanceId);
206
+ if (!hb) return "";
167
207
 
168
208
  const hashFile = join(coordRoot, ".harnery", `.last-task-nudge-hash.${selfInstanceId}`);
169
209
  // Subagents and workflow children have no human-owned session/tab to rename.
@@ -179,15 +219,17 @@ function computeFocusNudgeIfChanged(
179
219
  let message = "";
180
220
  let nudgeKind = "";
181
221
 
182
- if (opts.sessionNameNudge && !hb.suggested_session_name) {
222
+ const pendingSessionName = sessionNameDisplayPending(hb);
223
+ if (opts.sessionNameNudge && (!hb.suggested_session_name || pendingSessionName)) {
183
224
  // Keyed on "a name was ever produced", not task_updated_at: a bare clear
184
225
  // as the first declaration must not end the naming window.
185
226
  needsNudge = true;
186
227
  nudgeKind = "session-name";
187
- message =
188
- `This session has no name yet: run \`${bin} agents set-task "<2-5 word session topic>"\` as your first tool call. ` +
189
- "When it returns `first_of_session: true`, reproduce its `suggested_session_name` value by itself inside a fenced code block at the very top of your reply so the operator can one-click-copy it as the session/tab title. " +
190
- "Then continue with the task; that `set-task` also satisfies this turn's focus declaration.";
228
+ message = pendingSessionName
229
+ ? `This session name is still pending display. Before any prose or another tool call, send this exact block as your next assistant text:\n\n${sessionNameDisplayBlock(pendingSessionName)}`
230
+ : `This session has no name yet: run \`${bin} agents set-task "<2-5 word session topic>"\` as your first tool call. ` +
231
+ "When it returns `first_of_session: true`, send its `suggested_session_name` as the exact fenced block requested by the PostToolUse instruction before any prose or another tool call. " +
232
+ "Then continue with the task; that `set-task` also satisfies this turn's focus declaration.";
191
233
  } else if (opts.taskNudge && !taskValue) {
192
234
  needsNudge = true;
193
235
  nudgeKind = "task-unset";
@@ -237,25 +279,21 @@ function computePeerTableIfChanged(
237
279
  selfInstanceId: string,
238
280
  selfSessionIdFallback: string,
239
281
  ): string {
240
- const activeDir = join(coordRoot, ".harnery", "active");
241
- if (!existsSync(activeDir)) return "";
242
-
243
282
  // Read self heartbeat for session_id (group key); fall back to the caller's hint.
244
283
  let mySessionId = selfSessionIdFallback;
245
- const selfHb = readHeartbeat(join(activeDir, `${selfInstanceId}.json`));
284
+ const rows = readLiveCoordinationRows(coordRoot);
285
+ const selfHb = readLiveCoordinationRow(coordRoot, selfInstanceId);
246
286
  if (selfHb?.session_id) mySessionId = selfHb.session_id;
247
287
  if (!mySessionId) return "";
248
288
 
249
- // Collect peer heartbeats.
250
- const peers: HeartbeatRow[] = [];
251
- for (const f of readdirSync(activeDir)) {
252
- if (!f.endsWith(".json")) continue;
253
- const hb = readHeartbeat(join(activeDir, f));
254
- if (!hb?.instance_id || hb.instance_id === selfInstanceId) continue;
255
- peers.push(hb);
256
- }
289
+ // Collect peers from the selected ledger route. Under V3 this is the
290
+ // canonical projection, never the legacy active directory.
291
+ const peers: HeartbeatRow[] = rows.filter(
292
+ (heartbeat) => heartbeat.instance_id && heartbeat.instance_id !== selfInstanceId,
293
+ );
257
294
  // Cross-machine presence (ADR 0016): sessions on other machines, from the
258
- // locally-fetched presence refs. Advisory (no claim blocking in v1).
295
+ // locally-fetched presence refs. Advisory only; remote presence never grants
296
+ // claim authority.
259
297
  const remote = readRemoteMachinesSafe(coordRoot);
260
298
  if (peers.length === 0 && remote.length === 0) return "";
261
299
 
@@ -269,6 +307,9 @@ function computePeerTableIfChanged(
269
307
  started_at: p.started_at ?? null,
270
308
  files_touched: Array.from(p.files_touched ?? []).sort(),
271
309
  platform: p.platform ?? null,
310
+ activity: p.activity ?? "unknown",
311
+ task_state: p.task_state ?? "active",
312
+ task_state_reason: p.task_state_reason ?? null,
272
313
  }))
273
314
  .sort((a, b) => (a.instance_id ?? "").localeCompare(b.instance_id ?? ""));
274
315
  // Remote basis: machine + per-agent identity/task/files (published_at is
@@ -279,6 +320,9 @@ function computePeerTableIfChanged(
279
320
  instance_id: a.instance_id,
280
321
  name: a.name ?? null,
281
322
  task: a.task ?? null,
323
+ activity: a.activity,
324
+ task_state: a.task_state,
325
+ task_state_reason: a.task_state_reason ?? null,
282
326
  files_touched: Array.from(a.files_touched ?? []).sort(),
283
327
  })),
284
328
  }));
@@ -392,14 +436,6 @@ function computeCouncilPendingIfChanged(
392
436
 
393
437
  /* ---------- helpers ---------- */
394
438
 
395
- function readHeartbeat(path: string): HeartbeatRow | null {
396
- try {
397
- return JSON.parse(readFileSync(path, "utf8")) as HeartbeatRow;
398
- } catch {
399
- return null;
400
- }
401
- }
402
-
403
439
  function safeRead(path: string): string {
404
440
  try {
405
441
  return readFileSync(path, "utf8").trim();
@@ -493,17 +529,24 @@ function renderSubtable(
493
529
 
494
530
  function formatRow(r: HeartbeatRow & { display_files: string[] }, nowSec: number): string {
495
531
  const taskPart = r.task ? ` "${r.task.slice(0, 60)}"` : "";
532
+ const statePart = formatState(r);
496
533
  // Fall back started_at → last_heartbeat; if neither is a valid timestamp,
497
534
  // show "age unknown" rather than the epoch-derived "20608d ago" ghost.
498
535
  const startedSec = parseIsoSec(r.started_at) ?? parseIsoSec(r.last_heartbeat);
499
536
  const ageFrom = startedSec === null ? "age unknown" : fmtAge(nowSec - startedSec);
500
537
  const filesPart = fmtFiles(r.display_files);
501
- const lastActivity = fmtLastActivity(r, nowSec);
502
- const turnSummary = r.turn_summary ? `\n last turn: ${r.turn_summary.slice(0, 80)}` : "";
503
538
  // Prefer a short instance_id over a bare "unknown" so an incomplete row is
504
539
  // still identifiable.
505
540
  const label = r.name ?? (r.instance_id ? r.instance_id.slice(0, 8) : "unknown");
506
- return ` - agent-${label}${taskPart} (${ageFrom}, ${filesPart}${lastActivity})${turnSummary}`;
541
+ return ` - agent-${label}${taskPart} (${statePart}, ${ageFrom}, ${filesPart})`;
542
+ }
543
+
544
+ function formatState(r: HeartbeatRow): string {
545
+ const activity = r.activity ?? "unknown";
546
+ const lifecycle = r.task_state ?? "active";
547
+ const reason =
548
+ lifecycle === "blocked" && r.task_state_reason ? `: ${r.task_state_reason.slice(0, 80)}` : "";
549
+ return `activity=${activity}, lifecycle=${lifecycle}${reason}`;
507
550
  }
508
551
 
509
552
  function fmtFiles(files: string[]): string {
@@ -512,15 +555,6 @@ function fmtFiles(files: string[]): string {
512
555
  return `holds: ${files.slice(0, 3).join(", ")}, +${files.length - 3} more`;
513
556
  }
514
557
 
515
- function fmtLastActivity(r: HeartbeatRow, nowSec: number): string {
516
- if (!r.last_tool) return "";
517
- const lastTs = parseIsoSec(r.last_heartbeat) ?? parseIsoSec(r.started_at);
518
- const tail = r.last_tool_target ? ` ${r.last_tool_target.slice(0, 60)}` : "";
519
- // No valid timestamp → render the tool without an absurd age.
520
- if (lastTs === null) return `, last: ${r.last_tool}${tail}`;
521
- return `, last: ${r.last_tool}${tail} ${fmtAge(nowSec - lastTs)}`;
522
- }
523
-
524
558
  /** Epoch-seconds for an ISO string, or null when missing/unparseable (so callers
525
559
  * can render "age unknown" instead of an epoch-derived absurd age). */
526
560
  function parseIsoSec(iso: string | undefined): number | null {