harnery 0.30.0 → 0.31.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 (501) hide show
  1. package/README.md +4 -4
  2. package/dist/commander.d.ts +8 -0
  3. package/dist/commander.d.ts.map +1 -1
  4. package/dist/commander.js +14 -13
  5. package/dist/commands/adapter.d.ts +12 -0
  6. package/dist/commands/adapter.d.ts.map +1 -0
  7. package/dist/commands/{harness.js → adapter.js} +35 -35
  8. package/dist/commands/agents.d.ts +3 -2
  9. package/dist/commands/agents.d.ts.map +1 -1
  10. package/dist/commands/agents.js +61 -60
  11. package/dist/commands/artifacts.d.ts +4 -0
  12. package/dist/commands/artifacts.d.ts.map +1 -0
  13. package/dist/commands/artifacts.js +143 -0
  14. package/dist/commands/backup.d.ts +1 -1
  15. package/dist/commands/backup.d.ts.map +1 -1
  16. package/dist/commands/backup.js +5 -2
  17. package/dist/commands/browse.d.ts.map +1 -1
  18. package/dist/commands/browse.js +310 -7
  19. package/dist/commands/checkpoint.d.ts +12 -0
  20. package/dist/commands/checkpoint.d.ts.map +1 -0
  21. package/dist/commands/checkpoint.js +162 -0
  22. package/dist/commands/context.d.ts +2 -2
  23. package/dist/commands/context.d.ts.map +1 -1
  24. package/dist/commands/context.js +3 -151
  25. package/dist/commands/decision.d.ts.map +1 -1
  26. package/dist/commands/decision.js +31 -7
  27. package/dist/commands/deinit.d.ts +2 -2
  28. package/dist/commands/deinit.js +15 -15
  29. package/dist/commands/doctor.js +23 -23
  30. package/dist/commands/governor.d.ts +4 -0
  31. package/dist/commands/governor.d.ts.map +1 -0
  32. package/dist/commands/{supervisor.js → governor.js} +133 -108
  33. package/dist/commands/init.d.ts +12 -12
  34. package/dist/commands/init.d.ts.map +1 -1
  35. package/dist/commands/init.js +44 -30
  36. package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
  37. package/dist/commands/journal.d.ts.map +1 -0
  38. package/dist/commands/{scratch.js → journal.js} +47 -47
  39. package/dist/commands/policy.js +1 -1
  40. package/dist/commands/presence.js +2 -2
  41. package/dist/commands/sync.d.ts +1 -1
  42. package/dist/commands/sync.js +3 -3
  43. package/dist/commands/tunnel.d.ts +16 -0
  44. package/dist/commands/tunnel.d.ts.map +1 -1
  45. package/dist/commands/tunnel.js +193 -23
  46. package/dist/commands/web-fetch.js +1 -1
  47. package/dist/commands/web.d.ts +19 -0
  48. package/dist/commands/web.d.ts.map +1 -1
  49. package/dist/commands/web.js +51 -0
  50. package/dist/commands/work.d.ts.map +1 -1
  51. package/dist/commands/work.js +38 -22
  52. package/dist/commands/workflow.d.ts.map +1 -1
  53. package/dist/commands/workflow.js +35 -28
  54. package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
  55. package/dist/core/adapters/attest-projection.d.ts.map +1 -0
  56. package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
  57. package/dist/core/adapters/attest.d.ts.map +1 -0
  58. package/dist/core/{harnesses → adapters}/attest.js +9 -9
  59. package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
  60. package/dist/core/adapters/attestation.d.ts.map +1 -0
  61. package/dist/core/{harnesses → adapters}/attestation.js +20 -20
  62. package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
  63. package/dist/core/adapters/bench.d.ts.map +1 -0
  64. package/dist/core/{harnesses → adapters}/bench.js +12 -12
  65. package/dist/core/adapters/index.d.ts +13 -0
  66. package/dist/core/adapters/index.d.ts.map +1 -0
  67. package/dist/core/adapters/index.js +6 -0
  68. package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
  69. package/dist/core/adapters/profiles.d.ts.map +1 -0
  70. package/dist/core/{harnesses → adapters}/profiles.js +11 -11
  71. package/dist/core/adapters/registry.d.ts +15 -0
  72. package/dist/core/adapters/registry.d.ts.map +1 -0
  73. package/dist/core/{harnesses → adapters}/registry.js +11 -11
  74. package/dist/core/{harnesses → adapters}/types.d.ts +23 -23
  75. package/dist/core/adapters/types.d.ts.map +1 -0
  76. package/dist/core/{harnesses → adapters}/types.js +1 -1
  77. package/dist/core/agents/canonical-emit.d.ts +4 -4
  78. package/dist/core/agents/canonical-emit.js +5 -5
  79. package/dist/core/agents/cli-emit.js +1 -1
  80. package/dist/core/agents/cli.js +30 -26
  81. package/dist/core/agents/codex-replay.d.ts +1 -1
  82. package/dist/core/agents/codex-replay.js +6 -6
  83. package/dist/core/agents/coord-client.d.ts +21 -2
  84. package/dist/core/agents/coord-client.d.ts.map +1 -1
  85. package/dist/core/agents/coord-client.js +122 -27
  86. package/dist/core/agents/events/consume.d.ts +1 -1
  87. package/dist/core/agents/events/emit.d.ts +5 -6
  88. package/dist/core/agents/events/emit.d.ts.map +1 -1
  89. package/dist/core/agents/events/emit.js +2 -2
  90. package/dist/core/agents/render/prompt-context.d.ts +21 -9
  91. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  92. package/dist/core/agents/render/prompt-context.js +82 -30
  93. package/dist/core/agents/render/session-context.d.ts +1 -1
  94. package/dist/core/agents/render/session-context.js +5 -5
  95. package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
  96. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  97. package/dist/core/agents/rules/claim-conflict.js +1 -1
  98. package/dist/core/agents/rules/commit-conflict.js +98 -22
  99. package/dist/core/agents/rules/stop-hook.d.ts +9 -9
  100. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  101. package/dist/core/agents/rules/stop-hook.js +23 -5
  102. package/dist/core/agents/rules/verdict.d.ts +14 -0
  103. package/dist/core/agents/rules/verdict.d.ts.map +1 -0
  104. package/dist/core/agents/rules/verdict.js +1 -0
  105. package/dist/core/agents/session-events.js +3 -3
  106. package/dist/core/agents/state/council.d.ts +1 -1
  107. package/dist/core/agents/state/council.js +1 -1
  108. package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
  109. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  110. package/dist/core/agents/state/heartbeat-projector.js +14 -10
  111. package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
  112. package/dist/core/agents/state/heartbeat-writer.js +12 -12
  113. package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
  114. package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
  115. package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
  116. package/dist/core/agents/state/pidmap.d.ts +28 -4
  117. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  118. package/dist/core/agents/state/pidmap.js +130 -15
  119. package/dist/core/agents/state/proc-start.d.ts +95 -0
  120. package/dist/core/agents/state/proc-start.d.ts.map +1 -0
  121. package/dist/core/agents/state/proc-start.js +199 -0
  122. package/dist/core/agents/state/shell-mutation.d.ts +1 -1
  123. package/dist/core/agents/state/shell-mutation.js +1 -1
  124. package/dist/core/agents/state/stale-sweep.js +7 -7
  125. package/dist/core/artifacts/index.d.ts +86 -0
  126. package/dist/core/artifacts/index.d.ts.map +1 -0
  127. package/dist/core/artifacts/index.js +421 -0
  128. package/dist/core/config.d.ts +9 -2
  129. package/dist/core/config.d.ts.map +1 -1
  130. package/dist/core/config.js +26 -2
  131. package/dist/core/context/index.d.ts +12 -12
  132. package/dist/core/context/index.js +10 -10
  133. package/dist/core/governor/adapter-spread.d.ts +52 -0
  134. package/dist/core/governor/adapter-spread.d.ts.map +1 -0
  135. package/dist/core/governor/adapter-spread.js +52 -0
  136. package/dist/core/governor/index.d.ts +7 -0
  137. package/dist/core/governor/index.d.ts.map +1 -0
  138. package/dist/core/governor/index.js +6 -0
  139. package/dist/core/governor/plan-api.d.ts +38 -0
  140. package/dist/core/governor/plan-api.d.ts.map +1 -0
  141. package/dist/core/governor/plan-api.js +83 -0
  142. package/dist/core/governor/plan-read.d.ts +5 -0
  143. package/dist/core/governor/plan-read.d.ts.map +1 -0
  144. package/dist/core/{supervisor → governor}/plan-read.js +66 -52
  145. package/dist/core/{supervisor → governor}/plan-types.d.ts +57 -56
  146. package/dist/core/governor/plan-types.d.ts.map +1 -0
  147. package/dist/core/{supervisor → governor}/plan-types.js +4 -4
  148. package/dist/core/governor/planning.d.ts +48 -0
  149. package/dist/core/governor/planning.d.ts.map +1 -0
  150. package/dist/core/{supervisor → governor}/planning.js +125 -94
  151. package/dist/core/governor/read.d.ts +6 -0
  152. package/dist/core/governor/read.d.ts.map +1 -0
  153. package/dist/core/governor/read.js +4 -0
  154. package/dist/core/governor/runner.d.ts +37 -0
  155. package/dist/core/governor/runner.d.ts.map +1 -0
  156. package/dist/core/{supervisor → governor}/runner.js +15 -15
  157. package/dist/core/governor/service-read.d.ts +6 -0
  158. package/dist/core/governor/service-read.d.ts.map +1 -0
  159. package/dist/core/{supervisor → governor}/service-read.js +12 -12
  160. package/dist/core/governor/service.d.ts +114 -0
  161. package/dist/core/governor/service.d.ts.map +1 -0
  162. package/dist/core/{supervisor → governor}/service.js +57 -57
  163. package/dist/core/governor/state.d.ts +116 -0
  164. package/dist/core/governor/state.d.ts.map +1 -0
  165. package/dist/core/{supervisor → governor}/state.js +228 -108
  166. package/dist/core/hooks/adapter/detect.d.ts +9 -0
  167. package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
  168. package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
  169. package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
  170. package/dist/core/hooks/adapter/events.d.ts.map +1 -0
  171. package/dist/core/hooks/{harness → adapter}/events.js +5 -5
  172. package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
  173. package/dist/core/hooks/adapter/output.d.ts.map +1 -0
  174. package/dist/core/hooks/{harness → adapter}/output.js +30 -22
  175. package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
  176. package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
  177. package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
  178. package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
  179. package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
  180. package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
  181. package/dist/core/hooks/cli.d.ts +3 -3
  182. package/dist/core/hooks/cli.js +175 -151
  183. package/dist/core/hooks/effects/image-capture.d.ts +5 -5
  184. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  185. package/dist/core/hooks/effects/image-capture.js +3 -3
  186. package/dist/core/hooks/effects/index.d.ts +12 -12
  187. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  188. package/dist/core/hooks/effects/index.js +17 -17
  189. package/dist/core/hooks/events/emit.d.ts +2 -2
  190. package/dist/core/hooks/events/emit.d.ts.map +1 -1
  191. package/dist/core/hooks/events/emit.js +1 -1
  192. package/dist/core/hooks/events/rotate.js +1 -1
  193. package/dist/core/hooks/events/schema.d.ts +13 -5
  194. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  195. package/dist/core/hooks/guard-path.d.ts +2 -2
  196. package/dist/core/hooks/guard-path.js +2 -2
  197. package/dist/core/hooks/index.d.ts +1 -1
  198. package/dist/core/hooks/index.d.ts.map +1 -1
  199. package/dist/core/hooks/resolve/anchor.d.ts +14 -5
  200. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
  201. package/dist/core/hooks/resolve/anchor.js +64 -7
  202. package/dist/core/hooks/resolve/coord-root.d.ts +3 -3
  203. package/dist/core/hooks/resolve/coord-root.js +4 -4
  204. package/dist/core/hooks/resolve/intent.js +1 -1
  205. package/dist/core/hooks/resolve/owner.d.ts +1 -1
  206. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  207. package/dist/core/hooks/resolve/owner.js +11 -5
  208. package/dist/core/{scratch → journal}/index.d.ts +25 -25
  209. package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
  210. package/dist/core/{scratch → journal}/index.js +38 -38
  211. package/dist/core/policy/evaluate.d.ts +1 -1
  212. package/dist/core/policy/evaluate.d.ts.map +1 -1
  213. package/dist/core/policy/evaluate.js +7 -7
  214. package/dist/core/policy/types.d.ts +3 -3
  215. package/dist/core/policy/types.d.ts.map +1 -1
  216. package/dist/core/work/state.d.ts +12 -4
  217. package/dist/core/work/state.d.ts.map +1 -1
  218. package/dist/core/work/state.js +50 -17
  219. package/dist/core/workflow/adapters.d.ts +17 -0
  220. package/dist/core/workflow/adapters.d.ts.map +1 -0
  221. package/dist/core/workflow/adapters.js +21 -0
  222. package/dist/core/workflow/approvals.js +6 -6
  223. package/dist/core/workflow/billing.d.ts +9 -9
  224. package/dist/core/workflow/billing.js +9 -9
  225. package/dist/core/workflow/child-env.d.ts +10 -2
  226. package/dist/core/workflow/child-env.d.ts.map +1 -1
  227. package/dist/core/workflow/child-env.js +12 -4
  228. package/dist/core/workflow/engine.d.ts +15 -5
  229. package/dist/core/workflow/engine.d.ts.map +1 -1
  230. package/dist/core/workflow/engine.js +138 -83
  231. package/dist/core/workflow/evidence-preflight.d.ts +24 -0
  232. package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
  233. package/dist/core/workflow/evidence-preflight.js +134 -0
  234. package/dist/core/workflow/index.d.ts +1 -1
  235. package/dist/core/workflow/index.d.ts.map +1 -1
  236. package/dist/core/workflow/proof.d.ts +11 -5
  237. package/dist/core/workflow/proof.d.ts.map +1 -1
  238. package/dist/core/workflow/proof.js +45 -36
  239. package/dist/core/workflow/run-state.d.ts +1 -1
  240. package/dist/core/workflow/run-state.js +7 -7
  241. package/dist/core/workflow/sandbox-projection.d.ts +6 -6
  242. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  243. package/dist/core/workflow/sandbox-projection.js +11 -11
  244. package/dist/core/workflow/spawn-claude.d.ts +3 -3
  245. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  246. package/dist/core/workflow/spawn-claude.js +9 -6
  247. package/dist/core/workflow/spawn-codex.d.ts +4 -4
  248. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  249. package/dist/core/workflow/spawn-codex.js +9 -6
  250. package/dist/core/workflow/spawn-cursor.d.ts +4 -4
  251. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  252. package/dist/core/workflow/spawn-cursor.js +9 -6
  253. package/dist/core/workflow/spawn-failure.d.ts +1 -1
  254. package/dist/core/workflow/spawn-failure.js +1 -1
  255. package/dist/core/workflow/specialists.js +2 -2
  256. package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
  257. package/dist/core/workflow/transcript.d.ts.map +1 -0
  258. package/dist/core/workflow/{journal.js → transcript.js} +14 -14
  259. package/dist/core/workflow/types.d.ts +98 -51
  260. package/dist/core/workflow/types.d.ts.map +1 -1
  261. package/dist/core/workflow/types.js +11 -1
  262. package/dist/core/workflow/validate.d.ts +1 -1
  263. package/dist/core/workflow/validate.js +1 -1
  264. package/dist/core/workflow/workspaces/cancellation.js +2 -2
  265. package/dist/core/workflow/workspaces/cleanup.js +2 -2
  266. package/dist/core/workflow/workspaces/execution.js +6 -6
  267. package/dist/core/workflow/workspaces/inspect.js +9 -9
  268. package/dist/core/workflow/workspaces/integration.js +14 -14
  269. package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
  270. package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
  271. package/dist/core/workflow/workspaces/lifecycle.js +9 -9
  272. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  273. package/dist/core/workflow/workspaces/local-git.js +39 -5
  274. package/dist/core/workflow/workspaces/recovery.js +3 -3
  275. package/dist/core/workflow/workspaces/state.d.ts +1 -1
  276. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  277. package/dist/core/workflow/workspaces/state.js +3 -3
  278. package/dist/core/workflow/workspaces/types.d.ts +1 -1
  279. package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
  280. package/dist/lib/browser/asserts.d.ts +28 -0
  281. package/dist/lib/browser/asserts.d.ts.map +1 -0
  282. package/dist/lib/browser/asserts.js +152 -0
  283. package/dist/lib/browser/client.d.ts +49 -0
  284. package/dist/lib/browser/client.d.ts.map +1 -1
  285. package/dist/lib/browser/client.js +155 -14
  286. package/dist/lib/browser/content-checks.d.ts +108 -0
  287. package/dist/lib/browser/content-checks.d.ts.map +1 -0
  288. package/dist/lib/browser/content-checks.js +474 -0
  289. package/dist/lib/browser/critique.d.ts +91 -0
  290. package/dist/lib/browser/critique.d.ts.map +1 -0
  291. package/dist/lib/browser/critique.js +167 -0
  292. package/dist/lib/browser/geometry.d.ts +32 -0
  293. package/dist/lib/browser/geometry.d.ts.map +1 -1
  294. package/dist/lib/browser/geometry.js +244 -1
  295. package/dist/lib/browser/index.d.ts +4 -1
  296. package/dist/lib/browser/index.d.ts.map +1 -1
  297. package/dist/lib/browser/index.js +2 -0
  298. package/dist/lib/claude-desktop.d.ts.map +1 -1
  299. package/dist/lib/claude-desktop.js +4 -1
  300. package/dist/lib/council/index.d.ts +6 -6
  301. package/dist/lib/council/index.d.ts.map +1 -1
  302. package/dist/lib/council/index.js +8 -8
  303. package/dist/lib/identities/assume.d.ts +2 -2
  304. package/dist/lib/identities/assume.js +7 -7
  305. package/dist/lib/instructions/apply.d.ts +3 -3
  306. package/dist/lib/instructions/apply.d.ts.map +1 -1
  307. package/dist/lib/instructions/apply.js +75 -27
  308. package/dist/lib/instructions/host-addendum.d.ts +54 -0
  309. package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
  310. package/dist/lib/instructions/host-addendum.js +107 -0
  311. package/dist/lib/instructions/templates.d.ts +10 -3
  312. package/dist/lib/instructions/templates.d.ts.map +1 -1
  313. package/dist/lib/instructions/templates.js +41 -12
  314. package/dist/lib/machine.d.ts +2 -2
  315. package/dist/lib/machine.js +2 -2
  316. package/package.json +26 -21
  317. package/schemas/config.schema.json +26 -1
  318. package/src/commander.ts +22 -13
  319. package/src/commands/{harness.ts → adapter.ts} +46 -46
  320. package/src/commands/agents.ts +71 -64
  321. package/src/commands/artifacts.ts +168 -0
  322. package/src/commands/backup.ts +47 -33
  323. package/src/commands/browse.ts +438 -2
  324. package/src/commands/checkpoint.ts +196 -0
  325. package/src/commands/context.ts +3 -176
  326. package/src/commands/decision.ts +41 -7
  327. package/src/commands/deinit.ts +16 -16
  328. package/src/commands/doctor.ts +28 -28
  329. package/src/commands/{supervisor.ts → governor.ts} +184 -154
  330. package/src/commands/init.ts +50 -34
  331. package/src/commands/{scratch.ts → journal.ts} +60 -60
  332. package/src/commands/policy.ts +1 -1
  333. package/src/commands/presence.ts +2 -2
  334. package/src/commands/sync.ts +3 -3
  335. package/src/commands/tunnel.ts +237 -34
  336. package/src/commands/web-fetch.ts +1 -1
  337. package/src/commands/web.ts +56 -1
  338. package/src/commands/work.ts +50 -35
  339. package/src/commands/workflow.ts +43 -34
  340. package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
  341. package/src/core/{harnesses → adapters}/attest.ts +27 -27
  342. package/src/core/{harnesses → adapters}/attestation.ts +41 -41
  343. package/src/core/{harnesses → adapters}/bench.ts +34 -43
  344. package/src/core/adapters/index.ts +56 -0
  345. package/src/core/{harnesses → adapters}/profiles.ts +16 -16
  346. package/src/core/{harnesses → adapters}/registry.ts +23 -23
  347. package/src/core/{harnesses → adapters}/types.ts +23 -23
  348. package/src/core/agents/canonical-emit.ts +6 -6
  349. package/src/core/agents/cli-emit.ts +1 -1
  350. package/src/core/agents/cli.ts +30 -28
  351. package/src/core/agents/codex-replay.ts +6 -6
  352. package/src/core/agents/coord-client.ts +119 -28
  353. package/src/core/agents/events/consume.ts +1 -1
  354. package/src/core/agents/events/emit.ts +7 -6
  355. package/src/core/agents/render/prompt-context.ts +107 -31
  356. package/src/core/agents/render/session-context.ts +6 -6
  357. package/src/core/agents/rules/claim-conflict.ts +4 -7
  358. package/src/core/agents/rules/commit-conflict.ts +103 -22
  359. package/src/core/agents/rules/stop-hook.ts +33 -17
  360. package/src/core/agents/rules/verdict.ts +13 -0
  361. package/src/core/agents/session-events.ts +4 -4
  362. package/src/core/agents/state/council.ts +1 -1
  363. package/src/core/agents/state/heartbeat-projector.ts +16 -12
  364. package/src/core/agents/state/heartbeat-writer.ts +12 -12
  365. package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
  366. package/src/core/agents/state/pidmap.ts +130 -15
  367. package/src/core/agents/state/proc-start.ts +208 -0
  368. package/src/core/agents/state/shell-mutation.ts +1 -1
  369. package/src/core/agents/state/stale-sweep.ts +7 -7
  370. package/src/core/artifacts/index.ts +557 -0
  371. package/src/core/config.ts +30 -2
  372. package/src/core/context/index.ts +15 -15
  373. package/src/core/governor/adapter-spread.ts +82 -0
  374. package/src/core/governor/index.ts +91 -0
  375. package/src/core/governor/plan-api.ts +115 -0
  376. package/src/core/{supervisor → governor}/plan-read.ts +112 -97
  377. package/src/core/{supervisor → governor}/plan-types.ts +59 -56
  378. package/src/core/{supervisor → governor}/planning.ts +223 -192
  379. package/src/core/governor/read.ts +59 -0
  380. package/src/core/{supervisor → governor}/runner.ts +39 -39
  381. package/src/core/{supervisor → governor}/service-read.ts +22 -22
  382. package/src/core/{supervisor → governor}/service.ts +117 -119
  383. package/src/core/{supervisor → governor}/state.ts +325 -167
  384. package/src/core/hooks/adapter/detect.ts +30 -0
  385. package/src/core/hooks/{harness → adapter}/events.ts +16 -14
  386. package/src/core/hooks/{harness → adapter}/output.ts +31 -23
  387. package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
  388. package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
  389. package/src/core/hooks/cli.ts +185 -162
  390. package/src/core/hooks/effects/image-capture.ts +6 -6
  391. package/src/core/hooks/effects/index.ts +17 -17
  392. package/src/core/hooks/events/emit.ts +3 -3
  393. package/src/core/hooks/events/rotate.ts +1 -1
  394. package/src/core/hooks/events/schema.ts +15 -7
  395. package/src/core/hooks/guard-path.ts +2 -2
  396. package/src/core/hooks/index.ts +1 -1
  397. package/src/core/hooks/resolve/anchor.ts +63 -8
  398. package/src/core/hooks/resolve/coord-root.ts +4 -4
  399. package/src/core/hooks/resolve/intent.ts +1 -1
  400. package/src/core/hooks/resolve/owner.ts +11 -5
  401. package/src/core/{scratch → journal}/index.ts +53 -53
  402. package/src/core/policy/evaluate.ts +8 -8
  403. package/src/core/policy/types.ts +3 -3
  404. package/src/core/work/state.ts +65 -23
  405. package/src/core/workflow/adapters.ts +33 -0
  406. package/src/core/workflow/approvals.ts +7 -7
  407. package/src/core/workflow/billing.ts +12 -12
  408. package/src/core/workflow/child-env.ts +13 -4
  409. package/src/core/workflow/engine.ts +155 -90
  410. package/src/core/workflow/evidence-preflight.ts +165 -0
  411. package/src/core/workflow/index.ts +3 -3
  412. package/src/core/workflow/proof.ts +60 -50
  413. package/src/core/workflow/run-state.ts +7 -7
  414. package/src/core/workflow/sandbox-projection.ts +16 -16
  415. package/src/core/workflow/spawn-claude.ts +13 -10
  416. package/src/core/workflow/spawn-codex.ts +13 -10
  417. package/src/core/workflow/spawn-cursor.ts +13 -10
  418. package/src/core/workflow/spawn-failure.ts +1 -1
  419. package/src/core/workflow/specialists.ts +2 -2
  420. package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
  421. package/src/core/workflow/types.ts +108 -51
  422. package/src/core/workflow/validate.ts +1 -1
  423. package/src/core/workflow/workspaces/cancellation.ts +2 -2
  424. package/src/core/workflow/workspaces/cleanup.ts +2 -2
  425. package/src/core/workflow/workspaces/execution.ts +6 -6
  426. package/src/core/workflow/workspaces/inspect.ts +14 -11
  427. package/src/core/workflow/workspaces/integration.ts +18 -14
  428. package/src/core/workflow/workspaces/lifecycle.ts +10 -10
  429. package/src/core/workflow/workspaces/local-git.ts +47 -5
  430. package/src/core/workflow/workspaces/recovery.ts +3 -3
  431. package/src/core/workflow/workspaces/state.ts +4 -3
  432. package/src/core/workflow/workspaces/types.ts +1 -1
  433. package/src/lib/browser/asserts.ts +177 -0
  434. package/src/lib/browser/client.ts +189 -14
  435. package/src/lib/browser/content-checks.ts +571 -0
  436. package/src/lib/browser/critique.ts +231 -0
  437. package/src/lib/browser/geometry.ts +297 -1
  438. package/src/lib/browser/index.ts +34 -0
  439. package/src/lib/claude-desktop.ts +4 -1
  440. package/src/lib/council/index.ts +9 -9
  441. package/src/lib/identities/assume.ts +7 -7
  442. package/src/lib/instructions/apply.ts +87 -25
  443. package/src/lib/instructions/host-addendum.ts +126 -0
  444. package/src/lib/instructions/templates.ts +44 -15
  445. package/src/lib/machine.ts +2 -2
  446. package/dist/commands/harness.d.ts +0 -12
  447. package/dist/commands/harness.d.ts.map +0 -1
  448. package/dist/commands/scratch.d.ts +0 -4
  449. package/dist/commands/scratch.d.ts.map +0 -1
  450. package/dist/commands/session.d.ts.map +0 -1
  451. package/dist/commands/session.js +0 -162
  452. package/dist/commands/supervisor.d.ts +0 -4
  453. package/dist/commands/supervisor.d.ts.map +0 -1
  454. package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
  455. package/dist/core/harnesses/attest.d.ts.map +0 -1
  456. package/dist/core/harnesses/attestation.d.ts.map +0 -1
  457. package/dist/core/harnesses/bench.d.ts.map +0 -1
  458. package/dist/core/harnesses/index.d.ts +0 -13
  459. package/dist/core/harnesses/index.d.ts.map +0 -1
  460. package/dist/core/harnesses/index.js +0 -6
  461. package/dist/core/harnesses/profiles.d.ts.map +0 -1
  462. package/dist/core/harnesses/registry.d.ts +0 -15
  463. package/dist/core/harnesses/registry.d.ts.map +0 -1
  464. package/dist/core/harnesses/types.d.ts.map +0 -1
  465. package/dist/core/hooks/harness/detect.d.ts +0 -9
  466. package/dist/core/hooks/harness/events.d.ts.map +0 -1
  467. package/dist/core/hooks/harness/output.d.ts.map +0 -1
  468. package/dist/core/supervisor/index.d.ts +0 -7
  469. package/dist/core/supervisor/index.d.ts.map +0 -1
  470. package/dist/core/supervisor/index.js +0 -6
  471. package/dist/core/supervisor/plan-api.d.ts +0 -23
  472. package/dist/core/supervisor/plan-api.d.ts.map +0 -1
  473. package/dist/core/supervisor/plan-api.js +0 -50
  474. package/dist/core/supervisor/plan-read.d.ts +0 -5
  475. package/dist/core/supervisor/plan-read.d.ts.map +0 -1
  476. package/dist/core/supervisor/plan-types.d.ts.map +0 -1
  477. package/dist/core/supervisor/planning.d.ts +0 -33
  478. package/dist/core/supervisor/planning.d.ts.map +0 -1
  479. package/dist/core/supervisor/read.d.ts +0 -6
  480. package/dist/core/supervisor/read.d.ts.map +0 -1
  481. package/dist/core/supervisor/read.js +0 -4
  482. package/dist/core/supervisor/runner.d.ts +0 -37
  483. package/dist/core/supervisor/runner.d.ts.map +0 -1
  484. package/dist/core/supervisor/service-read.d.ts +0 -6
  485. package/dist/core/supervisor/service-read.d.ts.map +0 -1
  486. package/dist/core/supervisor/service.d.ts +0 -114
  487. package/dist/core/supervisor/service.d.ts.map +0 -1
  488. package/dist/core/supervisor/state.d.ts +0 -95
  489. package/dist/core/supervisor/state.d.ts.map +0 -1
  490. package/dist/core/workflow/harnesses.d.ts +0 -17
  491. package/dist/core/workflow/harnesses.d.ts.map +0 -1
  492. package/dist/core/workflow/harnesses.js +0 -21
  493. package/dist/core/workflow/journal.d.ts.map +0 -1
  494. package/src/commands/session.ts +0 -187
  495. package/src/core/harnesses/index.ts +0 -56
  496. package/src/core/hooks/harness/detect.ts +0 -30
  497. package/src/core/supervisor/index.ts +0 -89
  498. package/src/core/supervisor/plan-api.ts +0 -73
  499. package/src/core/supervisor/read.ts +0 -59
  500. package/src/core/workflow/harnesses.ts +0 -33
  501. /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
@@ -0,0 +1,571 @@
1
+ // Content-level checks for `harn browse` — the failures a human still catches
2
+ // by eye that rendered-geometry checks miss:
3
+ //
4
+ // placeholder Unrendered template tokens / bound-value tells that leaked to
5
+ // the page: `${x}`, `{{x}}`, `[object Object]`, `Invalid Date`,
6
+ // `NaN`, or an element whose whole text is literally
7
+ // "undefined" / "null".
8
+ // image <img> that failed to load (naturalWidth 0), is still loading,
9
+ // or is visibly stretched (rendered aspect ratio far from the
10
+ // intrinsic one, with an object-fit that does not correct it).
11
+ // truncation Text actively cut off by an ellipsis or -webkit-line-clamp —
12
+ // i.e. the author asked to truncate AND the content overflows.
13
+ // contrast Rendered text below the WCAG AA contrast ratio against its
14
+ // effective background. Runs at whatever theme the page is in,
15
+ // so pair it with a theme toggle to cover light + dark.
16
+ //
17
+ // One evaluator runs every requested check in a single page.evaluate so the
18
+ // shared helpers (label, hidden walk, scroll offsets) are defined once. The
19
+ // returned function is serialized into the page by Playwright and cannot reach
20
+ // module-scope closures, which is why the helpers are nested.
21
+
22
+ export interface CheckRect {
23
+ x: number;
24
+ y: number;
25
+ width: number;
26
+ height: number;
27
+ }
28
+
29
+ export interface PlaceholderHit {
30
+ label: string;
31
+ token: string;
32
+ kind: "js-template" | "mustache" | "object" | "invalid-date" | "nan" | "empty-binding";
33
+ snippet: string;
34
+ rect: CheckRect;
35
+ }
36
+ export interface PlaceholderResult {
37
+ rule: "placeholder";
38
+ found: boolean;
39
+ outcome: "pass" | "fail";
40
+ scanned: number;
41
+ truncated: boolean;
42
+ hits: PlaceholderHit[];
43
+ }
44
+
45
+ export interface ImageHit {
46
+ label: string;
47
+ src: string;
48
+ reason: "missing" | "loading" | "stretched";
49
+ naturalWidth: number;
50
+ naturalHeight: number;
51
+ renderedWidth: number;
52
+ renderedHeight: number;
53
+ objectFit: string;
54
+ rect: CheckRect;
55
+ }
56
+ export interface ImageHealthResult {
57
+ rule: "image";
58
+ found: boolean;
59
+ outcome: "pass" | "fail";
60
+ scanned: number;
61
+ truncated: boolean;
62
+ issues: ImageHit[];
63
+ }
64
+
65
+ export interface TruncationHit {
66
+ label: string;
67
+ axis: "x" | "y";
68
+ how: "ellipsis" | "line-clamp";
69
+ overflowPx: number;
70
+ snippet: string;
71
+ rect: CheckRect;
72
+ }
73
+ export interface TruncationResult {
74
+ rule: "truncation";
75
+ found: boolean;
76
+ outcome: "pass" | "fail";
77
+ scanned: number;
78
+ truncated: boolean;
79
+ hits: TruncationHit[];
80
+ }
81
+
82
+ export interface ContrastHit {
83
+ label: string;
84
+ ratio: number;
85
+ required: number;
86
+ color: string;
87
+ background: string;
88
+ largeText: boolean;
89
+ snippet: string;
90
+ outcome: "fail" | "unknown";
91
+ rect: CheckRect;
92
+ }
93
+ export interface ContrastResult {
94
+ rule: "contrast";
95
+ found: boolean;
96
+ outcome: "pass" | "fail" | "unknown";
97
+ scanned: number;
98
+ truncated: boolean;
99
+ hits: ContrastHit[];
100
+ }
101
+
102
+ export interface ContentChecksRequest {
103
+ placeholder: { scope: string | null } | null;
104
+ image: { scope: string | null; tolerance: number } | null;
105
+ truncation: { scope: string | null; tolerance: number } | null;
106
+ contrast: { scope: string | null } | null;
107
+ }
108
+
109
+ export interface ContentChecksResult {
110
+ placeholder?: PlaceholderResult;
111
+ image?: ImageHealthResult;
112
+ truncation?: TruncationResult;
113
+ contrast?: ContrastResult;
114
+ }
115
+
116
+ export function buildContentChecks(): (request: ContentChecksRequest) => ContentChecksResult {
117
+ return (request) => {
118
+ const NODE_CAP = 6000;
119
+ const HIT_CAP = 100;
120
+
121
+ const sx = window.scrollX;
122
+ const sy = window.scrollY;
123
+
124
+ const labelOf = (element: Element): string => {
125
+ const tag = element.tagName.toLowerCase();
126
+ if (element.id) return `${tag}#${element.id}`;
127
+ const cls =
128
+ typeof element.className === "string"
129
+ ? element.className.trim()
130
+ : (element.getAttribute("class") ?? "").trim();
131
+ return cls ? `${tag}.${cls.split(/\s+/).slice(0, 2).join(".")}` : tag;
132
+ };
133
+
134
+ const isHidden = (element: Element): boolean => {
135
+ let current: Element | null = element;
136
+ while (current) {
137
+ const style = getComputedStyle(current);
138
+ const cv = (style as CSSStyleDeclaration & { contentVisibility?: string })
139
+ .contentVisibility;
140
+ if (
141
+ style.display === "none" ||
142
+ style.visibility === "hidden" ||
143
+ style.visibility === "collapse" ||
144
+ Number.parseFloat(style.opacity || "1") === 0 ||
145
+ cv === "hidden"
146
+ ) {
147
+ return true;
148
+ }
149
+ current = current.parentElement;
150
+ }
151
+ return false;
152
+ };
153
+
154
+ const docRect = (r: DOMRect): CheckRect => ({
155
+ x: Math.round(r.x + sx),
156
+ y: Math.round(r.y + sy),
157
+ width: Math.round(r.width),
158
+ height: Math.round(r.height),
159
+ });
160
+
161
+ const rootOf = (scope: string | null): Element | null =>
162
+ scope ? document.querySelector(scope) : document.body;
163
+
164
+ const snippetOf = (text: string): string => text.replace(/\s+/g, " ").trim().slice(0, 80);
165
+
166
+ // ---- placeholder --------------------------------------------------------
167
+ let placeholder: PlaceholderResult | undefined;
168
+ if (request.placeholder) {
169
+ const root = rootOf(request.placeholder.scope);
170
+ const hits: PlaceholderHit[] = [];
171
+ let scanned = 0;
172
+ let truncated = false;
173
+ const EMPTY = new Set(["undefined", "null", "NaN", "[object Object]", "Invalid Date"]);
174
+ // Ordered so the most specific token wins the label for a given match.
175
+ const TOKENS: Array<{ re: RegExp; kind: PlaceholderHit["kind"] }> = [
176
+ { re: /\$\{[^}]{1,120}\}/g, kind: "js-template" },
177
+ { re: /\{\{[^}]{1,120}\}\}/g, kind: "mustache" },
178
+ { re: /\[object [A-Z][A-Za-z]+\]/g, kind: "object" },
179
+ { re: /\bInvalid Date\b/g, kind: "invalid-date" },
180
+ { re: /(?<![A-Za-z])NaN(?![A-Za-z])/g, kind: "nan" },
181
+ ];
182
+ const rectForTextNode = (node: Text, index: number, length: number): CheckRect | null => {
183
+ try {
184
+ const range = document.createRange();
185
+ range.setStart(node, index);
186
+ range.setEnd(node, Math.min(index + length, node.nodeValue?.length ?? index));
187
+ const r = range.getBoundingClientRect();
188
+ if (r.width <= 0 && r.height <= 0) return null;
189
+ return docRect(r);
190
+ } catch {
191
+ return null;
192
+ }
193
+ };
194
+ if (root && !isHidden(root)) {
195
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
196
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
197
+ if (scanned >= NODE_CAP) {
198
+ truncated = true;
199
+ break;
200
+ }
201
+ const parent = node.parentElement;
202
+ if (!parent) continue;
203
+ if (["SCRIPT", "STYLE", "NOSCRIPT", "TEXTAREA"].includes(parent.tagName)) continue;
204
+ if (isHidden(parent)) continue;
205
+ scanned++;
206
+ const value = node.nodeValue ?? "";
207
+ for (const { re, kind } of TOKENS) {
208
+ re.lastIndex = 0;
209
+ for (const m of value.matchAll(re)) {
210
+ if (m.index === undefined) continue;
211
+ const rect = rectForTextNode(node as Text, m.index, m[0].length) ?? {
212
+ x: 0,
213
+ y: 0,
214
+ width: 0,
215
+ height: 0,
216
+ };
217
+ hits.push({
218
+ label: labelOf(parent),
219
+ token: m[0],
220
+ kind,
221
+ snippet: snippetOf(value),
222
+ rect,
223
+ });
224
+ if (hits.length >= HIT_CAP) break;
225
+ }
226
+ if (hits.length >= HIT_CAP) break;
227
+ }
228
+ if (hits.length >= HIT_CAP) {
229
+ truncated = true;
230
+ break;
231
+ }
232
+ }
233
+ // Whole-element-equals sweep for bare undefined/null (prose-safe: only
234
+ // when the trimmed text of a leaf element is exactly the tell).
235
+ if (hits.length < HIT_CAP) {
236
+ const leaves = root.querySelectorAll("*");
237
+ for (let i = 0; i < leaves.length && i < NODE_CAP; i++) {
238
+ const el = leaves[i];
239
+ if (!el || el.children.length > 0) continue;
240
+ if (isHidden(el)) continue;
241
+ const text = (el.textContent ?? "").trim();
242
+ if (!EMPTY.has(text)) continue;
243
+ const r = el.getBoundingClientRect();
244
+ hits.push({
245
+ label: labelOf(el),
246
+ token: text,
247
+ kind: "empty-binding",
248
+ snippet: text,
249
+ rect: docRect(r),
250
+ });
251
+ if (hits.length >= HIT_CAP) {
252
+ truncated = true;
253
+ break;
254
+ }
255
+ }
256
+ }
257
+ }
258
+ placeholder = {
259
+ rule: "placeholder",
260
+ found: root !== null,
261
+ outcome: root && hits.length === 0 ? "pass" : "fail",
262
+ scanned,
263
+ truncated,
264
+ hits,
265
+ };
266
+ }
267
+
268
+ // ---- image health -------------------------------------------------------
269
+ let image: ImageHealthResult | undefined;
270
+ if (request.image) {
271
+ const root = rootOf(request.image.scope);
272
+ const tol = request.image.tolerance;
273
+ const issues: ImageHit[] = [];
274
+ let scanned = 0;
275
+ let truncated = false;
276
+ if (root) {
277
+ const imgs = root.querySelectorAll("img");
278
+ for (let i = 0; i < imgs.length; i++) {
279
+ const img = imgs[i] as HTMLImageElement | undefined;
280
+ if (!img) continue;
281
+ if (isHidden(img)) continue;
282
+ if (scanned >= NODE_CAP) {
283
+ truncated = true;
284
+ break;
285
+ }
286
+ scanned++;
287
+ const rect = img.getBoundingClientRect();
288
+ const objectFit = getComputedStyle(img).objectFit || "fill";
289
+ let reason: ImageHit["reason"] | null = null;
290
+ if (!img.complete) {
291
+ reason = "loading";
292
+ } else if (img.naturalWidth === 0 || img.naturalHeight === 0) {
293
+ reason = "missing";
294
+ } else if (
295
+ rect.width > 1 &&
296
+ rect.height > 1 &&
297
+ objectFit !== "cover" &&
298
+ objectFit !== "contain" &&
299
+ objectFit !== "scale-down"
300
+ ) {
301
+ const renderedAR = rect.width / rect.height;
302
+ const naturalAR = img.naturalWidth / img.naturalHeight;
303
+ const deviation = Math.abs(renderedAR - naturalAR) / naturalAR;
304
+ if (deviation > tol) reason = "stretched";
305
+ }
306
+ if (!reason) continue;
307
+ issues.push({
308
+ label: labelOf(img),
309
+ src: img.currentSrc || img.src || "(none)",
310
+ reason,
311
+ naturalWidth: img.naturalWidth,
312
+ naturalHeight: img.naturalHeight,
313
+ renderedWidth: Math.round(rect.width),
314
+ renderedHeight: Math.round(rect.height),
315
+ objectFit,
316
+ rect: docRect(rect),
317
+ });
318
+ if (issues.length >= HIT_CAP) {
319
+ truncated = true;
320
+ break;
321
+ }
322
+ }
323
+ }
324
+ // "loading" alone is not a failure — the capture may have raced the load.
325
+ const failing = issues.some((i) => i.reason !== "loading");
326
+ image = {
327
+ rule: "image",
328
+ found: root !== null,
329
+ outcome: !root || failing ? "fail" : "pass",
330
+ scanned,
331
+ truncated,
332
+ issues,
333
+ };
334
+ }
335
+
336
+ // ---- truncation ---------------------------------------------------------
337
+ let truncation: TruncationResult | undefined;
338
+ if (request.truncation) {
339
+ const root = rootOf(request.truncation.scope);
340
+ const tol = request.truncation.tolerance;
341
+ const hits: TruncationHit[] = [];
342
+ let scanned = 0;
343
+ let truncated = false;
344
+ if (root) {
345
+ const all = root.querySelectorAll("*");
346
+ for (let i = 0; i < all.length; i++) {
347
+ const el = all[i] as HTMLElement | undefined;
348
+ if (!el) continue;
349
+ if (scanned >= NODE_CAP) {
350
+ truncated = true;
351
+ break;
352
+ }
353
+ const text = (el.textContent ?? "").trim();
354
+ if (!text) continue;
355
+ if (isHidden(el)) continue;
356
+ scanned++;
357
+ const style = getComputedStyle(el);
358
+ // Horizontal: an active single-line ellipsis.
359
+ if (
360
+ style.textOverflow === "ellipsis" &&
361
+ (style.overflowX === "hidden" || style.overflowX === "clip") &&
362
+ el.scrollWidth - el.clientWidth > tol
363
+ ) {
364
+ hits.push({
365
+ label: labelOf(el),
366
+ axis: "x",
367
+ how: "ellipsis",
368
+ overflowPx: Math.round(el.scrollWidth - el.clientWidth),
369
+ snippet: snippetOf(text),
370
+ rect: docRect(el.getBoundingClientRect()),
371
+ });
372
+ } else {
373
+ // Vertical: an active -webkit-line-clamp.
374
+ const clampRaw = style.getPropertyValue("-webkit-line-clamp");
375
+ const clamp = Number.parseInt(clampRaw, 10);
376
+ if (Number.isFinite(clamp) && clamp > 0 && el.scrollHeight - el.clientHeight > tol) {
377
+ hits.push({
378
+ label: labelOf(el),
379
+ axis: "y",
380
+ how: "line-clamp",
381
+ overflowPx: Math.round(el.scrollHeight - el.clientHeight),
382
+ snippet: snippetOf(text),
383
+ rect: docRect(el.getBoundingClientRect()),
384
+ });
385
+ }
386
+ }
387
+ if (hits.length >= HIT_CAP) {
388
+ truncated = true;
389
+ break;
390
+ }
391
+ }
392
+ }
393
+ truncation = {
394
+ rule: "truncation",
395
+ found: root !== null,
396
+ outcome: !root || hits.length > 0 ? "fail" : "pass",
397
+ scanned,
398
+ truncated,
399
+ hits,
400
+ };
401
+ }
402
+
403
+ // ---- contrast -----------------------------------------------------------
404
+ let contrast: ContrastResult | undefined;
405
+ if (request.contrast) {
406
+ const root = rootOf(request.contrast.scope);
407
+ const hits: ContrastHit[] = [];
408
+ let scanned = 0;
409
+ let truncated = false;
410
+
411
+ const parseColor = (value: string): { r: number; g: number; b: number; a: number } | null => {
412
+ const m = value.match(/rgba?\(([^)]+)\)/);
413
+ if (!m?.[1]) return null;
414
+ const parts = m[1].split(",").map((p) => Number.parseFloat(p.trim()));
415
+ if (parts.length < 3 || parts.some((n) => Number.isNaN(n))) return null;
416
+ return { r: parts[0]!, g: parts[1]!, b: parts[2]!, a: parts[3] ?? 1 };
417
+ };
418
+ const lin = (c: number): number => {
419
+ const s = c / 255;
420
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
421
+ };
422
+ const luminance = (r: number, g: number, b: number): number =>
423
+ 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
424
+ const over = (
425
+ fg: { r: number; g: number; b: number; a: number },
426
+ bg: { r: number; g: number; b: number },
427
+ ) => ({
428
+ r: fg.r * fg.a + bg.r * (1 - fg.a),
429
+ g: fg.g * fg.a + bg.g * (1 - fg.a),
430
+ b: fg.b * fg.a + bg.b * (1 - fg.a),
431
+ });
432
+
433
+ const hasDirectText = (el: Element): boolean => {
434
+ for (const node of el.childNodes) {
435
+ if (node.nodeType === Node.TEXT_NODE && (node.nodeValue ?? "").trim().length > 0) {
436
+ return true;
437
+ }
438
+ }
439
+ return false;
440
+ };
441
+
442
+ // Effective background: first opaque ancestor background-color. A
443
+ // background-image or gradient anywhere in the chain makes it unknowable.
444
+ const effectiveBg = (
445
+ el: Element,
446
+ ): { rgb: { r: number; g: number; b: number }; unknown: boolean } => {
447
+ let current: Element | null = el;
448
+ while (current) {
449
+ const style = getComputedStyle(current);
450
+ if (style.backgroundImage && style.backgroundImage !== "none") {
451
+ return { rgb: { r: 255, g: 255, b: 255 }, unknown: true };
452
+ }
453
+ const bg = parseColor(style.backgroundColor);
454
+ if (bg && bg.a >= 0.999) return { rgb: { r: bg.r, g: bg.g, b: bg.b }, unknown: false };
455
+ current = current.parentElement;
456
+ }
457
+ // Fell through to the canvas: assume white (the common page default).
458
+ return { rgb: { r: 255, g: 255, b: 255 }, unknown: false };
459
+ };
460
+
461
+ if (root) {
462
+ const all = root.querySelectorAll("*");
463
+ for (let i = 0; i < all.length; i++) {
464
+ const el = all[i];
465
+ if (!el) continue;
466
+ if (scanned >= NODE_CAP) {
467
+ truncated = true;
468
+ break;
469
+ }
470
+ if (!hasDirectText(el)) continue;
471
+ if (isHidden(el)) continue;
472
+ const rect = el.getBoundingClientRect();
473
+ if (rect.width <= 0 || rect.height <= 0) continue;
474
+ scanned++;
475
+ const style = getComputedStyle(el);
476
+ const fg = parseColor(style.color);
477
+ if (!fg) continue;
478
+ const size = Number.parseFloat(style.fontSize) || 16;
479
+ const weight = Number.parseInt(style.fontWeight, 10) || 400;
480
+ const largeText = size >= 24 || (size >= 18.66 && weight >= 700);
481
+ const required = largeText ? 3 : 4.5;
482
+ const bg = effectiveBg(el);
483
+ const composited = fg.a < 0.999 ? over(fg, bg.rgb) : { r: fg.r, g: fg.g, b: fg.b };
484
+ const lFg = luminance(composited.r, composited.g, composited.b);
485
+ const lBg = luminance(bg.rgb.r, bg.rgb.g, bg.rgb.b);
486
+ const ratio = (Math.max(lFg, lBg) + 0.05) / (Math.min(lFg, lBg) + 0.05);
487
+ const rounded = Math.round(ratio * 100) / 100;
488
+ if (bg.unknown) {
489
+ hits.push({
490
+ label: labelOf(el),
491
+ ratio: rounded,
492
+ required,
493
+ color: style.color,
494
+ background: "(image/gradient)",
495
+ largeText,
496
+ snippet: snippetOf(el.textContent ?? ""),
497
+ outcome: "unknown",
498
+ rect: docRect(rect),
499
+ });
500
+ } else if (ratio < required) {
501
+ hits.push({
502
+ label: labelOf(el),
503
+ ratio: rounded,
504
+ required,
505
+ color: style.color,
506
+ background: style.backgroundColor,
507
+ largeText,
508
+ snippet: snippetOf(el.textContent ?? ""),
509
+ outcome: "fail",
510
+ rect: docRect(rect),
511
+ });
512
+ }
513
+ if (hits.length >= HIT_CAP) {
514
+ truncated = true;
515
+ break;
516
+ }
517
+ }
518
+ }
519
+ const anyFail = hits.some((h) => h.outcome === "fail");
520
+ const anyUnknown = hits.some((h) => h.outcome === "unknown");
521
+ contrast = {
522
+ rule: "contrast",
523
+ found: root !== null,
524
+ outcome: !root ? "fail" : anyFail ? "fail" : anyUnknown ? "unknown" : "pass",
525
+ scanned,
526
+ truncated,
527
+ hits,
528
+ };
529
+ }
530
+
531
+ const result: ContentChecksResult = {};
532
+ if (placeholder) result.placeholder = placeholder;
533
+ if (image) result.image = image;
534
+ if (truncation) result.truncation = truncation;
535
+ if (contrast) result.contrast = contrast;
536
+ return result;
537
+ };
538
+ }
539
+
540
+ export interface ContentAnnotationBox {
541
+ rect: CheckRect;
542
+ label: string;
543
+ color: string;
544
+ }
545
+
546
+ export function buildContentAnnotateScript(): (args: { boxes: ContentAnnotationBox[] }) => void {
547
+ return ({ boxes }) => {
548
+ const ROOT_ID = "__bp-content-check-annotations__";
549
+ document.getElementById(ROOT_ID)?.remove();
550
+ const root = document.createElement("div");
551
+ root.id = ROOT_ID;
552
+ root.style.cssText =
553
+ "position:absolute;top:0;left:0;width:100%;height:0;overflow:visible;pointer-events:none;z-index:2147483646";
554
+ document.body.appendChild(root);
555
+ for (const b of boxes) {
556
+ const node = document.createElement("div");
557
+ node.style.cssText = `position:absolute;left:${b.rect.x - 2}px;top:${b.rect.y - 2}px;width:${Math.max(b.rect.width, 6) + 4}px;height:${Math.max(b.rect.height, 6) + 4}px;border:2px solid ${b.color};box-sizing:border-box;background:${b.color}1a;pointer-events:none`;
558
+ const tag = document.createElement("div");
559
+ tag.textContent = b.label.slice(0, 120);
560
+ tag.style.cssText = `position:absolute;left:0;top:-18px;background:${b.color};color:#fff;font:12px/1.4 system-ui,sans-serif;padding:1px 5px;border-radius:3px;white-space:nowrap`;
561
+ node.appendChild(tag);
562
+ root.appendChild(node);
563
+ }
564
+ };
565
+ }
566
+
567
+ export function buildClearContentAnnotationsScript(): () => void {
568
+ return () => {
569
+ document.getElementById("__bp-content-check-annotations__")?.remove();
570
+ };
571
+ }