harnery 0.38.0 → 0.40.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 (378) hide show
  1. package/dist/commands/agents.d.ts.map +1 -1
  2. package/dist/commands/agents.js +32 -8
  3. package/dist/commands/artifacts.d.ts.map +1 -1
  4. package/dist/commands/artifacts.js +97 -6
  5. package/dist/commands/backup.d.ts +16 -0
  6. package/dist/commands/backup.d.ts.map +1 -1
  7. package/dist/commands/backup.js +239 -33
  8. package/dist/commands/browse.d.ts +8 -0
  9. package/dist/commands/browse.d.ts.map +1 -1
  10. package/dist/commands/browse.js +466 -231
  11. package/dist/commands/diagnostics.d.ts.map +1 -1
  12. package/dist/commands/diagnostics.js +6 -3
  13. package/dist/commands/ledger-v3.d.ts.map +1 -1
  14. package/dist/commands/ledger-v3.js +20 -4
  15. package/dist/commands/qa-run.d.ts.map +1 -1
  16. package/dist/commands/qa-run.js +26 -3
  17. package/dist/commands/qa-status.d.ts.map +1 -1
  18. package/dist/commands/qa-status.js +7 -2
  19. package/dist/commands/resources.d.ts.map +1 -1
  20. package/dist/commands/resources.js +23 -3
  21. package/dist/commands/review-pack.d.ts.map +1 -1
  22. package/dist/commands/review-pack.js +98 -23
  23. package/dist/commands/tunnel.d.ts.map +1 -1
  24. package/dist/commands/tunnel.js +2 -2
  25. package/dist/core/adapters/profiles.d.ts +1 -1
  26. package/dist/core/agents/canonical-emit.d.ts +3 -2
  27. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  28. package/dist/core/agents/canonical-emit.js +2 -0
  29. package/dist/core/agents/cli.js +13 -6
  30. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  31. package/dist/core/agents/live-authority-v3.js +5 -3
  32. package/dist/core/agents/live-lifecycle-v3.d.ts +2 -2
  33. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  34. package/dist/core/agents/render/prompt-context.d.ts +7 -5
  35. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  36. package/dist/core/agents/render/prompt-context.js +63 -8
  37. package/dist/core/agents/session-finalizer-v3.d.ts +2 -2
  38. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
  39. package/dist/core/agents/state/live-coordination-view.d.ts +3 -3
  40. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  41. package/dist/core/agents/state/live-coordination-view.js +2 -1
  42. package/dist/core/agents/state/names.d.ts +35 -2
  43. package/dist/core/agents/state/names.d.ts.map +1 -1
  44. package/dist/core/agents/state/names.js +80 -5
  45. package/dist/core/agents/state/stale-sweep.d.ts +2 -1
  46. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  47. package/dist/core/agents/state/stale-sweep.js +23 -6
  48. package/dist/core/agents/turn-elapsed.d.ts +51 -0
  49. package/dist/core/agents/turn-elapsed.d.ts.map +1 -0
  50. package/dist/core/agents/turn-elapsed.js +91 -0
  51. package/dist/core/artifacts/activity.d.ts +14 -0
  52. package/dist/core/artifacts/activity.d.ts.map +1 -0
  53. package/dist/core/artifacts/activity.js +52 -0
  54. package/dist/core/artifacts/delivery-card.d.ts +39 -0
  55. package/dist/core/artifacts/delivery-card.d.ts.map +1 -0
  56. package/dist/core/artifacts/delivery-card.js +282 -0
  57. package/dist/core/artifacts/index.d.ts +42 -7
  58. package/dist/core/artifacts/index.d.ts.map +1 -1
  59. package/dist/core/artifacts/index.js +288 -76
  60. package/dist/core/artifacts/mutation-lock.d.ts +2 -0
  61. package/dist/core/artifacts/mutation-lock.d.ts.map +1 -0
  62. package/dist/core/artifacts/mutation-lock.js +91 -0
  63. package/dist/core/backup/host-snapshot.d.ts +18 -0
  64. package/dist/core/backup/host-snapshot.d.ts.map +1 -0
  65. package/dist/core/backup/host-snapshot.js +64 -0
  66. package/dist/core/config.d.ts +20 -5
  67. package/dist/core/config.d.ts.map +1 -1
  68. package/dist/core/config.js +44 -10
  69. package/dist/core/diagnostics/advice.d.ts +38 -3
  70. package/dist/core/diagnostics/advice.d.ts.map +1 -1
  71. package/dist/core/diagnostics/advice.js +103 -99
  72. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  73. package/dist/core/diagnostics/bundle.js +111 -47
  74. package/dist/core/diagnostics/comparison.d.ts.map +1 -1
  75. package/dist/core/diagnostics/comparison.js +3 -2
  76. package/dist/core/diagnostics/contract.d.ts +15 -31
  77. package/dist/core/diagnostics/contract.d.ts.map +1 -1
  78. package/dist/core/diagnostics/contract.js +2 -5
  79. package/dist/core/diagnostics/index.d.ts +2 -0
  80. package/dist/core/diagnostics/index.d.ts.map +1 -1
  81. package/dist/core/diagnostics/index.js +2 -0
  82. package/dist/core/diagnostics/pressure-contract.d.ts +260 -0
  83. package/dist/core/diagnostics/pressure-contract.d.ts.map +1 -0
  84. package/dist/core/diagnostics/pressure-contract.js +70 -0
  85. package/dist/core/diagnostics/pressure.d.ts +25 -0
  86. package/dist/core/diagnostics/pressure.d.ts.map +1 -0
  87. package/dist/core/diagnostics/pressure.js +1138 -0
  88. package/dist/core/diagnostics/replay.d.ts.map +1 -1
  89. package/dist/core/diagnostics/replay.js +90 -10
  90. package/dist/core/events/v3/adapter-id.d.ts +6 -0
  91. package/dist/core/events/v3/adapter-id.d.ts.map +1 -0
  92. package/dist/core/events/v3/adapter-id.js +7 -0
  93. package/dist/core/events/v3/archive-retention.d.ts.map +1 -1
  94. package/dist/core/events/v3/archive-retention.js +3 -2
  95. package/dist/core/events/v3/bootstrap.d.ts +41 -4
  96. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  97. package/dist/core/events/v3/bootstrap.js +120 -17
  98. package/dist/core/events/v3/capabilities.d.ts +16 -9
  99. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  100. package/dist/core/events/v3/capabilities.js +37 -2
  101. package/dist/core/events/v3/capability-drift.d.ts +2 -2
  102. package/dist/core/events/v3/capability-drift.d.ts.map +1 -1
  103. package/dist/core/events/v3/control-witness.d.ts +48 -5
  104. package/dist/core/events/v3/control-witness.d.ts.map +1 -1
  105. package/dist/core/events/v3/control-witness.js +91 -36
  106. package/dist/core/events/v3/control.d.ts.map +1 -1
  107. package/dist/core/events/v3/control.js +22 -2
  108. package/dist/core/events/v3/index.d.ts +2 -1
  109. package/dist/core/events/v3/index.d.ts.map +1 -1
  110. package/dist/core/events/v3/index.js +2 -1
  111. package/dist/core/events/v3/live-feed.d.ts +7 -1
  112. package/dist/core/events/v3/live-feed.d.ts.map +1 -1
  113. package/dist/core/events/v3/live-feed.js +21 -3
  114. package/dist/core/events/v3/live-routing.d.ts.map +1 -1
  115. package/dist/core/events/v3/live-routing.js +10 -2
  116. package/dist/core/events/v3/producers/command-recorder.d.ts +2 -2
  117. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
  118. package/dist/core/events/v3/producers/command-recorder.js +2 -1
  119. package/dist/core/events/v3/producers/coordination-recorder.d.ts +2 -2
  120. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  121. package/dist/core/events/v3/producers/coordination-recorder.js +2 -1
  122. package/dist/core/events/v3/producers/hook-base.d.ts +2 -2
  123. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  124. package/dist/core/events/v3/producers/hook-base.js +15 -5
  125. package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
  126. package/dist/core/events/v3/producers/hook.js +17 -4
  127. package/dist/core/events/v3/producers/intake.d.ts +4 -4
  128. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  129. package/dist/core/events/v3/producers/intake.js +3 -2
  130. package/dist/core/events/v3/producers/producer-state-observer.d.ts.map +1 -1
  131. package/dist/core/events/v3/producers/producer-state-observer.js +2 -1
  132. package/dist/core/events/v3/producers/recorder.d.ts +16 -5
  133. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  134. package/dist/core/events/v3/producers/recorder.js +31 -9
  135. package/dist/core/events/v3/reader.d.ts.map +1 -1
  136. package/dist/core/events/v3/reader.js +10 -13
  137. package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
  138. package/dist/core/events/v3/runtime-build.js +3 -2
  139. package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts +2 -2
  140. package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts.map +1 -1
  141. package/dist/core/events/v3/turn-telemetry.d.ts +2 -2
  142. package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -1
  143. package/dist/core/hooks/adapter/output.d.ts +16 -14
  144. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  145. package/dist/core/hooks/adapter/output.js +36 -38
  146. package/dist/core/hooks/backup-schedule.d.ts +34 -0
  147. package/dist/core/hooks/backup-schedule.d.ts.map +1 -0
  148. package/dist/core/hooks/backup-schedule.js +114 -0
  149. package/dist/core/hooks/cli.js +61 -18
  150. package/dist/core/hooks/codex-wsl-bridge.d.ts +2 -0
  151. package/dist/core/hooks/codex-wsl-bridge.d.ts.map +1 -1
  152. package/dist/core/hooks/codex-wsl-bridge.js +18 -4
  153. package/dist/core/hooks/index.d.ts +1 -0
  154. package/dist/core/hooks/index.d.ts.map +1 -1
  155. package/dist/core/hooks/index.js +1 -0
  156. package/dist/core/hooks/prompt-context/contract.d.ts +61 -0
  157. package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -0
  158. package/dist/core/hooks/prompt-context/contract.js +102 -0
  159. package/dist/core/hooks/prompt-context/index.d.ts +5 -0
  160. package/dist/core/hooks/prompt-context/index.d.ts.map +1 -0
  161. package/dist/core/hooks/prompt-context/index.js +2 -0
  162. package/dist/core/hooks/prompt-context/runner.d.ts +24 -0
  163. package/dist/core/hooks/prompt-context/runner.d.ts.map +1 -0
  164. package/dist/core/hooks/prompt-context/runner.js +249 -0
  165. package/dist/core/qa-artifacts.d.ts +3 -0
  166. package/dist/core/qa-artifacts.d.ts.map +1 -1
  167. package/dist/core/qa-artifacts.js +25 -3
  168. package/dist/core/resources/contract.d.ts +69 -3
  169. package/dist/core/resources/contract.d.ts.map +1 -1
  170. package/dist/core/resources/contract.js +1 -1
  171. package/dist/core/resources/index.d.ts +1 -0
  172. package/dist/core/resources/index.d.ts.map +1 -1
  173. package/dist/core/resources/index.js +1 -0
  174. package/dist/core/resources/native.d.ts +36 -0
  175. package/dist/core/resources/native.d.ts.map +1 -0
  176. package/dist/core/resources/native.js +418 -0
  177. package/dist/core/resources/sampler.d.ts +2 -0
  178. package/dist/core/resources/sampler.d.ts.map +1 -1
  179. package/dist/core/resources/sampler.js +31 -1
  180. package/dist/core/resources/status.d.ts +61 -0
  181. package/dist/core/resources/status.d.ts.map +1 -0
  182. package/dist/core/resources/status.js +536 -0
  183. package/dist/core/resources/system.d.ts +27 -0
  184. package/dist/core/resources/system.d.ts.map +1 -0
  185. package/dist/core/resources/system.js +439 -0
  186. package/dist/core/semantic/contract.d.ts +13 -13
  187. package/dist/core/semantic/service.d.ts +2 -0
  188. package/dist/core/semantic/service.d.ts.map +1 -1
  189. package/dist/core/semantic/service.js +27 -4
  190. package/dist/core/storage/builtins.js +41 -0
  191. package/dist/core/supervisor/contract.d.ts +41 -3
  192. package/dist/core/supervisor/contract.d.ts.map +1 -1
  193. package/dist/core/supervisor/contract.js +3 -2
  194. package/dist/core/supervisor/findings.d.ts +3 -0
  195. package/dist/core/supervisor/findings.d.ts.map +1 -1
  196. package/dist/core/supervisor/findings.js +88 -0
  197. package/dist/core/supervisor/history.d.ts.map +1 -1
  198. package/dist/core/supervisor/history.js +14 -0
  199. package/dist/core/supervisor/ledger-state.d.ts +55 -0
  200. package/dist/core/supervisor/ledger-state.d.ts.map +1 -0
  201. package/dist/core/supervisor/ledger-state.js +0 -0
  202. package/dist/core/supervisor/service.d.ts.map +1 -1
  203. package/dist/core/supervisor/service.js +120 -7
  204. package/dist/core/supervisor/storage.d.ts +3 -1
  205. package/dist/core/supervisor/storage.d.ts.map +1 -1
  206. package/dist/core/supervisor/storage.js +4 -0
  207. package/dist/core/workflow/admission.d.ts +3 -0
  208. package/dist/core/workflow/admission.d.ts.map +1 -1
  209. package/dist/core/workflow/admission.js +28 -16
  210. package/dist/core/workflow/engine.js +2 -2
  211. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  212. package/dist/core/workflow/live-session-v3.js +6 -1
  213. package/dist/core/workflow/proof.d.ts.map +1 -1
  214. package/dist/core/workflow/proof.js +4 -3
  215. package/dist/lib/browser/capture-fidelity.d.ts +7 -0
  216. package/dist/lib/browser/capture-fidelity.d.ts.map +1 -1
  217. package/dist/lib/browser/capture-fidelity.js +17 -0
  218. package/dist/lib/browser/client.d.ts +9 -0
  219. package/dist/lib/browser/client.d.ts.map +1 -1
  220. package/dist/lib/browser/client.js +85 -37
  221. package/dist/lib/browser/index.d.ts +3 -1
  222. package/dist/lib/browser/index.d.ts.map +1 -1
  223. package/dist/lib/browser/index.js +2 -1
  224. package/dist/lib/browser/page-review-budget.d.ts +13 -0
  225. package/dist/lib/browser/page-review-budget.d.ts.map +1 -0
  226. package/dist/lib/browser/page-review-budget.js +251 -0
  227. package/dist/lib/browser/page-review-contracts.d.ts +127 -0
  228. package/dist/lib/browser/page-review-contracts.d.ts.map +1 -0
  229. package/dist/lib/browser/page-review-contracts.js +5 -0
  230. package/dist/lib/browser/page-review-evidence.d.ts +48 -0
  231. package/dist/lib/browser/page-review-evidence.d.ts.map +1 -0
  232. package/dist/lib/browser/page-review-evidence.js +340 -0
  233. package/dist/lib/browser/page-review-judge.d.ts +1 -1
  234. package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
  235. package/dist/lib/browser/page-review-judge.js +46 -38
  236. package/dist/lib/browser/page-review-pack.d.ts +44 -22
  237. package/dist/lib/browser/page-review-pack.d.ts.map +1 -1
  238. package/dist/lib/browser/page-review-pack.js +116 -36
  239. package/dist/lib/browser/page-review-source.d.ts +12 -0
  240. package/dist/lib/browser/page-review-source.d.ts.map +1 -0
  241. package/dist/lib/browser/page-review-source.js +22 -0
  242. package/dist/lib/browser/qa-run-contracts.d.ts +35 -9
  243. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  244. package/dist/lib/browser/qa-run-contracts.js +48 -14
  245. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  246. package/dist/lib/browser/qa-run.js +215 -68
  247. package/dist/lib/browser/qa-snapshot.d.ts +6 -0
  248. package/dist/lib/browser/qa-snapshot.d.ts.map +1 -1
  249. package/dist/lib/browser/qa-snapshot.js +259 -48
  250. package/dist/lib/browser/review-capture-transaction.d.ts +13 -0
  251. package/dist/lib/browser/review-capture-transaction.d.ts.map +1 -0
  252. package/dist/lib/browser/review-capture-transaction.js +79 -0
  253. package/dist/lib/browser/standalone-html.d.ts.map +1 -1
  254. package/dist/lib/browser/standalone-html.js +4 -2
  255. package/dist/lib/browser/thumbnail-association.d.ts +34 -0
  256. package/dist/lib/browser/thumbnail-association.d.ts.map +1 -0
  257. package/dist/lib/browser/thumbnail-association.js +158 -0
  258. package/dist/lib/browser/tiling.d.ts +30 -0
  259. package/dist/lib/browser/tiling.d.ts.map +1 -1
  260. package/dist/lib/browser/tiling.js +38 -0
  261. package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
  262. package/dist/lib/docs-metadata-managed.js +4 -1
  263. package/dist/lib/identities/assume.d.ts.map +1 -1
  264. package/dist/lib/identities/assume.js +7 -1
  265. package/dist/lib/instructions/templates.d.ts.map +1 -1
  266. package/dist/lib/instructions/templates.js +1 -0
  267. package/dist/lib/local-file-url.d.ts +12 -0
  268. package/dist/lib/local-file-url.d.ts.map +1 -1
  269. package/dist/lib/local-file-url.js +16 -2
  270. package/dist/lib/tunnel/state.d.ts +19 -4
  271. package/dist/lib/tunnel/state.d.ts.map +1 -1
  272. package/dist/lib/tunnel/state.js +53 -13
  273. package/package.json +7 -4
  274. package/schemas/config.schema.json +69 -2
  275. package/src/commands/agents.ts +42 -9
  276. package/src/commands/artifacts.ts +130 -6
  277. package/src/commands/backup.ts +287 -34
  278. package/src/commands/browse.ts +564 -267
  279. package/src/commands/diagnostics.ts +6 -3
  280. package/src/commands/ledger-v3.ts +20 -3
  281. package/src/commands/qa-run.ts +37 -3
  282. package/src/commands/qa-status.ts +11 -2
  283. package/src/commands/resources.ts +23 -3
  284. package/src/commands/review-pack.ts +165 -62
  285. package/src/commands/tunnel.ts +2 -1
  286. package/src/core/agents/canonical-emit.ts +4 -2
  287. package/src/core/agents/cli.ts +14 -6
  288. package/src/core/agents/live-authority-v3.ts +7 -3
  289. package/src/core/agents/live-lifecycle-v3.ts +2 -2
  290. package/src/core/agents/render/prompt-context.ts +68 -14
  291. package/src/core/agents/session-finalizer-v3.ts +2 -2
  292. package/src/core/agents/state/live-coordination-view.ts +4 -4
  293. package/src/core/agents/state/names.ts +92 -7
  294. package/src/core/agents/state/stale-sweep.ts +22 -8
  295. package/src/core/agents/turn-elapsed.ts +135 -0
  296. package/src/core/artifacts/activity.ts +71 -0
  297. package/src/core/artifacts/delivery-card.ts +375 -0
  298. package/src/core/artifacts/index.ts +385 -91
  299. package/src/core/artifacts/mutation-lock.ts +106 -0
  300. package/src/core/backup/host-snapshot.ts +76 -0
  301. package/src/core/config.ts +66 -13
  302. package/src/core/diagnostics/advice.ts +148 -120
  303. package/src/core/diagnostics/bundle.ts +138 -52
  304. package/src/core/diagnostics/comparison.ts +6 -2
  305. package/src/core/diagnostics/contract.ts +39 -42
  306. package/src/core/diagnostics/index.ts +2 -0
  307. package/src/core/diagnostics/pressure-contract.ts +294 -0
  308. package/src/core/diagnostics/pressure.ts +1361 -0
  309. package/src/core/diagnostics/replay.ts +116 -11
  310. package/src/core/events/v3/adapter-id.ts +12 -0
  311. package/src/core/events/v3/archive-retention.ts +3 -6
  312. package/src/core/events/v3/bootstrap.ts +163 -16
  313. package/src/core/events/v3/capabilities.ts +75 -25
  314. package/src/core/events/v3/capability-drift.ts +2 -2
  315. package/src/core/events/v3/control-witness.ts +172 -41
  316. package/src/core/events/v3/control.ts +34 -2
  317. package/src/core/events/v3/index.ts +3 -0
  318. package/src/core/events/v3/live-feed.ts +23 -5
  319. package/src/core/events/v3/live-routing.ts +14 -2
  320. package/src/core/events/v3/producers/command-recorder.ts +4 -4
  321. package/src/core/events/v3/producers/coordination-recorder.ts +4 -4
  322. package/src/core/events/v3/producers/hook-base.ts +21 -16
  323. package/src/core/events/v3/producers/hook.ts +21 -4
  324. package/src/core/events/v3/producers/intake.ts +6 -6
  325. package/src/core/events/v3/producers/producer-state-observer.ts +2 -1
  326. package/src/core/events/v3/producers/recorder.ts +50 -21
  327. package/src/core/events/v3/reader.ts +14 -13
  328. package/src/core/events/v3/runtime-build.ts +3 -2
  329. package/src/core/events/v3/runtime-telemetry-capabilities.ts +3 -3
  330. package/src/core/events/v3/turn-telemetry.ts +4 -4
  331. package/src/core/hooks/adapter/output.ts +50 -36
  332. package/src/core/hooks/backup-schedule.ts +147 -0
  333. package/src/core/hooks/cli.ts +65 -14
  334. package/src/core/hooks/codex-wsl-bridge.ts +21 -5
  335. package/src/core/hooks/index.ts +1 -0
  336. package/src/core/hooks/prompt-context/contract.ts +177 -0
  337. package/src/core/hooks/prompt-context/index.ts +22 -0
  338. package/src/core/hooks/prompt-context/runner.ts +303 -0
  339. package/src/core/qa-artifacts.ts +29 -3
  340. package/src/core/resources/contract.ts +76 -3
  341. package/src/core/resources/index.ts +1 -0
  342. package/src/core/resources/native.ts +481 -0
  343. package/src/core/resources/sampler.ts +46 -1
  344. package/src/core/resources/status.ts +717 -0
  345. package/src/core/resources/system.ts +530 -0
  346. package/src/core/semantic/service.ts +37 -3
  347. package/src/core/storage/builtins.ts +41 -0
  348. package/src/core/supervisor/contract.ts +45 -3
  349. package/src/core/supervisor/findings.ts +159 -0
  350. package/src/core/supervisor/history.ts +15 -0
  351. package/src/core/supervisor/ledger-state.ts +0 -0
  352. package/src/core/supervisor/service.ts +133 -5
  353. package/src/core/supervisor/storage.ts +7 -0
  354. package/src/core/workflow/admission.ts +33 -17
  355. package/src/core/workflow/engine.ts +2 -2
  356. package/src/core/workflow/live-session-v3.ts +6 -8
  357. package/src/core/workflow/proof.ts +4 -3
  358. package/src/lib/browser/capture-fidelity.ts +23 -0
  359. package/src/lib/browser/client.ts +96 -44
  360. package/src/lib/browser/index.ts +15 -1
  361. package/src/lib/browser/page-review-budget.ts +309 -0
  362. package/src/lib/browser/page-review-contracts.ts +129 -0
  363. package/src/lib/browser/page-review-evidence.ts +465 -0
  364. package/src/lib/browser/page-review-judge.ts +56 -40
  365. package/src/lib/browser/page-review-pack.ts +181 -43
  366. package/src/lib/browser/page-review-source.ts +30 -0
  367. package/src/lib/browser/qa-run-contracts.ts +62 -18
  368. package/src/lib/browser/qa-run.ts +259 -71
  369. package/src/lib/browser/qa-snapshot.ts +289 -48
  370. package/src/lib/browser/review-capture-transaction.ts +106 -0
  371. package/src/lib/browser/standalone-html.ts +4 -2
  372. package/src/lib/browser/thumbnail-association.ts +190 -0
  373. package/src/lib/browser/tiling.ts +63 -0
  374. package/src/lib/docs-metadata-managed.ts +4 -1
  375. package/src/lib/identities/assume.ts +12 -4
  376. package/src/lib/instructions/templates.ts +1 -0
  377. package/src/lib/local-file-url.ts +17 -2
  378. package/src/lib/tunnel/state.ts +70 -13
@@ -1,8 +1,10 @@
1
- import type { Adapter } from "../../adapter.ts";
1
+ import type { EventAdapterIdV3 } from "./adapter-id.ts";
2
2
  import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
3
3
 
4
4
  export type CapabilitySupportV3 = "native" | "derived" | "conditional" | "unsupported";
5
5
 
6
+ export type AdapterDurationCapabilityV3 = "turn_duration" | "tool_duration";
7
+
6
8
  export type CursorExecutionModeV3 = "local" | "cloud" | "unknown";
7
9
 
8
10
  export type AdapterWaitKindV3 =
@@ -59,7 +61,7 @@ export type AdapterSignalV3 =
59
61
  export interface AdapterCapabilityProfileV3 {
60
62
  format: "harnery-v3-adapter-capabilities";
61
63
  format_version: 1;
62
- adapter: Adapter;
64
+ adapter: EventAdapterIdV3;
63
65
  signals: Record<AdapterSignalV3, CapabilitySupportV3>;
64
66
  }
65
67
 
@@ -79,7 +81,10 @@ const SHARED_SIGNAL_SUPPORT = {
79
81
  model_identity: "conditional",
80
82
  } as const;
81
83
 
82
- const BASE_SIGNAL_SUPPORT: Record<Adapter, Record<BaseAdapterSignalV3, CapabilitySupportV3>> = {
84
+ const BASE_SIGNAL_SUPPORT: Record<
85
+ EventAdapterIdV3,
86
+ Record<BaseAdapterSignalV3, CapabilitySupportV3>
87
+ > = {
83
88
  "claude-code": {
84
89
  ...SHARED_SIGNAL_SUPPORT,
85
90
  session_end: "native",
@@ -110,11 +115,32 @@ const BASE_SIGNAL_SUPPORT: Record<Adapter, Record<BaseAdapterSignalV3, Capabilit
110
115
  post_compaction: "unsupported",
111
116
  context_usage: "conditional",
112
117
  },
118
+ openclaw: {
119
+ session_start: "conditional",
120
+ session_end: "native",
121
+ prompt: "native",
122
+ turn_id: "native",
123
+ turn_completion: "native",
124
+ assistant_reply_text: "unsupported",
125
+ tool_request: "native",
126
+ tool_result: "native",
127
+ tool_failure: "conditional",
128
+ tool_call_id: "native",
129
+ tool_duration: "unsupported",
130
+ permission: "unsupported",
131
+ subagent: "unsupported",
132
+ shell: "native",
133
+ pre_compaction: "unsupported",
134
+ post_compaction: "unsupported",
135
+ context_usage: "unsupported",
136
+ model_identity: "unsupported",
137
+ },
113
138
  };
114
139
 
115
140
  function profile(
116
- adapter: Adapter,
141
+ adapter: EventAdapterIdV3,
117
142
  economics: Pick<Record<AdapterSignalV3, CapabilitySupportV3>, "model_usage" | "inference_timing">,
143
+ harnessTiming: CapabilitySupportV3 = "derived",
118
144
  ): AdapterCapabilityProfileV3 {
119
145
  return {
120
146
  format: "harnery-v3-adapter-capabilities",
@@ -123,56 +149,80 @@ function profile(
123
149
  signals: {
124
150
  ...BASE_SIGNAL_SUPPORT[adapter],
125
151
  ...economics,
126
- harness_timing: "derived",
152
+ harness_timing: harnessTiming,
127
153
  capability_drift: "derived",
128
154
  },
129
155
  };
130
156
  }
131
157
 
132
- export const ADAPTER_CAPABILITY_PROFILES_V3: Record<Adapter, AdapterCapabilityProfileV3> = {
133
- "claude-code": profile("claude-code", {
134
- model_usage: "conditional",
135
- inference_timing: "unsupported",
136
- }),
137
- codex: profile("codex", {
138
- model_usage: "conditional",
139
- inference_timing: "unsupported",
140
- }),
141
- cursor: profile("cursor", {
142
- model_usage: "unsupported",
143
- inference_timing: "unsupported",
144
- }),
145
- };
158
+ export const ADAPTER_CAPABILITY_PROFILES_V3: Record<EventAdapterIdV3, AdapterCapabilityProfileV3> =
159
+ {
160
+ "claude-code": profile("claude-code", {
161
+ model_usage: "conditional",
162
+ inference_timing: "unsupported",
163
+ }),
164
+ codex: profile("codex", {
165
+ model_usage: "conditional",
166
+ inference_timing: "unsupported",
167
+ }),
168
+ cursor: profile("cursor", {
169
+ model_usage: "unsupported",
170
+ inference_timing: "unsupported",
171
+ }),
172
+ openclaw: profile(
173
+ "openclaw",
174
+ {
175
+ model_usage: "unsupported",
176
+ inference_timing: "unsupported",
177
+ },
178
+ "unsupported",
179
+ ),
180
+ };
146
181
 
147
- export function adapterCapabilityProfileV3(adapter: Adapter): AdapterCapabilityProfileV3 {
182
+ export function adapterCapabilityProfileV3(adapter: EventAdapterIdV3): AdapterCapabilityProfileV3 {
148
183
  return ADAPTER_CAPABILITY_PROFILES_V3[adapter];
149
184
  }
150
185
 
151
- export function adapterCapabilityProfileDigestV3(adapter: Adapter): `cap_${string}` {
186
+ export function adapterCapabilityProfileDigestV3(adapter: EventAdapterIdV3): `cap_${string}` {
152
187
  return `cap_${sha256V3(canonicalJsonV3(adapterCapabilityProfileV3(adapter))).slice(7)}`;
153
188
  }
154
189
 
155
190
  export function adapterSignalSupportV3(
156
- adapter: Adapter,
191
+ adapter: EventAdapterIdV3,
157
192
  signal: AdapterSignalV3,
158
193
  ): CapabilitySupportV3 {
159
194
  return adapterCapabilityProfileV3(adapter).signals[signal];
160
195
  }
161
196
 
197
+ /**
198
+ * Duration support for terminal spans. Tool duration is part of the digested
199
+ * adapter profile. Turn duration predates that profile dimension, so keep its
200
+ * compatibility-preserving classification here until the next contract major.
201
+ */
202
+ export function adapterDurationSupportV3(
203
+ adapter: EventAdapterIdV3,
204
+ capability: AdapterDurationCapabilityV3,
205
+ ): CapabilitySupportV3 {
206
+ if (capability === "tool_duration") {
207
+ return adapterSignalSupportV3(adapter, "tool_duration");
208
+ }
209
+ return adapter === "openclaw" ? "unsupported" : "derived";
210
+ }
211
+
162
212
  /**
163
213
  * Adapter hooks only expose permission waits today. Other wait kinds can be
164
214
  * authored by coordination producers, but they are not observable from an
165
215
  * adapter's native turn channel.
166
216
  */
167
217
  export function adapterWaitKindSupportV3(
168
- adapter: Adapter,
218
+ adapter: EventAdapterIdV3,
169
219
  kind: AdapterWaitKindV3,
170
220
  ): CapabilitySupportV3 {
171
221
  return kind === "permission" ? adapterSignalSupportV3(adapter, "permission") : "unsupported";
172
222
  }
173
223
 
174
224
  /** No current adapter supplies an independent completed-turn wait aggregate. */
175
- export function adapterTurnWaitCountSupportV3(_adapter: Adapter): CapabilitySupportV3 {
225
+ export function adapterTurnWaitCountSupportV3(_adapter: EventAdapterIdV3): CapabilitySupportV3 {
176
226
  return "unsupported";
177
227
  }
178
228
 
@@ -182,7 +232,7 @@ export function adapterTurnWaitCountSupportV3(_adapter: Adapter): CapabilitySupp
182
232
  * never proves that an absent span means zero.
183
233
  */
184
234
  export function adapterWaitCoverageMatrixV3(
185
- adapter: Adapter,
235
+ adapter: EventAdapterIdV3,
186
236
  ): Record<AdapterWaitKindV3, WaitKindCapabilityV3> {
187
237
  return Object.fromEntries(
188
238
  ADAPTER_WAIT_KINDS_V3.map((kind) => [
@@ -1,4 +1,4 @@
1
- import type { Adapter } from "../../adapter.ts";
1
+ import type { EventAdapterIdV3 } from "./adapter-id.ts";
2
2
  import {
3
3
  type AdapterSignalV3,
4
4
  adapterSignalSupportV3,
@@ -23,7 +23,7 @@ export type CapabilityDriftPayloadV3 = EventPayloadV3<"health.capability_drift">
23
23
  * failures.
24
24
  */
25
25
  export function capabilityDriftPayloadsV3(
26
- adapter: Adapter,
26
+ adapter: EventAdapterIdV3,
27
27
  events: readonly EventV3[],
28
28
  options: { generation_ended?: boolean } = {},
29
29
  ): CapabilityDriftPayloadV3[] {
@@ -28,15 +28,23 @@ import {
28
28
 
29
29
  export { EVENT_V3_CONTROL_CHECKPOINT_RELATIVE_PATH, EVENT_V3_CONTROL_WITNESS_RELATIVE_PATH };
30
30
 
31
- interface EventV3ControlWitnessPayload {
31
+ /**
32
+ * A witness answers one question cheaply: does an authenticated record already
33
+ * name this exact control pair over this exact storage? Both control states get
34
+ * one, because a hook must never pay a whole-history parse for either.
35
+ *
36
+ * `control_state` is the discriminator, and it is authenticated with the rest of
37
+ * the payload. A candidate witness therefore cannot be replayed as an active
38
+ * one, and an active witness cannot survive its activation being removed.
39
+ */
40
+ export type EventV3ControlWitnessControlStateV3 = "active" | "candidate";
41
+
42
+ interface EventV3ControlWitnessPayloadCommonV3 {
32
43
  format: "harnery-event-v3-control-witness";
33
- format_version: 1;
44
+ format_version: 2;
34
45
  genesis_id: `gex_${string}`;
35
- activation_id: `act_${string}`;
36
46
  genesis_event_id: `evt_${string}`;
37
- activation_event_id: `evt_${string}`;
38
47
  genesis_manifest_digest: `sha256:${string}`;
39
- activation_manifest_digest: `sha256:${string}`;
40
48
  candidate_manifest_digest: `sha256:${string}`;
41
49
  privacy_key_epoch: `pep_${string}`;
42
50
  root_id: `root_${string}`;
@@ -44,6 +52,21 @@ interface EventV3ControlWitnessPayload {
44
52
  checkpoint_digest: `sha256:${string}`;
45
53
  }
46
54
 
55
+ interface EventV3ActiveControlWitnessPayloadV3 extends EventV3ControlWitnessPayloadCommonV3 {
56
+ control_state: "active";
57
+ activation_id: `act_${string}`;
58
+ activation_event_id: `evt_${string}`;
59
+ activation_manifest_digest: `sha256:${string}`;
60
+ }
61
+
62
+ interface EventV3CandidateControlWitnessPayloadV3 extends EventV3ControlWitnessPayloadCommonV3 {
63
+ control_state: "candidate";
64
+ }
65
+
66
+ type EventV3ControlWitnessPayload =
67
+ | EventV3ActiveControlWitnessPayloadV3
68
+ | EventV3CandidateControlWitnessPayloadV3;
69
+
47
70
  interface EventV3ControlWitness {
48
71
  payload: EventV3ControlWitnessPayload;
49
72
  authentication: FingerprintV3;
@@ -55,22 +78,43 @@ export interface ActiveControlWitnessBindingV3 {
55
78
  candidate_manifest_digest: `sha256:${string}`;
56
79
  }
57
80
 
81
+ /** The candidate equivalent: a validated genesis with no activation yet. */
82
+ export interface CandidateControlWitnessBindingV3 {
83
+ genesis: CandidateGenesisManifestV3;
84
+ candidate_manifest_digest: `sha256:${string}`;
85
+ }
86
+
58
87
  /** True only when one authenticated witness names this exact active control pair and storage. */
59
88
  export function activeControlWitnessMatchesV3(
60
89
  coordRoot: string,
61
90
  binding: ActiveControlWitnessBindingV3,
62
91
  ): boolean {
63
- try {
64
- const before = eventV3AuthorityStorageVersionV3(coordRoot);
65
- const witness = readAuthenticatedWitnessV3(coordRoot);
66
- if (!witness) return false;
67
- const expected = witnessPayloadV3(binding, before, witness.payload.checkpoint_digest);
68
- if (canonicalJsonV3(witness.payload) !== canonicalJsonV3(expected)) return false;
69
- const after = eventV3AuthorityStorageVersionV3(coordRoot);
70
- return sameStorageVersion(before, after);
71
- } catch {
72
- return false;
73
- }
92
+ return witnessMatchesV3(coordRoot, "active", (storage, checkpointDigest) =>
93
+ activeWitnessPayloadV3(binding, storage, checkpointDigest),
94
+ );
95
+ }
96
+
97
+ /**
98
+ * True only when one authenticated witness names this exact candidate control
99
+ * pair and storage.
100
+ *
101
+ * A candidate epoch is not a degraded active one: its gate is narrower, but a
102
+ * hook still has to resolve it, and paying a whole-history parse for that
103
+ * answer is what made a stranded epoch cost seconds and hundreds of megabytes
104
+ * per process. The tamper coverage is the active witness's, unchanged: the
105
+ * authenticated payload names the storage version, so a corrupt append, a
106
+ * same-size rewrite, a changed sealed segment, and a replaced inode all miss
107
+ * and fall back to canonical validation. Because `control_state` is inside the
108
+ * authenticated payload, an active witness can never answer here and a
109
+ * candidate witness can never answer the active gate.
110
+ */
111
+ export function candidateControlWitnessMatchesV3(
112
+ coordRoot: string,
113
+ binding: CandidateControlWitnessBindingV3,
114
+ ): boolean {
115
+ return witnessMatchesV3(coordRoot, "candidate", (storage, checkpointDigest) =>
116
+ candidateWitnessPayloadV3(binding, storage, checkpointDigest),
117
+ );
74
118
  }
75
119
 
76
120
  /**
@@ -83,17 +127,31 @@ export function publishActiveControlWitnessV3(
83
127
  validatedStorage: EventV3AuthorityStorageVersionV3,
84
128
  checkpoint: EventV3AppendValidationCheckpointV3,
85
129
  ): boolean {
86
- try {
87
- const current = eventV3AuthorityStorageVersionV3(coordRoot);
88
- if (!sameStorageVersion(validatedStorage, current)) return false;
89
- return publishWitnessV3(
90
- coordRoot,
91
- witnessPayloadV3(binding, current, checkpointDigestV3(checkpoint)),
92
- checkpoint,
93
- );
94
- } catch {
95
- return false;
96
- }
130
+ return publishControlWitnessV3(
131
+ coordRoot,
132
+ validatedStorage,
133
+ checkpoint,
134
+ (storage, checkpointDigest) => activeWitnessPayloadV3(binding, storage, checkpointDigest),
135
+ );
136
+ }
137
+
138
+ /**
139
+ * The candidate equivalent, published on the same terms: only when the storage
140
+ * the caller validated is still current, and silently skipped otherwise so the
141
+ * next read keeps the canonical full-scan fallback.
142
+ */
143
+ export function publishCandidateControlWitnessV3(
144
+ coordRoot: string,
145
+ binding: CandidateControlWitnessBindingV3,
146
+ validatedStorage: EventV3AuthorityStorageVersionV3,
147
+ checkpoint: EventV3AppendValidationCheckpointV3,
148
+ ): boolean {
149
+ return publishControlWitnessV3(
150
+ coordRoot,
151
+ validatedStorage,
152
+ checkpoint,
153
+ (storage, checkpointDigest) => candidateWitnessPayloadV3(binding, storage, checkpointDigest),
154
+ );
97
155
  }
98
156
 
99
157
  /**
@@ -155,21 +213,87 @@ export function advanceControlWitnessV3(
155
213
  }
156
214
  }
157
215
 
158
- function witnessPayloadV3(
216
+ type WitnessPayloadBuilderV3 = (
217
+ storage: EventV3AuthorityStorageVersionV3,
218
+ checkpointDigest: `sha256:${string}`,
219
+ ) => EventV3ControlWitnessPayload;
220
+
221
+ /**
222
+ * One matcher for both control states. The witness on disk must authenticate,
223
+ * carry the requested `control_state`, and canonicalize identically to the
224
+ * payload this exact binding would produce over the storage observed both
225
+ * before and after the read.
226
+ */
227
+ function witnessMatchesV3(
228
+ coordRoot: string,
229
+ controlState: EventV3ControlWitnessControlStateV3,
230
+ build: WitnessPayloadBuilderV3,
231
+ ): boolean {
232
+ try {
233
+ const before = eventV3AuthorityStorageVersionV3(coordRoot);
234
+ const witness = readAuthenticatedWitnessV3(coordRoot);
235
+ if (!witness || witness.payload.control_state !== controlState) return false;
236
+ const expected = build(before, witness.payload.checkpoint_digest);
237
+ if (canonicalJsonV3(witness.payload) !== canonicalJsonV3(expected)) return false;
238
+ const after = eventV3AuthorityStorageVersionV3(coordRoot);
239
+ return sameStorageVersion(before, after);
240
+ } catch {
241
+ return false;
242
+ }
243
+ }
244
+
245
+ function publishControlWitnessV3(
246
+ coordRoot: string,
247
+ validatedStorage: EventV3AuthorityStorageVersionV3,
248
+ checkpoint: EventV3AppendValidationCheckpointV3,
249
+ build: WitnessPayloadBuilderV3,
250
+ ): boolean {
251
+ try {
252
+ const current = eventV3AuthorityStorageVersionV3(coordRoot);
253
+ if (!sameStorageVersion(validatedStorage, current)) return false;
254
+ return publishWitnessV3(coordRoot, build(current, checkpointDigestV3(checkpoint)), checkpoint);
255
+ } catch {
256
+ return false;
257
+ }
258
+ }
259
+
260
+ function activeWitnessPayloadV3(
159
261
  binding: ActiveControlWitnessBindingV3,
160
262
  storage: EventV3AuthorityStorageVersionV3,
161
263
  checkpointDigest: `sha256:${string}`,
162
- ): EventV3ControlWitnessPayload {
264
+ ): EventV3ActiveControlWitnessPayloadV3 {
265
+ return {
266
+ ...commonWitnessPayloadV3(binding, storage, checkpointDigest),
267
+ control_state: "active",
268
+ activation_id: binding.activation.activation_id,
269
+ activation_event_id: binding.activation.event.event_id as `evt_${string}`,
270
+ activation_manifest_digest: sha256V3(canonicalJsonV3(binding.activation)),
271
+ };
272
+ }
273
+
274
+ function candidateWitnessPayloadV3(
275
+ binding: CandidateControlWitnessBindingV3,
276
+ storage: EventV3AuthorityStorageVersionV3,
277
+ checkpointDigest: `sha256:${string}`,
278
+ ): EventV3CandidateControlWitnessPayloadV3 {
279
+ return {
280
+ ...commonWitnessPayloadV3(binding, storage, checkpointDigest),
281
+ control_state: "candidate",
282
+ };
283
+ }
284
+
285
+ function commonWitnessPayloadV3(
286
+ binding: CandidateControlWitnessBindingV3,
287
+ storage: EventV3AuthorityStorageVersionV3,
288
+ checkpointDigest: `sha256:${string}`,
289
+ ): EventV3ControlWitnessPayloadCommonV3 {
163
290
  const scope = binding.genesis.event.scope as { root_id: `root_${string}` };
164
291
  return {
165
292
  format: "harnery-event-v3-control-witness",
166
- format_version: 1,
293
+ format_version: 2,
167
294
  genesis_id: binding.genesis.event.payload.genesis_id as `gex_${string}`,
168
- activation_id: binding.activation.activation_id,
169
295
  genesis_event_id: binding.genesis.event.event_id as `evt_${string}`,
170
- activation_event_id: binding.activation.event.event_id as `evt_${string}`,
171
296
  genesis_manifest_digest: sha256V3(canonicalJsonV3(binding.genesis)),
172
- activation_manifest_digest: sha256V3(canonicalJsonV3(binding.activation)),
173
297
  candidate_manifest_digest: binding.candidate_manifest_digest,
174
298
  privacy_key_epoch: binding.genesis.profile.privacy_key_epoch,
175
299
  root_id: scope.root_id,
@@ -265,12 +389,14 @@ function parseWitnessV3(value: unknown): EventV3ControlWitness {
265
389
  throw new Error("control witness envelope is invalid");
266
390
  }
267
391
  const payload = object(envelope.payload);
268
- const expectedPayloadKeys = [
269
- "activation_event_id",
270
- "activation_id",
271
- "activation_manifest_digest",
392
+ const controlState = payload.control_state;
393
+ if (controlState !== "active" && controlState !== "candidate") {
394
+ throw new Error("control witness control state is invalid");
395
+ }
396
+ const commonPayloadKeys = [
272
397
  "candidate_manifest_digest",
273
398
  "checkpoint_digest",
399
+ "control_state",
274
400
  "format",
275
401
  "format_version",
276
402
  "genesis_event_id",
@@ -280,6 +406,10 @@ function parseWitnessV3(value: unknown): EventV3ControlWitness {
280
406
  "root_id",
281
407
  "storage",
282
408
  ];
409
+ const expectedPayloadKeys =
410
+ controlState === "active"
411
+ ? [...commonPayloadKeys, "activation_event_id", "activation_id", "activation_manifest_digest"]
412
+ : commonPayloadKeys;
283
413
  if (Object.keys(payload).sort().join("\0") !== expectedPayloadKeys.sort().join("\0")) {
284
414
  throw new Error("control witness payload shape is invalid");
285
415
  }
@@ -289,13 +419,14 @@ function parseWitnessV3(value: unknown): EventV3ControlWitness {
289
419
  }
290
420
  if (
291
421
  payload.format !== "harnery-event-v3-control-witness" ||
292
- payload.format_version !== 1 ||
422
+ payload.format_version !== 2 ||
293
423
  !token(payload.genesis_id, "gex_") ||
294
- !token(payload.activation_id, "act_") ||
295
424
  !token(payload.genesis_event_id, "evt_") ||
296
- !token(payload.activation_event_id, "evt_") ||
425
+ (controlState === "active" &&
426
+ (!token(payload.activation_id, "act_") ||
427
+ !token(payload.activation_event_id, "evt_") ||
428
+ !digest(payload.activation_manifest_digest))) ||
297
429
  !digest(payload.genesis_manifest_digest) ||
298
- !digest(payload.activation_manifest_digest) ||
299
430
  !digest(payload.candidate_manifest_digest) ||
300
431
  !digest(payload.checkpoint_digest) ||
301
432
  !token(payload.privacy_key_epoch, "pep_") ||
@@ -3,7 +3,12 @@ import { join } from "node:path";
3
3
  import { buildEventV3 } from "./builder.ts";
4
4
  import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
5
5
  import type { EventV3 } from "./contract.ts";
6
- import { activeControlWitnessMatchesV3, publishActiveControlWitnessV3 } from "./control-witness.ts";
6
+ import {
7
+ activeControlWitnessMatchesV3,
8
+ candidateControlWitnessMatchesV3,
9
+ publishActiveControlWitnessV3,
10
+ publishCandidateControlWitnessV3,
11
+ } from "./control-witness.ts";
7
12
  import { EVENT_V3_SCHEMA_DIGEST } from "./generated.ts";
8
13
  import { activationIdV3, eventIdV3, genesisIdV3 } from "./ids.ts";
9
14
  import {
@@ -275,6 +280,19 @@ export function readEventV3ControlState(coordRoot: string): EventV3ControlState
275
280
  candidate_manifest_digest: candidateDigest,
276
281
  };
277
282
  }
283
+ // A candidate epoch answers from its own witness on the same terms. The
284
+ // absent activation manifest is part of the question, so the fast path is
285
+ // only taken when none is published: the moment one appears, this read goes
286
+ // back through canonical validation and can report the repairable window.
287
+ if (
288
+ !hasActivation &&
289
+ candidateControlWitnessMatchesV3(coordRoot, {
290
+ genesis,
291
+ candidate_manifest_digest: candidateDigest,
292
+ })
293
+ ) {
294
+ return { state: "candidate", genesis, candidate_manifest_digest: candidateDigest };
295
+ }
278
296
  const storageBeforeValidation = eventV3AuthorityStorageVersionV3(coordRoot);
279
297
  const ledger = readControlLedger(coordRoot);
280
298
  if (!ledger.complete) {
@@ -294,7 +312,21 @@ export function readEventV3ControlState(coordRoot: string): EventV3ControlState
294
312
  if (ledger.events.some(({ event }) => event.event_type === "ledger.activated")) {
295
313
  return { state: "invalid", reason: "activation_event_without_manifest" };
296
314
  }
297
- return { state: "candidate", genesis, candidate_manifest_digest: candidateDigest };
315
+ const candidate: Extract<EventV3ControlState, { state: "candidate" }> = {
316
+ state: "candidate",
317
+ genesis,
318
+ candidate_manifest_digest: candidateDigest,
319
+ };
320
+ const candidateCheckpoint = readEventV3AppendValidationCheckpointV3(coordRoot);
321
+ if (candidateCheckpoint) {
322
+ publishCandidateControlWitnessV3(
323
+ coordRoot,
324
+ candidate,
325
+ storageBeforeValidation,
326
+ candidateCheckpoint,
327
+ );
328
+ }
329
+ return candidate;
298
330
  }
299
331
 
300
332
  const activationResult =
@@ -1,3 +1,4 @@
1
+ export { EVENT_ADAPTER_IDS_V3, type EventAdapterIdV3 } from "./adapter-id.ts";
1
2
  export {
2
3
  type SchemaAdvanceEligibilityV3,
3
4
  validateAdditiveSchemaAdvanceV3,
@@ -45,10 +46,12 @@ export {
45
46
  ADAPTER_CAPABILITY_PROFILES_V3,
46
47
  ADAPTER_WAIT_KINDS_V3,
47
48
  type AdapterCapabilityProfileV3,
49
+ type AdapterDurationCapabilityV3,
48
50
  type AdapterSignalV3,
49
51
  type AdapterWaitKindV3,
50
52
  adapterCapabilityProfileDigestV3,
51
53
  adapterCapabilityProfileV3,
54
+ adapterDurationSupportV3,
52
55
  adapterSignalSupportV3,
53
56
  adapterWaitCoverageMatrixV3,
54
57
  adapterWaitKindSupportV3,
@@ -121,23 +121,40 @@ export function tryWriteLiveDisplayV3(
121
121
  }
122
122
  }
123
123
 
124
- /** Return every unexpired live-display row across generations. */
124
+ /**
125
+ * Return every unexpired live-display row across generations.
126
+ *
127
+ * A generation file whose last write is older than the hard TTL cannot hold a
128
+ * live row, so it is skipped on the stat alone. Without that guard a directory
129
+ * that has not been swept turns every read into a parse of days of residue.
130
+ */
125
131
  export function listLiveDisplayV3(
126
132
  coordRoot: string,
127
133
  now: () => Date = () => new Date(),
128
134
  ): LiveDisplayRowV3[] {
129
135
  const root = liveRoot(coordRoot);
130
136
  if (!existsSync(root)) return [];
137
+ const nowMs = now().getTime();
131
138
  const rows: LiveDisplayRowV3[] = [];
132
139
  for (const name of readdirSync(root)) {
133
140
  if (!name.endsWith(".ndjson")) continue;
134
141
  const generationId = name.slice(0, -".ndjson".length);
135
142
  if (!GENERATION_PATTERN.test(generationId)) continue;
143
+ if (fileHardExpiredV3(join(root, name), nowMs)) continue;
136
144
  rows.push(...readLiveDisplayV3(coordRoot, generationId, now));
137
145
  }
138
146
  return rows;
139
147
  }
140
148
 
149
+ /** True when the file's last write predates the hard TTL; a missing file counts as expired. */
150
+ function fileHardExpiredV3(path: string, nowMs: number): boolean {
151
+ try {
152
+ return statSync(path).mtimeMs + HARD_MAX_TTL_MS <= nowMs;
153
+ } catch {
154
+ return true;
155
+ }
156
+ }
157
+
141
158
  /** Return only unexpired, structurally valid rows. Expiry is enforced before cleanup. */
142
159
  export function readLiveDisplayV3(
143
160
  coordRoot: string,
@@ -180,10 +197,11 @@ export function janitorLiveDisplayV3(
180
197
  if (!name.endsWith(".ndjson")) continue;
181
198
  scanned += 1;
182
199
  const path = join(root, basename(name));
183
- const visible = GENERATION_PATTERN.test(name.slice(0, -".ndjson".length))
184
- ? readLiveDisplayV3(coordRoot, name.slice(0, -".ndjson".length), now)
185
- : [];
186
- const hardExpired = statSync(path).mtimeMs + HARD_MAX_TTL_MS <= nowMs;
200
+ const hardExpired = fileHardExpiredV3(path, nowMs);
201
+ const visible =
202
+ !hardExpired && GENERATION_PATTERN.test(name.slice(0, -".ndjson".length))
203
+ ? readLiveDisplayV3(coordRoot, name.slice(0, -".ndjson".length), now)
204
+ : [];
187
205
  if (visible.length === 0 || hardExpired) {
188
206
  rmSync(path, { force: true });
189
207
  removed += 1;
@@ -1,6 +1,10 @@
1
1
  import type { Adapter } from "../../adapter.ts";
2
2
  import type { ParsedPayload } from "../../hooks/adapter/parse.ts";
3
- import { refreshIncompatibleEventLedgerV3, rotateOversizedEventLedgerV3 } from "./bootstrap.ts";
3
+ import {
4
+ refreshIncompatibleEventLedgerV3,
5
+ repairStrandedEventLedgerV3Candidate,
6
+ rotateOversizedEventLedgerV3,
7
+ } from "./bootstrap.ts";
4
8
  import type { EventV3 } from "./contract.ts";
5
9
  import { readEventV3ControlState } from "./control.ts";
6
10
  import { repairEventV3ControlPair } from "./control-writer.ts";
@@ -54,7 +58,15 @@ export function resolveLiveEventLedgerRouteV3(coordRoot: string): LiveEventLedge
54
58
  };
55
59
  }
56
60
  }
57
- if (control.state === "active") {
61
+ if (control.state === "candidate") {
62
+ // An epoch is created and activated as one locked step, so a candidate on
63
+ // this boundary is a stranded epoch: a crashed or failed activation that
64
+ // every later hook would otherwise keep serving with a candidate-only
65
+ // write gate. Finish it here. A live bootstrap lease or any other refusal
66
+ // leaves the candidate serving and the next boundary retries.
67
+ control = repairStrandedEventLedgerV3Candidate(coordRoot).control;
68
+ }
69
+ if (control.state === "active" || control.state === "candidate") {
58
70
  // Bound the epoch every producer must re-validate: an oversized active
59
71
  // segment rotates into the archive here, on the shared route boundary.
60
72
  // Rotation failure never blocks recording; the current epoch stays live.