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
@@ -0,0 +1,717 @@
1
+ import { closeSync, fstatSync, openSync, readSync, statSync } from "node:fs";
2
+ import { unknownPressureAssessment } from "../diagnostics/advice.ts";
3
+ import {
4
+ PRESSURE_ASSESSMENT_SCHEMA_VERSION,
5
+ PRESSURE_POLICY,
6
+ type PressureAssessment,
7
+ type PressureHysteresisState,
8
+ } from "../diagnostics/contract.ts";
9
+ import {
10
+ SUPERVISOR_PRESSURE_SCHEMA_VERSION,
11
+ type SupervisorCapability,
12
+ type SupervisorPressureRecord,
13
+ } from "../supervisor/contract.ts";
14
+ import { readSupervisorStatus } from "../supervisor/status.ts";
15
+ import { supervisorPaths } from "../supervisor/storage.ts";
16
+ import {
17
+ RESOURCE_SNAPSHOT_SCHEMA_VERSION,
18
+ type ResourceDiskSample,
19
+ type ResourceHostSample,
20
+ type ResourceIoSample,
21
+ type ResourceMachineSample,
22
+ type ResourceOomSample,
23
+ type ResourcePressureSample,
24
+ type ResourceProcessSample,
25
+ type ResourceSnapshot,
26
+ type ResourceSupportState,
27
+ } from "./contract.ts";
28
+ import { resourcePaths } from "./storage.ts";
29
+
30
+ export const RESOURCE_STATUS_STALE_MS = 15_000;
31
+ export const RESOURCE_STATUS_MAX_BYTES = 4 * 1024 * 1024;
32
+ export const RESOURCE_STATUS_PROCESS_LIMIT = 20;
33
+ export const RESOURCE_HOST_STATUS_STALE_MS = 30_000;
34
+ const FUTURE_TOLERANCE_MS = 1_000;
35
+
36
+ export interface ResourceStatus {
37
+ schema_version: 2;
38
+ state: "fresh" | "stale" | "unavailable";
39
+ reason: string | null;
40
+ sampled_at: string | null;
41
+ sample_age_ms: number | null;
42
+ stale_after_ms: number;
43
+ platform: NodeJS.Platform | null;
44
+ namespace: ResourceSnapshot["namespace"];
45
+ support: ResourceSnapshot["support"] | null;
46
+ writer: { running: boolean; stale: boolean };
47
+ /**
48
+ * The one published pressure assessment, read from the observer's record so
49
+ * this surface, the prompt notice, the dashboard, and a diagnostic bundle all
50
+ * report the same answer for the same input.
51
+ */
52
+ assessment: PressureAssessment;
53
+ /** The state the sample before the published one carried forward. */
54
+ prior_hysteresis: PressureHysteresisState | null;
55
+ /** Where the assessment came from, and why it is missing when it is. */
56
+ assessment_capability: SupervisorCapability;
57
+ machine: ResourceMachineSample | null;
58
+ disks: ResourceDiskSample[];
59
+ pressure: ResourcePressureSample | null;
60
+ oom: ResourceOomSample | null;
61
+ io: ResourceIoSample | null;
62
+ host: ResourceHostSample | null;
63
+ processes?: Array<
64
+ Pick<
65
+ ResourceProcessSample,
66
+ "pid" | "name" | "cpu_percent" | "rss_bytes" | "owner_kind" | "owner_id"
67
+ >
68
+ >;
69
+ processes_omitted?: number;
70
+ }
71
+
72
+ /** A bounded cache-only read. Never samples the OS or starts the writer. */
73
+ export function readResourceStatus(
74
+ coordRoot: string,
75
+ options: { nowMs?: number; includeProcesses?: boolean } = {},
76
+ ): ResourceStatus {
77
+ const nowMs = options.nowMs ?? Date.now();
78
+ const writerState = readWriterState(coordRoot, nowMs);
79
+ const published = readPublishedPressure(coordRoot, nowMs, writerState);
80
+ const result: ResourceStatus = {
81
+ schema_version: 2,
82
+ state: "unavailable",
83
+ reason: "snapshot_missing",
84
+ sampled_at: null,
85
+ sample_age_ms: null,
86
+ stale_after_ms: RESOURCE_STATUS_STALE_MS,
87
+ platform: null,
88
+ namespace: "unknown",
89
+ support: null,
90
+ writer: writerState,
91
+ assessment: published.assessment,
92
+ prior_hysteresis: published.prior_hysteresis,
93
+ assessment_capability: published.capability,
94
+ machine: null,
95
+ disks: [],
96
+ pressure: null,
97
+ oom: null,
98
+ io: null,
99
+ host: null,
100
+ };
101
+ let value: unknown;
102
+ try {
103
+ value = readBoundedJson(resourcePaths(coordRoot).snapshot);
104
+ } catch (error) {
105
+ result.reason =
106
+ (error as NodeJS.ErrnoException).code === "ENOENT"
107
+ ? "snapshot_missing"
108
+ : error instanceof Error && error.message === "snapshot_too_large"
109
+ ? "snapshot_too_large"
110
+ : "snapshot_invalid";
111
+ return result;
112
+ }
113
+ if (!validSnapshot(value)) return { ...result, reason: "snapshot_invalid" };
114
+ const sampleMs = Date.parse(value.sampled_at);
115
+ if (!Number.isFinite(sampleMs) || sampleMs - nowMs > FUTURE_TOLERANCE_MS) {
116
+ return { ...result, reason: "snapshot_clock_invalid" };
117
+ }
118
+ result.sampled_at = value.sampled_at;
119
+ result.sample_age_ms = Math.max(0, nowMs - sampleMs);
120
+ result.platform = value.platform;
121
+ result.namespace = value.namespace;
122
+ result.support = {
123
+ state: value.support.state,
124
+ sampler: value.support.sampler,
125
+ ...reason(value.support.reason),
126
+ };
127
+ if (result.sample_age_ms > RESOURCE_STATUS_STALE_MS) {
128
+ return { ...result, state: "stale", reason: "snapshot_stale" };
129
+ }
130
+ if (value.support.state === "error" || value.support.state === "unsupported") {
131
+ return { ...result, reason: `sampler_${value.support.state}` };
132
+ }
133
+ result.state = "fresh";
134
+ result.reason = null;
135
+ result.machine = projectMachine(value.machine);
136
+ result.disks = (value.disks ?? []).map(projectDisk);
137
+ result.pressure = value.pressure
138
+ ? {
139
+ state: value.pressure.state,
140
+ ...reason(value.pressure.reason),
141
+ cpu: projectWindow(value.pressure.cpu),
142
+ memory: projectWindow(value.pressure.memory),
143
+ io: projectWindow(value.pressure.io),
144
+ memory_full: projectWindow(value.pressure.memory_full),
145
+ io_full: projectWindow(value.pressure.io_full),
146
+ }
147
+ : null;
148
+ result.oom = value.oom
149
+ ? {
150
+ state: value.oom.state,
151
+ total_kills: value.oom.total_kills,
152
+ kills_since_last_sample: value.oom.kills_since_last_sample,
153
+ last_kill_age_ms: value.oom.last_kill_age_ms,
154
+ ...reason(value.oom.reason),
155
+ }
156
+ : null;
157
+ result.io = value.io
158
+ ? {
159
+ state: value.io.state,
160
+ read_bytes_per_second: value.io.read_bytes_per_second,
161
+ write_bytes_per_second: value.io.write_bytes_per_second,
162
+ ...reason(value.io.reason),
163
+ }
164
+ : null;
165
+ if (value.host) {
166
+ const hostAge = nowMs - Date.parse(value.host.sampled_at);
167
+ result.host =
168
+ hostAge > RESOURCE_HOST_STATUS_STALE_MS || hostAge < -FUTURE_TOLERANCE_MS
169
+ ? {
170
+ platform: "win32",
171
+ sampled_at: value.host.sampled_at,
172
+ state: "error",
173
+ machine: null,
174
+ disks: [],
175
+ reason: "host_snapshot_stale",
176
+ }
177
+ : {
178
+ platform: "win32",
179
+ sampled_at: value.host.sampled_at,
180
+ state: value.host.state,
181
+ machine: value.host.machine ? projectMachine(value.host.machine) : null,
182
+ disks: value.host.disks.map(projectDisk),
183
+ ...reason(value.host.reason),
184
+ };
185
+ }
186
+ if (options.includeProcesses) {
187
+ result.processes = [...value.processes]
188
+ .sort((a, b) => b.rss_bytes - a.rss_bytes)
189
+ .slice(0, RESOURCE_STATUS_PROCESS_LIMIT)
190
+ .map((p) => ({
191
+ pid: p.pid,
192
+ name: text(p.name),
193
+ cpu_percent: p.cpu_percent,
194
+ rss_bytes: p.rss_bytes,
195
+ owner_kind: p.owner_kind,
196
+ owner_id: p.owner_id === null ? null : text(p.owner_id),
197
+ }));
198
+ result.processes_omitted =
199
+ value.omitted_process_count + value.processes.length - result.processes.length;
200
+ }
201
+ return result;
202
+ }
203
+
204
+ export function formatResourceSummary(status: ResourceStatus): string {
205
+ const lead = status.assessment.summary;
206
+ if (status.state !== "fresh" || !status.machine) {
207
+ const age =
208
+ status.sample_age_ms === null ? "" : ` (${Math.round(status.sample_age_ms / 1_000)}s old)`;
209
+ return `${lead} Measurements are ${status.state}${age}: ${status.reason?.replaceAll("_", " ") ?? "no measurements"}.`;
210
+ }
211
+ const m = status.machine;
212
+ const disk = status.disks
213
+ .filter((d) => d.available_bytes !== null)
214
+ .reduce<ResourceDiskSample | null>(
215
+ (least, d) =>
216
+ !least || (d.available_bytes ?? Infinity) < (least.available_bytes ?? Infinity) ? d : least,
217
+ null,
218
+ );
219
+ const cpu = m.cpu_percent === null ? "warming up" : `${Math.round(m.cpu_percent)}%`;
220
+ const ram =
221
+ m.memory_available_bytes === null
222
+ ? "unknown"
223
+ : `${formatBytes(m.memory_available_bytes)} available`;
224
+ const diskText = disk ? `${formatBytes(disk.available_bytes as number)} available` : "unknown";
225
+ return `${lead} ${status.namespace}/${status.platform}: CPU ${cpu}, RAM ${ram}, disk ${diskText}; ${Math.round((status.sample_age_ms ?? 0) / 1_000)}s old${status.support?.state === "partial" ? "; partial" : ""}${!status.writer.running ? "; writer stopped" : ""}.`;
226
+ }
227
+
228
+ export function formatResourceStatus(status: ResourceStatus, bin: string): string {
229
+ const a = status.assessment;
230
+ const lines = [formatResourceSummary(status)];
231
+ lines.push(
232
+ `Assessment: ${a.state} at ${a.scope} scope; limiting resource ${a.limiting_resource}; trend ${a.trend}; evidence ${a.evidence_state}; recommended action ${a.recommended_action}.`,
233
+ );
234
+ lines.push(
235
+ `Evidence age: ${a.sample_age_ms === null ? "unknown" : `${Math.round(a.sample_age_ms / 1_000)}s`}; assessment source ${status.assessment_capability.state}${status.assessment_capability.reason_code ? ` (${status.assessment_capability.reason_code})` : ""}.`,
236
+ );
237
+ for (const item of a.reasons) lines.push(`Reason ${item.code}: ${item.summary}`);
238
+ for (const item of a.guidance)
239
+ lines.push(`Guidance for ${item.workload_class} work: ${item.summary}`);
240
+ if (a.contributors.length) {
241
+ lines.push("Contributors (who is using the resource, never the machine state):");
242
+ for (const item of a.contributors) lines.push(` ${formatContributor(item)}`);
243
+ if (a.omitted_contributor_count)
244
+ lines.push(` ${a.omitted_contributor_count} other contributors omitted.`);
245
+ }
246
+ if (a.unattributed_memory_percent !== null)
247
+ lines.push(
248
+ `Unattributed memory: ${Math.round(a.unattributed_memory_percent)}% of machine memory has no validated owner.`,
249
+ );
250
+ if (status.state !== "fresh" || !status.writer.running)
251
+ lines.push(
252
+ `Start or inspect the writer with ${bin} supervisor start --keep-alive and ${bin} supervisor status.`,
253
+ );
254
+ if (status.host)
255
+ lines.push(
256
+ `Windows host: ${status.host.state}${status.host.machine ? `; CPU ${status.host.machine.cpu_percent === null ? "warming up" : `${Math.round(status.host.machine.cpu_percent)}%`}, RAM ${status.host.machine.memory_available_bytes === null ? "unknown" : `${formatBytes(status.host.machine.memory_available_bytes)} available`}` : `; ${status.host.reason ?? "unavailable"}`}`,
257
+ );
258
+ for (const disk of status.disks)
259
+ lines.push(
260
+ `Disk ${disk.path}: ${disk.available_bytes === null ? (disk.reason ?? disk.state) : `${formatBytes(disk.available_bytes)} available (${disk.used_percent === null ? "unknown" : `${Math.round(disk.used_percent)}%`} used)`}`,
261
+ );
262
+ if (status.pressure)
263
+ lines.push(
264
+ `Pressure: ${status.pressure.state}${status.pressure.reason ? ` (${status.pressure.reason})` : ""}`,
265
+ );
266
+ for (const [name, window] of [
267
+ ["memory", status.pressure?.memory_full],
268
+ ["I/O", status.pressure?.io_full],
269
+ ] as const) {
270
+ if (!window) continue;
271
+ const direction =
272
+ window.avg10 > window.avg60 ? "rising" : window.avg10 < window.avg60 ? "falling" : "steady";
273
+ lines.push(
274
+ `Full ${name} stalls: ${window.avg10}% / ${window.avg60}% / ${window.avg300}% (10s / 60s / 300s; ${direction}).`,
275
+ );
276
+ }
277
+ if (status.oom) {
278
+ const oom = status.oom;
279
+ lines.push(
280
+ `OOM kills: ${oom.total_kills ?? "unknown"} total; ${oom.kills_since_last_sample ?? "unknown"} since previous sample${oom.last_kill_age_ms === null ? "" : `; last increase ${Math.round(oom.last_kill_age_ms / 1000)}s ago`}${oom.reason ? ` (${oom.reason})` : ""}.`,
281
+ );
282
+ }
283
+ if (status.io)
284
+ lines.push(
285
+ `Disk I/O: ${status.io.state}; read ${status.io.read_bytes_per_second === null ? "unknown" : `${formatBytes(status.io.read_bytes_per_second)}/s`}, write ${status.io.write_bytes_per_second === null ? "unknown" : `${formatBytes(status.io.write_bytes_per_second)}/s`}`,
286
+ );
287
+ if (status.processes) {
288
+ lines.push("Processes (largest memory first):");
289
+ for (const p of status.processes)
290
+ lines.push(
291
+ ` ${p.pid} ${p.name}: ${formatBytes(p.rss_bytes)}, CPU ${p.cpu_percent === null ? "unknown" : `${Math.round(p.cpu_percent)}%`}, ${p.owner_kind}${p.owner_id ? ` ${p.owner_id}` : ""}`,
292
+ );
293
+ if (status.processes_omitted)
294
+ lines.push(` ${status.processes_omitted} other processes omitted.`);
295
+ }
296
+ return lines.join("\n");
297
+ }
298
+
299
+ /** Ownership is stated only when attribution is exact. */
300
+ function formatContributor(item: PressureAssessment["contributors"][number]): string {
301
+ const owner =
302
+ item.attribution_confidence === "exact" && item.owner_id
303
+ ? `${item.owner_kind ?? "owner"} ${item.owner_id}`
304
+ : item.attribution_state === "unattributed"
305
+ ? "no validated owner"
306
+ : "owner not confirmed";
307
+ return `${item.finding_kind} ${item.scope_kind}:${item.scope_id} (${owner}): ${item.summary}`;
308
+ }
309
+
310
+ export const PRESSURE_RECORD_MAX_BYTES = 256 * 1024;
311
+
312
+ export interface PublishedPressure {
313
+ assessment: PressureAssessment;
314
+ prior_hysteresis: PressureHysteresisState | null;
315
+ capability: SupervisorCapability;
316
+ record: SupervisorPressureRecord | null;
317
+ }
318
+
319
+ /**
320
+ * Read the assessment the observer published. Bounded, cache only, and never a
321
+ * computation: a missing, stale, or malformed record yields `unknown` with the
322
+ * reason stated, because a gap in the evidence is not a measurement of health.
323
+ */
324
+ export function readPublishedPressure(
325
+ coordRoot: string,
326
+ nowMs = Date.now(),
327
+ writer?: ResourceStatus["writer"],
328
+ ): PublishedPressure {
329
+ const sourceKind = "supervisor.pressure";
330
+ const observedAt = new Date(nowMs).toISOString();
331
+ const gap = (
332
+ state: Exclude<SupervisorCapability["state"], "supported">,
333
+ reasonCode: string,
334
+ summary: string,
335
+ stale = false,
336
+ ): PublishedPressure => ({
337
+ assessment: unknownPressureAssessment({
338
+ observedAt,
339
+ reasonCode: stale ? "snapshot_stale" : "evidence_unavailable",
340
+ summary,
341
+ }),
342
+ prior_hysteresis: null,
343
+ capability: { source_kind: sourceKind, state, reason_code: reasonCode },
344
+ record: null,
345
+ });
346
+ // A record can only be trusted while its writer is alive. Every surface makes
347
+ // the same check here so none of them reports a state the others do not.
348
+ if (!(writer ?? readWriterState(coordRoot, nowMs)).running) {
349
+ return gap(
350
+ "expired",
351
+ "pressure_observer_not_running",
352
+ "Local resource pressure cannot be determined because the resource observer is not running.",
353
+ );
354
+ }
355
+ const path = supervisorPaths(coordRoot).pressure;
356
+ let record: unknown;
357
+ try {
358
+ const age = nowMs - statSync(path).mtimeMs;
359
+ if (age > PRESSURE_POLICY.sample_staleness_ms || age < -FUTURE_TOLERANCE_MS) {
360
+ return gap(
361
+ "expired",
362
+ "pressure_record_stale",
363
+ `Local resource pressure cannot be determined because the published assessment is ${Math.round(Math.abs(age) / 1_000)} seconds old.`,
364
+ true,
365
+ );
366
+ }
367
+ record = readBoundedJson(path, PRESSURE_RECORD_MAX_BYTES);
368
+ } catch (error) {
369
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
370
+ return gap(
371
+ "unsupported",
372
+ "pressure_record_missing",
373
+ "Local resource pressure cannot be determined because the observer has not published an assessment yet.",
374
+ );
375
+ }
376
+ if (error instanceof Error && error.message === "snapshot_too_large") {
377
+ return gap(
378
+ "error",
379
+ "pressure_record_too_large",
380
+ "Local resource pressure cannot be determined because the published assessment is larger than the read limit.",
381
+ );
382
+ }
383
+ return gap(
384
+ "malformed",
385
+ "pressure_record_malformed",
386
+ "Local resource pressure cannot be determined because the published assessment could not be parsed.",
387
+ );
388
+ }
389
+ if (!validPressureRecord(record)) {
390
+ return gap(
391
+ "malformed",
392
+ "pressure_record_malformed",
393
+ "Local resource pressure cannot be determined because the published assessment did not match the expected shape.",
394
+ );
395
+ }
396
+ return {
397
+ assessment: record.assessment,
398
+ prior_hysteresis: record.prior_hysteresis,
399
+ capability: { source_kind: sourceKind, state: "supported" },
400
+ record,
401
+ };
402
+ }
403
+
404
+ /** Whether the observer that writes both the snapshot and the record is alive. */
405
+ function readWriterState(coordRoot: string, nowMs: number): ResourceStatus["writer"] {
406
+ try {
407
+ // Keep malformed service metadata from making the read expensive. The shared
408
+ // status reader retains ownership of PID/start-token liveness checks.
409
+ if (statSync(supervisorPaths(coordRoot).service).size > 65_536) {
410
+ return { running: false, stale: false };
411
+ }
412
+ const writer = readSupervisorStatus(coordRoot, nowMs);
413
+ const futureHeartbeat =
414
+ writer.record && Date.parse(writer.record.heartbeat_at) > nowMs + FUTURE_TOLERANCE_MS;
415
+ return {
416
+ running: writer.running && !futureHeartbeat,
417
+ stale: writer.stale || !!futureHeartbeat,
418
+ };
419
+ } catch {
420
+ // A missing writer must not break normal coordination.
421
+ return { running: false, stale: false };
422
+ }
423
+ }
424
+
425
+ function validPressureRecord(v: unknown): v is SupervisorPressureRecord {
426
+ if (
427
+ !object(v) ||
428
+ v.schema_version !== SUPERVISOR_PRESSURE_SCHEMA_VERSION ||
429
+ !string(v.published_at) ||
430
+ !string(v.observer_generation) ||
431
+ !validAssessment(v.assessment) ||
432
+ !(v.prior_hysteresis === null || validHysteresis(v.prior_hysteresis))
433
+ )
434
+ return false;
435
+ return true;
436
+ }
437
+
438
+ function validAssessment(v: unknown): v is PressureAssessment {
439
+ if (
440
+ !object(v) ||
441
+ v.schema_version !== PRESSURE_ASSESSMENT_SCHEMA_VERSION ||
442
+ v.observer_only !== true ||
443
+ !["normal", "elevated", "critical", "unknown"].includes(v.state as string) ||
444
+ !["guest", "native", "windows-host"].includes(v.scope as string) ||
445
+ !["memory", "cpu", "io", "storage", "none", "unknown"].includes(
446
+ v.limiting_resource as string,
447
+ ) ||
448
+ !["rising", "steady", "falling", "unknown"].includes(v.trend as string) ||
449
+ !string(v.observed_at) ||
450
+ !nullable(v.sample_age_ms) ||
451
+ !["complete", "partial", "unavailable"].includes(v.evidence_state as string) ||
452
+ !boundedArray(v.evidence, PRESSURE_POLICY.limits.max_evidence) ||
453
+ !boundedArray(v.reasons, PRESSURE_POLICY.limits.max_reasons) ||
454
+ !boundedArray(v.contributors, PRESSURE_POLICY.limits.max_contributors) ||
455
+ !number(v.omitted_contributor_count) ||
456
+ !percent(v.unattributed_memory_percent) ||
457
+ !["proceed", "limit-heavy-work", "avoid-new-heavy-work", "unknown"].includes(
458
+ v.recommended_action as string,
459
+ ) ||
460
+ !string(v.summary) ||
461
+ !boundedArray(v.guidance, 8) ||
462
+ !validHysteresis(v.hysteresis) ||
463
+ !number(v.policy_version)
464
+ )
465
+ return false;
466
+ return (
467
+ (v.reasons as unknown[]).every(
468
+ (row) => object(row) && string(row.code) && string(row.summary),
469
+ ) &&
470
+ (v.guidance as unknown[]).every(
471
+ (row) => object(row) && string(row.workload_class) && string(row.summary),
472
+ ) &&
473
+ (v.contributors as unknown[]).every(
474
+ (row) =>
475
+ object(row) &&
476
+ string(row.finding_id) &&
477
+ string(row.finding_kind) &&
478
+ string(row.summary) &&
479
+ ["exact", "none"].includes(row.attribution_confidence as string),
480
+ )
481
+ );
482
+ }
483
+
484
+ function validHysteresis(v: unknown): v is PressureHysteresisState {
485
+ return (
486
+ object(v) &&
487
+ ["normal", "elevated", "critical", "unknown"].includes(v.state as string) &&
488
+ string(v.state_since) &&
489
+ number(v.consecutive_clear_samples) &&
490
+ object(v.dimension_streaks) &&
491
+ (v.oom_baseline_total_kills === null || number(v.oom_baseline_total_kills)) &&
492
+ (v.oom_hold_until === null || string(v.oom_hold_until)) &&
493
+ (v.observer_generation === null || string(v.observer_generation))
494
+ );
495
+ }
496
+
497
+ function boundedArray(v: unknown, max: number): v is unknown[] {
498
+ return Array.isArray(v) && v.length <= max;
499
+ }
500
+
501
+ function readBoundedJson(path: string, maxBytes = RESOURCE_STATUS_MAX_BYTES): unknown {
502
+ const fd = openSync(path, "r");
503
+ try {
504
+ const size = fstatSync(fd).size;
505
+ if (size > maxBytes) throw new Error("snapshot_too_large");
506
+ const buffer = Buffer.alloc(Math.min(maxBytes + 1, size + 1));
507
+ const length = readSync(fd, buffer, 0, buffer.length, 0);
508
+ if (length > maxBytes) throw new Error("snapshot_too_large");
509
+ return JSON.parse(buffer.subarray(0, length).toString("utf8"));
510
+ } finally {
511
+ closeSync(fd);
512
+ }
513
+ }
514
+
515
+ function object(v: unknown): v is Record<string, unknown> {
516
+ return v !== null && typeof v === "object" && !Array.isArray(v);
517
+ }
518
+ function number(v: unknown): v is number {
519
+ return typeof v === "number" && Number.isFinite(v) && v >= 0;
520
+ }
521
+ function nullable(v: unknown): boolean {
522
+ return v === null || number(v);
523
+ }
524
+ function percent(v: unknown): boolean {
525
+ return v === null || (number(v) && v <= 100);
526
+ }
527
+ function support(v: unknown): v is ResourceSupportState {
528
+ return ["supported", "partial", "unsupported", "error"].includes(v as string);
529
+ }
530
+ function string(v: unknown): v is string {
531
+ return typeof v === "string" && v.length <= 4096;
532
+ }
533
+ function validMachine(v: unknown): v is ResourceMachineSample {
534
+ return (
535
+ object(v) &&
536
+ percent(v.cpu_percent) &&
537
+ number(v.cpu_logical_count) &&
538
+ (v.cpu_available_parallelism === undefined || number(v.cpu_available_parallelism)) &&
539
+ (v.load_average === null ||
540
+ (Array.isArray(v.load_average) &&
541
+ v.load_average.length === 3 &&
542
+ v.load_average.every(number))) &&
543
+ [
544
+ "memory_total_bytes",
545
+ "memory_available_bytes",
546
+ "memory_used_bytes",
547
+ "swap_total_bytes",
548
+ "swap_used_bytes",
549
+ ].every((k) => nullable(v[k])) &&
550
+ percent(v.memory_percent) &&
551
+ nullable(v.process_count) &&
552
+ !(
553
+ number(v.memory_total_bytes) &&
554
+ number(v.memory_available_bytes) &&
555
+ v.memory_available_bytes > v.memory_total_bytes
556
+ )
557
+ );
558
+ }
559
+ function validDisk(v: unknown): v is ResourceDiskSample {
560
+ return (
561
+ object(v) &&
562
+ string(v.path) &&
563
+ support(v.state) &&
564
+ nullable(v.total_bytes) &&
565
+ nullable(v.available_bytes) &&
566
+ percent(v.used_percent) &&
567
+ (v.reason === undefined || string(v.reason)) &&
568
+ !(number(v.total_bytes) && number(v.available_bytes) && v.available_bytes > v.total_bytes)
569
+ );
570
+ }
571
+ function validWindow(v: unknown): boolean {
572
+ return (
573
+ v === null ||
574
+ (object(v) && ["avg10", "avg60", "avg300"].every((k) => number(v[k]) && v[k] <= 100))
575
+ );
576
+ }
577
+ function validSnapshot(v: unknown): v is ResourceSnapshot {
578
+ if (
579
+ !object(v) ||
580
+ v.schema_version !== RESOURCE_SNAPSHOT_SCHEMA_VERSION ||
581
+ !string(v.sampled_at) ||
582
+ ![
583
+ "aix",
584
+ "android",
585
+ "darwin",
586
+ "freebsd",
587
+ "haiku",
588
+ "linux",
589
+ "openbsd",
590
+ "sunos",
591
+ "win32",
592
+ "cygwin",
593
+ "netbsd",
594
+ ].includes(v.platform as string) ||
595
+ !["host", "wsl", "unknown"].includes(v.namespace as string) ||
596
+ !object(v.support) ||
597
+ !support(v.support.state) ||
598
+ !["procfs", "darwin", "win32", "unsupported"].includes(v.support.sampler as string) ||
599
+ (v.support.reason !== undefined && !string(v.support.reason)) ||
600
+ !validMachine(v.machine) ||
601
+ !nullable(v.interval_ms) ||
602
+ !number(v.sample_duration_ms) ||
603
+ !number(v.collector_cpu_ms) ||
604
+ !number(v.omitted_process_count) ||
605
+ !Array.isArray(v.processes) ||
606
+ v.processes.length > 10000
607
+ )
608
+ return false;
609
+ if (
610
+ v.disks !== undefined &&
611
+ (!Array.isArray(v.disks) || v.disks.length > 64 || !v.disks.every(validDisk))
612
+ )
613
+ return false;
614
+ if (
615
+ v.pressure !== undefined &&
616
+ (!object(v.pressure) ||
617
+ !support(v.pressure.state) ||
618
+ !validWindow(v.pressure.cpu) ||
619
+ !validWindow(v.pressure.memory) ||
620
+ !validWindow(v.pressure.io) ||
621
+ !validWindow(v.pressure.memory_full) ||
622
+ !validWindow(v.pressure.io_full) ||
623
+ (v.pressure.reason !== undefined && !string(v.pressure.reason)))
624
+ )
625
+ return false;
626
+ if (
627
+ v.oom !== undefined &&
628
+ (!object(v.oom) ||
629
+ !support(v.oom.state) ||
630
+ !["total_kills", "kills_since_last_sample"].every(
631
+ (key) =>
632
+ v.oom !== null &&
633
+ object(v.oom) &&
634
+ (v.oom[key] === null || (number(v.oom[key]) && Number.isSafeInteger(v.oom[key]))),
635
+ ) ||
636
+ !nullable(v.oom.last_kill_age_ms) ||
637
+ (v.oom.reason !== undefined && !string(v.oom.reason)))
638
+ )
639
+ return false;
640
+ if (
641
+ v.io !== undefined &&
642
+ (!object(v.io) ||
643
+ !support(v.io.state) ||
644
+ !nullable(v.io.read_bytes_per_second) ||
645
+ !nullable(v.io.write_bytes_per_second) ||
646
+ (v.io.reason !== undefined && !string(v.io.reason)))
647
+ )
648
+ return false;
649
+ if (
650
+ v.host !== undefined &&
651
+ (!object(v.host) ||
652
+ v.host.platform !== "win32" ||
653
+ !support(v.host.state) ||
654
+ !string(v.host.sampled_at) ||
655
+ !Number.isFinite(Date.parse(v.host.sampled_at)) ||
656
+ (v.host.machine !== null && !validMachine(v.host.machine)) ||
657
+ !Array.isArray(v.host.disks) ||
658
+ v.host.disks.length > 64 ||
659
+ !v.host.disks.every(validDisk) ||
660
+ (v.host.reason !== undefined && !string(v.host.reason)))
661
+ )
662
+ return false;
663
+ return v.processes.every(
664
+ (p) =>
665
+ object(p) &&
666
+ Number.isSafeInteger(p.pid) &&
667
+ number(p.pid) &&
668
+ string(p.name) &&
669
+ nullable(p.cpu_percent) &&
670
+ number(p.rss_bytes) &&
671
+ ["agent", "service", "unattributed"].includes(p.owner_kind as string) &&
672
+ (p.owner_id === null || string(p.owner_id)),
673
+ );
674
+ }
675
+
676
+ function projectMachine(m: ResourceMachineSample): ResourceMachineSample {
677
+ return {
678
+ cpu_percent: m.cpu_percent,
679
+ cpu_logical_count: m.cpu_logical_count,
680
+ ...(m.cpu_available_parallelism === undefined
681
+ ? {}
682
+ : { cpu_available_parallelism: m.cpu_available_parallelism }),
683
+ load_average: m.load_average,
684
+ memory_total_bytes: m.memory_total_bytes,
685
+ memory_available_bytes: m.memory_available_bytes,
686
+ memory_used_bytes: m.memory_used_bytes,
687
+ memory_percent: m.memory_percent,
688
+ swap_total_bytes: m.swap_total_bytes,
689
+ swap_used_bytes: m.swap_used_bytes,
690
+ process_count: m.process_count,
691
+ };
692
+ }
693
+ function projectDisk(d: ResourceDiskSample): ResourceDiskSample {
694
+ return {
695
+ path: text(d.path),
696
+ state: d.state,
697
+ total_bytes: d.total_bytes,
698
+ available_bytes: d.available_bytes,
699
+ used_percent: d.used_percent,
700
+ ...reason(d.reason),
701
+ };
702
+ }
703
+ function projectWindow(v: ResourcePressureSample["cpu"]): ResourcePressureSample["cpu"] {
704
+ return v ? { avg10: v.avg10, avg60: v.avg60, avg300: v.avg300 } : null;
705
+ }
706
+ function text(s: string): string {
707
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: Cache strings must not inject terminal control sequences.
708
+ return s.replace(/[\x00-\x1f\x7f]/g, " ").slice(0, 240);
709
+ }
710
+ function reason(s: string | undefined): { reason?: string } {
711
+ return s === undefined ? {} : { reason: text(s) };
712
+ }
713
+ function formatBytes(bytes: number): string {
714
+ return bytes >= 1024 ** 3
715
+ ? `${(bytes / 1024 ** 3).toFixed(1)} GiB`
716
+ : `${Math.round(bytes / 1024 ** 2)} MiB`;
717
+ }