harnery 0.30.0 → 0.31.1

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 (517) 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 +185 -74
  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/grep.d.ts +1 -1
  34. package/dist/commands/grep.d.ts.map +1 -1
  35. package/dist/commands/grep.js +19 -2
  36. package/dist/commands/init.d.ts +12 -12
  37. package/dist/commands/init.d.ts.map +1 -1
  38. package/dist/commands/init.js +44 -30
  39. package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
  40. package/dist/commands/journal.d.ts.map +1 -0
  41. package/dist/commands/{scratch.js → journal.js} +47 -47
  42. package/dist/commands/policy.js +1 -1
  43. package/dist/commands/presence.js +2 -2
  44. package/dist/commands/sync.d.ts +1 -1
  45. package/dist/commands/sync.js +3 -3
  46. package/dist/commands/tunnel.d.ts +16 -0
  47. package/dist/commands/tunnel.d.ts.map +1 -1
  48. package/dist/commands/tunnel.js +213 -27
  49. package/dist/commands/web-fetch.js +1 -1
  50. package/dist/commands/web.d.ts +19 -0
  51. package/dist/commands/web.d.ts.map +1 -1
  52. package/dist/commands/web.js +51 -0
  53. package/dist/commands/work.d.ts.map +1 -1
  54. package/dist/commands/work.js +38 -22
  55. package/dist/commands/workflow.d.ts.map +1 -1
  56. package/dist/commands/workflow.js +35 -28
  57. package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
  58. package/dist/core/adapters/attest-projection.d.ts.map +1 -0
  59. package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
  60. package/dist/core/adapters/attest.d.ts.map +1 -0
  61. package/dist/core/{harnesses → adapters}/attest.js +9 -9
  62. package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
  63. package/dist/core/adapters/attestation.d.ts.map +1 -0
  64. package/dist/core/{harnesses → adapters}/attestation.js +20 -20
  65. package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
  66. package/dist/core/adapters/bench.d.ts.map +1 -0
  67. package/dist/core/{harnesses → adapters}/bench.js +12 -12
  68. package/dist/core/adapters/index.d.ts +13 -0
  69. package/dist/core/adapters/index.d.ts.map +1 -0
  70. package/dist/core/adapters/index.js +6 -0
  71. package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
  72. package/dist/core/adapters/profiles.d.ts.map +1 -0
  73. package/dist/core/{harnesses → adapters}/profiles.js +11 -11
  74. package/dist/core/adapters/registry.d.ts +15 -0
  75. package/dist/core/adapters/registry.d.ts.map +1 -0
  76. package/dist/core/{harnesses → adapters}/registry.js +11 -11
  77. package/dist/core/{harnesses → adapters}/types.d.ts +23 -23
  78. package/dist/core/adapters/types.d.ts.map +1 -0
  79. package/dist/core/{harnesses → adapters}/types.js +1 -1
  80. package/dist/core/agents/canonical-emit.d.ts +9 -14
  81. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  82. package/dist/core/agents/canonical-emit.js +15 -44
  83. package/dist/core/agents/cli-emit.js +1 -1
  84. package/dist/core/agents/cli.js +30 -26
  85. package/dist/core/agents/codex-replay.d.ts +1 -1
  86. package/dist/core/agents/codex-replay.js +6 -6
  87. package/dist/core/agents/coord-bin.d.ts +33 -0
  88. package/dist/core/agents/coord-bin.d.ts.map +1 -0
  89. package/dist/core/agents/coord-bin.js +89 -0
  90. package/dist/core/agents/coord-client.d.ts +78 -8
  91. package/dist/core/agents/coord-client.d.ts.map +1 -1
  92. package/dist/core/agents/coord-client.js +303 -75
  93. package/dist/core/agents/events/consume.d.ts +1 -1
  94. package/dist/core/agents/events/emit.d.ts +5 -6
  95. package/dist/core/agents/events/emit.d.ts.map +1 -1
  96. package/dist/core/agents/events/emit.js +2 -2
  97. package/dist/core/agents/paths.d.ts +5 -2
  98. package/dist/core/agents/paths.d.ts.map +1 -1
  99. package/dist/core/agents/paths.js +7 -13
  100. package/dist/core/agents/render/prompt-context.d.ts +21 -9
  101. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  102. package/dist/core/agents/render/prompt-context.js +82 -30
  103. package/dist/core/agents/render/session-context.d.ts +1 -1
  104. package/dist/core/agents/render/session-context.js +5 -5
  105. package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
  106. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  107. package/dist/core/agents/rules/claim-conflict.js +1 -1
  108. package/dist/core/agents/rules/commit-conflict.js +98 -22
  109. package/dist/core/agents/rules/stop-hook.d.ts +9 -9
  110. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  111. package/dist/core/agents/rules/stop-hook.js +23 -5
  112. package/dist/core/agents/rules/verdict.d.ts +14 -0
  113. package/dist/core/agents/rules/verdict.d.ts.map +1 -0
  114. package/dist/core/agents/rules/verdict.js +1 -0
  115. package/dist/core/agents/session-events.js +3 -3
  116. package/dist/core/agents/state/council.d.ts +1 -1
  117. package/dist/core/agents/state/council.js +1 -1
  118. package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
  119. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  120. package/dist/core/agents/state/heartbeat-projector.js +14 -10
  121. package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
  122. package/dist/core/agents/state/heartbeat-writer.js +12 -12
  123. package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
  124. package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
  125. package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
  126. package/dist/core/agents/state/pidmap.d.ts +28 -4
  127. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  128. package/dist/core/agents/state/pidmap.js +130 -15
  129. package/dist/core/agents/state/proc-start.d.ts +95 -0
  130. package/dist/core/agents/state/proc-start.d.ts.map +1 -0
  131. package/dist/core/agents/state/proc-start.js +199 -0
  132. package/dist/core/agents/state/shell-mutation.d.ts +1 -1
  133. package/dist/core/agents/state/shell-mutation.js +1 -1
  134. package/dist/core/agents/state/stale-sweep.js +7 -7
  135. package/dist/core/artifacts/index.d.ts +86 -0
  136. package/dist/core/artifacts/index.d.ts.map +1 -0
  137. package/dist/core/artifacts/index.js +421 -0
  138. package/dist/core/config.d.ts +9 -2
  139. package/dist/core/config.d.ts.map +1 -1
  140. package/dist/core/config.js +26 -2
  141. package/dist/core/context/index.d.ts +12 -12
  142. package/dist/core/context/index.js +10 -10
  143. package/dist/core/governor/adapter-spread.d.ts +52 -0
  144. package/dist/core/governor/adapter-spread.d.ts.map +1 -0
  145. package/dist/core/governor/adapter-spread.js +52 -0
  146. package/dist/core/governor/index.d.ts +7 -0
  147. package/dist/core/governor/index.d.ts.map +1 -0
  148. package/dist/core/governor/index.js +6 -0
  149. package/dist/core/governor/plan-api.d.ts +38 -0
  150. package/dist/core/governor/plan-api.d.ts.map +1 -0
  151. package/dist/core/governor/plan-api.js +83 -0
  152. package/dist/core/governor/plan-read.d.ts +5 -0
  153. package/dist/core/governor/plan-read.d.ts.map +1 -0
  154. package/dist/core/{supervisor → governor}/plan-read.js +66 -52
  155. package/dist/core/{supervisor → governor}/plan-types.d.ts +57 -56
  156. package/dist/core/governor/plan-types.d.ts.map +1 -0
  157. package/dist/core/{supervisor → governor}/plan-types.js +4 -4
  158. package/dist/core/governor/planning.d.ts +48 -0
  159. package/dist/core/governor/planning.d.ts.map +1 -0
  160. package/dist/core/{supervisor → governor}/planning.js +125 -94
  161. package/dist/core/governor/read.d.ts +6 -0
  162. package/dist/core/governor/read.d.ts.map +1 -0
  163. package/dist/core/governor/read.js +4 -0
  164. package/dist/core/governor/runner.d.ts +37 -0
  165. package/dist/core/governor/runner.d.ts.map +1 -0
  166. package/dist/core/{supervisor → governor}/runner.js +15 -15
  167. package/dist/core/governor/service-read.d.ts +6 -0
  168. package/dist/core/governor/service-read.d.ts.map +1 -0
  169. package/dist/core/{supervisor → governor}/service-read.js +12 -12
  170. package/dist/core/governor/service.d.ts +114 -0
  171. package/dist/core/governor/service.d.ts.map +1 -0
  172. package/dist/core/{supervisor → governor}/service.js +57 -57
  173. package/dist/core/governor/state.d.ts +116 -0
  174. package/dist/core/governor/state.d.ts.map +1 -0
  175. package/dist/core/{supervisor → governor}/state.js +228 -108
  176. package/dist/core/hooks/adapter/detect.d.ts +9 -0
  177. package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
  178. package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
  179. package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
  180. package/dist/core/hooks/adapter/events.d.ts.map +1 -0
  181. package/dist/core/hooks/{harness → adapter}/events.js +5 -5
  182. package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
  183. package/dist/core/hooks/adapter/output.d.ts.map +1 -0
  184. package/dist/core/hooks/{harness → adapter}/output.js +30 -22
  185. package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
  186. package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
  187. package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
  188. package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
  189. package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
  190. package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
  191. package/dist/core/hooks/cli.d.ts +3 -3
  192. package/dist/core/hooks/cli.js +187 -162
  193. package/dist/core/hooks/effects/image-capture.d.ts +5 -5
  194. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  195. package/dist/core/hooks/effects/image-capture.js +3 -3
  196. package/dist/core/hooks/effects/index.d.ts +12 -12
  197. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  198. package/dist/core/hooks/effects/index.js +19 -19
  199. package/dist/core/hooks/events/emit.d.ts +2 -2
  200. package/dist/core/hooks/events/emit.d.ts.map +1 -1
  201. package/dist/core/hooks/events/emit.js +1 -1
  202. package/dist/core/hooks/events/rotate.js +1 -1
  203. package/dist/core/hooks/events/schema.d.ts +13 -5
  204. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  205. package/dist/core/hooks/guard-path.d.ts +2 -2
  206. package/dist/core/hooks/guard-path.js +2 -2
  207. package/dist/core/hooks/index.d.ts +1 -1
  208. package/dist/core/hooks/index.d.ts.map +1 -1
  209. package/dist/core/hooks/resolve/anchor.d.ts +14 -5
  210. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
  211. package/dist/core/hooks/resolve/anchor.js +64 -7
  212. package/dist/core/hooks/resolve/coord-root.d.ts +16 -11
  213. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -1
  214. package/dist/core/hooks/resolve/coord-root.js +18 -43
  215. package/dist/core/hooks/resolve/intent.js +1 -1
  216. package/dist/core/hooks/resolve/owner.d.ts +1 -1
  217. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  218. package/dist/core/hooks/resolve/owner.js +11 -5
  219. package/dist/core/{scratch → journal}/index.d.ts +25 -25
  220. package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
  221. package/dist/core/{scratch → journal}/index.js +38 -38
  222. package/dist/core/policy/evaluate.d.ts +1 -1
  223. package/dist/core/policy/evaluate.d.ts.map +1 -1
  224. package/dist/core/policy/evaluate.js +7 -7
  225. package/dist/core/policy/types.d.ts +3 -3
  226. package/dist/core/policy/types.d.ts.map +1 -1
  227. package/dist/core/work/state.d.ts +12 -4
  228. package/dist/core/work/state.d.ts.map +1 -1
  229. package/dist/core/work/state.js +50 -17
  230. package/dist/core/workflow/adapters.d.ts +17 -0
  231. package/dist/core/workflow/adapters.d.ts.map +1 -0
  232. package/dist/core/workflow/adapters.js +21 -0
  233. package/dist/core/workflow/approvals.js +6 -6
  234. package/dist/core/workflow/billing.d.ts +9 -9
  235. package/dist/core/workflow/billing.js +9 -9
  236. package/dist/core/workflow/child-env.d.ts +10 -2
  237. package/dist/core/workflow/child-env.d.ts.map +1 -1
  238. package/dist/core/workflow/child-env.js +12 -4
  239. package/dist/core/workflow/engine.d.ts +15 -5
  240. package/dist/core/workflow/engine.d.ts.map +1 -1
  241. package/dist/core/workflow/engine.js +138 -83
  242. package/dist/core/workflow/evidence-preflight.d.ts +24 -0
  243. package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
  244. package/dist/core/workflow/evidence-preflight.js +134 -0
  245. package/dist/core/workflow/index.d.ts +1 -1
  246. package/dist/core/workflow/index.d.ts.map +1 -1
  247. package/dist/core/workflow/proof.d.ts +11 -5
  248. package/dist/core/workflow/proof.d.ts.map +1 -1
  249. package/dist/core/workflow/proof.js +45 -36
  250. package/dist/core/workflow/run-state.d.ts +1 -1
  251. package/dist/core/workflow/run-state.js +7 -7
  252. package/dist/core/workflow/sandbox-projection.d.ts +6 -6
  253. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  254. package/dist/core/workflow/sandbox-projection.js +11 -11
  255. package/dist/core/workflow/spawn-claude.d.ts +3 -3
  256. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  257. package/dist/core/workflow/spawn-claude.js +9 -6
  258. package/dist/core/workflow/spawn-codex.d.ts +4 -4
  259. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  260. package/dist/core/workflow/spawn-codex.js +9 -6
  261. package/dist/core/workflow/spawn-cursor.d.ts +4 -4
  262. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  263. package/dist/core/workflow/spawn-cursor.js +9 -6
  264. package/dist/core/workflow/spawn-failure.d.ts +1 -1
  265. package/dist/core/workflow/spawn-failure.js +1 -1
  266. package/dist/core/workflow/specialists.js +2 -2
  267. package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
  268. package/dist/core/workflow/transcript.d.ts.map +1 -0
  269. package/dist/core/workflow/{journal.js → transcript.js} +14 -14
  270. package/dist/core/workflow/types.d.ts +98 -51
  271. package/dist/core/workflow/types.d.ts.map +1 -1
  272. package/dist/core/workflow/types.js +11 -1
  273. package/dist/core/workflow/validate.d.ts +1 -1
  274. package/dist/core/workflow/validate.js +1 -1
  275. package/dist/core/workflow/workspaces/cancellation.js +2 -2
  276. package/dist/core/workflow/workspaces/cleanup.js +2 -2
  277. package/dist/core/workflow/workspaces/execution.js +6 -6
  278. package/dist/core/workflow/workspaces/inspect.js +9 -9
  279. package/dist/core/workflow/workspaces/integration.js +14 -14
  280. package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
  281. package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
  282. package/dist/core/workflow/workspaces/lifecycle.js +9 -9
  283. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  284. package/dist/core/workflow/workspaces/local-git.js +39 -5
  285. package/dist/core/workflow/workspaces/recovery.js +3 -3
  286. package/dist/core/workflow/workspaces/state.d.ts +1 -1
  287. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  288. package/dist/core/workflow/workspaces/state.js +3 -3
  289. package/dist/core/workflow/workspaces/types.d.ts +1 -1
  290. package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
  291. package/dist/lib/browser/asserts.d.ts +28 -0
  292. package/dist/lib/browser/asserts.d.ts.map +1 -0
  293. package/dist/lib/browser/asserts.js +152 -0
  294. package/dist/lib/browser/client.d.ts +49 -0
  295. package/dist/lib/browser/client.d.ts.map +1 -1
  296. package/dist/lib/browser/client.js +155 -14
  297. package/dist/lib/browser/content-checks.d.ts +108 -0
  298. package/dist/lib/browser/content-checks.d.ts.map +1 -0
  299. package/dist/lib/browser/content-checks.js +474 -0
  300. package/dist/lib/browser/critique.d.ts +91 -0
  301. package/dist/lib/browser/critique.d.ts.map +1 -0
  302. package/dist/lib/browser/critique.js +167 -0
  303. package/dist/lib/browser/geometry.d.ts +32 -0
  304. package/dist/lib/browser/geometry.d.ts.map +1 -1
  305. package/dist/lib/browser/geometry.js +244 -1
  306. package/dist/lib/browser/index.d.ts +4 -1
  307. package/dist/lib/browser/index.d.ts.map +1 -1
  308. package/dist/lib/browser/index.js +2 -0
  309. package/dist/lib/claude-desktop.d.ts.map +1 -1
  310. package/dist/lib/claude-desktop.js +4 -1
  311. package/dist/lib/context/index.js +9 -2
  312. package/dist/lib/council/index.d.ts +6 -6
  313. package/dist/lib/council/index.d.ts.map +1 -1
  314. package/dist/lib/council/index.js +8 -8
  315. package/dist/lib/identities/assume.d.ts +2 -2
  316. package/dist/lib/identities/assume.js +7 -7
  317. package/dist/lib/instructions/apply.d.ts +3 -3
  318. package/dist/lib/instructions/apply.d.ts.map +1 -1
  319. package/dist/lib/instructions/apply.js +75 -27
  320. package/dist/lib/instructions/host-addendum.d.ts +54 -0
  321. package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
  322. package/dist/lib/instructions/host-addendum.js +107 -0
  323. package/dist/lib/instructions/templates.d.ts +10 -3
  324. package/dist/lib/instructions/templates.d.ts.map +1 -1
  325. package/dist/lib/instructions/templates.js +41 -12
  326. package/dist/lib/machine.d.ts +2 -2
  327. package/dist/lib/machine.js +2 -2
  328. package/package.json +26 -21
  329. package/schemas/config.schema.json +26 -1
  330. package/src/commander.ts +22 -13
  331. package/src/commands/{harness.ts → adapter.ts} +46 -46
  332. package/src/commands/agents.ts +200 -78
  333. package/src/commands/artifacts.ts +168 -0
  334. package/src/commands/backup.ts +47 -33
  335. package/src/commands/browse.ts +438 -2
  336. package/src/commands/checkpoint.ts +196 -0
  337. package/src/commands/context.ts +3 -176
  338. package/src/commands/decision.ts +41 -7
  339. package/src/commands/deinit.ts +16 -16
  340. package/src/commands/doctor.ts +28 -28
  341. package/src/commands/{supervisor.ts → governor.ts} +184 -154
  342. package/src/commands/grep.ts +19 -2
  343. package/src/commands/init.ts +50 -34
  344. package/src/commands/{scratch.ts → journal.ts} +60 -60
  345. package/src/commands/policy.ts +1 -1
  346. package/src/commands/presence.ts +2 -2
  347. package/src/commands/sync.ts +3 -3
  348. package/src/commands/tunnel.ts +256 -38
  349. package/src/commands/web-fetch.ts +1 -1
  350. package/src/commands/web.ts +56 -1
  351. package/src/commands/work.ts +50 -35
  352. package/src/commands/workflow.ts +43 -34
  353. package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
  354. package/src/core/{harnesses → adapters}/attest.ts +27 -27
  355. package/src/core/{harnesses → adapters}/attestation.ts +41 -41
  356. package/src/core/{harnesses → adapters}/bench.ts +34 -43
  357. package/src/core/adapters/index.ts +56 -0
  358. package/src/core/{harnesses → adapters}/profiles.ts +16 -16
  359. package/src/core/{harnesses → adapters}/registry.ts +23 -23
  360. package/src/core/{harnesses → adapters}/types.ts +23 -23
  361. package/src/core/agents/canonical-emit.ts +16 -43
  362. package/src/core/agents/cli-emit.ts +1 -1
  363. package/src/core/agents/cli.ts +30 -28
  364. package/src/core/agents/codex-replay.ts +6 -6
  365. package/src/core/agents/coord-bin.ts +92 -0
  366. package/src/core/agents/coord-client.ts +302 -78
  367. package/src/core/agents/events/consume.ts +1 -1
  368. package/src/core/agents/events/emit.ts +7 -6
  369. package/src/core/agents/paths.ts +7 -11
  370. package/src/core/agents/render/prompt-context.ts +107 -31
  371. package/src/core/agents/render/session-context.ts +6 -6
  372. package/src/core/agents/rules/claim-conflict.ts +4 -7
  373. package/src/core/agents/rules/commit-conflict.ts +103 -22
  374. package/src/core/agents/rules/stop-hook.ts +33 -17
  375. package/src/core/agents/rules/verdict.ts +13 -0
  376. package/src/core/agents/session-events.ts +4 -4
  377. package/src/core/agents/state/council.ts +1 -1
  378. package/src/core/agents/state/heartbeat-projector.ts +16 -12
  379. package/src/core/agents/state/heartbeat-writer.ts +12 -12
  380. package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
  381. package/src/core/agents/state/pidmap.ts +130 -15
  382. package/src/core/agents/state/proc-start.ts +208 -0
  383. package/src/core/agents/state/shell-mutation.ts +1 -1
  384. package/src/core/agents/state/stale-sweep.ts +7 -7
  385. package/src/core/artifacts/index.ts +557 -0
  386. package/src/core/config.ts +30 -2
  387. package/src/core/context/index.ts +15 -15
  388. package/src/core/governor/adapter-spread.ts +82 -0
  389. package/src/core/governor/index.ts +91 -0
  390. package/src/core/governor/plan-api.ts +115 -0
  391. package/src/core/{supervisor → governor}/plan-read.ts +112 -97
  392. package/src/core/{supervisor → governor}/plan-types.ts +59 -56
  393. package/src/core/{supervisor → governor}/planning.ts +223 -192
  394. package/src/core/governor/read.ts +59 -0
  395. package/src/core/{supervisor → governor}/runner.ts +39 -39
  396. package/src/core/{supervisor → governor}/service-read.ts +22 -22
  397. package/src/core/{supervisor → governor}/service.ts +117 -119
  398. package/src/core/{supervisor → governor}/state.ts +325 -167
  399. package/src/core/hooks/adapter/detect.ts +30 -0
  400. package/src/core/hooks/{harness → adapter}/events.ts +16 -14
  401. package/src/core/hooks/{harness → adapter}/output.ts +31 -23
  402. package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
  403. package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
  404. package/src/core/hooks/cli.ts +197 -173
  405. package/src/core/hooks/effects/image-capture.ts +6 -6
  406. package/src/core/hooks/effects/index.ts +19 -19
  407. package/src/core/hooks/events/emit.ts +3 -3
  408. package/src/core/hooks/events/rotate.ts +1 -1
  409. package/src/core/hooks/events/schema.ts +15 -7
  410. package/src/core/hooks/guard-path.ts +2 -2
  411. package/src/core/hooks/index.ts +1 -1
  412. package/src/core/hooks/resolve/anchor.ts +63 -8
  413. package/src/core/hooks/resolve/coord-root.ts +18 -40
  414. package/src/core/hooks/resolve/intent.ts +1 -1
  415. package/src/core/hooks/resolve/owner.ts +11 -5
  416. package/src/core/{scratch → journal}/index.ts +53 -53
  417. package/src/core/policy/evaluate.ts +8 -8
  418. package/src/core/policy/types.ts +3 -3
  419. package/src/core/work/state.ts +65 -23
  420. package/src/core/workflow/adapters.ts +33 -0
  421. package/src/core/workflow/approvals.ts +7 -7
  422. package/src/core/workflow/billing.ts +12 -12
  423. package/src/core/workflow/child-env.ts +13 -4
  424. package/src/core/workflow/engine.ts +155 -90
  425. package/src/core/workflow/evidence-preflight.ts +165 -0
  426. package/src/core/workflow/index.ts +3 -3
  427. package/src/core/workflow/proof.ts +60 -50
  428. package/src/core/workflow/run-state.ts +7 -7
  429. package/src/core/workflow/sandbox-projection.ts +16 -16
  430. package/src/core/workflow/spawn-claude.ts +13 -10
  431. package/src/core/workflow/spawn-codex.ts +13 -10
  432. package/src/core/workflow/spawn-cursor.ts +13 -10
  433. package/src/core/workflow/spawn-failure.ts +1 -1
  434. package/src/core/workflow/specialists.ts +2 -2
  435. package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
  436. package/src/core/workflow/types.ts +108 -51
  437. package/src/core/workflow/validate.ts +1 -1
  438. package/src/core/workflow/workspaces/cancellation.ts +2 -2
  439. package/src/core/workflow/workspaces/cleanup.ts +2 -2
  440. package/src/core/workflow/workspaces/execution.ts +6 -6
  441. package/src/core/workflow/workspaces/inspect.ts +14 -11
  442. package/src/core/workflow/workspaces/integration.ts +18 -14
  443. package/src/core/workflow/workspaces/lifecycle.ts +10 -10
  444. package/src/core/workflow/workspaces/local-git.ts +47 -5
  445. package/src/core/workflow/workspaces/recovery.ts +3 -3
  446. package/src/core/workflow/workspaces/state.ts +4 -3
  447. package/src/core/workflow/workspaces/types.ts +1 -1
  448. package/src/lib/browser/asserts.ts +177 -0
  449. package/src/lib/browser/client.ts +189 -14
  450. package/src/lib/browser/content-checks.ts +571 -0
  451. package/src/lib/browser/critique.ts +231 -0
  452. package/src/lib/browser/geometry.ts +297 -1
  453. package/src/lib/browser/index.ts +34 -0
  454. package/src/lib/claude-desktop.ts +4 -1
  455. package/src/lib/context/index.ts +11 -1
  456. package/src/lib/council/index.ts +9 -9
  457. package/src/lib/identities/assume.ts +7 -7
  458. package/src/lib/instructions/apply.ts +87 -25
  459. package/src/lib/instructions/host-addendum.ts +126 -0
  460. package/src/lib/instructions/templates.ts +44 -15
  461. package/src/lib/machine.ts +2 -2
  462. package/dist/commands/harness.d.ts +0 -12
  463. package/dist/commands/harness.d.ts.map +0 -1
  464. package/dist/commands/scratch.d.ts +0 -4
  465. package/dist/commands/scratch.d.ts.map +0 -1
  466. package/dist/commands/session.d.ts.map +0 -1
  467. package/dist/commands/session.js +0 -162
  468. package/dist/commands/supervisor.d.ts +0 -4
  469. package/dist/commands/supervisor.d.ts.map +0 -1
  470. package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
  471. package/dist/core/harnesses/attest.d.ts.map +0 -1
  472. package/dist/core/harnesses/attestation.d.ts.map +0 -1
  473. package/dist/core/harnesses/bench.d.ts.map +0 -1
  474. package/dist/core/harnesses/index.d.ts +0 -13
  475. package/dist/core/harnesses/index.d.ts.map +0 -1
  476. package/dist/core/harnesses/index.js +0 -6
  477. package/dist/core/harnesses/profiles.d.ts.map +0 -1
  478. package/dist/core/harnesses/registry.d.ts +0 -15
  479. package/dist/core/harnesses/registry.d.ts.map +0 -1
  480. package/dist/core/harnesses/types.d.ts.map +0 -1
  481. package/dist/core/hooks/harness/detect.d.ts +0 -9
  482. package/dist/core/hooks/harness/events.d.ts.map +0 -1
  483. package/dist/core/hooks/harness/output.d.ts.map +0 -1
  484. package/dist/core/supervisor/index.d.ts +0 -7
  485. package/dist/core/supervisor/index.d.ts.map +0 -1
  486. package/dist/core/supervisor/index.js +0 -6
  487. package/dist/core/supervisor/plan-api.d.ts +0 -23
  488. package/dist/core/supervisor/plan-api.d.ts.map +0 -1
  489. package/dist/core/supervisor/plan-api.js +0 -50
  490. package/dist/core/supervisor/plan-read.d.ts +0 -5
  491. package/dist/core/supervisor/plan-read.d.ts.map +0 -1
  492. package/dist/core/supervisor/plan-types.d.ts.map +0 -1
  493. package/dist/core/supervisor/planning.d.ts +0 -33
  494. package/dist/core/supervisor/planning.d.ts.map +0 -1
  495. package/dist/core/supervisor/read.d.ts +0 -6
  496. package/dist/core/supervisor/read.d.ts.map +0 -1
  497. package/dist/core/supervisor/read.js +0 -4
  498. package/dist/core/supervisor/runner.d.ts +0 -37
  499. package/dist/core/supervisor/runner.d.ts.map +0 -1
  500. package/dist/core/supervisor/service-read.d.ts +0 -6
  501. package/dist/core/supervisor/service-read.d.ts.map +0 -1
  502. package/dist/core/supervisor/service.d.ts +0 -114
  503. package/dist/core/supervisor/service.d.ts.map +0 -1
  504. package/dist/core/supervisor/state.d.ts +0 -95
  505. package/dist/core/supervisor/state.d.ts.map +0 -1
  506. package/dist/core/workflow/harnesses.d.ts +0 -17
  507. package/dist/core/workflow/harnesses.d.ts.map +0 -1
  508. package/dist/core/workflow/harnesses.js +0 -21
  509. package/dist/core/workflow/journal.d.ts.map +0 -1
  510. package/src/commands/session.ts +0 -187
  511. package/src/core/harnesses/index.ts +0 -56
  512. package/src/core/hooks/harness/detect.ts +0 -30
  513. package/src/core/supervisor/index.ts +0 -89
  514. package/src/core/supervisor/plan-api.ts +0 -73
  515. package/src/core/supervisor/read.ts +0 -59
  516. package/src/core/workflow/harnesses.ts +0 -33
  517. /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
@@ -7,10 +7,11 @@ function cachePath(tool: string, filename: string): string {
7
7
  return resolve(dir, filename);
8
8
  }
9
9
 
10
- import { spawn, spawnSync } from "node:child_process";
10
+ import { type ChildProcess, spawn, spawnSync } from "node:child_process";
11
11
  import { existsSync, openSync, readFileSync, writeFileSync } from "node:fs";
12
12
  import { resolve } from "node:path";
13
13
 
14
+ import { resolveBinName } from "../core/config.ts";
14
15
  import {
15
16
  clearState,
16
17
  DEFAULT_INSTANCE,
@@ -62,6 +63,11 @@ interface DownOpts {
62
63
  all?: boolean;
63
64
  }
64
65
 
66
+ interface ReloadOpts {
67
+ name?: string;
68
+ all?: boolean;
69
+ }
70
+
65
71
  interface StatusOpts {
66
72
  name?: string;
67
73
  }
@@ -203,12 +209,27 @@ function sweepStrays(gatePort: number, alreadyKilled: Set<number>): number {
203
209
  );
204
210
  }
205
211
 
206
- /** Ports currently bound by a LISTEN socket (best-effort via `ss`). */
212
+ /**
213
+ * Ports currently bound by a LISTEN socket (best-effort, platform-dependent).
214
+ *
215
+ * `ss` (iproute2) is Linux-only. Without the lsof fallback this returns an empty
216
+ * set on macOS/BSD. That does not fail loudly. It reports every port as free, so
217
+ * `allocateGatePort` can hand out an occupied port and the reload port-release
218
+ * check becomes inert.
219
+ */
207
220
  function listeningPorts(): Set<number> {
208
221
  const ports = new Set<number>();
209
- const r = spawnSync("ss", ["-tlnH"], { encoding: "utf-8" });
210
- if (r.status === 0 && typeof r.stdout === "string") {
211
- for (const m of r.stdout.matchAll(/:(\d+)\s/g)) ports.add(Number(m[1]));
222
+ const ss = spawnSync("ss", ["-tlnH"], { encoding: "utf-8" });
223
+ if (ss.status === 0 && typeof ss.stdout === "string") {
224
+ for (const m of ss.stdout.matchAll(/:(\d+)\s/g)) ports.add(Number(m[1]));
225
+ return ports;
226
+ }
227
+ // Rows look like: `bun 123 user 4u IPv4 0x… 0t0 TCP 127.0.0.1:58055 (LISTEN)`.
228
+ // Status can be nonzero while stdout still holds usable rows (lsof reports a
229
+ // failure when any single process is unreadable), so judge it on the output.
230
+ const lsof = spawnSync("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN"], { encoding: "utf-8" });
231
+ if (typeof lsof.stdout === "string") {
232
+ for (const m of lsof.stdout.matchAll(/:(\d+) \(LISTEN\)$/gm)) ports.add(Number(m[1]));
212
233
  }
213
234
  return ports;
214
235
  }
@@ -393,6 +414,49 @@ function tunnelIsAlive(state: TunnelState): boolean {
393
414
  return isProcessAlive(state.gate_pid) && providerIsAlive(state);
394
415
  }
395
416
 
417
+ interface GateSpawnOpts {
418
+ name: string;
419
+ gatePort: number;
420
+ target: string;
421
+ vhost: string;
422
+ provider: TunnelProvider;
423
+ allowedIps: string[];
424
+ gateLogPath: string;
425
+ }
426
+
427
+ /**
428
+ * Spawn the detached gate worker.
429
+ *
430
+ * Shared by `up` (fresh start) and `reload` (in-place restart) so both hand the
431
+ * gate an identical environment and record the same kind of PID. The allowlist
432
+ * is passed as an env var, which the gate snapshots at module load and never
433
+ * re-reads — that snapshot is precisely why `reload` has to exist.
434
+ */
435
+ function spawnGate(o: GateSpawnOpts): ChildProcess {
436
+ const gateFd = openSync(o.gateLogPath, "a");
437
+ // `--name`/`--port` on argv mirror the env vars; they're what makes the gate
438
+ // process distinguishable per-instance in `pgrep -f` (see sweepStrays).
439
+ const gateProc = spawn(
440
+ "bun",
441
+ ["run", gateScriptPath(), "--name", o.name, "--port", String(o.gatePort)],
442
+ {
443
+ detached: true,
444
+ stdio: ["ignore", gateFd, gateFd],
445
+ env: {
446
+ ...process.env,
447
+ HARNERY_TUNNEL_ALLOW: o.allowedIps.join(","),
448
+ HARNERY_TUNNEL_ACCESS:
449
+ o.provider === "cloudflare" ? "cloudflare-allowlist" : "trusted-local-proxy",
450
+ HARNERY_TUNNEL_TARGET: o.target,
451
+ HARNERY_TUNNEL_VHOST: o.vhost,
452
+ HARNERY_TUNNEL_PORT: String(o.gatePort),
453
+ },
454
+ },
455
+ );
456
+ gateProc.unref();
457
+ return gateProc;
458
+ }
459
+
396
460
  async function up(opts: UpOpts): Promise<void> {
397
461
  if (!bunAvailable()) {
398
462
  emit.error({
@@ -463,27 +527,15 @@ async function up(opts: UpOpts): Promise<void> {
463
527
  writeFileSync(gateLogPath, "");
464
528
  writeFileSync(providerLogPath, "");
465
529
 
466
- const gateFd = openSync(gateLogPath, "a");
467
- // `--name`/`--port` on argv mirror the env vars; they're what makes the gate
468
- // process distinguishable per-instance in `pgrep -f` (see sweepStrays).
469
- const gateProc = spawn(
470
- "bun",
471
- ["run", gateScriptPath(), "--name", name, "--port", String(gatePort)],
472
- {
473
- detached: true,
474
- stdio: ["ignore", gateFd, gateFd],
475
- env: {
476
- ...process.env,
477
- HARNERY_TUNNEL_ALLOW: cfg.allowed_ips.join(","),
478
- HARNERY_TUNNEL_ACCESS:
479
- provider === "cloudflare" ? "cloudflare-allowlist" : "trusted-local-proxy",
480
- HARNERY_TUNNEL_TARGET: target,
481
- HARNERY_TUNNEL_VHOST: vhost,
482
- HARNERY_TUNNEL_PORT: String(gatePort),
483
- },
484
- },
485
- );
486
- gateProc.unref();
530
+ const gateProc = spawnGate({
531
+ name,
532
+ gatePort,
533
+ target,
534
+ vhost,
535
+ provider,
536
+ allowedIps: cfg.allowed_ips,
537
+ gateLogPath,
538
+ });
487
539
 
488
540
  await sleep(800);
489
541
 
@@ -658,6 +710,156 @@ function down(opts: DownOpts): void {
658
710
  );
659
711
  }
660
712
 
713
+ /** Poll until `port` has no LISTEN socket (or the deadline passes). */
714
+ async function waitForPortFree(port: number, timeoutMs: number): Promise<boolean> {
715
+ const deadline = Date.now() + timeoutMs;
716
+ while (Date.now() < deadline) {
717
+ if (!listeningPorts().has(port)) return true;
718
+ await sleep(100);
719
+ }
720
+ return !listeningPorts().has(port);
721
+ }
722
+
723
+ /** Poll until `port` has a LISTEN socket (or the deadline passes). */
724
+ async function waitForPortBound(port: number, timeoutMs: number): Promise<boolean> {
725
+ const deadline = Date.now() + timeoutMs;
726
+ while (Date.now() < deadline) {
727
+ if (listeningPorts().has(port)) return true;
728
+ await sleep(100);
729
+ }
730
+ return listeningPorts().has(port);
731
+ }
732
+
733
+ /**
734
+ * Restart ONE instance's gate in place, deliberately leaving the provider
735
+ * process running.
736
+ *
737
+ * That asymmetry is the whole point. A Cloudflare quick tunnel's hostname is
738
+ * minted by cloudflared at startup, so a full `down`/`up` hands back a NEW
739
+ * random *.trycloudflare.com URL and breaks every link already shared — a
740
+ * miserable trade for adding one IP. cloudflared only ever forwards to
741
+ * `localhost:<gate_port>`, so the gate beneath it can be swapped out and the
742
+ * edge reconnects without noticing.
743
+ */
744
+ export async function reloadOne(state: TunnelState): Promise<{ ok: boolean; message: string }> {
745
+ const bin = resolveBinName();
746
+ const name = state.name;
747
+ const gatePort = state.gate_port;
748
+
749
+ if (!providerIsAlive(state)) {
750
+ return {
751
+ ok: false,
752
+ message:
753
+ `[${name}] the provider process is gone, so the public URL is already dead. ` +
754
+ `Reloading the gate can't bring it back — run \`${bin} tunnel up --name ${name}\`.`,
755
+ };
756
+ }
757
+ if (!bunAvailable()) {
758
+ return { ok: false, message: `[${name}] bun is not on PATH, so the gate can't be respawned.` };
759
+ }
760
+
761
+ // Kill ONLY the gate. sweepStrays() is deliberately avoided here: it also
762
+ // matches `--url http://localhost:<port>`, which is the provider process we
763
+ // are going out of our way to keep alive.
764
+ const killed = new Set<number>();
765
+ if (isProcessAlive(state.gate_pid)) {
766
+ try {
767
+ process.kill(state.gate_pid);
768
+ killed.add(state.gate_pid);
769
+ } catch {
770
+ /* race: already gone */
771
+ }
772
+ }
773
+ killByPattern(`gate\\.ts.*--port ${gatePort}( |$)`, killed);
774
+
775
+ if (!(await waitForPortFree(gatePort, 5_000))) {
776
+ return {
777
+ ok: false,
778
+ message:
779
+ `[${name}] port ${gatePort} never released, so the old gate looks wedged. ` +
780
+ `Tunnel is down; check \`${bin} tunnel status\`.`,
781
+ };
782
+ }
783
+
784
+ const cfg = readConfig();
785
+ const gateProc = spawnGate({
786
+ name,
787
+ gatePort,
788
+ target: state.target,
789
+ vhost: state.vhost,
790
+ provider: state.provider,
791
+ allowedIps: cfg.allowed_ips,
792
+ gateLogPath: cachePath("tunnel", gateLogFile(name)),
793
+ });
794
+
795
+ await sleep(800);
796
+ const pid = gateProc.pid;
797
+ if (
798
+ typeof pid !== "number" ||
799
+ !isProcessAlive(pid) ||
800
+ !(await waitForPortBound(gatePort, 5_000))
801
+ ) {
802
+ return {
803
+ ok: false,
804
+ message:
805
+ `[${name}] the gate did not come back on port ${gatePort}. ` +
806
+ `Tunnel is down; check \`${bin} tunnel logs --name ${name}\`.`,
807
+ };
808
+ }
809
+
810
+ writeState({ ...state, gate_pid: pid });
811
+ const allowNote =
812
+ state.provider === "cloudflare" ? ` Allowlist: ${cfg.allowed_ips.length} IP(s).` : "";
813
+ return { ok: true, message: `[${name}] gate reloaded, URL unchanged: ${state.url}.${allowNote}` };
814
+ }
815
+
816
+ async function reload(opts: ReloadOpts): Promise<void> {
817
+ const bin = resolveBinName();
818
+
819
+ if (opts.all) {
820
+ const all = listStates();
821
+ // Only live tunnels are reloadable, and a box commonly accumulates stale
822
+ // state files from long-dead runs. Reporting those as failures would make
823
+ // the common post-`allow add` path exit non-zero for no real reason.
824
+ const live = all.filter((s) => providerIsAlive(s));
825
+ const stale = all.length - live.length;
826
+ if (live.length === 0) {
827
+ emit.text(
828
+ all.length === 0
829
+ ? "No tunnels up. Nothing to reload.\n"
830
+ : `No live tunnels to reload (${all.length} stale). Start one with \`${bin} tunnel up\`.\n`,
831
+ );
832
+ return;
833
+ }
834
+ let failed = 0;
835
+ for (const s of live) {
836
+ const r = await reloadOne(s);
837
+ emit.text(`${r.ok ? "ok " : "FAIL"} ${r.message}\n`);
838
+ if (!r.ok) failed++;
839
+ }
840
+ emit.text(
841
+ `Reloaded ${live.length - failed} of ${live.length} live tunnel(s)` +
842
+ (stale > 0 ? `; skipped ${stale} stale.\n` : ".\n"),
843
+ );
844
+ if (failed > 0) process.exitCode = 1;
845
+ return;
846
+ }
847
+
848
+ const name = resolveName(opts.name);
849
+ const state = readState(name);
850
+ if (!state) {
851
+ emit.error({
852
+ code: "tunnel_not_found",
853
+ message: `No tunnel state for [${name}]. Start it with \`${bin} tunnel up --name ${name}\`.`,
854
+ });
855
+ process.exit(1);
856
+ return;
857
+ }
858
+ const r = await reloadOne(state);
859
+ emit.text(`${r.message}\n`);
860
+ if (!r.ok) process.exit(1);
861
+ }
862
+
661
863
  function instanceState(state: TunnelState): "up" | "stale" {
662
864
  return tunnelIsAlive(state) ? "up" : "stale";
663
865
  }
@@ -772,6 +974,23 @@ function allowList(): void {
772
974
  for (const ip of cfg.allowed_ips) emit.text(`${ip}\n`);
773
975
  }
774
976
 
977
+ /**
978
+ * Trailer for `allow add`/`allow rm`. Each gate snapshots the allowlist at
979
+ * spawn, so a config edit alone changes nothing until the gates restart.
980
+ * Only LIVE tunnels are listed — stale state files pile up on a long-running
981
+ * box, and naming nine dead instances made the old hint read like nine
982
+ * restarts were owed.
983
+ */
984
+ function allowChangedHint(): void {
985
+ const live = listStates().filter((s) => s.provider === "cloudflare" && providerIsAlive(s));
986
+ if (live.length === 0) return;
987
+ emit.text(
988
+ `Each gate reads the allowlist once at start, so this applies on reload ` +
989
+ `(${live.map((s) => s.name).join(", ")}):\n` +
990
+ ` ${resolveBinName()} tunnel reload --all\n`,
991
+ );
992
+ }
993
+
775
994
  function allowAdd(ip: string): void {
776
995
  const cfg = readConfig();
777
996
  if (cfg.allowed_ips.includes(ip)) {
@@ -781,12 +1000,7 @@ function allowAdd(ip: string): void {
781
1000
  cfg.allowed_ips.push(ip);
782
1001
  writeConfig(cfg);
783
1002
  emit.text(`Added ${ip}.\n`);
784
- const up = listStates().filter((s) => s.provider === "cloudflare");
785
- if (up.length > 0) {
786
- emit.text(
787
- `Cloudflare allowlist is shared across Cloudflare tunnels; restart each to apply (${up.map((s) => s.name).join(", ")}).\n`,
788
- );
789
- }
1003
+ allowChangedHint();
790
1004
  }
791
1005
 
792
1006
  function allowRm(ip: string): void {
@@ -799,12 +1013,7 @@ function allowRm(ip: string): void {
799
1013
  cfg.allowed_ips.splice(idx, 1);
800
1014
  writeConfig(cfg);
801
1015
  emit.text(`Removed ${ip}.\n`);
802
- const up = listStates().filter((s) => s.provider === "cloudflare");
803
- if (up.length > 0) {
804
- emit.text(
805
- `Cloudflare allowlist is shared across Cloudflare tunnels; restart each to apply (${up.map((s) => s.name).join(", ")}).\n`,
806
- );
807
- }
1016
+ allowChangedHint();
808
1017
  }
809
1018
 
810
1019
  let emit: EmitContext;
@@ -858,6 +1067,15 @@ export function registerTunnelCommand(
858
1067
  .option("--all", "stop every running tunnel")
859
1068
  .action(down);
860
1069
 
1070
+ cmd
1071
+ .command("reload")
1072
+ .description(
1073
+ "Restart an instance's gate in place to pick up allowlist changes, keeping the public URL",
1074
+ )
1075
+ .option("--name <name>", "instance to reload", DEFAULT_INSTANCE)
1076
+ .option("--all", "reload every live tunnel")
1077
+ .action(reload);
1078
+
861
1079
  cmd
862
1080
  .command("status")
863
1081
  .description("Show tunnel state: a table of all instances, or detail for one via --name")
@@ -16,7 +16,7 @@ import type { EmitContext } from "../commander.ts";
16
16
  * harnery repo at the *matching version tag* into a cache dir and install the
17
17
  * web app's deps. Subsequent runs reuse the cache.
18
18
  *
19
- * The only thing `web/` pulls from `src/` is `harnery/core/scratch` (a single,
19
+ * The only thing `web/` pulls from `src/` is `harnery/core/journal` (a single,
20
20
  * dependency-free module), so the install is `web/` deps only: no root install,
21
21
  * no Playwright browser download.
22
22
  */
@@ -32,6 +32,41 @@ function webRoot(): string {
32
32
  return path.resolve(here, "..", "..", "web");
33
33
  }
34
34
 
35
+ /** Default V8 old-space ceiling (MB) for the dashboard server. */
36
+ const DEFAULT_MAX_OLD_SPACE_MB = 2048;
37
+
38
+ /**
39
+ * Resolve the V8 old-space ceiling for the dashboard process.
40
+ *
41
+ * Next sizes `--max-old-space-size` to roughly half of system RAM when the
42
+ * flag is absent, which on a large machine hands a long-lived dashboard a
43
+ * ceiling it will never approach — so V8 never feels enough pressure to run a
44
+ * major GC and the process settles at a multi-gigabyte working set made mostly
45
+ * of collectable garbage. Pinning a modest ceiling restores that pressure.
46
+ *
47
+ * Precedence: explicit flag → `HARNERY_WEB_MAX_OLD_SPACE` → the default.
48
+ * `0` (or any non-positive value) opts out and restores Next's own sizing.
49
+ */
50
+ export function resolveMaxOldSpaceMb(flag?: string): number {
51
+ const raw = flag ?? process.env.HARNERY_WEB_MAX_OLD_SPACE;
52
+ if (raw === undefined || raw === "") return DEFAULT_MAX_OLD_SPACE_MB;
53
+ const n = Number(raw);
54
+ if (!Number.isFinite(n) || n <= 0) return 0;
55
+ return Math.floor(n);
56
+ }
57
+
58
+ /**
59
+ * Append the heap ceiling to any inherited `NODE_OPTIONS`. Next only supplies
60
+ * its own `--max-old-space-size` when the flag is absent, so appending here
61
+ * wins without clobbering flags the caller set (source maps, inspector, …).
62
+ */
63
+ export function nodeOptionsWithHeapCap(mb: number): string | undefined {
64
+ const inherited = process.env.NODE_OPTIONS ?? "";
65
+ if (mb <= 0) return inherited || undefined;
66
+ if (/--max-old-space-size[=\s]/.test(inherited)) return inherited;
67
+ return `${inherited} --max-old-space-size=${mb}`.trim();
68
+ }
69
+
35
70
  /**
36
71
  * Resolve the web app root. A local `web/` (source checkout / submodule) wins.
37
72
  * Otherwise, on an npm install, lazy-fetch it (clone + install) unless the
@@ -57,6 +92,7 @@ interface UpOpts {
57
92
  coordRoot?: string;
58
93
  prod?: boolean;
59
94
  fetch?: boolean;
95
+ maxOldSpace?: string;
60
96
  }
61
97
 
62
98
  export function registerWebCommand(program: Command, emit: EmitContext): void {
@@ -77,6 +113,10 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
77
113
  )
78
114
  .option("--prod", "Use next start instead of next dev (requires prior build)")
79
115
  .option("--no-fetch", "Don't auto-fetch the dashboard if it's missing (npm installs)")
116
+ .option(
117
+ "--max-old-space <mb>",
118
+ `V8 old-space ceiling in MB; 0 restores Next's own sizing (default ${DEFAULT_MAX_OLD_SPACE_MB}, env HARNERY_WEB_MAX_OLD_SPACE)`,
119
+ )
80
120
  .action(async (opts: UpOpts) => {
81
121
  const root = resolveWebRoot(emit, opts.fetch !== false);
82
122
  if (!root) {
@@ -101,8 +141,11 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
101
141
  }
102
142
  }
103
143
 
144
+ const heapMb = resolveMaxOldSpaceMb(opts.maxOldSpace);
104
145
  emit.log(`harn web · http://localhost:${port} (${mode})`, "info");
146
+ emit.log(`files origin · http://harnery-files.localhost:${port}`, "info");
105
147
  emit.log(`reading .harnery/ from: ${coordRoot}`, "info");
148
+ if (heapMb > 0) emit.log(`heap ceiling · ${heapMb} MB`, "info");
106
149
 
107
150
  const child = spawn(webRunner(), ["run", mode], {
108
151
  cwd: root,
@@ -110,6 +153,9 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
110
153
  ...process.env,
111
154
  HARNERY_COORD_ROOT: coordRoot,
112
155
  HARNERY_WEB_PORT: port,
156
+ ...(nodeOptionsWithHeapCap(heapMb)
157
+ ? { NODE_OPTIONS: nodeOptionsWithHeapCap(heapMb) as string }
158
+ : {}),
113
159
  },
114
160
  stdio: "inherit",
115
161
  });
@@ -154,7 +200,11 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
154
200
  .option("-p, --port <port>", "Listen port", "9000")
155
201
  .option("--coord-root <dir>", "Override the coord root")
156
202
  .option("--no-fetch", "Don't auto-fetch the dashboard if it's missing (npm installs)")
157
- .action((opts: { port: string; coordRoot?: string; fetch?: boolean }) => {
203
+ .option(
204
+ "--max-old-space <mb>",
205
+ `V8 old-space ceiling in MB; 0 restores Next's own sizing (default ${DEFAULT_MAX_OLD_SPACE_MB}, env HARNERY_WEB_MAX_OLD_SPACE)`,
206
+ )
207
+ .action((opts: { port: string; coordRoot?: string; fetch?: boolean; maxOldSpace?: string }) => {
158
208
  const root = resolveWebRoot(emit, opts.fetch !== false);
159
209
  if (!root) {
160
210
  process.exitCode = 1;
@@ -172,8 +222,10 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
172
222
  }
173
223
  const coordRoot = opts.coordRoot ?? process.cwd();
174
224
  const port = String(opts.port);
225
+ const heapMb = resolveMaxOldSpaceMb(opts.maxOldSpace);
175
226
  emit.log(`harn web · http://localhost:${port} (start)`, "info");
176
227
  emit.log(`reading .harnery/ from: ${coordRoot}`, "info");
228
+ if (heapMb > 0) emit.log(`heap ceiling · ${heapMb} MB`, "info");
177
229
 
178
230
  const child = spawn(webRunner(), ["run", "start"], {
179
231
  cwd: root,
@@ -181,6 +233,9 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
181
233
  ...process.env,
182
234
  HARNERY_COORD_ROOT: coordRoot,
183
235
  HARNERY_WEB_PORT: port,
236
+ ...(nodeOptionsWithHeapCap(heapMb)
237
+ ? { NODE_OPTIONS: nodeOptionsWithHeapCap(heapMb) as string }
238
+ : {}),
184
239
  },
185
240
  stdio: "inherit",
186
241
  });
@@ -1,12 +1,13 @@
1
1
  import { resolve } from "node:path";
2
2
  import type { Command } from "commander";
3
3
  import type { EmitContext } from "../commander.ts";
4
- import { workflowSubscriptionOnly } from "../core/config.ts";
5
4
  import {
6
- createBuiltinHarnessRegistry,
7
- harnessProofInputs,
5
+ adapterProofInputs,
6
+ createBuiltinAdapterRegistry,
8
7
  probeBinaryVersion,
9
- } from "../core/harnesses/index.ts";
8
+ } from "../core/adapters/index.ts";
9
+ import { workflowSubscriptionOnly } from "../core/config.ts";
10
+ import { findCompletedMissionGoverning, reopenGovernorMission } from "../core/governor/index.ts";
10
11
  import { findCoordRoot } from "../core/hooks/resolve/coord-root.ts";
11
12
  import type { PolicyIsolation } from "../core/policy/index.ts";
12
13
  import { loadPolicyFile } from "../core/policy/index.ts";
@@ -38,7 +39,7 @@ interface WorkCreateOpts {
38
39
  }
39
40
 
40
41
  interface WorkRunOpts {
41
- harness?: string;
42
+ adapter?: string;
42
43
  maxAgents?: string;
43
44
  concurrency?: string;
44
45
  cwd?: string;
@@ -60,11 +61,11 @@ interface GovernanceOpts {
60
61
  dispose?: string[];
61
62
  }
62
63
 
63
- const MAX_ATTEMPT_JOURNAL_ERROR_RENDER = 240;
64
+ const MAX_ATTEMPT_TRANSCRIPT_ERROR_RENDER = 240;
64
65
 
65
66
  export function registerWorkCommand(program: Command, emit: EmitContext): void {
66
- const registry = createBuiltinHarnessRegistry();
67
- const harnesses = registry.ids();
67
+ const registry = createBuiltinAdapterRegistry();
68
+ const adapters = registry.ids();
68
69
  const work = program
69
70
  .command("work")
70
71
  .description("Track durable objectives across bounded workflow attempts.");
@@ -158,8 +159,8 @@ export function registerWorkCommand(program: Command, emit: EmitContext): void {
158
159
  });
159
160
  });
160
161
 
161
- registerRunCommand(work, "run", false, registry, harnesses, emit);
162
- registerRunCommand(work, "retry", true, registry, harnesses, emit);
162
+ registerRunCommand(work, "run", false, registry, adapters, emit);
163
+ registerRunCommand(work, "retry", true, registry, adapters, emit);
163
164
  registerGovernanceCommand(work, "accept", emit);
164
165
  registerGovernanceCommand(work, "cancel", emit);
165
166
  registerGovernanceCommand(work, "reopen", emit);
@@ -169,8 +170,8 @@ function registerRunCommand(
169
170
  work: Command,
170
171
  name: "run" | "retry",
171
172
  retry: boolean,
172
- registry: ReturnType<typeof createBuiltinHarnessRegistry>,
173
- harnesses: string[],
173
+ registry: ReturnType<typeof createBuiltinAdapterRegistry>,
174
+ adapters: string[],
174
175
  emit: EmitContext,
175
176
  ): void {
176
177
  work
@@ -183,8 +184,8 @@ function registerRunCommand(
183
184
  .option("--max-agents <n>", "Total-agent ceiling for the workflow")
184
185
  .option("--concurrency <n>", "Concurrent-subagent cap")
185
186
  .option("--cwd <dir>", "Working directory children spawn in")
186
- .option("--harness <name>", `Default harness: ${harnesses.join(" | ")}`)
187
- .option("--subscription-only", "Require stored harness-login billing")
187
+ .option("--adapter <name>", `Default adapter: ${adapters.join(" | ")}`)
188
+ .option("--subscription-only", "Require stored adapter-login billing")
188
189
  .option("--allow-api-billing", "Permit API-key override billing")
189
190
  .option("--policy <file>", "Host policy JSON/JSONC")
190
191
  .option("--isolation <mode>", "shared | worktree | sandbox | remote")
@@ -194,8 +195,8 @@ function registerRunCommand(
194
195
  .option("--json", "Emit the workflow report or parked result as JSON")
195
196
  .action(async (workId: string, opts: WorkRunOpts) => {
196
197
  await withWorkRootAsync(emit, async (coordRoot) => {
197
- if (opts.harness && !registry.get(opts.harness)) {
198
- throw new Error(`unknown harness ${JSON.stringify(opts.harness)}`);
198
+ if (opts.adapter && !registry.get(opts.adapter)) {
199
+ throw new Error(`unknown adapter ${JSON.stringify(opts.adapter)}`);
199
200
  }
200
201
  if (opts.workspaceRoot && opts.isolation !== "worktree") {
201
202
  throw new Error("--workspace-root requires --isolation worktree");
@@ -219,14 +220,14 @@ function registerRunCommand(
219
220
  actor: opts.actor,
220
221
  engine: {
221
222
  spawners: registry.spawners(),
222
- defaultHarness: opts.harness,
223
+ defaultAdapter: opts.adapter,
223
224
  maxAgents: opts.maxAgents ? Number.parseInt(opts.maxAgents, 10) : undefined,
224
225
  concurrency: opts.concurrency ? Number.parseInt(opts.concurrency, 10) : undefined,
225
226
  cwd: opts.cwd,
226
227
  subscriptionOnly:
227
228
  opts.subscriptionOnly === true ? true : workflowSubscriptionOnly(coordRoot),
228
229
  allowApiBilling: opts.allowApiBilling,
229
- ...harnessProofInputs(
230
+ ...adapterProofInputs(
230
231
  registry.list().map((adapter) => adapter.profile),
231
232
  { versionProbe: probeBinaryVersion },
232
233
  ),
@@ -266,7 +267,7 @@ function registerRunCommand(
266
267
  workId,
267
268
  runId: error.runId,
268
269
  approvalId: error.approvalId,
269
- journalPath: error.journalPath,
270
+ transcriptPath: error.transcriptPath,
270
271
  });
271
272
  } else {
272
273
  emit.text(
@@ -320,16 +321,30 @@ function registerGovernanceCommand(
320
321
  withWorkRoot(emit, (coordRoot) => {
321
322
  const fn =
322
323
  name === "accept" ? acceptWorkItem : name === "cancel" ? cancelWorkItem : reopenWorkItem;
323
- emitWork(
324
- fn(coordRoot, workId, {
325
- ...opts,
326
- ...(opts.finding?.length ? { findings: opts.finding } : {}),
327
- ...(opts.dispose?.length ? { dispositions: opts.dispose.map(parseDisposition) } : {}),
328
- }),
329
- opts.json,
330
- emit,
331
- true,
332
- );
324
+ // ADR 0050: a reopen under a mission that already succeeded has to reopen the
325
+ // mission too, or the item lands in ready_work that the governor will never
326
+ // dispatch. Resolve the goal BEFORE touching the work item so a refusal leaves
327
+ // nothing half-done.
328
+ const goalId =
329
+ name === "reopen" ? findCompletedMissionGoverning(coordRoot, workId) : undefined;
330
+ const record = fn(coordRoot, workId, {
331
+ ...opts,
332
+ ...(opts.finding?.length ? { findings: opts.finding } : {}),
333
+ ...(opts.dispose?.length ? { dispositions: opts.dispose.map(parseDisposition) } : {}),
334
+ });
335
+ if (goalId) {
336
+ reopenGovernorMission({
337
+ coordRoot,
338
+ goalId,
339
+ actor: opts.actor,
340
+ reason: opts.reason?.trim() || `work ${workId} was reopened after mission completion`,
341
+ });
342
+ emit.log(
343
+ `mission ${goalId} had completed; its completion was reopened so ${workId} can be dispatched`,
344
+ "warn",
345
+ );
346
+ }
347
+ emitWork(record, opts.json, emit, true);
333
348
  });
334
349
  });
335
350
  }
@@ -399,17 +414,17 @@ function emitWork(
399
414
 
400
415
  export function renderAttemptRow(attempt: WorkAttempt): string {
401
416
  const statusDetail =
402
- attempt.status === "journal_unreadable"
403
- ? `: ${renderAttemptJournalError(attempt.journal_error)}`
417
+ attempt.status === "transcript_unreadable"
418
+ ? `: ${renderAttemptTranscriptError(attempt.transcript_error)}`
404
419
  : "";
405
420
  return ` ${attempt.number}. ${attempt.run_id} ${attempt.status}${statusDetail}`;
406
421
  }
407
422
 
408
- function renderAttemptJournalError(error: string | undefined): string {
423
+ function renderAttemptTranscriptError(error: string | undefined): string {
409
424
  const normalized = (error ?? "").replace(/\s+/g, " ").trim();
410
- const value = normalized || "unknown journal read error";
411
- if (value.length <= MAX_ATTEMPT_JOURNAL_ERROR_RENDER) return value;
412
- return `${value.slice(0, MAX_ATTEMPT_JOURNAL_ERROR_RENDER - 3).trimEnd()}...`;
425
+ const value = normalized || "unknown transcript read error";
426
+ if (value.length <= MAX_ATTEMPT_TRANSCRIPT_ERROR_RENDER) return value;
427
+ return `${value.slice(0, MAX_ATTEMPT_TRANSCRIPT_ERROR_RENDER - 3).trimEnd()}...`;
413
428
  }
414
429
 
415
430
  function renderWorkRow(record: WorkRecord): string {