harnery 0.29.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 (512) 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 +2 -1
  51. package/dist/commands/work.d.ts.map +1 -1
  52. package/dist/commands/work.js +55 -25
  53. package/dist/commands/workflow.d.ts.map +1 -1
  54. package/dist/commands/workflow.js +35 -28
  55. package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
  56. package/dist/core/adapters/attest-projection.d.ts.map +1 -0
  57. package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
  58. package/dist/core/adapters/attest.d.ts.map +1 -0
  59. package/dist/core/{harnesses → adapters}/attest.js +9 -9
  60. package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
  61. package/dist/core/adapters/attestation.d.ts.map +1 -0
  62. package/dist/core/{harnesses → adapters}/attestation.js +20 -20
  63. package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
  64. package/dist/core/adapters/bench.d.ts.map +1 -0
  65. package/dist/core/{harnesses → adapters}/bench.js +12 -12
  66. package/dist/core/adapters/index.d.ts +13 -0
  67. package/dist/core/adapters/index.d.ts.map +1 -0
  68. package/dist/core/adapters/index.js +6 -0
  69. package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
  70. package/dist/core/adapters/profiles.d.ts.map +1 -0
  71. package/dist/core/{harnesses → adapters}/profiles.js +11 -11
  72. package/dist/core/adapters/registry.d.ts +15 -0
  73. package/dist/core/adapters/registry.d.ts.map +1 -0
  74. package/dist/core/{harnesses → adapters}/registry.js +11 -11
  75. package/dist/core/{harnesses → adapters}/types.d.ts +28 -23
  76. package/dist/core/adapters/types.d.ts.map +1 -0
  77. package/dist/core/{harnesses → adapters}/types.js +1 -1
  78. package/dist/core/agents/canonical-emit.d.ts +4 -4
  79. package/dist/core/agents/canonical-emit.js +5 -5
  80. package/dist/core/agents/cli-emit.js +1 -1
  81. package/dist/core/agents/cli.js +30 -26
  82. package/dist/core/agents/codex-replay.d.ts +1 -1
  83. package/dist/core/agents/codex-replay.js +6 -6
  84. package/dist/core/agents/coord-client.d.ts +21 -2
  85. package/dist/core/agents/coord-client.d.ts.map +1 -1
  86. package/dist/core/agents/coord-client.js +122 -27
  87. package/dist/core/agents/events/consume.d.ts +1 -1
  88. package/dist/core/agents/events/emit.d.ts +5 -6
  89. package/dist/core/agents/events/emit.d.ts.map +1 -1
  90. package/dist/core/agents/events/emit.js +2 -2
  91. package/dist/core/agents/render/prompt-context.d.ts +21 -9
  92. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  93. package/dist/core/agents/render/prompt-context.js +82 -30
  94. package/dist/core/agents/render/session-context.d.ts +1 -1
  95. package/dist/core/agents/render/session-context.js +5 -5
  96. package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
  97. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  98. package/dist/core/agents/rules/claim-conflict.js +1 -1
  99. package/dist/core/agents/rules/commit-conflict.js +98 -22
  100. package/dist/core/agents/rules/stop-hook.d.ts +9 -9
  101. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  102. package/dist/core/agents/rules/stop-hook.js +23 -5
  103. package/dist/core/agents/rules/verdict.d.ts +14 -0
  104. package/dist/core/agents/rules/verdict.d.ts.map +1 -0
  105. package/dist/core/agents/rules/verdict.js +1 -0
  106. package/dist/core/agents/session-events.js +3 -3
  107. package/dist/core/agents/state/council.d.ts +1 -1
  108. package/dist/core/agents/state/council.js +1 -1
  109. package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
  110. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  111. package/dist/core/agents/state/heartbeat-projector.js +14 -10
  112. package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
  113. package/dist/core/agents/state/heartbeat-writer.js +12 -12
  114. package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
  115. package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
  116. package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
  117. package/dist/core/agents/state/pidmap.d.ts +28 -4
  118. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  119. package/dist/core/agents/state/pidmap.js +130 -15
  120. package/dist/core/agents/state/proc-start.d.ts +95 -0
  121. package/dist/core/agents/state/proc-start.d.ts.map +1 -0
  122. package/dist/core/agents/state/proc-start.js +199 -0
  123. package/dist/core/agents/state/shell-mutation.d.ts +1 -1
  124. package/dist/core/agents/state/shell-mutation.js +1 -1
  125. package/dist/core/agents/state/stale-sweep.js +7 -7
  126. package/dist/core/artifacts/index.d.ts +86 -0
  127. package/dist/core/artifacts/index.d.ts.map +1 -0
  128. package/dist/core/artifacts/index.js +421 -0
  129. package/dist/core/config.d.ts +9 -2
  130. package/dist/core/config.d.ts.map +1 -1
  131. package/dist/core/config.js +26 -2
  132. package/dist/core/context/index.d.ts +12 -12
  133. package/dist/core/context/index.js +10 -10
  134. package/dist/core/governor/adapter-spread.d.ts +52 -0
  135. package/dist/core/governor/adapter-spread.d.ts.map +1 -0
  136. package/dist/core/governor/adapter-spread.js +52 -0
  137. package/dist/core/governor/index.d.ts +7 -0
  138. package/dist/core/governor/index.d.ts.map +1 -0
  139. package/dist/core/governor/index.js +6 -0
  140. package/dist/core/governor/plan-api.d.ts +38 -0
  141. package/dist/core/governor/plan-api.d.ts.map +1 -0
  142. package/dist/core/governor/plan-api.js +83 -0
  143. package/dist/core/governor/plan-read.d.ts +5 -0
  144. package/dist/core/governor/plan-read.d.ts.map +1 -0
  145. package/dist/core/{supervisor → governor}/plan-read.js +71 -51
  146. package/dist/core/governor/plan-types.d.ts +181 -0
  147. package/dist/core/governor/plan-types.d.ts.map +1 -0
  148. package/dist/core/{supervisor → governor}/plan-types.js +4 -4
  149. package/dist/core/governor/planning.d.ts +48 -0
  150. package/dist/core/governor/planning.d.ts.map +1 -0
  151. package/dist/core/{supervisor → governor}/planning.js +143 -94
  152. package/dist/core/governor/read.d.ts +6 -0
  153. package/dist/core/governor/read.d.ts.map +1 -0
  154. package/dist/core/governor/read.js +4 -0
  155. package/dist/core/governor/runner.d.ts +37 -0
  156. package/dist/core/governor/runner.d.ts.map +1 -0
  157. package/dist/core/{supervisor → governor}/runner.js +15 -15
  158. package/dist/core/governor/service-read.d.ts +6 -0
  159. package/dist/core/governor/service-read.d.ts.map +1 -0
  160. package/dist/core/{supervisor → governor}/service-read.js +12 -12
  161. package/dist/core/governor/service.d.ts +114 -0
  162. package/dist/core/governor/service.d.ts.map +1 -0
  163. package/dist/core/{supervisor → governor}/service.js +57 -57
  164. package/dist/core/governor/state.d.ts +116 -0
  165. package/dist/core/governor/state.d.ts.map +1 -0
  166. package/dist/core/{supervisor → governor}/state.js +290 -116
  167. package/dist/core/hooks/adapter/detect.d.ts +9 -0
  168. package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
  169. package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
  170. package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
  171. package/dist/core/hooks/adapter/events.d.ts.map +1 -0
  172. package/dist/core/hooks/{harness → adapter}/events.js +5 -5
  173. package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
  174. package/dist/core/hooks/adapter/output.d.ts.map +1 -0
  175. package/dist/core/hooks/{harness → adapter}/output.js +30 -22
  176. package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
  177. package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
  178. package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
  179. package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
  180. package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
  181. package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
  182. package/dist/core/hooks/cli.d.ts +3 -3
  183. package/dist/core/hooks/cli.js +175 -151
  184. package/dist/core/hooks/effects/image-capture.d.ts +5 -5
  185. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  186. package/dist/core/hooks/effects/image-capture.js +3 -3
  187. package/dist/core/hooks/effects/index.d.ts +12 -12
  188. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  189. package/dist/core/hooks/effects/index.js +17 -17
  190. package/dist/core/hooks/events/emit.d.ts +2 -2
  191. package/dist/core/hooks/events/emit.d.ts.map +1 -1
  192. package/dist/core/hooks/events/emit.js +1 -1
  193. package/dist/core/hooks/events/rotate.js +1 -1
  194. package/dist/core/hooks/events/schema.d.ts +13 -5
  195. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  196. package/dist/core/hooks/guard-path.d.ts +2 -2
  197. package/dist/core/hooks/guard-path.js +2 -2
  198. package/dist/core/hooks/index.d.ts +1 -1
  199. package/dist/core/hooks/index.d.ts.map +1 -1
  200. package/dist/core/hooks/resolve/anchor.d.ts +14 -5
  201. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
  202. package/dist/core/hooks/resolve/anchor.js +64 -7
  203. package/dist/core/hooks/resolve/coord-root.d.ts +3 -3
  204. package/dist/core/hooks/resolve/coord-root.js +4 -4
  205. package/dist/core/hooks/resolve/intent.js +1 -1
  206. package/dist/core/hooks/resolve/owner.d.ts +1 -1
  207. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  208. package/dist/core/hooks/resolve/owner.js +11 -5
  209. package/dist/core/{scratch → journal}/index.d.ts +25 -25
  210. package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
  211. package/dist/core/{scratch → journal}/index.js +38 -38
  212. package/dist/core/policy/evaluate.d.ts +1 -1
  213. package/dist/core/policy/evaluate.d.ts.map +1 -1
  214. package/dist/core/policy/evaluate.js +7 -7
  215. package/dist/core/policy/types.d.ts +3 -3
  216. package/dist/core/policy/types.d.ts.map +1 -1
  217. package/dist/core/work/runner.d.ts.map +1 -1
  218. package/dist/core/work/runner.js +11 -2
  219. package/dist/core/work/state.d.ts +28 -3
  220. package/dist/core/work/state.d.ts.map +1 -1
  221. package/dist/core/work/state.js +148 -16
  222. package/dist/core/workflow/adapters.d.ts +17 -0
  223. package/dist/core/workflow/adapters.d.ts.map +1 -0
  224. package/dist/core/workflow/adapters.js +21 -0
  225. package/dist/core/workflow/approvals.js +6 -6
  226. package/dist/core/workflow/billing.d.ts +9 -9
  227. package/dist/core/workflow/billing.js +9 -9
  228. package/dist/core/workflow/child-env.d.ts +10 -2
  229. package/dist/core/workflow/child-env.d.ts.map +1 -1
  230. package/dist/core/workflow/child-env.js +12 -4
  231. package/dist/core/workflow/engine.d.ts +15 -5
  232. package/dist/core/workflow/engine.d.ts.map +1 -1
  233. package/dist/core/workflow/engine.js +151 -83
  234. package/dist/core/workflow/evidence-preflight.d.ts +24 -0
  235. package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
  236. package/dist/core/workflow/evidence-preflight.js +134 -0
  237. package/dist/core/workflow/index.d.ts +3 -2
  238. package/dist/core/workflow/index.d.ts.map +1 -1
  239. package/dist/core/workflow/index.js +2 -1
  240. package/dist/core/workflow/proof.d.ts +28 -5
  241. package/dist/core/workflow/proof.d.ts.map +1 -1
  242. package/dist/core/workflow/proof.js +77 -34
  243. package/dist/core/workflow/run-state.d.ts +1 -1
  244. package/dist/core/workflow/run-state.js +7 -7
  245. package/dist/core/workflow/sandbox-projection.d.ts +6 -6
  246. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  247. package/dist/core/workflow/sandbox-projection.js +11 -11
  248. package/dist/core/workflow/spawn-claude.d.ts +3 -3
  249. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  250. package/dist/core/workflow/spawn-claude.js +28 -8
  251. package/dist/core/workflow/spawn-codex.d.ts +4 -4
  252. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  253. package/dist/core/workflow/spawn-codex.js +26 -8
  254. package/dist/core/workflow/spawn-cursor.d.ts +4 -4
  255. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  256. package/dist/core/workflow/spawn-cursor.js +27 -8
  257. package/dist/core/workflow/spawn-failure.d.ts +15 -1
  258. package/dist/core/workflow/spawn-failure.d.ts.map +1 -1
  259. package/dist/core/workflow/spawn-failure.js +35 -1
  260. package/dist/core/workflow/specialists.js +2 -2
  261. package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
  262. package/dist/core/workflow/transcript.d.ts.map +1 -0
  263. package/dist/core/workflow/{journal.js → transcript.js} +14 -14
  264. package/dist/core/workflow/types.d.ts +119 -46
  265. package/dist/core/workflow/types.d.ts.map +1 -1
  266. package/dist/core/workflow/types.js +11 -1
  267. package/dist/core/workflow/validate.d.ts +1 -1
  268. package/dist/core/workflow/validate.js +1 -1
  269. package/dist/core/workflow/workspaces/cancellation.js +2 -2
  270. package/dist/core/workflow/workspaces/cleanup.js +2 -2
  271. package/dist/core/workflow/workspaces/execution.js +6 -6
  272. package/dist/core/workflow/workspaces/inspect.js +9 -9
  273. package/dist/core/workflow/workspaces/integration.js +14 -14
  274. package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
  275. package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
  276. package/dist/core/workflow/workspaces/lifecycle.js +9 -9
  277. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  278. package/dist/core/workflow/workspaces/local-git.js +39 -5
  279. package/dist/core/workflow/workspaces/recovery.js +3 -3
  280. package/dist/core/workflow/workspaces/state.d.ts +1 -1
  281. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  282. package/dist/core/workflow/workspaces/state.js +3 -3
  283. package/dist/core/workflow/workspaces/types.d.ts +1 -1
  284. package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
  285. package/dist/lib/browser/asserts.d.ts +28 -0
  286. package/dist/lib/browser/asserts.d.ts.map +1 -0
  287. package/dist/lib/browser/asserts.js +152 -0
  288. package/dist/lib/browser/client.d.ts +49 -0
  289. package/dist/lib/browser/client.d.ts.map +1 -1
  290. package/dist/lib/browser/client.js +155 -14
  291. package/dist/lib/browser/content-checks.d.ts +108 -0
  292. package/dist/lib/browser/content-checks.d.ts.map +1 -0
  293. package/dist/lib/browser/content-checks.js +474 -0
  294. package/dist/lib/browser/critique.d.ts +91 -0
  295. package/dist/lib/browser/critique.d.ts.map +1 -0
  296. package/dist/lib/browser/critique.js +167 -0
  297. package/dist/lib/browser/geometry.d.ts +32 -0
  298. package/dist/lib/browser/geometry.d.ts.map +1 -1
  299. package/dist/lib/browser/geometry.js +244 -1
  300. package/dist/lib/browser/index.d.ts +4 -1
  301. package/dist/lib/browser/index.d.ts.map +1 -1
  302. package/dist/lib/browser/index.js +2 -0
  303. package/dist/lib/claude-desktop.d.ts.map +1 -1
  304. package/dist/lib/claude-desktop.js +4 -1
  305. package/dist/lib/council/index.d.ts +6 -6
  306. package/dist/lib/council/index.d.ts.map +1 -1
  307. package/dist/lib/council/index.js +8 -8
  308. package/dist/lib/exec.d.ts +7 -0
  309. package/dist/lib/exec.d.ts.map +1 -1
  310. package/dist/lib/exec.js +6 -3
  311. package/dist/lib/identities/assume.d.ts +2 -2
  312. package/dist/lib/identities/assume.js +7 -7
  313. package/dist/lib/instructions/apply.d.ts +3 -3
  314. package/dist/lib/instructions/apply.d.ts.map +1 -1
  315. package/dist/lib/instructions/apply.js +75 -27
  316. package/dist/lib/instructions/host-addendum.d.ts +54 -0
  317. package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
  318. package/dist/lib/instructions/host-addendum.js +107 -0
  319. package/dist/lib/instructions/templates.d.ts +10 -3
  320. package/dist/lib/instructions/templates.d.ts.map +1 -1
  321. package/dist/lib/instructions/templates.js +41 -12
  322. package/dist/lib/machine.d.ts +2 -2
  323. package/dist/lib/machine.js +2 -2
  324. package/package.json +26 -21
  325. package/schemas/config.schema.json +26 -1
  326. package/src/commander.ts +22 -13
  327. package/src/commands/{harness.ts → adapter.ts} +46 -46
  328. package/src/commands/agents.ts +71 -64
  329. package/src/commands/artifacts.ts +168 -0
  330. package/src/commands/backup.ts +47 -33
  331. package/src/commands/browse.ts +438 -2
  332. package/src/commands/checkpoint.ts +196 -0
  333. package/src/commands/context.ts +3 -176
  334. package/src/commands/decision.ts +41 -7
  335. package/src/commands/deinit.ts +16 -16
  336. package/src/commands/doctor.ts +28 -28
  337. package/src/commands/{supervisor.ts → governor.ts} +184 -154
  338. package/src/commands/init.ts +50 -34
  339. package/src/commands/{scratch.ts → journal.ts} +60 -60
  340. package/src/commands/policy.ts +1 -1
  341. package/src/commands/presence.ts +2 -2
  342. package/src/commands/sync.ts +3 -3
  343. package/src/commands/tunnel.ts +237 -34
  344. package/src/commands/web-fetch.ts +1 -1
  345. package/src/commands/web.ts +56 -1
  346. package/src/commands/work.ts +72 -38
  347. package/src/commands/workflow.ts +43 -34
  348. package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
  349. package/src/core/{harnesses → adapters}/attest.ts +27 -27
  350. package/src/core/{harnesses → adapters}/attestation.ts +41 -41
  351. package/src/core/{harnesses → adapters}/bench.ts +34 -43
  352. package/src/core/adapters/index.ts +56 -0
  353. package/src/core/{harnesses → adapters}/profiles.ts +16 -16
  354. package/src/core/{harnesses → adapters}/registry.ts +23 -23
  355. package/src/core/{harnesses → adapters}/types.ts +28 -23
  356. package/src/core/agents/canonical-emit.ts +6 -6
  357. package/src/core/agents/cli-emit.ts +1 -1
  358. package/src/core/agents/cli.ts +30 -28
  359. package/src/core/agents/codex-replay.ts +6 -6
  360. package/src/core/agents/coord-client.ts +119 -28
  361. package/src/core/agents/events/consume.ts +1 -1
  362. package/src/core/agents/events/emit.ts +7 -6
  363. package/src/core/agents/render/prompt-context.ts +107 -31
  364. package/src/core/agents/render/session-context.ts +6 -6
  365. package/src/core/agents/rules/claim-conflict.ts +4 -7
  366. package/src/core/agents/rules/commit-conflict.ts +103 -22
  367. package/src/core/agents/rules/stop-hook.ts +33 -17
  368. package/src/core/agents/rules/verdict.ts +13 -0
  369. package/src/core/agents/session-events.ts +4 -4
  370. package/src/core/agents/state/council.ts +1 -1
  371. package/src/core/agents/state/heartbeat-projector.ts +16 -12
  372. package/src/core/agents/state/heartbeat-writer.ts +12 -12
  373. package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
  374. package/src/core/agents/state/pidmap.ts +130 -15
  375. package/src/core/agents/state/proc-start.ts +208 -0
  376. package/src/core/agents/state/shell-mutation.ts +1 -1
  377. package/src/core/agents/state/stale-sweep.ts +7 -7
  378. package/src/core/artifacts/index.ts +557 -0
  379. package/src/core/config.ts +30 -2
  380. package/src/core/context/index.ts +15 -15
  381. package/src/core/governor/adapter-spread.ts +82 -0
  382. package/src/core/governor/index.ts +91 -0
  383. package/src/core/governor/plan-api.ts +115 -0
  384. package/src/core/{supervisor → governor}/plan-read.ts +120 -96
  385. package/src/core/{supervisor → governor}/plan-types.ts +64 -50
  386. package/src/core/{supervisor → governor}/planning.ts +240 -188
  387. package/src/core/governor/read.ts +59 -0
  388. package/src/core/{supervisor → governor}/runner.ts +39 -39
  389. package/src/core/{supervisor → governor}/service-read.ts +22 -22
  390. package/src/core/{supervisor → governor}/service.ts +117 -119
  391. package/src/core/{supervisor → governor}/state.ts +389 -174
  392. package/src/core/hooks/adapter/detect.ts +30 -0
  393. package/src/core/hooks/{harness → adapter}/events.ts +16 -14
  394. package/src/core/hooks/{harness → adapter}/output.ts +31 -23
  395. package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
  396. package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
  397. package/src/core/hooks/cli.ts +185 -162
  398. package/src/core/hooks/effects/image-capture.ts +6 -6
  399. package/src/core/hooks/effects/index.ts +17 -17
  400. package/src/core/hooks/events/emit.ts +3 -3
  401. package/src/core/hooks/events/rotate.ts +1 -1
  402. package/src/core/hooks/events/schema.ts +15 -7
  403. package/src/core/hooks/guard-path.ts +2 -2
  404. package/src/core/hooks/index.ts +1 -1
  405. package/src/core/hooks/resolve/anchor.ts +63 -8
  406. package/src/core/hooks/resolve/coord-root.ts +4 -4
  407. package/src/core/hooks/resolve/intent.ts +1 -1
  408. package/src/core/hooks/resolve/owner.ts +11 -5
  409. package/src/core/{scratch → journal}/index.ts +53 -53
  410. package/src/core/policy/evaluate.ts +8 -8
  411. package/src/core/policy/types.ts +3 -3
  412. package/src/core/work/runner.ts +11 -2
  413. package/src/core/work/state.ts +185 -21
  414. package/src/core/workflow/adapters.ts +33 -0
  415. package/src/core/workflow/approvals.ts +7 -7
  416. package/src/core/workflow/billing.ts +12 -12
  417. package/src/core/workflow/child-env.ts +13 -4
  418. package/src/core/workflow/engine.ts +166 -90
  419. package/src/core/workflow/evidence-preflight.ts +165 -0
  420. package/src/core/workflow/index.ts +6 -3
  421. package/src/core/workflow/proof.ts +94 -48
  422. package/src/core/workflow/run-state.ts +7 -7
  423. package/src/core/workflow/sandbox-projection.ts +16 -16
  424. package/src/core/workflow/spawn-claude.ts +34 -12
  425. package/src/core/workflow/spawn-codex.ts +30 -12
  426. package/src/core/workflow/spawn-cursor.ts +31 -12
  427. package/src/core/workflow/spawn-failure.ts +41 -1
  428. package/src/core/workflow/specialists.ts +2 -2
  429. package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
  430. package/src/core/workflow/types.ts +130 -46
  431. package/src/core/workflow/validate.ts +1 -1
  432. package/src/core/workflow/workspaces/cancellation.ts +2 -2
  433. package/src/core/workflow/workspaces/cleanup.ts +2 -2
  434. package/src/core/workflow/workspaces/execution.ts +6 -6
  435. package/src/core/workflow/workspaces/inspect.ts +14 -11
  436. package/src/core/workflow/workspaces/integration.ts +18 -14
  437. package/src/core/workflow/workspaces/lifecycle.ts +10 -10
  438. package/src/core/workflow/workspaces/local-git.ts +47 -5
  439. package/src/core/workflow/workspaces/recovery.ts +3 -3
  440. package/src/core/workflow/workspaces/state.ts +4 -3
  441. package/src/core/workflow/workspaces/types.ts +1 -1
  442. package/src/lib/browser/asserts.ts +177 -0
  443. package/src/lib/browser/client.ts +189 -14
  444. package/src/lib/browser/content-checks.ts +571 -0
  445. package/src/lib/browser/critique.ts +231 -0
  446. package/src/lib/browser/geometry.ts +297 -1
  447. package/src/lib/browser/index.ts +34 -0
  448. package/src/lib/claude-desktop.ts +4 -1
  449. package/src/lib/council/index.ts +9 -9
  450. package/src/lib/exec.ts +13 -3
  451. package/src/lib/identities/assume.ts +7 -7
  452. package/src/lib/instructions/apply.ts +87 -25
  453. package/src/lib/instructions/host-addendum.ts +126 -0
  454. package/src/lib/instructions/templates.ts +44 -15
  455. package/src/lib/machine.ts +2 -2
  456. package/dist/commands/harness.d.ts +0 -12
  457. package/dist/commands/harness.d.ts.map +0 -1
  458. package/dist/commands/scratch.d.ts +0 -4
  459. package/dist/commands/scratch.d.ts.map +0 -1
  460. package/dist/commands/session.d.ts.map +0 -1
  461. package/dist/commands/session.js +0 -162
  462. package/dist/commands/supervisor.d.ts +0 -4
  463. package/dist/commands/supervisor.d.ts.map +0 -1
  464. package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
  465. package/dist/core/harnesses/attest.d.ts.map +0 -1
  466. package/dist/core/harnesses/attestation.d.ts.map +0 -1
  467. package/dist/core/harnesses/bench.d.ts.map +0 -1
  468. package/dist/core/harnesses/index.d.ts +0 -13
  469. package/dist/core/harnesses/index.d.ts.map +0 -1
  470. package/dist/core/harnesses/index.js +0 -6
  471. package/dist/core/harnesses/profiles.d.ts.map +0 -1
  472. package/dist/core/harnesses/registry.d.ts +0 -15
  473. package/dist/core/harnesses/registry.d.ts.map +0 -1
  474. package/dist/core/harnesses/types.d.ts.map +0 -1
  475. package/dist/core/hooks/harness/detect.d.ts +0 -9
  476. package/dist/core/hooks/harness/events.d.ts.map +0 -1
  477. package/dist/core/hooks/harness/output.d.ts.map +0 -1
  478. package/dist/core/supervisor/index.d.ts +0 -7
  479. package/dist/core/supervisor/index.d.ts.map +0 -1
  480. package/dist/core/supervisor/index.js +0 -6
  481. package/dist/core/supervisor/plan-api.d.ts +0 -23
  482. package/dist/core/supervisor/plan-api.d.ts.map +0 -1
  483. package/dist/core/supervisor/plan-api.js +0 -50
  484. package/dist/core/supervisor/plan-read.d.ts +0 -5
  485. package/dist/core/supervisor/plan-read.d.ts.map +0 -1
  486. package/dist/core/supervisor/plan-types.d.ts +0 -170
  487. package/dist/core/supervisor/plan-types.d.ts.map +0 -1
  488. package/dist/core/supervisor/planning.d.ts +0 -33
  489. package/dist/core/supervisor/planning.d.ts.map +0 -1
  490. package/dist/core/supervisor/read.d.ts +0 -6
  491. package/dist/core/supervisor/read.d.ts.map +0 -1
  492. package/dist/core/supervisor/read.js +0 -4
  493. package/dist/core/supervisor/runner.d.ts +0 -37
  494. package/dist/core/supervisor/runner.d.ts.map +0 -1
  495. package/dist/core/supervisor/service-read.d.ts +0 -6
  496. package/dist/core/supervisor/service-read.d.ts.map +0 -1
  497. package/dist/core/supervisor/service.d.ts +0 -114
  498. package/dist/core/supervisor/service.d.ts.map +0 -1
  499. package/dist/core/supervisor/state.d.ts +0 -95
  500. package/dist/core/supervisor/state.d.ts.map +0 -1
  501. package/dist/core/workflow/harnesses.d.ts +0 -17
  502. package/dist/core/workflow/harnesses.d.ts.map +0 -1
  503. package/dist/core/workflow/harnesses.js +0 -21
  504. package/dist/core/workflow/journal.d.ts.map +0 -1
  505. package/src/commands/session.ts +0 -187
  506. package/src/core/harnesses/index.ts +0 -56
  507. package/src/core/hooks/harness/detect.ts +0 -30
  508. package/src/core/supervisor/index.ts +0 -89
  509. package/src/core/supervisor/plan-api.ts +0 -73
  510. package/src/core/supervisor/read.ts +0 -59
  511. package/src/core/workflow/harnesses.ts +0 -33
  512. /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
@@ -2,7 +2,7 @@ import { mkdirSync, statSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, resolve } from "node:path";
4
4
  import { resolveBinName } from "../core/config.js";
5
- import { Browser, captureDevOverlay, wslHeadedLaunchArgs, } from "../lib/browser/index.js";
5
+ import { Browser, captureDevOverlay, DEFAULT_CRITIQUE_RUBRIC, parseAssertSpec, runCritique, tilesFromFullPage, wslHeadedLaunchArgs, } from "../lib/browser/index.js";
6
6
  import { diffAgainstBaseline, saveBaseline, } from "../lib/browser/visual-diff.js";
7
7
  import { CookieJar } from "../lib/cookies/index.js";
8
8
  /**
@@ -125,6 +125,60 @@ export function registerBrowseCommand(program, emitParam, context) {
125
125
  .option("--check-overlap-threshold <px>", "Allowed overlap depth on either axis in CSS pixels (default 0).", "0")
126
126
  .option("--check-overlap-fail", "Exit 2 when an overlap target fails or is missing.")
127
127
  .option("--no-check-overlap-annotate", "Skip overlap screenshot annotations.")
128
+ .option("--check-crowd <selector>", "Flag adjacent card panels (full border, modest corner radius, or box-shadow) " +
129
+ "that touch or nearly touch. Also treats wrappers that contain panels as " +
130
+ "peers, measuring the nearest face panels inside them so a card-grid " +
131
+ "flush against the next card is caught. Walks the whole subtree under the " +
132
+ "selector, so one --check-crowd .wrap catches nested cases. Fills the gap " +
133
+ "between --check-overlap (needs a 2D intersection) and --check-gap (flags " +
134
+ "uneven spacing, so a uniformly-flush stack passes). Repeatable.", (value, previous = []) => [...previous, value], [])
135
+ .option("--check-crowd-min <px>", "Minimum acceptable edge gap between adjacent panel faces in CSS px (default 6). " +
136
+ "Pairs closer than this are flagged; negative separation (overlap) always flags.", "6")
137
+ .option("--check-crowd-fail", "Exit 2 when a crowd target fails or is missing.")
138
+ .option("--no-check-crowd-annotate", "Skip crowd screenshot annotations.")
139
+ .option("--check-placeholder [selector]", "Scan rendered text for unrendered template tokens and bound-value tells: " +
140
+ "JS template literals, `{{x}}` handlebars, `[object Object]`, `Invalid Date`, " +
141
+ "`NaN`, or an element whose whole text is literally 'undefined' / 'null'. Optional selector " +
142
+ "scopes the sweep (default: whole body). Catches the most common generation " +
143
+ "bug on funnels and reports. Do not point it at a page that documents template " +
144
+ "syntax in prose.")
145
+ .option("--check-placeholder-fail", "Exit 2 if any placeholder tell is found.")
146
+ .option("--no-check-placeholder-annotate", "Skip placeholder screenshot annotations.")
147
+ .option("--check-images [selector]", "Audit <img> elements for load + aspect health: a failed load (naturalWidth 0), " +
148
+ "a still-loading image, or a stretched image (rendered aspect ratio far from the " +
149
+ "intrinsic one, with an object-fit that does not correct it). Optional selector " +
150
+ "scopes the sweep (default: whole body).")
151
+ .option("--check-images-tolerance <ratio>", "Aspect-ratio deviation above which an image counts as stretched (0–1, default 0.1 = 10%).", "0.1")
152
+ .option("--check-images-fail", "Exit 2 if any image failed to load or is stretched.")
153
+ .option("--no-check-images-annotate", "Skip image screenshot annotations.")
154
+ .option("--check-truncation [selector]", "Flag text actively cut off by an ellipsis or -webkit-line-clamp — the author asked " +
155
+ "to truncate AND the content overflows. High precision: it does not flag plain " +
156
+ "overflow:hidden clips. Optional selector scopes the sweep (default: whole body).")
157
+ .option("--check-truncation-tolerance <px>", "Overflow past the clip box, in CSS px, above which truncation counts (default 2).", "2")
158
+ .option("--check-truncation-fail", "Exit 2 if any text is actively truncated.")
159
+ .option("--no-check-truncation-annotate", "Skip truncation screenshot annotations.")
160
+ .option("--check-contrast [selector]", "Flag rendered text below the WCAG AA contrast ratio (4.5:1 normal, 3:1 large) against " +
161
+ "its effective background. Runs at whatever theme the page is in, so toggle the theme " +
162
+ "with --batch to cover light and dark. Text over an image or gradient is reported as " +
163
+ "`unknown`, not failed. Optional selector scopes the sweep (default: whole body).")
164
+ .option("--check-contrast-fail", "Exit 2 if any text fails the contrast ratio.")
165
+ .option("--no-check-contrast-annotate", "Skip contrast screenshot annotations.")
166
+ .option("--check-critique [selector]", "Hand the rendered page to a vision model for a visual-defect review, tile by " +
167
+ "tile. Catches the long tail heuristic checks can't enumerate. Default tiling " +
168
+ "cuts the page into overlapping vertical bands; pass a selector to tile one " +
169
+ "screenshot per matching element (semantic tiling). Requires the host to inject " +
170
+ "a critiqueProvider (harnery ships no model client) — without one the check " +
171
+ "reports `skipped`. Findings land under `critique` in the JSON envelope.")
172
+ .option("--check-critique-band <px>", "Band height for default tiling (default 1400).", "1400")
173
+ .option("--check-critique-overlap <px>", "Vertical overlap between bands so a finding at a seam stays visible (default 120).", "120")
174
+ .option("--check-critique-max-tiles <n>", "Cap on tiles sent to the model, to bound cost (default 24).", "24")
175
+ .option("--check-critique-rubric <text>", "Override the default critique rubric.")
176
+ .option("--check-critique-fail", "Exit 2 if the critique returns any high-severity finding.")
177
+ .option("--assert <expr>", "Assert a page value (repeatable). Grammar: '<op> <selector> => <expected>', where op is " +
178
+ "text (trimmed text equals), contains (text includes), matches (text matches a regex), " +
179
+ "count (match count vs a number or >=/<=/>/< comparator), exists / absent (no => needed). " +
180
+ "e.g. --assert 'text h1 => Welcome' --assert 'count .card => >=3' --assert 'absent .error'.", (value, previous = []) => [...previous, value], [])
181
+ .option("--assert-fail", "Exit 2 if any --assert fails.")
128
182
  .option("--check-hit [selector]", "Check pointer-target size and spacing in the document or optional scope (repeatable).", (value, previous = []) => [
129
183
  ...previous,
130
184
  typeof value === "string" ? value : null,
@@ -163,6 +217,7 @@ async function runBrowse(url, opts, context) {
163
217
  : parseNonNegativeNumber(opts.checkGapExpected, "--check-gap-expected");
164
218
  const clipThreshold = parseNonNegativeNumber(opts.checkClipThreshold ?? "0", "--check-clip-threshold");
165
219
  const overlapThreshold = parseNonNegativeNumber(opts.checkOverlapThreshold ?? "0", "--check-overlap-threshold");
220
+ const crowdMin = parseNonNegativeNumber(opts.checkCrowdMin ?? "6", "--check-crowd-min");
166
221
  const hitProfile = parseTargetSizeProfile(opts.checkHitProfile ?? "wcag-aa");
167
222
  // Commander: `--no-cookies` turns `opts.cookies` into `false`. Default is `true`.
168
223
  const jar = opts.cookies === false
@@ -238,7 +293,8 @@ async function runBrowse(url, opts, context) {
238
293
  const hasLayoutLint = (opts.checkAlign?.length ?? 0) > 0 ||
239
294
  (opts.checkGap?.length ?? 0) > 0 ||
240
295
  (opts.checkClip?.length ?? 0) > 0 ||
241
- (opts.checkOverlap?.length ?? 0) > 0;
296
+ (opts.checkOverlap?.length ?? 0) > 0 ||
297
+ (opts.checkCrowd?.length ?? 0) > 0;
242
298
  let layoutLint;
243
299
  if (hasLayoutLint) {
244
300
  layoutLint = await browser.checkLayoutLint({
@@ -261,12 +317,58 @@ async function runBrowse(url, opts, context) {
261
317
  selector,
262
318
  tolerancePx: overlapThreshold,
263
319
  })),
320
+ crowd: (opts.checkCrowd ?? []).map((selector) => ({
321
+ selector,
322
+ minGapPx: crowdMin,
323
+ })),
264
324
  });
265
325
  }
266
326
  let hit;
267
327
  if (opts.checkHit && opts.checkHit.length > 0) {
268
328
  hit = await browser.checkTargetSize(opts.checkHit, hitProfile);
269
329
  }
330
+ const hasContentChecks = opts.checkPlaceholder !== undefined ||
331
+ opts.checkImages !== undefined ||
332
+ opts.checkTruncation !== undefined ||
333
+ opts.checkContrast !== undefined;
334
+ let content;
335
+ if (hasContentChecks) {
336
+ content = await browser.checkContent({
337
+ placeholder: opts.checkPlaceholder !== undefined
338
+ ? { scope: contentScope(opts.checkPlaceholder) }
339
+ : null,
340
+ image: opts.checkImages !== undefined
341
+ ? {
342
+ scope: contentScope(opts.checkImages),
343
+ tolerance: parseNonNegativeNumber(opts.checkImagesTolerance ?? "0.1", "--check-images-tolerance"),
344
+ }
345
+ : null,
346
+ truncation: opts.checkTruncation !== undefined
347
+ ? {
348
+ scope: contentScope(opts.checkTruncation),
349
+ tolerance: parseNonNegativeNumber(opts.checkTruncationTolerance ?? "2", "--check-truncation-tolerance"),
350
+ }
351
+ : null,
352
+ contrast: opts.checkContrast !== undefined ? { scope: contentScope(opts.checkContrast) } : null,
353
+ });
354
+ }
355
+ // Vision critique. Capture tiles BEFORE any annotation overlays are injected
356
+ // so the model sees the real page, not our boxes.
357
+ let critique;
358
+ if (opts.checkCritique !== undefined) {
359
+ const tiles = await captureCritiqueTiles(browser, opts);
360
+ critique = await runCritique({
361
+ url: navResult.url,
362
+ rubric: opts.checkCritiqueRubric ?? DEFAULT_CRITIQUE_RUBRIC,
363
+ tiles,
364
+ provider: context?.critiqueProvider,
365
+ });
366
+ }
367
+ let asserts;
368
+ if (opts.assert && opts.assert.length > 0) {
369
+ const specs = opts.assert.map(parseAssertSpec);
370
+ asserts = await browser.checkAsserts(specs);
371
+ }
270
372
  const widthThreshold = Number.parseFloat(opts.checkWidthThreshold ?? "0.9");
271
373
  const annotateWidth = widths && opts.checkWidthAnnotate !== false;
272
374
  const annotateOverflow = overflow && opts.checkOverflowAnnotate !== false;
@@ -287,19 +389,25 @@ async function runBrowse(url, opts, context) {
287
389
  gap: opts.checkGapAnnotate === false ? [] : layoutLint.gap,
288
390
  clip: opts.checkClipAnnotate === false ? [] : layoutLint.clip,
289
391
  overlap: opts.checkOverlapAnnotate === false ? [] : layoutLint.overlap,
392
+ crowd: opts.checkCrowdAnnotate === false ? [] : layoutLint.crowd,
290
393
  }
291
394
  : undefined;
292
395
  if (annotateLayoutLint &&
293
396
  (annotateLayoutLint.align.length > 0 ||
294
397
  annotateLayoutLint.gap.length > 0 ||
295
398
  annotateLayoutLint.clip.length > 0 ||
296
- annotateLayoutLint.overlap.length > 0)) {
399
+ annotateLayoutLint.overlap.length > 0 ||
400
+ annotateLayoutLint.crowd.length > 0)) {
297
401
  await browser.annotateLayoutLint(annotateLayoutLint);
298
402
  }
299
403
  const annotateHit = hit && opts.checkHitAnnotate !== false;
300
404
  if (annotateHit) {
301
405
  await browser.annotateTargetSize(hit);
302
406
  }
407
+ const contentBoxes = content ? collectContentBoxes(content, opts) : [];
408
+ if (contentBoxes.length > 0) {
409
+ await browser.annotateContent(contentBoxes);
410
+ }
303
411
  if (opts.login) {
304
412
  await new Promise((res) => {
305
413
  emit.log("[--login] Headed Chromium is open. Walk through your auth flow now. Press Enter here to close + persist cookies into the profile.", "info");
@@ -319,10 +427,10 @@ async function runBrowse(url, opts, context) {
319
427
  if (opts.baseline || opts.diff) {
320
428
  throw new Error("--baseline / --diff require trio mode (screenshot file). Remove --snapshot/--html/--json or capture the screenshot first.");
321
429
  }
322
- await runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, devOverlay, batchResult);
430
+ await runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, content, critique, asserts, devOverlay, batchResult);
323
431
  }
324
432
  else {
325
- await runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, devOverlay, batchResult);
433
+ await runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, content, critique, asserts, devOverlay, batchResult);
326
434
  }
327
435
  if (visibility && opts.checkVisibleAnnotate !== false) {
328
436
  await browser.clearVisibilityAnnotations();
@@ -339,6 +447,9 @@ async function runBrowse(url, opts, context) {
339
447
  if (annotateHit) {
340
448
  await browser.clearTargetSizeAnnotations();
341
449
  }
450
+ if (contentBoxes.length > 0) {
451
+ await browser.clearContentAnnotations();
452
+ }
342
453
  if (opts.checkVisibleFail && visibility) {
343
454
  const threshold = Number.parseFloat(opts.checkVisibleThreshold ?? "0.9");
344
455
  const failed = visibility.filter((r) => !r.found || !r.cssVisible || r.visibleRatio < threshold);
@@ -389,6 +500,20 @@ async function runBrowse(url, opts, context) {
389
500
  process.exitCode = 2;
390
501
  }
391
502
  applyLayoutLintFailGates(opts, layoutLint, hit);
503
+ applyContentFailGates(opts, content);
504
+ if (opts.checkCritiqueFail && critique && critique.outcome === "fail") {
505
+ const high = critique.findings.filter((f) => f.severity === "high").length;
506
+ emit.log(`check-critique FAIL: ${high} high-severity finding(s)`, "warn");
507
+ process.exitCode = 2;
508
+ }
509
+ if (opts.assertFail && asserts) {
510
+ const failed = asserts.filter((a) => a.outcome === "fail");
511
+ for (const a of failed) {
512
+ emit.log(`assert FAIL: ${a.op} ${a.selector} → "${a.actual}"${a.error ? ` (${a.error})` : ""}`, "warn");
513
+ }
514
+ if (failed.length > 0)
515
+ process.exitCode = 2;
516
+ }
392
517
  }
393
518
  finally {
394
519
  await browser.close();
@@ -488,7 +613,7 @@ function splitBatchSteps(input) {
488
613
  // ---------------------------------------------------------------------------
489
614
  // Print mode (--snapshot / --html / --json): stdout, no file writes
490
615
  // ---------------------------------------------------------------------------
491
- async function runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, devOverlay, batchResult) {
616
+ async function runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, content, critique, asserts, devOverlay, batchResult) {
492
617
  let body = null;
493
618
  if (opts.html) {
494
619
  body = await browser.htmlContent(opts.selector);
@@ -522,9 +647,16 @@ async function runPrintMode(browser, navResult, opts, evalResult, visibility, wi
522
647
  result.gap = layoutLint.gap;
523
648
  result.clip = layoutLint.clip;
524
649
  result.overlap = layoutLint.overlap;
650
+ result.crowd = layoutLint.crowd;
525
651
  }
526
652
  if (hit)
527
653
  result.hit = hit;
654
+ if (content)
655
+ assignContent(result, content);
656
+ if (critique)
657
+ result.critique = critique;
658
+ if (asserts)
659
+ result.asserts = asserts;
528
660
  if (devOverlay)
529
661
  result.devOverlay = devOverlay;
530
662
  if (batchResult && batchResult.clipboardReads.length > 0) {
@@ -548,7 +680,7 @@ async function runPrintMode(browser, navResult, opts, evalResult, visibility, wi
548
680
  // ---------------------------------------------------------------------------
549
681
  // Trio mode (default): write <prefix>.{png,html,json}
550
682
  // ---------------------------------------------------------------------------
551
- async function runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, devOverlay, batchResult) {
683
+ async function runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, layoutLint, hit, content, critique, asserts, devOverlay, batchResult) {
552
684
  const prefix = resolveOutPrefix(opts.out);
553
685
  mkdirSync(dirname(prefix), { recursive: true });
554
686
  const written = [];
@@ -618,9 +750,16 @@ async function runTrioMode(browser, navResult, opts, evalResult, visibility, wid
618
750
  envelope.gap = layoutLint.gap;
619
751
  envelope.clip = layoutLint.clip;
620
752
  envelope.overlap = layoutLint.overlap;
753
+ envelope.crowd = layoutLint.crowd;
621
754
  }
622
755
  if (hit)
623
756
  envelope.hit = hit;
757
+ if (content)
758
+ assignContent(envelope, content);
759
+ if (critique)
760
+ envelope.critique = critique;
761
+ if (asserts)
762
+ envelope.asserts = asserts;
624
763
  if (devOverlay)
625
764
  envelope.devOverlay = devOverlay;
626
765
  if (batchResult && batchResult.clipboardReads.length > 0) {
@@ -719,6 +858,22 @@ async function runTrioMode(browser, navResult, opts, evalResult, visibility, wid
719
858
  });
720
859
  emit.log(`check-hit:\n${lines.join("\n")}`, "info");
721
860
  }
861
+ if (content) {
862
+ logContentSummary(content);
863
+ }
864
+ if (critique) {
865
+ logCritiqueSummary(critique);
866
+ }
867
+ if (asserts && asserts.length > 0) {
868
+ const lines = asserts.map((a) => {
869
+ const detail = a.error ??
870
+ (a.op === "count" || a.op === "exists" || a.op === "absent"
871
+ ? `${a.selector} → ${a.actual}${a.expected ? ` (want ${a.expected})` : ""}`
872
+ : `${a.selector} → "${a.actual.slice(0, 60)}"${a.expected ? ` (want ${a.op} "${a.expected}")` : ""}`);
873
+ return ` [${a.outcome.toUpperCase()}] ${a.op} ${detail}`;
874
+ });
875
+ emit.log(`asserts:\n${lines.join("\n")}`, "info");
876
+ }
722
877
  if (savedBaseline) {
723
878
  emit.log(`baseline saved: ${savedBaseline.name} → ${savedBaseline.path} (${savedBaseline.bytes} bytes)`, "info");
724
879
  }
@@ -775,6 +930,12 @@ function logLayoutLintSummary(result) {
775
930
  const worst = Math.max(0, ...check.issues.map((issue) => issue.areaPx));
776
931
  lines.push(` [${check.outcome.toUpperCase()}] overlap ${check.selector}: ${check.issues.length} collisions, worst ${worst.toFixed(0)}px²`);
777
932
  }
933
+ for (const check of result.crowd) {
934
+ const tightest = check.issues.length > 0
935
+ ? Math.min(...check.issues.map((issue) => issue.separationPx))
936
+ : check.minGapPx;
937
+ lines.push(` [${check.outcome.toUpperCase()}] crowd ${check.selector}: ${check.issues.length} tight pair${check.issues.length === 1 ? "" : "s"} (min gap ${check.minGapPx}px, tightest ${tightest.toFixed(1)}px)`);
938
+ }
778
939
  if (lines.length > 0)
779
940
  emit.log(`layout-lint:\n${lines.join("\n")}`, "info");
780
941
  }
@@ -793,6 +954,7 @@ function applyLayoutLintFailGates(opts, layoutLint, hit) {
793
954
  gate(opts.checkGapFail, "gap", layoutLint?.gap);
794
955
  gate(opts.checkClipFail, "clip", layoutLint?.clip);
795
956
  gate(opts.checkOverlapFail, "overlap", layoutLint?.overlap);
957
+ gate(opts.checkCrowdFail, "crowd", layoutLint?.crowd);
796
958
  if (opts.checkHitFail && hit) {
797
959
  const failed = hit.filter((result) => !result.found || result.outcome === "fail" || result.outcome === "unknown");
798
960
  for (const result of failed) {
@@ -802,6 +964,147 @@ function applyLayoutLintFailGates(opts, layoutLint, hit) {
802
964
  process.exitCode = 2;
803
965
  }
804
966
  }
967
+ // ---------------------------------------------------------------------------
968
+ // Content checks (placeholder / image / truncation / contrast)
969
+ // ---------------------------------------------------------------------------
970
+ const CONTENT_COLORS = {
971
+ placeholder: "#dc2626",
972
+ image: "#f97316",
973
+ truncation: "#a855f7",
974
+ contrast: "#0891b2",
975
+ };
976
+ // Optional-selector flags resolve to a scope: a string selector, or null for
977
+ // the whole body when the flag was passed bare.
978
+ function contentScope(value) {
979
+ return typeof value === "string" ? value : null;
980
+ }
981
+ function assignContent(target, content) {
982
+ if (content.placeholder)
983
+ target.placeholder = content.placeholder;
984
+ if (content.image)
985
+ target.image = content.image;
986
+ if (content.truncation)
987
+ target.truncation = content.truncation;
988
+ if (content.contrast)
989
+ target.contrast = content.contrast;
990
+ }
991
+ function collectContentBoxes(content, opts) {
992
+ const boxes = [];
993
+ if (content.placeholder && opts.checkPlaceholderAnnotate !== false) {
994
+ for (const hit of content.placeholder.hits) {
995
+ boxes.push({
996
+ rect: hit.rect,
997
+ label: `placeholder ${hit.token}`,
998
+ color: CONTENT_COLORS.placeholder,
999
+ });
1000
+ }
1001
+ }
1002
+ if (content.image && opts.checkImagesAnnotate !== false) {
1003
+ for (const issue of content.image.issues) {
1004
+ boxes.push({ rect: issue.rect, label: `image ${issue.reason}`, color: CONTENT_COLORS.image });
1005
+ }
1006
+ }
1007
+ if (content.truncation && opts.checkTruncationAnnotate !== false) {
1008
+ for (const hit of content.truncation.hits) {
1009
+ boxes.push({
1010
+ rect: hit.rect,
1011
+ label: `truncated ${hit.how}`,
1012
+ color: CONTENT_COLORS.truncation,
1013
+ });
1014
+ }
1015
+ }
1016
+ if (content.contrast && opts.checkContrastAnnotate !== false) {
1017
+ for (const hit of content.contrast.hits) {
1018
+ if (hit.outcome !== "fail")
1019
+ continue;
1020
+ boxes.push({
1021
+ rect: hit.rect,
1022
+ label: `contrast ${hit.ratio}:1 < ${hit.required}`,
1023
+ color: CONTENT_COLORS.contrast,
1024
+ });
1025
+ }
1026
+ }
1027
+ return boxes;
1028
+ }
1029
+ function logContentSummary(content) {
1030
+ const lines = [];
1031
+ if (content.placeholder) {
1032
+ const c = content.placeholder;
1033
+ lines.push(` [${c.outcome.toUpperCase()}] placeholder: ${c.hits.length} tell(s) in ${c.scanned} node(s)`);
1034
+ }
1035
+ if (content.image) {
1036
+ const c = content.image;
1037
+ const missing = c.issues.filter((i) => i.reason === "missing").length;
1038
+ const stretched = c.issues.filter((i) => i.reason === "stretched").length;
1039
+ lines.push(` [${c.outcome.toUpperCase()}] image: ${missing} missing, ${stretched} stretched of ${c.scanned}`);
1040
+ }
1041
+ if (content.truncation) {
1042
+ const c = content.truncation;
1043
+ lines.push(` [${c.outcome.toUpperCase()}] truncation: ${c.hits.length} clipped of ${c.scanned}`);
1044
+ }
1045
+ if (content.contrast) {
1046
+ const c = content.contrast;
1047
+ const fails = c.hits.filter((h) => h.outcome === "fail").length;
1048
+ const unknown = c.hits.filter((h) => h.outcome === "unknown").length;
1049
+ lines.push(` [${c.outcome.toUpperCase()}] contrast: ${fails} fail, ${unknown} unknown of ${c.scanned}`);
1050
+ }
1051
+ if (lines.length > 0)
1052
+ emit.log(`content-checks:\n${lines.join("\n")}`, "info");
1053
+ }
1054
+ function applyContentFailGates(opts, content) {
1055
+ if (!content)
1056
+ return;
1057
+ const gate = (enabled, rule, result) => {
1058
+ if (!enabled || !result)
1059
+ return;
1060
+ if (!result.found || result.outcome === "fail") {
1061
+ emit.log(`check-${rule} FAIL: ${result.found ? "issues found" : "scope not found"}`, "warn");
1062
+ process.exitCode = 2;
1063
+ }
1064
+ };
1065
+ gate(opts.checkPlaceholderFail, "placeholder", content.placeholder);
1066
+ gate(opts.checkImagesFail, "images", content.image);
1067
+ gate(opts.checkTruncationFail, "truncation", content.truncation);
1068
+ gate(opts.checkContrastFail, "contrast", content.contrast);
1069
+ }
1070
+ // ---------------------------------------------------------------------------
1071
+ // Vision critique tiling
1072
+ // ---------------------------------------------------------------------------
1073
+ async function captureCritiqueTiles(browser, opts) {
1074
+ const maxTiles = Math.max(1, Number.parseInt(opts.checkCritiqueMaxTiles ?? "24", 10));
1075
+ const band = Math.max(200, Number.parseInt(opts.checkCritiqueBand ?? "1400", 10));
1076
+ const overlap = Math.max(0, Number.parseInt(opts.checkCritiqueOverlap ?? "120", 10));
1077
+ // Capture the whole page ONCE, then crop tiles from the pixels. Playwright's
1078
+ // clip is viewport-relative, so a below-fold band clip throws ("clipped area
1079
+ // outside the resulting image") — cropping the full-page image sidesteps that
1080
+ // and keeps every tile at full resolution.
1081
+ const buffer = await browser.fullPageScreenshotBuffer();
1082
+ const elementRects = typeof opts.checkCritique === "string"
1083
+ ? await browser.elementTiles(opts.checkCritique)
1084
+ : undefined;
1085
+ const tiles = tilesFromFullPage(buffer, { elementRects, bandHeight: band, overlap, maxTiles });
1086
+ if (elementRects && elementRects.length > maxTiles) {
1087
+ emit.log(`check-critique: ${elementRects.length} elements matched, capped to ${maxTiles} tiles`, "warn");
1088
+ }
1089
+ else if (!elementRects && tiles.length >= maxTiles) {
1090
+ emit.log(`check-critique: page banded to the ${maxTiles}-tile cap (raise --check-critique-max-tiles for full coverage)`, "warn");
1091
+ }
1092
+ return tiles;
1093
+ }
1094
+ function logCritiqueSummary(critique) {
1095
+ if (critique.outcome === "skipped") {
1096
+ emit.log(`check-critique: SKIPPED (${critique.error ?? "no provider"})`, "warn");
1097
+ return;
1098
+ }
1099
+ const high = critique.findings.filter((f) => f.severity === "high").length;
1100
+ const med = critique.findings.filter((f) => f.severity === "medium").length;
1101
+ const low = critique.findings.filter((f) => f.severity === "low").length;
1102
+ const lines = critique.findings
1103
+ .slice(0, 20)
1104
+ .map((f) => ` [${f.severity}] tile ${f.tile} ${f.category}: ${f.description}`);
1105
+ emit.log(`check-critique [${critique.outcome.toUpperCase()}] ${critique.tiles} tiles → ${high} high / ${med} med / ${low} low` +
1106
+ (lines.length ? `\n${lines.join("\n")}` : ""), critique.outcome === "fail" ? "warn" : "info");
1107
+ }
805
1108
  function summarizeDiagnostics(diag) {
806
1109
  return {
807
1110
  viewport: diag.viewport,
@@ -0,0 +1,12 @@
1
+ import type { Command } from "commander";
2
+ import type { EmitContext, HarneryProgramContext } from "../commander.js";
3
+ /**
4
+ * `harn checkpoint`: durable context continuity across compaction.
5
+ *
6
+ * A checkpoint captures enough of the current work that a fresh generation of
7
+ * the same session can resume it after the adapter compacts or restarts.
8
+ * `create` writes a capsule, `show` prints the latest one, `status` reports the
9
+ * phase, generation, and whatever context telemetry the adapter supplied.
10
+ */
11
+ export declare function registerCheckpointCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
12
+ //# sourceMappingURL=checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/commands/checkpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAU1E;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA2HN"}
@@ -0,0 +1,162 @@
1
+ import { monorepoRoot, readHeartbeat, resolveOwner } from "../core/agents/index.js";
2
+ import { checkpointContext, readContextState, readLatestCapsule, } from "../core/context/index.js";
3
+ /**
4
+ * `harn checkpoint`: durable context continuity across compaction.
5
+ *
6
+ * A checkpoint captures enough of the current work that a fresh generation of
7
+ * the same session can resume it after the adapter compacts or restarts.
8
+ * `create` writes a capsule, `show` prints the latest one, `status` reports the
9
+ * phase, generation, and whatever context telemetry the adapter supplied.
10
+ */
11
+ export function registerCheckpointCommand(program, emit, context) {
12
+ const command = program
13
+ .command("checkpoint")
14
+ .description("Durable context-continuity capsules: create one, show the latest, or report continuity phase.")
15
+ .option("--json", "Structured JSON output");
16
+ command
17
+ .command("status")
18
+ .description("Show context-continuity phase, generation, and latest telemetry")
19
+ .option("--session <id>", "Session id (defaults to the current agent heartbeat)")
20
+ .option("--instance <id>", "Agent instance id (defaults to the current owner)")
21
+ .option("--json", "Structured JSON output")
22
+ .action((opts) => {
23
+ try {
24
+ const json = opts.json || Boolean(command.opts().json);
25
+ const identity = resolveContinuityIdentity(context, opts);
26
+ const state = readContextState(identity.coordRoot, identity.sessionId);
27
+ if (json) {
28
+ emit.config({ format: "json" });
29
+ emit.data({ state, identity });
30
+ return;
31
+ }
32
+ if (!state) {
33
+ emit.text(`No context continuity state for session ${identity.sessionId}.\n`);
34
+ return;
35
+ }
36
+ const sample = state.latest_context;
37
+ const usage = sample
38
+ ? `${sample.used_tokens ?? "?"}/${sample.window_tokens ?? "?"} tokens${sample.used_percent === undefined ? "" : ` (${sample.used_percent}%)`}`
39
+ : "not reported by adapter";
40
+ emit.text(`${[
41
+ `Session: ${state.session_id}`,
42
+ `Phase: ${state.phase}`,
43
+ `Generation: ${state.generation}`,
44
+ `Telemetry: ${usage}`,
45
+ `Capsule: ${state.latest_capsule ?? "none"}`,
46
+ state.degraded_reason ? `Degraded: ${state.degraded_reason}` : "",
47
+ ]
48
+ .filter(Boolean)
49
+ .join("\n")}\n`);
50
+ }
51
+ catch (err) {
52
+ emitCommandError(emit, "checkpoint_status_failed", err);
53
+ }
54
+ });
55
+ command
56
+ .command("create")
57
+ .description("Create a durable context-continuity capsule for the current work")
58
+ .option("--session <id>", "Session id (defaults to the current agent heartbeat)")
59
+ .option("--instance <id>", "Agent instance id (defaults to the current owner)")
60
+ .option("--adapter <id>", "claude-code, codex, or cursor (inferred from heartbeat)")
61
+ .option("--reason <reason>", "manual, pressure, pre_compact, or session_end", "manual")
62
+ .option("--note <text>", "Short continuation note for the recovered agent")
63
+ .option("--json", "Structured JSON output")
64
+ .action((opts) => {
65
+ try {
66
+ const json = opts.json || Boolean(command.opts().json);
67
+ const reason = parseCheckpointReason(opts.reason);
68
+ const identity = resolveContinuityIdentity(context, opts);
69
+ if (!identity.adapter) {
70
+ throw new Error("could not infer the adapter; pass --adapter claude-code|codex|cursor");
71
+ }
72
+ const result = checkpointContext(identity.coordRoot, {
73
+ sessionId: identity.sessionId,
74
+ instanceId: identity.instanceId,
75
+ adapter: identity.adapter,
76
+ cwd: context?.repoRoot ?? process.cwd(),
77
+ reason,
78
+ continuationNote: opts.note,
79
+ });
80
+ const output = {
81
+ capsule_id: result.capsule.capsule_id,
82
+ generation: result.capsule.generation,
83
+ path: result.state.latest_capsule,
84
+ phase: result.state.phase,
85
+ reused: result.reused,
86
+ };
87
+ if (json) {
88
+ emit.config({ format: "json" });
89
+ emit.data(output);
90
+ return;
91
+ }
92
+ emit.text(`Checkpointed context generation ${output.generation}: ${output.path ?? result.path}\n`);
93
+ }
94
+ catch (err) {
95
+ emitCommandError(emit, "checkpoint_create_failed", err);
96
+ }
97
+ });
98
+ command
99
+ .command("show")
100
+ .description("Show the latest durable context-continuity capsule")
101
+ .option("--session <id>", "Session id (defaults to the current agent heartbeat)")
102
+ .option("--instance <id>", "Agent instance id (defaults to the current owner)")
103
+ .option("--json", "Structured JSON output")
104
+ .action((opts) => {
105
+ try {
106
+ const json = opts.json || Boolean(command.opts().json);
107
+ const identity = resolveContinuityIdentity(context, opts);
108
+ const capsule = readLatestCapsule(identity.coordRoot, identity.sessionId);
109
+ if (!capsule) {
110
+ throw new Error(`no context capsule exists for session ${identity.sessionId}`);
111
+ }
112
+ if (json)
113
+ emit.config({ format: "json" });
114
+ emit.data(capsule);
115
+ }
116
+ catch (err) {
117
+ emitCommandError(emit, "checkpoint_show_failed", err);
118
+ }
119
+ });
120
+ }
121
+ function resolveContinuityIdentity(context, opts) {
122
+ const coordRoot = context?.resolveCoordRoot?.() ?? context?.repoRoot ?? monorepoRoot();
123
+ if (!coordRoot)
124
+ throw new Error("could not resolve a project containing .harnery/");
125
+ const instanceId = opts.instance ?? resolveOwner();
126
+ if (!instanceId) {
127
+ throw new Error("could not resolve the current agent; pass --instance and --session");
128
+ }
129
+ const heartbeat = readHeartbeat(instanceId);
130
+ const sessionId = opts.session ?? heartbeat?.session_id ?? instanceId;
131
+ const adapter = opts.adapter
132
+ ? parseAdapter(opts.adapter)
133
+ : adapterFromPlatform(heartbeat?.platform);
134
+ return { coordRoot, instanceId, sessionId, adapter };
135
+ }
136
+ function adapterFromPlatform(platform) {
137
+ if (platform === "claude_code" || platform === "claude-code")
138
+ return "claude-code";
139
+ if (platform === "cursor")
140
+ return "cursor";
141
+ if (platform === "codex")
142
+ return "codex";
143
+ return null;
144
+ }
145
+ function parseAdapter(value) {
146
+ if (value === "claude-code" || value === "codex" || value === "cursor")
147
+ return value;
148
+ throw new Error(`invalid adapter "${value}"; expected claude-code, codex, or cursor`);
149
+ }
150
+ function parseCheckpointReason(value) {
151
+ if (value === "manual" ||
152
+ value === "pressure" ||
153
+ value === "pre_compact" ||
154
+ value === "session_end") {
155
+ return value;
156
+ }
157
+ throw new Error(`invalid reason "${value}"; expected manual, pressure, pre_compact, or session_end`);
158
+ }
159
+ function emitCommandError(emit, code, err) {
160
+ emit.error({ code, message: err instanceof Error ? err.message : String(err) });
161
+ emit.setExitCode(1);
162
+ }
@@ -1,11 +1,11 @@
1
1
  import type { Command } from "commander";
2
2
  import type { EmitContext, HarneryProgramContext } from "../commander.js";
3
3
  /**
4
- * `harn context`: one-shot orientation snapshot.
4
+ * `harn agents context`: one-shot orientation snapshot.
5
5
  *
6
6
  * Aggregates self / time / repo / commits / submodules / peers (default) plus
7
7
  * opt-in services. Useful at session start, post-compaction, or before
8
8
  * dispatching subagents. Fail-open per section.
9
9
  */
10
- export declare function registerContextCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
10
+ export declare function registerContextCommand(parent: Command, emit: EmitContext, context?: HarneryProgramContext): void;
11
11
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAiB1E;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAqKN"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAS1E;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAiDN"}