harnery 0.35.0 → 0.36.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 (616) hide show
  1. package/dist/commander.d.ts +30 -5
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +124 -96
  4. package/dist/commands/agents.d.ts +23 -3
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +379 -198
  7. package/dist/commands/backup.d.ts.map +1 -1
  8. package/dist/commands/backup.js +10 -10
  9. package/dist/commands/browse-session.d.ts.map +1 -1
  10. package/dist/commands/browse-session.js +12 -0
  11. package/dist/commands/browse.d.ts +1 -1
  12. package/dist/commands/browse.d.ts.map +1 -1
  13. package/dist/commands/browse.js +5 -4
  14. package/dist/commands/conversations.d.ts +10 -0
  15. package/dist/commands/conversations.d.ts.map +1 -0
  16. package/dist/commands/conversations.js +201 -0
  17. package/dist/commands/deinit.d.ts.map +1 -1
  18. package/dist/commands/deinit.js +5 -0
  19. package/dist/commands/docs.d.ts.map +1 -1
  20. package/dist/commands/docs.js +13 -2
  21. package/dist/commands/doctor.d.ts +5 -0
  22. package/dist/commands/doctor.d.ts.map +1 -1
  23. package/dist/commands/doctor.js +51 -7
  24. package/dist/commands/fetch.js +6 -5
  25. package/dist/commands/files.d.ts +22 -0
  26. package/dist/commands/files.d.ts.map +1 -0
  27. package/dist/commands/files.js +113 -0
  28. package/dist/commands/governor.js +14 -12
  29. package/dist/commands/inbox.d.ts +5 -0
  30. package/dist/commands/inbox.d.ts.map +1 -0
  31. package/dist/commands/inbox.js +57 -0
  32. package/dist/commands/init.d.ts +4 -5
  33. package/dist/commands/init.d.ts.map +1 -1
  34. package/dist/commands/init.js +63 -13
  35. package/dist/commands/ledger-v3.d.ts.map +1 -1
  36. package/dist/commands/ledger-v3.js +347 -1
  37. package/dist/commands/logs.d.ts +4 -0
  38. package/dist/commands/logs.d.ts.map +1 -0
  39. package/dist/commands/logs.js +169 -0
  40. package/dist/commands/messages.d.ts +5 -0
  41. package/dist/commands/messages.d.ts.map +1 -0
  42. package/dist/commands/messages.js +45 -0
  43. package/dist/commands/outline.d.ts +1 -1
  44. package/dist/commands/outline.d.ts.map +1 -1
  45. package/dist/commands/outline.js +2 -2
  46. package/dist/commands/read.d.ts +1 -1
  47. package/dist/commands/read.d.ts.map +1 -1
  48. package/dist/commands/read.js +2 -2
  49. package/dist/commands/semantic.d.ts.map +1 -1
  50. package/dist/commands/semantic.js +54 -2
  51. package/dist/commands/storage.d.ts +4 -0
  52. package/dist/commands/storage.d.ts.map +1 -0
  53. package/dist/commands/storage.js +238 -0
  54. package/dist/commands/tunnel.d.ts +5 -1
  55. package/dist/commands/tunnel.d.ts.map +1 -1
  56. package/dist/commands/tunnel.js +44 -31
  57. package/dist/commands/web.d.ts +3 -0
  58. package/dist/commands/web.d.ts.map +1 -1
  59. package/dist/commands/web.js +67 -7
  60. package/dist/commands/work.js +3 -3
  61. package/dist/commands/workflow.js +13 -13
  62. package/dist/core/adapters/profiles.d.ts +1 -1
  63. package/dist/core/agents/cli.js +209 -39
  64. package/dist/core/agents/live-authority-v3.d.ts +47 -1
  65. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  66. package/dist/core/agents/live-authority-v3.js +244 -10
  67. package/dist/core/agents/live-lifecycle-v3.js +6 -1
  68. package/dist/core/agents/live-observation-v3.d.ts.map +1 -1
  69. package/dist/core/agents/live-observation-v3.js +6 -1
  70. package/dist/core/agents/render/prompt-context.d.ts +9 -3
  71. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  72. package/dist/core/agents/render/prompt-context.js +24 -7
  73. package/dist/core/agents/rules/stop-hook.d.ts +2 -0
  74. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  75. package/dist/core/agents/rules/stop-hook.js +94 -13
  76. package/dist/core/agents/rules/verdict.d.ts +1 -0
  77. package/dist/core/agents/rules/verdict.d.ts.map +1 -1
  78. package/dist/core/agents/session-events.d.ts.map +1 -1
  79. package/dist/core/agents/session-events.js +4 -0
  80. package/dist/core/agents/session-finalization-state-v3.js +1 -1
  81. package/dist/core/agents/session-finalizer-v3.js +5 -1
  82. package/dist/core/agents/state/activity-log.d.ts +3 -0
  83. package/dist/core/agents/state/activity-log.d.ts.map +1 -1
  84. package/dist/core/agents/state/activity-log.js +6 -3
  85. package/dist/core/agents/state/heartbeat-writer.d.ts +6 -0
  86. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  87. package/dist/core/agents/state/heartbeat-writer.js +18 -0
  88. package/dist/core/agents/state/live-coordination-view.d.ts +5 -7
  89. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  90. package/dist/core/agents/state/live-coordination-view.js +15 -90
  91. package/dist/core/agents/state/live-coordination-writer.d.ts +12 -0
  92. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -0
  93. package/dist/core/agents/state/live-coordination-writer.js +92 -0
  94. package/dist/core/artifacts/index.d.ts +21 -0
  95. package/dist/core/artifacts/index.d.ts.map +1 -1
  96. package/dist/core/artifacts/index.js +46 -1
  97. package/dist/core/config.d.ts +62 -4
  98. package/dist/core/config.d.ts.map +1 -1
  99. package/dist/core/config.js +98 -4
  100. package/dist/core/conversations/archive.d.ts +49 -0
  101. package/dist/core/conversations/archive.d.ts.map +1 -0
  102. package/dist/core/conversations/archive.js +136 -0
  103. package/dist/core/conversations/catalog.d.ts +10 -0
  104. package/dist/core/conversations/catalog.d.ts.map +1 -0
  105. package/dist/core/conversations/catalog.js +72 -0
  106. package/dist/core/conversations/context-pack.d.ts +6 -0
  107. package/dist/core/conversations/context-pack.d.ts.map +1 -0
  108. package/dist/core/conversations/context-pack.js +60 -0
  109. package/dist/core/conversations/contract.d.ts +155 -0
  110. package/dist/core/conversations/contract.d.ts.map +1 -0
  111. package/dist/core/conversations/contract.js +1 -0
  112. package/dist/core/conversations/evaluation.d.ts +16 -0
  113. package/dist/core/conversations/evaluation.d.ts.map +1 -0
  114. package/dist/core/conversations/evaluation.js +9 -0
  115. package/dist/core/conversations/index.d.ts +10 -0
  116. package/dist/core/conversations/index.d.ts.map +1 -0
  117. package/dist/core/conversations/index.js +9 -0
  118. package/dist/core/conversations/lifecycle.d.ts +28 -0
  119. package/dist/core/conversations/lifecycle.d.ts.map +1 -0
  120. package/dist/core/conversations/lifecycle.js +29 -0
  121. package/dist/core/conversations/normalize.d.ts +14 -0
  122. package/dist/core/conversations/normalize.d.ts.map +1 -0
  123. package/dist/core/conversations/normalize.js +82 -0
  124. package/dist/core/conversations/query.d.ts +8 -0
  125. package/dist/core/conversations/query.d.ts.map +1 -0
  126. package/dist/core/conversations/query.js +280 -0
  127. package/dist/core/conversations/search-index.d.ts +9 -0
  128. package/dist/core/conversations/search-index.d.ts.map +1 -0
  129. package/dist/core/conversations/search-index.js +29 -0
  130. package/dist/core/events/legacy-storage/canary.d.ts +18 -0
  131. package/dist/core/events/legacy-storage/canary.d.ts.map +1 -0
  132. package/dist/core/events/legacy-storage/canary.js +150 -0
  133. package/dist/core/events/legacy-storage/fence.d.ts +15 -0
  134. package/dist/core/events/legacy-storage/fence.d.ts.map +1 -0
  135. package/dist/core/events/legacy-storage/fence.js +115 -0
  136. package/dist/core/events/legacy-storage/index.d.ts +6 -0
  137. package/dist/core/events/legacy-storage/index.d.ts.map +1 -0
  138. package/dist/core/events/legacy-storage/index.js +5 -0
  139. package/dist/core/events/legacy-storage/inventory.d.ts +10 -0
  140. package/dist/core/events/legacy-storage/inventory.d.ts.map +1 -0
  141. package/dist/core/events/legacy-storage/inventory.js +40 -0
  142. package/dist/core/events/legacy-storage/manifest.d.ts +23 -0
  143. package/dist/core/events/legacy-storage/manifest.d.ts.map +1 -0
  144. package/dist/core/events/legacy-storage/manifest.js +108 -0
  145. package/dist/core/events/legacy-storage/reader.d.ts +15 -0
  146. package/dist/core/events/legacy-storage/reader.d.ts.map +1 -0
  147. package/dist/core/events/legacy-storage/reader.js +154 -0
  148. package/dist/core/events/v3/authority-outbox.d.ts +7 -2
  149. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  150. package/dist/core/events/v3/authority-outbox.js +16 -3
  151. package/dist/core/events/v3/base-contract.d.ts +81 -41
  152. package/dist/core/events/v3/base-contract.d.ts.map +1 -1
  153. package/dist/core/events/v3/base-contract.js +17 -7
  154. package/dist/core/events/v3/bootstrap.d.ts +39 -0
  155. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  156. package/dist/core/events/v3/bootstrap.js +167 -3
  157. package/dist/core/events/v3/capabilities.js +3 -3
  158. package/dist/core/events/v3/control-witness.d.ts +44 -0
  159. package/dist/core/events/v3/control-witness.d.ts.map +1 -0
  160. package/dist/core/events/v3/control-witness.js +233 -0
  161. package/dist/core/events/v3/control-writer.d.ts +4 -0
  162. package/dist/core/events/v3/control-writer.d.ts.map +1 -0
  163. package/dist/core/events/v3/control-writer.js +16 -0
  164. package/dist/core/events/v3/control.d.ts +8 -2
  165. package/dist/core/events/v3/control.d.ts.map +1 -1
  166. package/dist/core/events/v3/control.js +46 -18
  167. package/dist/core/events/v3/coordination-transaction-recovery.d.ts +42 -0
  168. package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -0
  169. package/dist/core/events/v3/coordination-transaction-recovery.js +342 -0
  170. package/dist/core/events/v3/coordination-view.d.ts.map +1 -1
  171. package/dist/core/events/v3/coordination-view.js +16 -1
  172. package/dist/core/events/v3/generated.d.ts +1 -1
  173. package/dist/core/events/v3/generated.js +1 -1
  174. package/dist/core/events/v3/index.d.ts +4 -2
  175. package/dist/core/events/v3/index.d.ts.map +1 -1
  176. package/dist/core/events/v3/index.js +3 -1
  177. package/dist/core/events/v3/live-route-observer.d.ts +31 -0
  178. package/dist/core/events/v3/live-route-observer.d.ts.map +1 -0
  179. package/dist/core/events/v3/live-route-observer.js +58 -0
  180. package/dist/core/events/v3/live-routing.d.ts +13 -21
  181. package/dist/core/events/v3/live-routing.d.ts.map +1 -1
  182. package/dist/core/events/v3/live-routing.js +45 -41
  183. package/dist/core/events/v3/producers/coordination-recorder.d.ts +11 -1
  184. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  185. package/dist/core/events/v3/producers/coordination-recorder.js +76 -10
  186. package/dist/core/events/v3/producers/diagnostic-summaries.d.ts +129 -0
  187. package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -0
  188. package/dist/core/events/v3/producers/diagnostic-summaries.js +499 -0
  189. package/dist/core/events/v3/producers/hook-base.js +1 -1
  190. package/dist/core/events/v3/producers/intake.d.ts +7 -0
  191. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  192. package/dist/core/events/v3/producers/intake.js +25 -2
  193. package/dist/core/events/v3/producers/producer-state-observer.d.ts +8 -0
  194. package/dist/core/events/v3/producers/producer-state-observer.d.ts.map +1 -0
  195. package/dist/core/events/v3/producers/producer-state-observer.js +45 -0
  196. package/dist/core/events/v3/producers/recorder.d.ts +55 -1
  197. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  198. package/dist/core/events/v3/producers/recorder.js +353 -24
  199. package/dist/core/events/v3/reader.d.ts +51 -0
  200. package/dist/core/events/v3/reader.d.ts.map +1 -1
  201. package/dist/core/events/v3/reader.js +181 -1
  202. package/dist/core/events/v3/rotation-config.d.ts +9 -0
  203. package/dist/core/events/v3/rotation-config.d.ts.map +1 -0
  204. package/dist/core/events/v3/rotation-config.js +91 -0
  205. package/dist/core/events/v3/runtime-build.d.ts +11 -0
  206. package/dist/core/events/v3/runtime-build.d.ts.map +1 -0
  207. package/dist/core/events/v3/runtime-build.js +39 -0
  208. package/dist/core/events/v3/runtime-identity.d.ts +5 -0
  209. package/dist/core/events/v3/runtime-identity.d.ts.map +1 -0
  210. package/dist/core/events/v3/runtime-identity.js +18 -0
  211. package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts +6 -5
  212. package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts.map +1 -1
  213. package/dist/core/events/v3/runtime-telemetry-capabilities.js +1 -1
  214. package/dist/core/events/v3/support-storage/classify.d.ts +46 -0
  215. package/dist/core/events/v3/support-storage/classify.d.ts.map +1 -0
  216. package/dist/core/events/v3/support-storage/classify.js +149 -0
  217. package/dist/core/events/v3/support-storage/eligibility.d.ts +29 -0
  218. package/dist/core/events/v3/support-storage/eligibility.d.ts.map +1 -0
  219. package/dist/core/events/v3/support-storage/eligibility.js +35 -0
  220. package/dist/core/events/v3/support-storage/index.d.ts +10 -0
  221. package/dist/core/events/v3/support-storage/index.d.ts.map +1 -0
  222. package/dist/core/events/v3/support-storage/index.js +9 -0
  223. package/dist/core/events/v3/support-storage/inventory.d.ts +30 -0
  224. package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -0
  225. package/dist/core/events/v3/support-storage/inventory.js +94 -0
  226. package/dist/core/events/v3/support-storage/logical-authority.d.ts +14 -0
  227. package/dist/core/events/v3/support-storage/logical-authority.d.ts.map +1 -0
  228. package/dist/core/events/v3/support-storage/logical-authority.js +104 -0
  229. package/dist/core/events/v3/support-storage/maintenance.d.ts +98 -0
  230. package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -0
  231. package/dist/core/events/v3/support-storage/maintenance.js +352 -0
  232. package/dist/core/events/v3/support-storage/pack-contract.d.ts +59 -0
  233. package/dist/core/events/v3/support-storage/pack-contract.d.ts.map +1 -0
  234. package/dist/core/events/v3/support-storage/pack-contract.js +249 -0
  235. package/dist/core/events/v3/support-storage/pack-reader.d.ts +34 -0
  236. package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -0
  237. package/dist/core/events/v3/support-storage/pack-reader.js +258 -0
  238. package/dist/core/events/v3/support-storage/pack-writer.d.ts +30 -0
  239. package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -0
  240. package/dist/core/events/v3/support-storage/pack-writer.js +268 -0
  241. package/dist/core/events/v3/support-storage/recovery.d.ts +15 -0
  242. package/dist/core/events/v3/support-storage/recovery.d.ts.map +1 -0
  243. package/dist/core/events/v3/support-storage/recovery.js +22 -0
  244. package/dist/core/events/v3/turn-telemetry.d.ts +14 -8
  245. package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -1
  246. package/dist/core/events/v3/turn-telemetry.js +32 -2
  247. package/dist/core/events/v3/writer.d.ts +13 -1
  248. package/dist/core/events/v3/writer.d.ts.map +1 -1
  249. package/dist/core/events/v3/writer.js +126 -12
  250. package/dist/core/governor/plan-read.d.ts.map +1 -1
  251. package/dist/core/governor/plan-read.js +5 -21
  252. package/dist/core/governor/planning.js +6 -8
  253. package/dist/core/governor/service-read.d.ts +12 -0
  254. package/dist/core/governor/service-read.d.ts.map +1 -1
  255. package/dist/core/governor/service-read.js +266 -2
  256. package/dist/core/governor/service.d.ts +1 -0
  257. package/dist/core/governor/service.d.ts.map +1 -1
  258. package/dist/core/governor/service.js +53 -18
  259. package/dist/core/guard/evidence-v3.d.ts.map +1 -1
  260. package/dist/core/guard/evidence-v3.js +2 -0
  261. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  262. package/dist/core/hooks/adapter/events.js +1 -0
  263. package/dist/core/hooks/adapter/output.d.ts +4 -2
  264. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  265. package/dist/core/hooks/adapter/output.js +13 -5
  266. package/dist/core/hooks/adapter/runtime-telemetry.d.ts +27 -4
  267. package/dist/core/hooks/adapter/runtime-telemetry.d.ts.map +1 -1
  268. package/dist/core/hooks/adapter/runtime-telemetry.js +307 -6
  269. package/dist/core/hooks/cli.js +212 -30
  270. package/dist/core/hooks/effects/image-capture.d.ts +1 -1
  271. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  272. package/dist/core/hooks/effects/image-capture.js +4 -2
  273. package/dist/core/hooks/remediation-cap.d.ts +8 -3
  274. package/dist/core/hooks/remediation-cap.d.ts.map +1 -1
  275. package/dist/core/hooks/remediation-cap.js +3 -11
  276. package/dist/core/hooks/resolve/coord-root.d.ts +22 -9
  277. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -1
  278. package/dist/core/hooks/resolve/coord-root.js +92 -11
  279. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  280. package/dist/core/hooks/resolve/owner.js +5 -0
  281. package/dist/core/hooks/resolve/transcript.d.ts +8 -0
  282. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  283. package/dist/core/hooks/resolve/transcript.js +11 -0
  284. package/dist/core/inbox/contract.d.ts +64 -0
  285. package/dist/core/inbox/contract.d.ts.map +1 -0
  286. package/dist/core/inbox/contract.js +13 -0
  287. package/dist/core/inbox/index.d.ts +5 -0
  288. package/dist/core/inbox/index.d.ts.map +1 -0
  289. package/dist/core/inbox/index.js +4 -0
  290. package/dist/core/inbox/lifecycle.d.ts +22 -0
  291. package/dist/core/inbox/lifecycle.d.ts.map +1 -0
  292. package/dist/core/inbox/lifecycle.js +41 -0
  293. package/dist/core/inbox/service.d.ts +36 -0
  294. package/dist/core/inbox/service.d.ts.map +1 -0
  295. package/dist/core/inbox/service.js +210 -0
  296. package/dist/core/inbox/watcher.d.ts +7 -0
  297. package/dist/core/inbox/watcher.d.ts.map +1 -0
  298. package/dist/core/inbox/watcher.js +26 -0
  299. package/dist/core/presence/codec-digest.d.ts.map +1 -1
  300. package/dist/core/presence/codec-digest.js +14 -3
  301. package/dist/core/presence/relay-client.d.ts +1 -0
  302. package/dist/core/presence/relay-client.d.ts.map +1 -1
  303. package/dist/core/presence/relay-client.js +52 -8
  304. package/dist/core/semantic/adapters.d.ts +11 -8
  305. package/dist/core/semantic/adapters.d.ts.map +1 -1
  306. package/dist/core/semantic/adapters.js +169 -31
  307. package/dist/core/semantic/contract.d.ts +532 -48
  308. package/dist/core/semantic/contract.d.ts.map +1 -1
  309. package/dist/core/semantic/contract.js +104 -3
  310. package/dist/core/semantic/index.d.ts +7 -3
  311. package/dist/core/semantic/index.d.ts.map +1 -1
  312. package/dist/core/semantic/index.js +7 -3
  313. package/dist/core/semantic/once.d.ts +6 -1
  314. package/dist/core/semantic/once.d.ts.map +1 -1
  315. package/dist/core/semantic/once.js +80 -9
  316. package/dist/core/semantic/prompt.d.ts.map +1 -1
  317. package/dist/core/semantic/prompt.js +14 -1
  318. package/dist/core/semantic/review.d.ts +144 -0
  319. package/dist/core/semantic/review.d.ts.map +1 -0
  320. package/dist/core/semantic/review.js +544 -0
  321. package/dist/core/semantic/routes.d.ts +10 -0
  322. package/dist/core/semantic/routes.d.ts.map +1 -0
  323. package/dist/core/semantic/routes.js +21 -0
  324. package/dist/core/semantic/scheduler.d.ts +14 -3
  325. package/dist/core/semantic/scheduler.d.ts.map +1 -1
  326. package/dist/core/semantic/scheduler.js +30 -6
  327. package/dist/core/semantic/service-status.d.ts +20 -0
  328. package/dist/core/semantic/service-status.d.ts.map +1 -1
  329. package/dist/core/semantic/service-status.js +60 -4
  330. package/dist/core/semantic/service.d.ts +1 -0
  331. package/dist/core/semantic/service.d.ts.map +1 -1
  332. package/dist/core/semantic/service.js +104 -15
  333. package/dist/core/semantic/soak.d.ts +75 -0
  334. package/dist/core/semantic/soak.d.ts.map +1 -0
  335. package/dist/core/semantic/soak.js +476 -0
  336. package/dist/core/semantic/storage.d.ts +11 -1
  337. package/dist/core/semantic/storage.d.ts.map +1 -1
  338. package/dist/core/semantic/storage.js +4 -3
  339. package/dist/core/semantic/usage.d.ts +43 -0
  340. package/dist/core/semantic/usage.d.ts.map +1 -0
  341. package/dist/core/semantic/usage.js +193 -0
  342. package/dist/core/semantic/validate.d.ts +3 -1
  343. package/dist/core/semantic/validate.d.ts.map +1 -1
  344. package/dist/core/semantic/validate.js +34 -1
  345. package/dist/core/storage/buffer.d.ts +28 -0
  346. package/dist/core/storage/buffer.d.ts.map +1 -0
  347. package/dist/core/storage/buffer.js +101 -0
  348. package/dist/core/storage/builtins.d.ts +3 -0
  349. package/dist/core/storage/builtins.d.ts.map +1 -0
  350. package/dist/core/storage/builtins.js +683 -0
  351. package/dist/core/storage/catalog.d.ts +19 -0
  352. package/dist/core/storage/catalog.d.ts.map +1 -0
  353. package/dist/core/storage/catalog.js +365 -0
  354. package/dist/core/storage/config.d.ts +16 -0
  355. package/dist/core/storage/config.d.ts.map +1 -0
  356. package/dist/core/storage/config.js +311 -0
  357. package/dist/core/storage/contract.d.ts +275 -0
  358. package/dist/core/storage/contract.d.ts.map +1 -0
  359. package/dist/core/storage/contract.js +3 -0
  360. package/dist/core/storage/durable-history.d.ts +36 -0
  361. package/dist/core/storage/durable-history.d.ts.map +1 -0
  362. package/dist/core/storage/durable-history.js +606 -0
  363. package/dist/core/storage/health.d.ts +4 -0
  364. package/dist/core/storage/health.d.ts.map +1 -0
  365. package/dist/core/storage/health.js +65 -0
  366. package/dist/core/storage/index.d.ts +16 -0
  367. package/dist/core/storage/index.d.ts.map +1 -0
  368. package/dist/core/storage/index.js +15 -0
  369. package/dist/core/storage/inventory.d.ts +25 -0
  370. package/dist/core/storage/inventory.d.ts.map +1 -0
  371. package/dist/core/storage/inventory.js +666 -0
  372. package/dist/core/storage/jsonl.d.ts +52 -0
  373. package/dist/core/storage/jsonl.d.ts.map +1 -0
  374. package/dist/core/storage/jsonl.js +156 -0
  375. package/dist/core/storage/log-retention.d.ts +52 -0
  376. package/dist/core/storage/log-retention.d.ts.map +1 -0
  377. package/dist/core/storage/log-retention.js +419 -0
  378. package/dist/core/storage/logger.d.ts +82 -0
  379. package/dist/core/storage/logger.d.ts.map +1 -0
  380. package/dist/core/storage/logger.js +411 -0
  381. package/dist/core/storage/maintenance-providers.d.ts +26 -0
  382. package/dist/core/storage/maintenance-providers.d.ts.map +1 -0
  383. package/dist/core/storage/maintenance-providers.js +141 -0
  384. package/dist/core/storage/maintenance.d.ts +125 -0
  385. package/dist/core/storage/maintenance.d.ts.map +1 -0
  386. package/dist/core/storage/maintenance.js +609 -0
  387. package/dist/core/storage/metrics.d.ts +60 -0
  388. package/dist/core/storage/metrics.d.ts.map +1 -0
  389. package/dist/core/storage/metrics.js +134 -0
  390. package/dist/core/storage/policy.d.ts +7 -0
  391. package/dist/core/storage/policy.d.ts.map +1 -0
  392. package/dist/core/storage/policy.js +110 -0
  393. package/dist/core/storage/process-log.d.ts +38 -0
  394. package/dist/core/storage/process-log.d.ts.map +1 -0
  395. package/dist/core/storage/process-log.js +165 -0
  396. package/dist/core/storage/query.d.ts +34 -0
  397. package/dist/core/storage/query.d.ts.map +1 -0
  398. package/dist/core/storage/query.js +295 -0
  399. package/dist/core/storage/receipts.d.ts +23 -0
  400. package/dist/core/storage/receipts.d.ts.map +1 -0
  401. package/dist/core/storage/receipts.js +117 -0
  402. package/dist/core/storage/segments.d.ts +84 -0
  403. package/dist/core/storage/segments.d.ts.map +1 -0
  404. package/dist/core/storage/segments.js +542 -0
  405. package/dist/core/storage/sink-graph.d.ts +65 -0
  406. package/dist/core/storage/sink-graph.d.ts.map +1 -0
  407. package/dist/core/storage/sink-graph.js +213 -0
  408. package/dist/core/storage/storm-control.d.ts +23 -0
  409. package/dist/core/storage/storm-control.d.ts.map +1 -0
  410. package/dist/core/storage/storm-control.js +59 -0
  411. package/dist/core/work/state.d.ts.map +1 -1
  412. package/dist/core/work/state.js +10 -14
  413. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  414. package/dist/core/workflow/live-session-v3.js +2 -1
  415. package/dist/index.d.ts +2 -0
  416. package/dist/index.d.ts.map +1 -1
  417. package/dist/index.js +2 -0
  418. package/dist/lazy-commands.d.ts +26 -0
  419. package/dist/lazy-commands.d.ts.map +1 -0
  420. package/dist/lazy-commands.js +210 -0
  421. package/dist/lib/docs-lint.d.ts +2 -0
  422. package/dist/lib/docs-lint.d.ts.map +1 -1
  423. package/dist/lib/docs-lint.js +45 -80
  424. package/dist/lib/docs-metadata-audit.d.ts +3 -0
  425. package/dist/lib/docs-metadata-audit.d.ts.map +1 -1
  426. package/dist/lib/docs-metadata-audit.js +13 -23
  427. package/dist/lib/docs-metadata-sync.d.ts +1 -0
  428. package/dist/lib/docs-metadata-sync.d.ts.map +1 -1
  429. package/dist/lib/docs-metadata-sync.js +28 -10
  430. package/dist/lib/docs-repository-view.d.ts +22 -0
  431. package/dist/lib/docs-repository-view.d.ts.map +1 -0
  432. package/dist/lib/docs-repository-view.js +185 -0
  433. package/dist/lib/http/client.d.ts +11 -5
  434. package/dist/lib/http/client.d.ts.map +1 -1
  435. package/dist/lib/http/client.js +22 -4
  436. package/dist/lib/http/index.d.ts +1 -1
  437. package/dist/lib/http/index.d.ts.map +1 -1
  438. package/dist/lib/http/index.js +1 -1
  439. package/dist/lib/indexer-exclusions.d.ts +27 -0
  440. package/dist/lib/indexer-exclusions.d.ts.map +1 -0
  441. package/dist/lib/indexer-exclusions.js +176 -0
  442. package/dist/lib/instructions/templates.d.ts.map +1 -1
  443. package/dist/lib/instructions/templates.js +5 -0
  444. package/dist/lib/local-file-url.d.ts +9 -0
  445. package/dist/lib/local-file-url.d.ts.map +1 -0
  446. package/dist/lib/local-file-url.js +20 -0
  447. package/dist/lib/web-performance.d.ts +118 -0
  448. package/dist/lib/web-performance.d.ts.map +1 -0
  449. package/dist/lib/web-performance.js +192 -0
  450. package/package.json +29 -2
  451. package/schemas/config.schema.json +58 -0
  452. package/schemas/event-v3.schema.json +324 -112
  453. package/src/commander.ts +462 -95
  454. package/src/commands/agents.ts +517 -190
  455. package/src/commands/backup.ts +18 -14
  456. package/src/commands/browse-session.ts +12 -0
  457. package/src/commands/browse.ts +6 -7
  458. package/src/commands/conversations.ts +279 -0
  459. package/src/commands/deinit.ts +6 -0
  460. package/src/commands/docs.ts +25 -6
  461. package/src/commands/doctor.ts +75 -9
  462. package/src/commands/fetch.ts +7 -6
  463. package/src/commands/files.ts +143 -0
  464. package/src/commands/governor.ts +13 -12
  465. package/src/commands/inbox.ts +61 -0
  466. package/src/commands/init.ts +78 -16
  467. package/src/commands/ledger-v3.ts +421 -1
  468. package/src/commands/logs.ts +224 -0
  469. package/src/commands/messages.ts +61 -0
  470. package/src/commands/outline.ts +6 -2
  471. package/src/commands/read.ts +6 -2
  472. package/src/commands/semantic.ts +66 -1
  473. package/src/commands/storage.ts +310 -0
  474. package/src/commands/tunnel.ts +66 -52
  475. package/src/commands/web.ts +77 -7
  476. package/src/commands/work.ts +3 -3
  477. package/src/commands/workflow.ts +13 -13
  478. package/src/core/agents/cli.ts +259 -53
  479. package/src/core/agents/live-authority-v3.ts +341 -10
  480. package/src/core/agents/live-lifecycle-v3.ts +6 -1
  481. package/src/core/agents/live-observation-v3.ts +6 -1
  482. package/src/core/agents/render/prompt-context.ts +27 -6
  483. package/src/core/agents/rules/stop-hook.ts +107 -16
  484. package/src/core/agents/rules/verdict.ts +1 -0
  485. package/src/core/agents/session-events.ts +4 -0
  486. package/src/core/agents/session-finalization-state-v3.ts +1 -1
  487. package/src/core/agents/session-finalizer-v3.ts +4 -1
  488. package/src/core/agents/state/activity-log.ts +7 -2
  489. package/src/core/agents/state/heartbeat-writer.ts +27 -0
  490. package/src/core/agents/state/live-coordination-view.ts +16 -105
  491. package/src/core/agents/state/live-coordination-writer.ts +141 -0
  492. package/src/core/artifacts/index.ts +64 -1
  493. package/src/core/config.ts +126 -5
  494. package/src/core/conversations/archive.ts +237 -0
  495. package/src/core/conversations/catalog.ts +79 -0
  496. package/src/core/conversations/context-pack.ts +72 -0
  497. package/src/core/conversations/contract.ts +178 -0
  498. package/src/core/conversations/evaluation.ts +30 -0
  499. package/src/core/conversations/index.ts +9 -0
  500. package/src/core/conversations/lifecycle.ts +63 -0
  501. package/src/core/conversations/normalize.ts +105 -0
  502. package/src/core/conversations/query.ts +318 -0
  503. package/src/core/conversations/search-index.ts +48 -0
  504. package/src/core/events/legacy-storage/canary.ts +177 -0
  505. package/src/core/events/legacy-storage/fence.ts +129 -0
  506. package/src/core/events/legacy-storage/index.ts +5 -0
  507. package/src/core/events/legacy-storage/inventory.ts +51 -0
  508. package/src/core/events/legacy-storage/manifest.ts +136 -0
  509. package/src/core/events/legacy-storage/reader.ts +178 -0
  510. package/src/core/events/v3/authority-outbox.ts +22 -1
  511. package/src/core/events/v3/base-contract.ts +17 -7
  512. package/src/core/events/v3/bootstrap.ts +195 -2
  513. package/src/core/events/v3/capabilities.ts +3 -3
  514. package/src/core/events/v3/control-witness.ts +339 -0
  515. package/src/core/events/v3/control-writer.ts +16 -0
  516. package/src/core/events/v3/control.ts +54 -18
  517. package/src/core/events/v3/coordination-transaction-recovery.ts +477 -0
  518. package/src/core/events/v3/coordination-view.ts +17 -1
  519. package/src/core/events/v3/generated.ts +1 -1
  520. package/src/core/events/v3/index.ts +5 -2
  521. package/src/core/events/v3/live-route-observer.ts +80 -0
  522. package/src/core/events/v3/live-routing.ts +64 -46
  523. package/src/core/events/v3/producers/coordination-recorder.ts +95 -10
  524. package/src/core/events/v3/producers/diagnostic-summaries.ts +593 -0
  525. package/src/core/events/v3/producers/hook-base.ts +1 -1
  526. package/src/core/events/v3/producers/intake.ts +30 -2
  527. package/src/core/events/v3/producers/producer-state-observer.ts +52 -0
  528. package/src/core/events/v3/producers/recorder.ts +571 -33
  529. package/src/core/events/v3/reader.ts +243 -2
  530. package/src/core/events/v3/rotation-config.ts +98 -0
  531. package/src/core/events/v3/runtime-build.ts +41 -0
  532. package/src/core/events/v3/runtime-identity.ts +16 -0
  533. package/src/core/events/v3/runtime-telemetry-capabilities.ts +14 -8
  534. package/src/core/events/v3/support-storage/classify.ts +209 -0
  535. package/src/core/events/v3/support-storage/eligibility.ts +58 -0
  536. package/src/core/events/v3/support-storage/index.ts +9 -0
  537. package/src/core/events/v3/support-storage/inventory.ts +135 -0
  538. package/src/core/events/v3/support-storage/logical-authority.ts +117 -0
  539. package/src/core/events/v3/support-storage/maintenance.ts +501 -0
  540. package/src/core/events/v3/support-storage/pack-contract.ts +337 -0
  541. package/src/core/events/v3/support-storage/pack-reader.ts +346 -0
  542. package/src/core/events/v3/support-storage/pack-writer.ts +352 -0
  543. package/src/core/events/v3/support-storage/recovery.ts +43 -0
  544. package/src/core/events/v3/turn-telemetry.ts +58 -7
  545. package/src/core/events/v3/writer.ts +168 -16
  546. package/src/core/governor/plan-read.ts +5 -18
  547. package/src/core/governor/planning.ts +5 -8
  548. package/src/core/governor/service-read.ts +320 -2
  549. package/src/core/governor/service.ts +49 -22
  550. package/src/core/guard/evidence-v3.ts +1 -0
  551. package/src/core/hooks/adapter/events.ts +1 -0
  552. package/src/core/hooks/adapter/output.ts +12 -5
  553. package/src/core/hooks/adapter/runtime-telemetry.ts +408 -13
  554. package/src/core/hooks/cli.ts +226 -32
  555. package/src/core/hooks/effects/image-capture.ts +4 -1
  556. package/src/core/hooks/remediation-cap.ts +12 -6
  557. package/src/core/hooks/resolve/coord-root.ts +91 -11
  558. package/src/core/hooks/resolve/owner.ts +4 -0
  559. package/src/core/hooks/resolve/transcript.ts +18 -0
  560. package/src/core/inbox/contract.ts +81 -0
  561. package/src/core/inbox/index.ts +4 -0
  562. package/src/core/inbox/lifecycle.ts +104 -0
  563. package/src/core/inbox/service.ts +305 -0
  564. package/src/core/inbox/watcher.ts +36 -0
  565. package/src/core/presence/codec-digest.ts +15 -9
  566. package/src/core/presence/relay-client.ts +55 -7
  567. package/src/core/semantic/adapters.ts +200 -41
  568. package/src/core/semantic/contract.ts +135 -3
  569. package/src/core/semantic/index.ts +76 -2
  570. package/src/core/semantic/once.ts +93 -7
  571. package/src/core/semantic/prompt.ts +13 -1
  572. package/src/core/semantic/review.ts +803 -0
  573. package/src/core/semantic/routes.ts +30 -0
  574. package/src/core/semantic/scheduler.ts +49 -5
  575. package/src/core/semantic/service-status.ts +99 -5
  576. package/src/core/semantic/service.ts +116 -15
  577. package/src/core/semantic/soak.ts +638 -0
  578. package/src/core/semantic/storage.ts +20 -6
  579. package/src/core/semantic/usage.ts +272 -0
  580. package/src/core/semantic/validate.ts +31 -0
  581. package/src/core/storage/buffer.ts +124 -0
  582. package/src/core/storage/builtins.ts +916 -0
  583. package/src/core/storage/catalog.ts +467 -0
  584. package/src/core/storage/config.ts +423 -0
  585. package/src/core/storage/contract.ts +342 -0
  586. package/src/core/storage/durable-history.ts +738 -0
  587. package/src/core/storage/health.ts +76 -0
  588. package/src/core/storage/index.ts +15 -0
  589. package/src/core/storage/inventory.ts +859 -0
  590. package/src/core/storage/jsonl.ts +213 -0
  591. package/src/core/storage/log-retention.ts +555 -0
  592. package/src/core/storage/logger.ts +546 -0
  593. package/src/core/storage/maintenance-providers.ts +200 -0
  594. package/src/core/storage/maintenance.ts +929 -0
  595. package/src/core/storage/metrics.ts +199 -0
  596. package/src/core/storage/policy.ts +141 -0
  597. package/src/core/storage/process-log.ts +203 -0
  598. package/src/core/storage/query.ts +360 -0
  599. package/src/core/storage/receipts.ts +162 -0
  600. package/src/core/storage/segments.ts +721 -0
  601. package/src/core/storage/sink-graph.ts +343 -0
  602. package/src/core/storage/storm-control.ts +91 -0
  603. package/src/core/work/state.ts +12 -21
  604. package/src/core/workflow/live-session-v3.ts +2 -4
  605. package/src/index.ts +2 -0
  606. package/src/lazy-commands.ts +294 -0
  607. package/src/lib/docs-lint.ts +58 -77
  608. package/src/lib/docs-metadata-audit.ts +26 -21
  609. package/src/lib/docs-metadata-sync.ts +32 -9
  610. package/src/lib/docs-repository-view.ts +220 -0
  611. package/src/lib/http/client.ts +44 -8
  612. package/src/lib/http/index.ts +6 -1
  613. package/src/lib/indexer-exclusions.ts +173 -0
  614. package/src/lib/instructions/templates.ts +5 -0
  615. package/src/lib/local-file-url.ts +31 -0
  616. package/src/lib/web-performance.ts +335 -0
@@ -0,0 +1,593 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import {
3
+ appendFileSync,
4
+ chmodSync,
5
+ closeSync,
6
+ existsSync,
7
+ fsyncSync,
8
+ lstatSync,
9
+ mkdirSync,
10
+ openSync,
11
+ readdirSync,
12
+ readFileSync,
13
+ renameSync,
14
+ rmdirSync,
15
+ statSync,
16
+ unlinkSync,
17
+ writeFileSync,
18
+ } from "node:fs";
19
+ import { hostname } from "node:os";
20
+ import { join, resolve } from "node:path";
21
+ import { coordEnv } from "../../../../lib/env.ts";
22
+ import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
23
+ import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.ts";
24
+ import { EVENT_V3_LEDGER_RELATIVE_ROOT } from "../writer.ts";
25
+
26
+ /**
27
+ * Producer-side bound on duplicate diagnostic file creation.
28
+ *
29
+ * A producer fault can emit the same diagnostic thousands of times per day,
30
+ * and every emission used to become one loose file in the diagnostics spool.
31
+ * This module keeps the first bounded loose exemplars per
32
+ * (category, reason, instance) key and UTC day unchanged, then coalesces
33
+ * further identical emissions into one deterministic summary file per key and
34
+ * window, preserving the exact logical count, first and last times,
35
+ * represented bytes, hourly rate buckets, and bounded exemplar digests.
36
+ *
37
+ * Below the bound the gate creates no state at all: admitted loose filenames
38
+ * carry the key digest as a suffix, and the gate counts a key's emissions for
39
+ * the window from those filenames alone. A summary file and its short-lived
40
+ * per-key lease exist only for a key that actually crossed the bound, so a
41
+ * healthy spool gains zero files and zero directories from this mitigation.
42
+ *
43
+ * The mitigation is fail-open: any count, lease, read, or publish failure
44
+ * admits the loose diagnostic exactly as before and appends to a lock-free,
45
+ * size-capped emergency log. It never deletes, moves, or rewrites an existing
46
+ * loose diagnostic, and it never calls back into the diagnostic writer.
47
+ */
48
+
49
+ export const DIAGNOSTIC_SUMMARY_FORMAT = "harnery-v3-diagnostic-summary" as const;
50
+ export const DIAGNOSTIC_SUMMARY_VERSION = 1 as const;
51
+
52
+ /**
53
+ * Loose exemplars admitted per (category, reason, instance) key per UTC day
54
+ * before identical emissions coalesce into the key's summary. Frozen from a
55
+ * whole-history measurement: healthy categories peak well under this value
56
+ * per day across ALL keys combined, while a producer fault exceeds it within
57
+ * minutes. The bound is enforced from filename counts without cross-process
58
+ * locking, so simultaneous writers straddling the crossing can admit a few
59
+ * extra exemplars; the bound is a flood stop, not an exact quota.
60
+ */
61
+ export const DIAGNOSTIC_LOOSE_EXEMPLAR_LIMIT = 32;
62
+
63
+ /** Bounded exemplar digests retained inside one summary. */
64
+ const SUMMARY_EXEMPLAR_LIMIT = 4;
65
+ /**
66
+ * Hourly rate buckets retained inside one summary. One summary covers one UTC
67
+ * day, so at most 24 buckets are reachable; the margin absorbs clock skew.
68
+ * Consumer windows wider than a day (doctor reads 48h) span multiple
69
+ * day-scoped summary files, not more buckets in one file.
70
+ */
71
+ const SUMMARY_HOUR_BUCKET_LIMIT = 26;
72
+ /** Refuse to publish a summary larger than this; falls open to loose writes. */
73
+ const SUMMARY_MAX_BYTES = 128 * 1024;
74
+ /** Coalesce updates fsync only every Nth increment; a crash between durable
75
+ * points can lose at most this many recent increments from the summary. */
76
+ const SUMMARY_DURABLE_EVERY = 32;
77
+ /** Stop appending to the emergency fail-open log past this size. */
78
+ const MITIGATION_HEALTH_MAX_BYTES = 256 * 1024;
79
+ export const DIAGNOSTIC_MITIGATION_HEALTH_FILE = "mitigation-health.ndjson";
80
+ const LEASE_STALE_AFTER_MS = 5_000;
81
+ const LEASE_RETRIES = 4;
82
+ const LEASE_RETRY_MS = 10;
83
+ const METADATA_KEY_LIMIT = 16;
84
+ const METADATA_VALUE_LIMIT = 256;
85
+ /** Extracts the 15-digit epoch-ms segment of a loose diagnostic order key. */
86
+ const LOOSE_ORDER_KEY_TIMESTAMP = /-(\d{15})-\d{20}-/;
87
+
88
+ const sleepCell = new Int32Array(new SharedArrayBuffer(4));
89
+
90
+ export interface DiagnosticContentFingerprint {
91
+ bytes: number;
92
+ sha256: string;
93
+ }
94
+
95
+ export interface DiagnosticGateInputV3 {
96
+ /** Already sanitized by the diagnostic writer. */
97
+ category: string;
98
+ reason?: string;
99
+ instanceId?: string;
100
+ fingerprint: DiagnosticContentFingerprint;
101
+ /** Approved scalar metadata only; stored once per summary. */
102
+ metadata: Record<string, string | number | boolean | null>;
103
+ }
104
+
105
+ export type DiagnosticGateDecisionV3 =
106
+ | {
107
+ admit: true;
108
+ /**
109
+ * Appended to the loose filename before `.json` so future gate calls
110
+ * can count the key's admitted exemplars from the directory listing.
111
+ * Empty when the gate is disabled.
112
+ */
113
+ loose_name_suffix: string;
114
+ }
115
+ | {
116
+ admit: false;
117
+ /** The summary file the emission was durably coalesced into. */
118
+ summary_path: string;
119
+ };
120
+
121
+ export interface DiagnosticSummaryV3 {
122
+ format: typeof DIAGNOSTIC_SUMMARY_FORMAT;
123
+ format_version: typeof DIAGNOSTIC_SUMMARY_VERSION;
124
+ category: string;
125
+ reason: string;
126
+ instance_id: string;
127
+ /** UTC day this summary covers, as YYYY-MM-DD. */
128
+ window: string;
129
+ /** Loose exemplar files observed for the key when the bound was crossed. */
130
+ loose_count: number;
131
+ /** Identical emissions coalesced here instead of becoming loose files. */
132
+ summarized_count: number;
133
+ first_summarized_at: string | null;
134
+ last_summarized_at: string | null;
135
+ /** Sum of content-fingerprint bytes across summarized emissions. */
136
+ represented_bytes: number;
137
+ /** Digests of the first summarized emissions, bounded. */
138
+ exemplars: DiagnosticContentFingerprint[];
139
+ /** Approved scalar metadata from the first summarized emission. */
140
+ metadata: Record<string, string | number | boolean | null>;
141
+ /** Summarized-only counts per UTC hour ("YYYY-MM-DDTHH"), pruned. */
142
+ recent_hours: Record<string, number>;
143
+ }
144
+
145
+ /** Aggregate of the append-only emergency fail-open log. */
146
+ export interface DiagnosticMitigationHealthV3 {
147
+ fail_open_count: number;
148
+ last_fail_open_at: string | null;
149
+ by_stage: Record<string, number>;
150
+ /** True when the log hit its size cap and later fail-opens were dropped. */
151
+ truncated: boolean;
152
+ }
153
+
154
+ export function diagnosticSummariesRootV3(coordRoot: string): string {
155
+ return join(resolve(coordRoot), EVENT_V3_LEDGER_RELATIVE_ROOT, "diagnostic-summaries");
156
+ }
157
+
158
+ function looseDiagnosticsRootV3(coordRoot: string): string {
159
+ return join(resolve(coordRoot), EVENT_V3_LEDGER_RELATIVE_ROOT, "diagnostics");
160
+ }
161
+
162
+ export function diagnosticSummariesEnabledV3(): boolean {
163
+ return coordEnv("V3_DIAGNOSTIC_SUMMARIES") !== "0";
164
+ }
165
+
166
+ function sanitizeComponent(value: string | undefined, fallback: string): string {
167
+ if (typeof value !== "string" || value.length === 0) return fallback;
168
+ const safe = value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 128);
169
+ return safe.length > 0 ? safe : fallback;
170
+ }
171
+
172
+ function keyDigest(category: string, reason: string, instanceId: string): string {
173
+ return createHash("sha256")
174
+ .update(`${category}\n${reason}\n${instanceId}`)
175
+ .digest("hex")
176
+ .slice(0, 16);
177
+ }
178
+
179
+ function utcDay(nowMs: number): string {
180
+ return new Date(nowMs).toISOString().slice(0, 10);
181
+ }
182
+
183
+ function utcHour(nowMs: number): string {
184
+ return new Date(nowMs).toISOString().slice(0, 13);
185
+ }
186
+
187
+ function summaryPath(root: string, category: string, window: string, digest: string): string {
188
+ return join(root, `${category}-${window.replaceAll("-", "")}-${digest}.json`);
189
+ }
190
+
191
+ function ensureOwnerOnlyDirectory(path: string): void {
192
+ mkdirSync(path, { recursive: true, mode: 0o700 });
193
+ chmodSync(path, 0o700);
194
+ }
195
+
196
+ function publishOwnerOnlyJson(path: string, value: unknown, durable: boolean): void {
197
+ const contents = `${JSON.stringify(value)}\n`;
198
+ if (Buffer.byteLength(contents, "utf8") > SUMMARY_MAX_BYTES) {
199
+ throw new Error("diagnostic summary exceeds its size bound");
200
+ }
201
+ const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
202
+ let fd: number | undefined;
203
+ try {
204
+ fd = openSync(temporary, "wx", 0o600);
205
+ writeFileSync(fd, contents, "utf8");
206
+ if (durable) fsyncSync(fd);
207
+ closeSync(fd);
208
+ fd = undefined;
209
+ renameSync(temporary, path);
210
+ if (durable) fsyncParentDirectory(path);
211
+ } finally {
212
+ if (fd !== undefined) closeSync(fd);
213
+ if (existsSync(temporary)) unlinkSync(temporary);
214
+ }
215
+ }
216
+
217
+ function pidIsAlive(pid: number): boolean {
218
+ if (!Number.isSafeInteger(pid) || pid < 1) return false;
219
+ try {
220
+ process.kill(pid, 0);
221
+ return true;
222
+ } catch (error) {
223
+ return (error as NodeJS.ErrnoException).code !== "ESRCH";
224
+ }
225
+ }
226
+
227
+ function withKeyLease<T>(root: string, digest: string, operation: () => T): T {
228
+ const leasesDir = join(root, "leases");
229
+ ensureOwnerOnlyDirectory(leasesDir);
230
+ const leaseDir = join(leasesDir, digest);
231
+ const authority = createHash("sha256").update(resolve(root)).digest("hex");
232
+ let lease: ReturnType<typeof acquireNoClobberLease> | undefined;
233
+ for (let attempt = 0; attempt < LEASE_RETRIES; attempt += 1) {
234
+ try {
235
+ lease = acquireNoClobberLease({
236
+ path: leaseDir,
237
+ scope: "event-v3-diagnostic-summary",
238
+ authoritySha256: authority,
239
+ staleAfterMs: LEASE_STALE_AFTER_MS,
240
+ validateStaleOwner: (owner) => owner.host === hostname() && !pidIsAlive(owner.pid),
241
+ });
242
+ break;
243
+ } catch (error) {
244
+ if (attempt === LEASE_RETRIES - 1) throw error;
245
+ Atomics.wait(sleepCell, 0, 0, LEASE_RETRY_MS);
246
+ }
247
+ }
248
+ if (!lease) throw new Error("diagnostic summary lease is busy");
249
+ try {
250
+ return operation();
251
+ } finally {
252
+ // A durably published result must survive release-time faults (an epoch
253
+ // rotation can remove the lease state mid-operation); stale takeover
254
+ // recovers any residue. The empty lease directory is removed so per-key
255
+ // lease state never outlives the contention that needed it.
256
+ try {
257
+ lease.release();
258
+ } catch {
259
+ // Swallowed: the operation's outcome already stands.
260
+ }
261
+ try {
262
+ rmdirSync(leaseDir);
263
+ } catch {
264
+ // A concurrent acquirer repopulated it, or release left residue.
265
+ }
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Count the key's admitted loose exemplars for a UTC-day window from their
271
+ * digest-suffixed filenames. Files written before this mitigation carry no
272
+ * suffix and are not counted, so a pre-existing flood does not consume the
273
+ * window's exemplar budget.
274
+ */
275
+ function countLooseExemplars(coordRoot: string, digest: string, window: string): number {
276
+ const directory = looseDiagnosticsRootV3(coordRoot);
277
+ if (!existsSync(directory)) return 0;
278
+ const windowStartMs = Date.parse(`${window}T00:00:00.000Z`);
279
+ const windowEndMs = windowStartMs + 24 * 60 * 60 * 1000;
280
+ const suffix = `-${digest}.json`;
281
+ let count = 0;
282
+ for (const name of readdirSync(directory)) {
283
+ if (!name.endsWith(suffix)) continue;
284
+ const epochMs = Number(LOOSE_ORDER_KEY_TIMESTAMP.exec(name)?.[1]);
285
+ if (Number.isFinite(epochMs) && epochMs >= windowStartMs && epochMs < windowEndMs) {
286
+ count += 1;
287
+ }
288
+ }
289
+ return count;
290
+ }
291
+
292
+ function boundedMetadata(
293
+ metadata: Record<string, string | number | boolean | null>,
294
+ ): Record<string, string | number | boolean | null> {
295
+ const bounded: Record<string, string | number | boolean | null> = {};
296
+ let kept = 0;
297
+ for (const [key, value] of Object.entries(metadata)) {
298
+ if (kept >= METADATA_KEY_LIMIT) break;
299
+ if (typeof value === "string" && value.length > METADATA_VALUE_LIMIT) continue;
300
+ bounded[key] = value;
301
+ kept += 1;
302
+ }
303
+ return bounded;
304
+ }
305
+
306
+ function pruneHourBuckets(buckets: Record<string, number>, nowMs: number): Record<string, number> {
307
+ const keys = Object.keys(buckets).sort();
308
+ const horizon = utcHour(nowMs - SUMMARY_HOUR_BUCKET_LIMIT * 60 * 60 * 1000);
309
+ const pruned: Record<string, number> = {};
310
+ for (const key of keys) {
311
+ if (key >= horizon) pruned[key] = buckets[key] as number;
312
+ }
313
+ // A pathological clock can still grow the map; hard-cap by newest keys.
314
+ const capped = Object.keys(pruned).sort().slice(-SUMMARY_HOUR_BUCKET_LIMIT);
315
+ const result: Record<string, number> = {};
316
+ for (const key of capped) result[key] = pruned[key] as number;
317
+ return result;
318
+ }
319
+
320
+ function freshSummary(
321
+ category: string,
322
+ reason: string,
323
+ instanceId: string,
324
+ window: string,
325
+ ): DiagnosticSummaryV3 {
326
+ return {
327
+ format: DIAGNOSTIC_SUMMARY_FORMAT,
328
+ format_version: DIAGNOSTIC_SUMMARY_VERSION,
329
+ category,
330
+ reason,
331
+ instance_id: instanceId,
332
+ window,
333
+ loose_count: 0,
334
+ summarized_count: 0,
335
+ first_summarized_at: null,
336
+ last_summarized_at: null,
337
+ represented_bytes: 0,
338
+ exemplars: [],
339
+ metadata: {},
340
+ recent_hours: {},
341
+ };
342
+ }
343
+
344
+ function readSummaryFile(path: string): DiagnosticSummaryV3 | undefined {
345
+ let parsed: unknown;
346
+ try {
347
+ const metadata = lstatSync(path);
348
+ if (!metadata.isFile() || metadata.isSymbolicLink()) return undefined;
349
+ parsed = JSON.parse(readFileSync(path, "utf8"));
350
+ } catch {
351
+ return undefined;
352
+ }
353
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
354
+ const summary = parsed as DiagnosticSummaryV3;
355
+ if (
356
+ summary.format !== DIAGNOSTIC_SUMMARY_FORMAT ||
357
+ summary.format_version !== DIAGNOSTIC_SUMMARY_VERSION ||
358
+ typeof summary.category !== "string" ||
359
+ typeof summary.reason !== "string" ||
360
+ typeof summary.instance_id !== "string" ||
361
+ !/^\d{4}-\d{2}-\d{2}$/.test(summary.window) ||
362
+ !Number.isSafeInteger(summary.loose_count) ||
363
+ summary.loose_count < 0 ||
364
+ !Number.isSafeInteger(summary.summarized_count) ||
365
+ summary.summarized_count < 0 ||
366
+ !Number.isSafeInteger(summary.represented_bytes) ||
367
+ summary.represented_bytes < 0 ||
368
+ !Array.isArray(summary.exemplars) ||
369
+ !summary.recent_hours ||
370
+ typeof summary.recent_hours !== "object" ||
371
+ Array.isArray(summary.recent_hours)
372
+ ) {
373
+ return undefined;
374
+ }
375
+ // Drop malformed rate buckets so a corrupt value can neither poison later
376
+ // increments nor silently diverge windowed counts from the total.
377
+ const hours: Record<string, number> = {};
378
+ for (const [key, value] of Object.entries(summary.recent_hours)) {
379
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}$/.test(key) && Number.isSafeInteger(value) && value >= 0) {
380
+ hours[key] = value;
381
+ }
382
+ }
383
+ summary.recent_hours = hours;
384
+ return summary;
385
+ }
386
+
387
+ /**
388
+ * Append one line to the emergency fail-open log. Lock-free by design: an
389
+ * O_APPEND write of one small line is atomic, so concurrent fail-opens never
390
+ * serialize on shared state. Best-effort, size-capped, and nonrecursive: a
391
+ * failure here is swallowed, never diagnosed, and never blocks the loose
392
+ * fallback write.
393
+ */
394
+ function recordFailOpen(root: string, stage: string, nowMs: number): void {
395
+ try {
396
+ ensureOwnerOnlyDirectory(root);
397
+ const path = join(root, DIAGNOSTIC_MITIGATION_HEALTH_FILE);
398
+ try {
399
+ if (statSync(path).size >= MITIGATION_HEALTH_MAX_BYTES) return;
400
+ } catch {
401
+ // Missing log starts fresh.
402
+ }
403
+ const line = JSON.stringify({ at: new Date(nowMs).toISOString(), stage, pid: process.pid });
404
+ appendFileSync(path, `${line}\n`, { mode: 0o600 });
405
+ } catch {
406
+ // The emergency log may undercount when storage is unavailable; it is a
407
+ // signal, not accounting, and must never throw into the producer path.
408
+ }
409
+ }
410
+
411
+ function readMitigationHealth(path: string): DiagnosticMitigationHealthV3 | null {
412
+ let contents: string;
413
+ let size = 0;
414
+ try {
415
+ const metadata = lstatSync(path);
416
+ if (!metadata.isFile() || metadata.isSymbolicLink()) return null;
417
+ size = metadata.size;
418
+ contents = readFileSync(path, "utf8");
419
+ } catch {
420
+ return null;
421
+ }
422
+ const health: DiagnosticMitigationHealthV3 = {
423
+ fail_open_count: 0,
424
+ last_fail_open_at: null,
425
+ by_stage: {},
426
+ truncated: size >= MITIGATION_HEALTH_MAX_BYTES,
427
+ };
428
+ for (const line of contents.split("\n")) {
429
+ if (line.trim().length === 0) continue;
430
+ // Every non-empty line was one fail-open, even a torn one.
431
+ health.fail_open_count += 1;
432
+ try {
433
+ const row = JSON.parse(line) as { at?: unknown; stage?: unknown };
434
+ if (typeof row.at === "string") health.last_fail_open_at = row.at;
435
+ if (typeof row.stage === "string" && row.stage.length <= 32) {
436
+ if (Object.keys(health.by_stage).length < 16 || health.by_stage[row.stage] !== undefined) {
437
+ health.by_stage[row.stage] = (health.by_stage[row.stage] ?? 0) + 1;
438
+ }
439
+ }
440
+ } catch {
441
+ // A torn tail line still counts above.
442
+ }
443
+ }
444
+ return health;
445
+ }
446
+
447
+ /**
448
+ * Decide whether one diagnostic emission may become a loose file.
449
+ *
450
+ * Below the per-key bound (counted from digest-suffixed loose filenames) the
451
+ * emission is admitted with the suffix to append, and no summary or lease
452
+ * state is created. At or past the bound the emission is durably coalesced
453
+ * into the key's summary under a short per-key lease and `admit: false` is
454
+ * returned with the summary path. Any mitigation failure, and the disabled
455
+ * gate, admit the loose write exactly as before.
456
+ */
457
+ export function gateProducerDiagnosticV3(
458
+ coordRoot: string,
459
+ input: DiagnosticGateInputV3,
460
+ nowMs = Date.now(),
461
+ ): DiagnosticGateDecisionV3 {
462
+ if (!diagnosticSummariesEnabledV3()) return { admit: true, loose_name_suffix: "" };
463
+ const at = Number.isFinite(nowMs) ? nowMs : Date.now();
464
+ const category = sanitizeComponent(input.category, "unknown");
465
+ const reason = sanitizeComponent(input.reason, "none");
466
+ const instanceId = sanitizeComponent(input.instanceId, "unknown");
467
+ const window = utcDay(at);
468
+ const digest = keyDigest(category, reason, instanceId);
469
+ const admitLoose: DiagnosticGateDecisionV3 = { admit: true, loose_name_suffix: `-${digest}` };
470
+ const root = diagnosticSummariesRootV3(coordRoot);
471
+ let stage = "peek";
472
+ try {
473
+ const path = summaryPath(root, category, window, digest);
474
+ if (!readSummaryFile(path)) {
475
+ stage = "count";
476
+ if (countLooseExemplars(coordRoot, digest, window) < DIAGNOSTIC_LOOSE_EXEMPLAR_LIMIT) {
477
+ return admitLoose;
478
+ }
479
+ }
480
+ stage = "coalesce";
481
+ ensureOwnerOnlyDirectory(root);
482
+ return withKeyLease(root, digest, () => {
483
+ const existing = readSummaryFile(path);
484
+ const state = existing ?? freshSummary(category, reason, instanceId, window);
485
+ if (!existing) state.loose_count = countLooseExemplars(coordRoot, digest, window);
486
+ const atIso = new Date(at).toISOString();
487
+ state.summarized_count += 1;
488
+ state.first_summarized_at ??= atIso;
489
+ state.last_summarized_at = atIso;
490
+ if (Number.isSafeInteger(input.fingerprint?.bytes) && input.fingerprint.bytes >= 0) {
491
+ state.represented_bytes += input.fingerprint.bytes;
492
+ }
493
+ if (state.exemplars.length < SUMMARY_EXEMPLAR_LIMIT && input.fingerprint) {
494
+ state.exemplars.push({
495
+ bytes: input.fingerprint.bytes,
496
+ sha256: input.fingerprint.sha256,
497
+ });
498
+ }
499
+ if (Object.keys(state.metadata).length === 0) {
500
+ state.metadata = boundedMetadata(input.metadata ?? {});
501
+ }
502
+ const hour = utcHour(at);
503
+ state.recent_hours[hour] = (state.recent_hours[hour] ?? 0) + 1;
504
+ state.recent_hours = pruneHourBuckets(state.recent_hours, at);
505
+ const durable = !existing || state.summarized_count % SUMMARY_DURABLE_EVERY === 0;
506
+ publishOwnerOnlyJson(path, state, durable);
507
+ return { admit: false, summary_path: path };
508
+ });
509
+ } catch {
510
+ recordFailOpen(root, stage, at);
511
+ return admitLoose;
512
+ }
513
+ }
514
+
515
+ export interface DiagnosticSummaryListingV3 {
516
+ summaries: DiagnosticSummaryV3[];
517
+ /** Physical summary files, including any that failed validation. */
518
+ file_count: number;
519
+ unreadable_count: number;
520
+ mitigation_health: DiagnosticMitigationHealthV3 | null;
521
+ }
522
+
523
+ /**
524
+ * Read every summary in the active epoch's `diagnostic-summaries/` subtree.
525
+ * Skips lease state, temp files, and the mitigation health log (which is
526
+ * aggregated into `mitigation_health` instead). Never throws; a missing
527
+ * subtree returns an empty listing.
528
+ */
529
+ export function listDiagnosticSummariesV3(coordRoot: string): DiagnosticSummaryListingV3 {
530
+ const root = diagnosticSummariesRootV3(coordRoot);
531
+ const listing: DiagnosticSummaryListingV3 = {
532
+ summaries: [],
533
+ file_count: 0,
534
+ unreadable_count: 0,
535
+ mitigation_health: null,
536
+ };
537
+ let names: string[];
538
+ try {
539
+ const metadata = lstatSync(root);
540
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) return listing;
541
+ names = readdirSync(root);
542
+ } catch {
543
+ return listing;
544
+ }
545
+ for (const name of names.sort()) {
546
+ if (name === "leases" || name.includes(".tmp-")) continue;
547
+ if (name.startsWith("mitigation-health")) {
548
+ if (name === DIAGNOSTIC_MITIGATION_HEALTH_FILE) {
549
+ listing.mitigation_health = readMitigationHealth(join(root, name));
550
+ }
551
+ continue;
552
+ }
553
+ if (!name.endsWith(".json")) continue;
554
+ listing.file_count += 1;
555
+ const summary = readSummaryFile(join(root, name));
556
+ if (summary) listing.summaries.push(summary);
557
+ else listing.unreadable_count += 1;
558
+ }
559
+ return listing;
560
+ }
561
+
562
+ /**
563
+ * Sum summarized-only occurrences inside a lookback window, from hour
564
+ * buckets. Hour-granular and conservative: a bucket is counted only when its
565
+ * whole hour lies inside `[sinceMs, nowMs]`, so a boundary bucket is excluded
566
+ * (up to 59 minutes undercounted) rather than fully included (up to 59
567
+ * minutes overcounted), and a future-dated bucket from clock skew never
568
+ * inflates a window.
569
+ */
570
+ export function countSummarizedSinceV3(
571
+ summaries: readonly DiagnosticSummaryV3[],
572
+ sinceMs: number,
573
+ filter?: (summary: DiagnosticSummaryV3) => boolean,
574
+ nowMs = Date.now(),
575
+ ): number {
576
+ let count = 0;
577
+ for (const summary of summaries) {
578
+ if (filter && !filter(summary)) continue;
579
+ for (const [hour, value] of Object.entries(summary.recent_hours)) {
580
+ const hourStartMs = Date.parse(`${hour}:00:00.000Z`);
581
+ if (
582
+ Number.isFinite(hourStartMs) &&
583
+ hourStartMs >= sinceMs &&
584
+ hourStartMs <= nowMs &&
585
+ Number.isSafeInteger(value) &&
586
+ value > 0
587
+ ) {
588
+ count += value;
589
+ }
590
+ }
591
+ }
592
+ return count;
593
+ }
@@ -472,7 +472,7 @@ function inputContextMeasurement(payload: ParsedPayload, phase: "before" | "afte
472
472
  const metadata = plainRecord(payload.raw.compact_metadata);
473
473
  const usedKeys =
474
474
  phase === "before"
475
- ? ["pre_tokens", "pre_compact_tokens", "used_tokens"]
475
+ ? ["pre_tokens", "pre_compact_tokens", "used_tokens", "context_tokens"]
476
476
  : ["post_tokens", "post_compact_tokens", "used_tokens"];
477
477
  const limitKeys = [
478
478
  "context_window_size",
@@ -18,6 +18,10 @@ import type { ParsedPayload } from "../../../hooks/adapter/parse.ts";
18
18
  import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
19
19
  import type { EventV3WriteMode } from "../control.ts";
20
20
  import { EVENT_V3_LEDGER_RELATIVE_ROOT } from "../writer.ts";
21
+ import {
22
+ type DiagnosticContentFingerprint,
23
+ gateProducerDiagnosticV3,
24
+ } from "./diagnostic-summaries.ts";
21
25
  import type { HookSignalV3 } from "./hook.ts";
22
26
  import type { TurnRitualEvidenceV3 } from "./hook-base.ts";
23
27
 
@@ -73,6 +77,10 @@ const DIAGNOSTIC_METADATA_KEYS = new Set([
73
77
  "sequence",
74
78
  "expected",
75
79
  "actual",
80
+ "thread_id_present",
81
+ "wslenv_present",
82
+ "wslenv_names",
83
+ "identity_recovery_source",
76
84
  ]);
77
85
 
78
86
  export interface HookIntakeRecordV3 {
@@ -288,6 +296,13 @@ function validTurnRitual(value: TurnRitualEvidenceV3 | undefined): boolean {
288
296
  * are reduced to byte counts and digests so the diagnostics spool never holds
289
297
  * prompt, tool input, or tool output bodies (invariant: delivered evidence is
290
298
  * never silently destroyed, and never re-exposed either).
299
+ *
300
+ * Identical emissions are bounded per (category, reason, instance) key and
301
+ * UTC day: past the loose-exemplar limit the emission is coalesced into the
302
+ * key's summary file. Returns the path of the durable record either way (the
303
+ * loose file when admitted, the summary file when coalesced); `undefined`
304
+ * means nothing durable was written. The gate is fail-open, so a mitigation
305
+ * failure still produces the loose file.
291
306
  */
292
307
  export function writeProducerDiagnosticV3(
293
308
  coordRoot: string,
@@ -296,13 +311,26 @@ export function writeProducerDiagnosticV3(
296
311
  ): string | undefined {
297
312
  try {
298
313
  const safeCategory = category.replace(/[^a-z0-9_-]/gi, "_").slice(0, 64);
314
+ const redacted = redactDiagnosticRecord(record);
315
+ const { content_fingerprint: fingerprint, ...metadata } = redacted;
316
+ const decision = gateProducerDiagnosticV3(coordRoot, {
317
+ category: safeCategory,
318
+ reason: typeof record.reason === "string" ? record.reason : undefined,
319
+ instanceId: typeof record.instance_id === "string" ? record.instance_id : undefined,
320
+ fingerprint: fingerprint as DiagnosticContentFingerprint,
321
+ metadata: metadata as Record<string, string | number | boolean | null>,
322
+ });
323
+ if (!decision.admit) return decision.summary_path;
299
324
  const root = diagnosticsRoot(coordRoot);
300
325
  ensureOwnerOnlyDirectory(root);
301
- const path = join(root, `${safeCategory}-${intakeOrderKey()}.json`);
326
+ const path = join(
327
+ root,
328
+ `${safeCategory}-${intakeOrderKey()}${decision.loose_name_suffix}.json`,
329
+ );
302
330
  const contents = `${JSON.stringify({
303
331
  recorded_at: new Date().toISOString(),
304
332
  category: safeCategory,
305
- ...redactDiagnosticRecord(record),
333
+ ...redacted,
306
334
  })}\n`;
307
335
  writeOwnerOnlyFileDurably(path, contents);
308
336
  return path;