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
@@ -1,23 +1,13 @@
1
1
  /**
2
- * Image-feed capture: a non-coordination side effect fired from the normalized
3
- * `agent-hook` tool handlers (see effects/index.ts for the family rationale).
2
+ * Privacy-safe image artifact capture for normalized tool hooks.
4
3
  *
5
- * When an agent VIEWS an image (Read tool on a `.png`/`.jpg`/…) or PRODUCES one
6
- * (a Bash command writes an image file via `harn browse`, `harn image`, `--diff`, …),
7
- * we content-address the bytes into `.harnery/images/<sha256>.<ext>` (dedup:
8
- * identical bytes collapse to one blob) and emit an `image.captured` event into
9
- * the canonical stream. The web image feed (`/images`) groups those events by
10
- * hash and streams them live over the existing SSE infra.
11
- *
12
- * Why this lives at the hook layer: it's the single adapter-agnostic chokepoint
13
- * that sees every tool call across Claude Code / Cursor / Codex with the full
14
- * `tool_input` (file paths) and `tool_response`. No per-command code needed.
15
- *
16
- * Everything here is best-effort and MUST NOT throw; callers wrap it in
17
- * try/catch + logError, matching every other effect.
4
+ * Image bytes are content-addressed under `.harnery/images/`. The canonical V3
5
+ * ledger receives an `artifact.observed` record after the source tool event is
6
+ * durable, so the web feed can recover provenance without retaining command,
7
+ * output, intent, or absolute paths.
18
8
  */
19
9
 
20
- import { createHash } from "node:crypto";
10
+ import { createHash, randomUUID } from "node:crypto";
21
11
  import {
22
12
  existsSync,
23
13
  mkdirSync,
@@ -28,185 +18,139 @@ import {
28
18
  statSync,
29
19
  writeFileSync,
30
20
  } from "node:fs";
31
- import { isAbsolute, join, resolve } from "node:path";
21
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
22
+ import { buildEventV3 } from "../../events/v3/builder.ts";
23
+ import type { EventV3 } from "../../events/v3/contract.ts";
24
+ import { writeEventV3 } from "../../events/v3/writer.ts";
32
25
  import type { ParsedPayload } from "../adapter/parse.ts";
33
- import { emit } from "../events/emit.ts";
34
- import type { Adapter } from "../events/schema.ts";
35
26
 
36
- /** Raster + vector image extensions the feed accepts. PDF is intentionally
37
- * excluded: book renders aren't an "image feed" and don't thumbnail inline. */
38
27
  const IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"]);
39
-
40
- /** Skip hashing files larger than this; screenshots are tens of KB; a huge
41
- * file mentioned in passing isn't worth the read. */
28
+ const MEDIA_TYPES: Record<string, string> = {
29
+ png: "image/png",
30
+ jpg: "image/jpeg",
31
+ jpeg: "image/jpeg",
32
+ gif: "image/gif",
33
+ webp: "image/webp",
34
+ bmp: "image/bmp",
35
+ svg: "image/svg+xml",
36
+ };
42
37
  const MAX_CAPTURE_BYTES = 25 * 1024 * 1024;
43
-
44
- /** A produced image must have been written within this window of the command
45
- * finishing, otherwise a path merely *mentioned* in output (an old baseline,
46
- * a doc reference) would be captured. Viewed images skip this gate. */
47
38
  const PRODUCED_MTIME_WINDOW_MS = 120_000;
39
+ const IMAGE_PATH_RE = /[\w./~@:+-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi;
48
40
 
49
- /** Matches image-ish paths in a command string or its output. Deliberately
50
- * permissive on the path body (`~`, `@`, `.`, `-`, `/`); existence + ext + the
51
- * mtime gate do the real filtering. */
52
- const IMAGE_PATH_RE = /[\w./~@+-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi;
53
-
54
- export interface CaptureContext {
55
- eventType: "tool.pre_use" | "tool.post_use";
56
- /** The built event data (tool_name, tool_input string, intent, tool_use_id). */
57
- data: Record<string, unknown>;
58
- /** The full parsed payload, used for the un-clamped command + tool_response. */
59
- payload: ParsedPayload | null;
60
- instanceId: string;
61
- sessionId: string;
62
- adapter: Adapter;
41
+ export interface CapturedImageArtifact {
42
+ hash: string;
43
+ ext: string;
44
+ bytes: number;
45
+ role: "viewed" | "produced";
46
+ workspace_path?: string;
63
47
  }
64
48
 
65
49
  interface Candidate {
66
- path: string; // resolved absolute path
67
- role: "viewed" | "produced";
68
- intent?: string;
69
- commandHead?: string;
50
+ path: string;
51
+ role: CapturedImageArtifact["role"];
70
52
  requireRecentMtime: boolean;
71
53
  }
72
54
 
73
- /**
74
- * Inspect one tool event for image references and capture any that resolve to
75
- * a real image on disk. Emits zero or more `image.captured` events.
76
- */
77
- export function captureImages(coordRoot: string, ctx: CaptureContext): void {
78
- const imagesDir = join(coordRoot, ".harnery", "images");
79
- const cwd = resolveCwd(ctx.payload);
80
- const toolName = String(ctx.data.tool_name ?? "");
81
-
82
- const candidates =
83
- ctx.eventType === "tool.pre_use"
84
- ? collectViewed(toolName, ctx.data, cwd)
85
- : collectProduced(toolName, ctx.payload, cwd);
86
-
87
- if (candidates.length === 0) return;
88
-
89
- for (const cand of candidates) {
90
- // Never re-capture our own blob store (would loop on Reads of the gallery).
91
- if (cand.path.startsWith(`${imagesDir}/`)) continue;
92
- const captured = captureOne(imagesDir, cand);
93
- if (!captured) continue;
94
- emit(coordRoot, {
95
- event_type: "image.captured",
96
- instance_id: ctx.instanceId,
97
- session_id: ctx.sessionId,
98
- adapter: ctx.adapter,
99
- data: {
100
- hash: captured.hash,
101
- ext: captured.ext,
102
- bytes: captured.bytes,
103
- role: cand.role,
104
- source_path: canonicalize(coordRoot, cand.path),
105
- tool_name: toolName,
106
- tool_use_id: ctx.data.tool_use_id as string | undefined,
107
- ...(cand.intent ? { intent: cand.intent } : {}),
108
- ...(cand.commandHead ? { command_head: cand.commandHead } : {}),
109
- },
110
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
111
- } as Parameters<typeof emit>[1]);
112
- }
113
- }
114
-
115
- /** Read tool → the file_path it's about to show (the "agent saw this" signal). */
116
- function collectViewed(toolName: string, data: Record<string, unknown>, cwd: string): Candidate[] {
117
- if (toolName !== "Read") return [];
118
- const input = parseToolInput(data.tool_input);
119
- const filePath = input?.file_path;
120
- if (typeof filePath !== "string" || !filePath) return [];
121
- if (!hasImageExt(filePath)) return [];
122
- return [
123
- {
124
- path: toAbsolute(filePath, cwd),
125
- role: "viewed",
126
- intent: typeof data.intent === "string" ? data.intent : undefined,
127
- requireRecentMtime: false,
128
- },
129
- ];
130
- }
131
-
132
- /** Bash tool → scan the command + its output for freshly-written image files. */
133
- function collectProduced(
134
- toolName: string,
55
+ /** Copy every qualifying image referenced by one tool hook into the blob store. */
56
+ export function captureImages(
57
+ coordRoot: string,
58
+ eventType: "tool.requested" | "tool.completed",
135
59
  payload: ParsedPayload | null,
136
- cwd: string,
137
- ): Candidate[] {
138
- if (toolName !== "Bash") return [];
139
- const command = bashCommand(payload);
140
- const responseText = stringifyResponse(payload?.tool_response);
141
- const commandHead = command ? command.slice(0, 120) : undefined;
142
-
60
+ ): CapturedImageArtifact[] {
61
+ const images = join(coordRoot, ".harnery", "images");
62
+ const cwd = payload?.cwd ? resolve(payload.cwd) : process.cwd();
63
+ const toolName = payload?.tool_name ?? "";
64
+ const candidates =
65
+ eventType === "tool.requested"
66
+ ? collectViewed(toolName, payload?.tool_input, cwd)
67
+ : collectProduced(toolName, payload, cwd);
68
+ const captured: CapturedImageArtifact[] = [];
143
69
  const seen = new Set<string>();
144
- const out: Candidate[] = [];
145
- for (const text of [command, responseText]) {
146
- if (!text) continue;
147
- for (const m of text.matchAll(IMAGE_PATH_RE)) {
148
- const raw = m[0];
149
- const abs = toAbsolute(raw, cwd);
150
- if (seen.has(abs)) continue;
151
- seen.add(abs);
152
- out.push({ path: abs, role: "produced", commandHead, requireRecentMtime: true });
153
- }
154
- }
155
- return out;
156
- }
157
70
 
158
- interface CapturedBlob {
159
- hash: string;
160
- ext: string;
161
- bytes: number;
71
+ for (const candidate of candidates) {
72
+ if (candidate.path.startsWith(`${images}${sep}`)) continue;
73
+ const blob = captureOne(images, candidate);
74
+ if (!blob) continue;
75
+ const dedupeKey = `${blob.hash}:${candidate.role}`;
76
+ if (seen.has(dedupeKey)) continue;
77
+ seen.add(dedupeKey);
78
+ const workspacePath = safeWorkspacePath(coordRoot, candidate.path);
79
+ captured.push({
80
+ ...blob,
81
+ role: candidate.role,
82
+ ...(workspacePath ? { workspace_path: workspacePath } : {}),
83
+ });
84
+ }
85
+ return captured;
162
86
  }
163
87
 
164
88
  /**
165
- * Validate the candidate on disk, hash it, and copy into the content-addressed
166
- * store (skipping the copy when the blob already exists). Returns null when the
167
- * candidate doesn't qualify.
89
+ * Emit content-addressed image references as canonical V3 artifact events.
90
+ * Each observation uses a one-event producer boot, which preserves independent
91
+ * producer sequence continuity while the ledger writer serializes appends.
168
92
  */
169
- function captureOne(imagesDir: string, cand: Candidate): CapturedBlob | null {
170
- let st: ReturnType<typeof statSync>;
171
- try {
172
- st = statSync(cand.path);
173
- } catch {
174
- return null; // path doesn't exist (common for "produced" false-positives)
175
- }
176
- if (!st.isFile() || st.size === 0 || st.size > MAX_CAPTURE_BYTES) return null;
177
- if (cand.requireRecentMtime && Date.now() - st.mtimeMs > PRODUCED_MTIME_WINDOW_MS) {
178
- return null; // mentioned but not freshly produced by this command
93
+ export function recordImageArtifactsV3(
94
+ coordRoot: string,
95
+ sourceEvent: EventV3,
96
+ artifacts: readonly CapturedImageArtifact[],
97
+ ): void {
98
+ if (!("generation_id" in sourceEvent.scope) || !sourceEvent.attestation_id) return;
99
+ for (const artifact of artifacts) {
100
+ writeEventV3(coordRoot, buildImageArtifactEventV3(sourceEvent, artifact));
179
101
  }
180
- const ext = extOf(cand.path);
181
- if (!ext || !IMAGE_EXTS.has(ext)) return null;
102
+ }
182
103
 
183
- let bytes: Buffer;
184
- try {
185
- bytes = readFileSync(cand.path);
186
- } catch {
187
- return null;
188
- }
189
- const hash = createHash("sha256").update(bytes).digest("hex");
190
- const dest = join(imagesDir, `${hash}.${ext}`);
191
- if (!existsSync(dest)) {
192
- try {
193
- mkdirSync(imagesDir, { recursive: true });
194
- const tmp = `${dest}.tmp.${process.pid}`;
195
- writeFileSync(tmp, bytes);
196
- renameSync(tmp, dest);
197
- } catch {
198
- return null; // couldn't store, don't emit a dangling event
199
- }
104
+ export function buildImageArtifactEventV3(
105
+ sourceEvent: EventV3,
106
+ artifact: CapturedImageArtifact,
107
+ ): EventV3 {
108
+ if (!("generation_id" in sourceEvent.scope) || !sourceEvent.attestation_id) {
109
+ throw new Error("image artifacts require a generation-scoped source event");
200
110
  }
201
- return { hash, ext, bytes: st.size };
111
+ return buildEventV3("artifact.observed", {
112
+ producer: {
113
+ producer_id: "prd_agent-hook-image-capture",
114
+ boot_id: `boot_${randomUUID()}`,
115
+ sequence: 1,
116
+ component: "agent-hook",
117
+ build_id: sourceEvent.producer.build_id,
118
+ platform: sourceEvent.producer.platform,
119
+ ...(sourceEvent.producer.bridge ? { bridge: sourceEvent.producer.bridge } : {}),
120
+ },
121
+ scope: {
122
+ root_id: sourceEvent.scope.root_id,
123
+ instance_id: sourceEvent.scope.instance_id,
124
+ session_id: sourceEvent.scope.session_id,
125
+ generation_id: sourceEvent.scope.generation_id,
126
+ ...(sourceEvent.scope.run_id ? { run_id: sourceEvent.scope.run_id } : {}),
127
+ ...(sourceEvent.scope.workflow_id ? { workflow_id: sourceEvent.scope.workflow_id } : {}),
128
+ ...(sourceEvent.scope.workflow_agent_id
129
+ ? { workflow_agent_id: sourceEvent.scope.workflow_agent_id }
130
+ : {}),
131
+ },
132
+ attestation_id: sourceEvent.attestation_id as `att_${string}`,
133
+ links: { caused_by: [sourceEvent.event_id] },
134
+ provenance: {
135
+ ...sourceEvent.provenance,
136
+ attestation: "derived",
137
+ confidence: "high",
138
+ },
139
+ payload: {
140
+ artifact: {
141
+ artifact_id: `art_${artifact.hash}`,
142
+ kind: "image",
143
+ media_type: MEDIA_TYPES[artifact.ext] ?? "image/png",
144
+ bytes: artifact.bytes,
145
+ retention_class: "bounded_local",
146
+ ...(artifact.workspace_path ? { workspace_path: artifact.workspace_path } : {}),
147
+ },
148
+ operation: artifact.role === "viewed" ? "viewed" : "created",
149
+ },
150
+ });
202
151
  }
203
152
 
204
- /**
205
- * Prune `.harnery/images/` past a size cap (default 2 GB) and an age cap
206
- * (default 30 days), oldest-mtime-first. Fired on session.start next to
207
- * journalJanitor. Pure-fs, fail-soft. Orphaned `image.captured` events whose
208
- * blob was pruned render as an "expired" placeholder in the gallery.
209
- */
153
+ /** Prune the bounded blob store by age and total size, oldest first. */
210
154
  export function imageJanitor(coordRoot: string): void {
211
155
  try {
212
156
  const dir = join(coordRoot, ".harnery", "images");
@@ -214,117 +158,163 @@ export function imageJanitor(coordRoot: string): void {
214
158
  const maxBytes = envInt("HARNERY_IMAGES_MAX_BYTES", 2 * 1024 * 1024 * 1024);
215
159
  const maxAgeMs = envInt("HARNERY_IMAGES_MAX_AGE_DAYS", 30) * 24 * 60 * 60 * 1000;
216
160
  const now = Date.now();
161
+ const entries: Array<{ path: string; size: number; mtimeMs: number }> = [];
217
162
 
218
- type Entry = { path: string; size: number; mtimeMs: number };
219
- const entries: Entry[] = [];
220
163
  for (const name of readdirSync(dir)) {
221
164
  if (name.endsWith(".tmp") || name.includes(".tmp.")) {
222
- // Orphaned temp from a crashed copy; sweep it.
223
165
  rmSync(join(dir, name), { force: true });
224
166
  continue;
225
167
  }
226
168
  const full = join(dir, name);
227
- let st: ReturnType<typeof statSync>;
228
169
  try {
229
- st = statSync(full);
170
+ const stat = statSync(full);
171
+ if (stat.isFile()) entries.push({ path: full, size: stat.size, mtimeMs: stat.mtimeMs });
230
172
  } catch {
231
- continue;
173
+ // A concurrent cleanup or write can make one entry disappear.
232
174
  }
233
- if (!st.isFile()) continue;
234
- entries.push({ path: full, size: st.size, mtimeMs: st.mtimeMs });
235
175
  }
236
176
 
237
- // Age cap.
238
177
  let total = 0;
239
- const survivors: Entry[] = [];
240
- for (const e of entries) {
241
- if (now - e.mtimeMs > maxAgeMs) {
242
- rmSync(e.path, { force: true });
243
- continue;
178
+ const survivors: typeof entries = [];
179
+ for (const entry of entries) {
180
+ if (now - entry.mtimeMs > maxAgeMs) {
181
+ rmSync(entry.path, { force: true });
182
+ } else {
183
+ total += entry.size;
184
+ survivors.push(entry);
244
185
  }
245
- total += e.size;
246
- survivors.push(e);
247
186
  }
248
-
249
- // Size cap: drop oldest first until under.
250
- if (total > maxBytes) {
251
- survivors.sort((a, b) => a.mtimeMs - b.mtimeMs);
252
- for (const e of survivors) {
253
- if (total <= maxBytes) break;
254
- rmSync(e.path, { force: true });
255
- total -= e.size;
256
- }
187
+ survivors.sort((a, b) => a.mtimeMs - b.mtimeMs);
188
+ for (const entry of survivors) {
189
+ if (total <= maxBytes) break;
190
+ rmSync(entry.path, { force: true });
191
+ total -= entry.size;
257
192
  }
258
193
  } catch {
259
- // best-effort
194
+ // Hook effects are always best-effort.
260
195
  }
261
196
  }
262
197
 
263
- /* ── helpers ─────────────────────────────────────────────────────────────── */
198
+ function collectViewed(toolName: string, toolInput: unknown, cwd: string): Candidate[] {
199
+ const name = normalizedToolName(toolName);
200
+ if (name !== "read" && name !== "view_image" && name !== "image") return [];
201
+ const input = objectInput(toolInput);
202
+ const filePath = input?.file_path ?? input?.path ?? input?.image_path;
203
+ if (typeof filePath !== "string" || !hasImageExt(filePath)) return [];
204
+ return [{ path: toAbsolute(filePath, cwd), role: "viewed", requireRecentMtime: false }];
205
+ }
264
206
 
265
- function envInt(name: string, fallback: number): number {
266
- const v = process.env[name];
267
- if (!v) return fallback;
268
- const n = Number(v);
269
- return Number.isFinite(n) && n > 0 ? n : fallback;
207
+ function collectProduced(
208
+ toolName: string,
209
+ payload: ParsedPayload | null,
210
+ cwd: string,
211
+ ): Candidate[] {
212
+ const name = normalizedToolName(toolName);
213
+ const producerTools = new Set([
214
+ "bash",
215
+ "shell",
216
+ "exec_command",
217
+ "imagegen",
218
+ "image_gen",
219
+ "browse",
220
+ "browse_ai",
221
+ "screenshot",
222
+ ]);
223
+ if (!producerTools.has(name)) return [];
224
+ const input = objectInput(payload?.tool_input);
225
+ const command = [input?.command, input?.cmd].find((value) => typeof value === "string") ?? "";
226
+ const response = stringify(payload?.tool_response).slice(0, 2_000_000);
227
+ const seen = new Set<string>();
228
+ const out: Candidate[] = [];
229
+ for (const text of [String(command), response]) {
230
+ for (const match of text.matchAll(IMAGE_PATH_RE)) {
231
+ const absolute = toAbsolute(match[0], cwd);
232
+ if (seen.has(absolute)) continue;
233
+ seen.add(absolute);
234
+ out.push({ path: absolute, role: "produced", requireRecentMtime: true });
235
+ }
236
+ }
237
+ return out;
270
238
  }
271
239
 
272
- function parseToolInput(raw: unknown): Record<string, unknown> | null {
273
- if (typeof raw !== "string") return null;
240
+ function captureOne(
241
+ imagesDir: string,
242
+ candidate: Candidate,
243
+ ): Omit<CapturedImageArtifact, "role" | "workspace_path"> | null {
274
244
  try {
275
- const parsed = JSON.parse(raw) as Record<string, unknown>;
276
- return parsed && typeof parsed === "object" ? parsed : null;
245
+ const stat = statSync(candidate.path);
246
+ if (!stat.isFile() || stat.size === 0 || stat.size > MAX_CAPTURE_BYTES) return null;
247
+ if (candidate.requireRecentMtime && Date.now() - stat.mtimeMs > PRODUCED_MTIME_WINDOW_MS) {
248
+ return null;
249
+ }
250
+ const ext = extOf(candidate.path);
251
+ if (!IMAGE_EXTS.has(ext)) return null;
252
+ const bytes = readFileSync(candidate.path);
253
+ const hash = createHash("sha256").update(bytes).digest("hex");
254
+ const destination = join(imagesDir, `${hash}.${ext}`);
255
+ if (!existsSync(destination)) {
256
+ mkdirSync(imagesDir, { recursive: true });
257
+ const temporary = `${destination}.tmp.${process.pid}.${randomUUID().slice(0, 8)}`;
258
+ writeFileSync(temporary, bytes, { mode: 0o600 });
259
+ renameSync(temporary, destination);
260
+ }
261
+ return { hash, ext, bytes: stat.size };
277
262
  } catch {
278
263
  return null;
279
264
  }
280
265
  }
281
266
 
282
- /** The un-clamped Bash command from the raw payload (falls back to clamped). */
283
- function bashCommand(payload: ParsedPayload | null): string {
284
- const ti = payload?.raw?.tool_input as Record<string, unknown> | undefined;
285
- const cmd = ti?.command;
286
- return typeof cmd === "string" ? cmd : "";
267
+ function normalizedToolName(value: string): string {
268
+ return (value.split(/[.:/]/).pop() ?? value).toLowerCase().replaceAll("-", "_");
287
269
  }
288
270
 
289
- function stringifyResponse(value: unknown): string {
290
- if (value == null) return "";
271
+ function objectInput(value: unknown): Record<string, unknown> | null {
272
+ if (value && typeof value === "object" && !Array.isArray(value)) {
273
+ return value as Record<string, unknown>;
274
+ }
275
+ if (typeof value !== "string") return null;
276
+ try {
277
+ const parsed = JSON.parse(value) as unknown;
278
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
279
+ ? (parsed as Record<string, unknown>)
280
+ : null;
281
+ } catch {
282
+ return null;
283
+ }
284
+ }
285
+
286
+ function stringify(value: unknown): string {
291
287
  if (typeof value === "string") return value;
292
288
  try {
293
- return JSON.stringify(value);
289
+ return JSON.stringify(value ?? "");
294
290
  } catch {
295
291
  return "";
296
292
  }
297
293
  }
298
294
 
299
- function resolveCwd(payload: ParsedPayload | null): string {
300
- const raw = payload?.raw as Record<string, unknown> | undefined;
301
- const cwd = raw?.cwd;
302
- return typeof cwd === "string" && cwd ? cwd : process.cwd();
295
+ function toAbsolute(value: string, cwd: string): string {
296
+ if (value.startsWith("~/") && process.env.HOME) return resolve(process.env.HOME, value.slice(2));
297
+ return isAbsolute(value) ? resolve(value) : resolve(cwd, value);
303
298
  }
304
299
 
305
- function hasImageExt(p: string): boolean {
306
- const ext = extOf(p);
307
- return !!ext && IMAGE_EXTS.has(ext);
300
+ function safeWorkspacePath(coordRoot: string, value: string): string | undefined {
301
+ const rel = relative(resolve(coordRoot), resolve(value));
302
+ if (!rel || isAbsolute(rel) || rel === ".." || rel.startsWith(`..${sep}`)) return undefined;
303
+ const normalized = rel.split(sep).join("/");
304
+ return normalized.length <= 240 && !normalized.startsWith("/") ? normalized : undefined;
308
305
  }
309
306
 
310
- function extOf(p: string): string {
311
- const clean = p.split(/[?#]/)[0] ?? p; // drop any query/fragment
312
- const dot = clean.lastIndexOf(".");
313
- if (dot < 0) return "";
314
- return clean.slice(dot + 1).toLowerCase();
307
+ function hasImageExt(value: string): boolean {
308
+ return IMAGE_EXTS.has(extOf(value));
315
309
  }
316
310
 
317
- function toAbsolute(p: string, cwd: string): string {
318
- let path = p;
319
- if (path.startsWith("~/")) {
320
- const home = process.env.HOME;
321
- if (home) path = join(home, path.slice(2));
322
- }
323
- return isAbsolute(path) ? path : resolve(cwd, path);
311
+ function extOf(value: string): string {
312
+ const clean = value.split(/[?#]/)[0] ?? value;
313
+ const dot = clean.lastIndexOf(".");
314
+ return dot < 0 ? "" : clean.slice(dot + 1).toLowerCase();
324
315
  }
325
316
 
326
- /** Strip the coordRoot prefix so the feed shows repo-relative paths. */
327
- function canonicalize(coordRoot: string, p: string): string {
328
- if (p.startsWith(`${coordRoot}/`)) return p.slice(coordRoot.length + 1);
329
- return p;
317
+ function envInt(name: string, fallback: number): number {
318
+ const parsed = Number(process.env[name]);
319
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
330
320
  }
@@ -19,8 +19,7 @@ import { join } from "node:path";
19
19
  import { applyDetection } from "../../../lib/presence.ts";
20
20
  import { coordBinPath } from "../../agents/coord-bin.ts";
21
21
 
22
- export type { CaptureContext } from "./image-capture.ts";
23
- export { captureImages, imageJanitor } from "./image-capture.ts";
22
+ export { captureImages, imageJanitor, recordImageArtifactsV3 } from "./image-capture.ts";
24
23
 
25
24
  /**
26
25
  * Play a notification sound via the cross-platform utility
@@ -151,40 +150,6 @@ export function runSessionSyncExtension(repoRoot: string, force: boolean): void
151
150
  }
152
151
  }
153
152
 
154
- /**
155
- * Fire the turn-summary extension (Haiku auto-summary of the turn → heartbeat
156
- * `turn_summary`). Detached + unref'd; it makes an Anthropic API call and must
157
- * never block the Stop hook. Claude-Code-only. The
158
- * script self-guards on ANTHROPIC_API_KEY / curl / jq / matching session.
159
- */
160
- export function runTurnSummary(
161
- repoRoot: string,
162
- owner: string,
163
- sessionId: string,
164
- transcriptPath: string | undefined,
165
- ): void {
166
- try {
167
- if (!transcriptPath || !existsSync(transcriptPath)) return;
168
- const script = join(
169
- repoRoot,
170
- "scripts",
171
- "hooks",
172
- "harness",
173
- "claude-code",
174
- "extensions",
175
- "turn-summary.sh",
176
- );
177
- if (!existsSync(script)) return;
178
- const child = spawn("bash", [script, owner, sessionId, transcriptPath], {
179
- detached: true,
180
- stdio: "ignore",
181
- });
182
- child.unref();
183
- } catch {
184
- // best-effort
185
- }
186
- }
187
-
188
153
  /** Reset per-turn sound rate-limit counters at the start of a new turn. */
189
154
  export function resetSoundCounters(sessionId: string): void {
190
155
  try {
@@ -0,0 +1,47 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ /** Canonical JSON for hashing adapter input before any event-payload clamp. */
4
+ export function canonicalToolInput(value: unknown): string {
5
+ return JSON.stringify(canonicalize(value));
6
+ }
7
+
8
+ /** Exact-input digest used by the report-only run-quality evaluator. */
9
+ export function toolInputHash(toolName: string, input: unknown): string {
10
+ return createHash("sha256")
11
+ .update(toolName)
12
+ .update("\n")
13
+ .update(canonicalToolInput(input))
14
+ .digest("hex");
15
+ }
16
+
17
+ /** Hash a semantic target without persisting its path, URL, or query. */
18
+ export function toolTargetHash(toolName: string, input: unknown): string | undefined {
19
+ if (!input || typeof input !== "object" || Array.isArray(input)) return undefined;
20
+ const record = input as Record<string, unknown>;
21
+ const target =
22
+ record.file_path ??
23
+ record.path ??
24
+ record.target ??
25
+ record.url ??
26
+ record.notebook_path ??
27
+ record.pattern;
28
+ if (target === undefined) return undefined;
29
+ return createHash("sha256")
30
+ .update(toolName)
31
+ .update("\n")
32
+ .update(canonicalToolInput(target))
33
+ .digest("hex");
34
+ }
35
+
36
+ function canonicalize(value: unknown): unknown {
37
+ if (Array.isArray(value)) return value.map(canonicalize);
38
+ if (value && typeof value === "object") {
39
+ const record = value as Record<string, unknown>;
40
+ return Object.fromEntries(
41
+ Object.keys(record)
42
+ .sort()
43
+ .map((key) => [key, canonicalize(record[key])]),
44
+ );
45
+ }
46
+ return value;
47
+ }