harnery 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (501) hide show
  1. package/README.md +4 -4
  2. package/dist/commander.d.ts +8 -0
  3. package/dist/commander.d.ts.map +1 -1
  4. package/dist/commander.js +14 -13
  5. package/dist/commands/adapter.d.ts +12 -0
  6. package/dist/commands/adapter.d.ts.map +1 -0
  7. package/dist/commands/{harness.js → adapter.js} +35 -35
  8. package/dist/commands/agents.d.ts +3 -2
  9. package/dist/commands/agents.d.ts.map +1 -1
  10. package/dist/commands/agents.js +61 -60
  11. package/dist/commands/artifacts.d.ts +4 -0
  12. package/dist/commands/artifacts.d.ts.map +1 -0
  13. package/dist/commands/artifacts.js +143 -0
  14. package/dist/commands/backup.d.ts +1 -1
  15. package/dist/commands/backup.d.ts.map +1 -1
  16. package/dist/commands/backup.js +5 -2
  17. package/dist/commands/browse.d.ts.map +1 -1
  18. package/dist/commands/browse.js +310 -7
  19. package/dist/commands/checkpoint.d.ts +12 -0
  20. package/dist/commands/checkpoint.d.ts.map +1 -0
  21. package/dist/commands/checkpoint.js +162 -0
  22. package/dist/commands/context.d.ts +2 -2
  23. package/dist/commands/context.d.ts.map +1 -1
  24. package/dist/commands/context.js +3 -151
  25. package/dist/commands/decision.d.ts.map +1 -1
  26. package/dist/commands/decision.js +31 -7
  27. package/dist/commands/deinit.d.ts +2 -2
  28. package/dist/commands/deinit.js +15 -15
  29. package/dist/commands/doctor.js +23 -23
  30. package/dist/commands/governor.d.ts +4 -0
  31. package/dist/commands/governor.d.ts.map +1 -0
  32. package/dist/commands/{supervisor.js → governor.js} +133 -108
  33. package/dist/commands/init.d.ts +12 -12
  34. package/dist/commands/init.d.ts.map +1 -1
  35. package/dist/commands/init.js +44 -30
  36. package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
  37. package/dist/commands/journal.d.ts.map +1 -0
  38. package/dist/commands/{scratch.js → journal.js} +47 -47
  39. package/dist/commands/policy.js +1 -1
  40. package/dist/commands/presence.js +2 -2
  41. package/dist/commands/sync.d.ts +1 -1
  42. package/dist/commands/sync.js +3 -3
  43. package/dist/commands/tunnel.d.ts +16 -0
  44. package/dist/commands/tunnel.d.ts.map +1 -1
  45. package/dist/commands/tunnel.js +193 -23
  46. package/dist/commands/web-fetch.js +1 -1
  47. package/dist/commands/web.d.ts +19 -0
  48. package/dist/commands/web.d.ts.map +1 -1
  49. package/dist/commands/web.js +51 -0
  50. package/dist/commands/work.d.ts.map +1 -1
  51. package/dist/commands/work.js +38 -22
  52. package/dist/commands/workflow.d.ts.map +1 -1
  53. package/dist/commands/workflow.js +35 -28
  54. package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
  55. package/dist/core/adapters/attest-projection.d.ts.map +1 -0
  56. package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
  57. package/dist/core/adapters/attest.d.ts.map +1 -0
  58. package/dist/core/{harnesses → adapters}/attest.js +9 -9
  59. package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
  60. package/dist/core/adapters/attestation.d.ts.map +1 -0
  61. package/dist/core/{harnesses → adapters}/attestation.js +20 -20
  62. package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
  63. package/dist/core/adapters/bench.d.ts.map +1 -0
  64. package/dist/core/{harnesses → adapters}/bench.js +12 -12
  65. package/dist/core/adapters/index.d.ts +13 -0
  66. package/dist/core/adapters/index.d.ts.map +1 -0
  67. package/dist/core/adapters/index.js +6 -0
  68. package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
  69. package/dist/core/adapters/profiles.d.ts.map +1 -0
  70. package/dist/core/{harnesses → adapters}/profiles.js +11 -11
  71. package/dist/core/adapters/registry.d.ts +15 -0
  72. package/dist/core/adapters/registry.d.ts.map +1 -0
  73. package/dist/core/{harnesses → adapters}/registry.js +11 -11
  74. package/dist/core/{harnesses → adapters}/types.d.ts +23 -23
  75. package/dist/core/adapters/types.d.ts.map +1 -0
  76. package/dist/core/{harnesses → adapters}/types.js +1 -1
  77. package/dist/core/agents/canonical-emit.d.ts +4 -4
  78. package/dist/core/agents/canonical-emit.js +5 -5
  79. package/dist/core/agents/cli-emit.js +1 -1
  80. package/dist/core/agents/cli.js +30 -26
  81. package/dist/core/agents/codex-replay.d.ts +1 -1
  82. package/dist/core/agents/codex-replay.js +6 -6
  83. package/dist/core/agents/coord-client.d.ts +21 -2
  84. package/dist/core/agents/coord-client.d.ts.map +1 -1
  85. package/dist/core/agents/coord-client.js +122 -27
  86. package/dist/core/agents/events/consume.d.ts +1 -1
  87. package/dist/core/agents/events/emit.d.ts +5 -6
  88. package/dist/core/agents/events/emit.d.ts.map +1 -1
  89. package/dist/core/agents/events/emit.js +2 -2
  90. package/dist/core/agents/render/prompt-context.d.ts +21 -9
  91. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  92. package/dist/core/agents/render/prompt-context.js +82 -30
  93. package/dist/core/agents/render/session-context.d.ts +1 -1
  94. package/dist/core/agents/render/session-context.js +5 -5
  95. package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
  96. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  97. package/dist/core/agents/rules/claim-conflict.js +1 -1
  98. package/dist/core/agents/rules/commit-conflict.js +98 -22
  99. package/dist/core/agents/rules/stop-hook.d.ts +9 -9
  100. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  101. package/dist/core/agents/rules/stop-hook.js +23 -5
  102. package/dist/core/agents/rules/verdict.d.ts +14 -0
  103. package/dist/core/agents/rules/verdict.d.ts.map +1 -0
  104. package/dist/core/agents/rules/verdict.js +1 -0
  105. package/dist/core/agents/session-events.js +3 -3
  106. package/dist/core/agents/state/council.d.ts +1 -1
  107. package/dist/core/agents/state/council.js +1 -1
  108. package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
  109. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  110. package/dist/core/agents/state/heartbeat-projector.js +14 -10
  111. package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
  112. package/dist/core/agents/state/heartbeat-writer.js +12 -12
  113. package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
  114. package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
  115. package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
  116. package/dist/core/agents/state/pidmap.d.ts +28 -4
  117. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  118. package/dist/core/agents/state/pidmap.js +130 -15
  119. package/dist/core/agents/state/proc-start.d.ts +95 -0
  120. package/dist/core/agents/state/proc-start.d.ts.map +1 -0
  121. package/dist/core/agents/state/proc-start.js +199 -0
  122. package/dist/core/agents/state/shell-mutation.d.ts +1 -1
  123. package/dist/core/agents/state/shell-mutation.js +1 -1
  124. package/dist/core/agents/state/stale-sweep.js +7 -7
  125. package/dist/core/artifacts/index.d.ts +86 -0
  126. package/dist/core/artifacts/index.d.ts.map +1 -0
  127. package/dist/core/artifacts/index.js +421 -0
  128. package/dist/core/config.d.ts +9 -2
  129. package/dist/core/config.d.ts.map +1 -1
  130. package/dist/core/config.js +26 -2
  131. package/dist/core/context/index.d.ts +12 -12
  132. package/dist/core/context/index.js +10 -10
  133. package/dist/core/governor/adapter-spread.d.ts +52 -0
  134. package/dist/core/governor/adapter-spread.d.ts.map +1 -0
  135. package/dist/core/governor/adapter-spread.js +52 -0
  136. package/dist/core/governor/index.d.ts +7 -0
  137. package/dist/core/governor/index.d.ts.map +1 -0
  138. package/dist/core/governor/index.js +6 -0
  139. package/dist/core/governor/plan-api.d.ts +38 -0
  140. package/dist/core/governor/plan-api.d.ts.map +1 -0
  141. package/dist/core/governor/plan-api.js +83 -0
  142. package/dist/core/governor/plan-read.d.ts +5 -0
  143. package/dist/core/governor/plan-read.d.ts.map +1 -0
  144. package/dist/core/{supervisor → governor}/plan-read.js +66 -52
  145. package/dist/core/{supervisor → governor}/plan-types.d.ts +57 -56
  146. package/dist/core/governor/plan-types.d.ts.map +1 -0
  147. package/dist/core/{supervisor → governor}/plan-types.js +4 -4
  148. package/dist/core/governor/planning.d.ts +48 -0
  149. package/dist/core/governor/planning.d.ts.map +1 -0
  150. package/dist/core/{supervisor → governor}/planning.js +125 -94
  151. package/dist/core/governor/read.d.ts +6 -0
  152. package/dist/core/governor/read.d.ts.map +1 -0
  153. package/dist/core/governor/read.js +4 -0
  154. package/dist/core/governor/runner.d.ts +37 -0
  155. package/dist/core/governor/runner.d.ts.map +1 -0
  156. package/dist/core/{supervisor → governor}/runner.js +15 -15
  157. package/dist/core/governor/service-read.d.ts +6 -0
  158. package/dist/core/governor/service-read.d.ts.map +1 -0
  159. package/dist/core/{supervisor → governor}/service-read.js +12 -12
  160. package/dist/core/governor/service.d.ts +114 -0
  161. package/dist/core/governor/service.d.ts.map +1 -0
  162. package/dist/core/{supervisor → governor}/service.js +57 -57
  163. package/dist/core/governor/state.d.ts +116 -0
  164. package/dist/core/governor/state.d.ts.map +1 -0
  165. package/dist/core/{supervisor → governor}/state.js +228 -108
  166. package/dist/core/hooks/adapter/detect.d.ts +9 -0
  167. package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
  168. package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
  169. package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
  170. package/dist/core/hooks/adapter/events.d.ts.map +1 -0
  171. package/dist/core/hooks/{harness → adapter}/events.js +5 -5
  172. package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
  173. package/dist/core/hooks/adapter/output.d.ts.map +1 -0
  174. package/dist/core/hooks/{harness → adapter}/output.js +30 -22
  175. package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
  176. package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
  177. package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
  178. package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
  179. package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
  180. package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
  181. package/dist/core/hooks/cli.d.ts +3 -3
  182. package/dist/core/hooks/cli.js +175 -151
  183. package/dist/core/hooks/effects/image-capture.d.ts +5 -5
  184. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  185. package/dist/core/hooks/effects/image-capture.js +3 -3
  186. package/dist/core/hooks/effects/index.d.ts +12 -12
  187. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  188. package/dist/core/hooks/effects/index.js +17 -17
  189. package/dist/core/hooks/events/emit.d.ts +2 -2
  190. package/dist/core/hooks/events/emit.d.ts.map +1 -1
  191. package/dist/core/hooks/events/emit.js +1 -1
  192. package/dist/core/hooks/events/rotate.js +1 -1
  193. package/dist/core/hooks/events/schema.d.ts +13 -5
  194. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  195. package/dist/core/hooks/guard-path.d.ts +2 -2
  196. package/dist/core/hooks/guard-path.js +2 -2
  197. package/dist/core/hooks/index.d.ts +1 -1
  198. package/dist/core/hooks/index.d.ts.map +1 -1
  199. package/dist/core/hooks/resolve/anchor.d.ts +14 -5
  200. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
  201. package/dist/core/hooks/resolve/anchor.js +64 -7
  202. package/dist/core/hooks/resolve/coord-root.d.ts +3 -3
  203. package/dist/core/hooks/resolve/coord-root.js +4 -4
  204. package/dist/core/hooks/resolve/intent.js +1 -1
  205. package/dist/core/hooks/resolve/owner.d.ts +1 -1
  206. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  207. package/dist/core/hooks/resolve/owner.js +11 -5
  208. package/dist/core/{scratch → journal}/index.d.ts +25 -25
  209. package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
  210. package/dist/core/{scratch → journal}/index.js +38 -38
  211. package/dist/core/policy/evaluate.d.ts +1 -1
  212. package/dist/core/policy/evaluate.d.ts.map +1 -1
  213. package/dist/core/policy/evaluate.js +7 -7
  214. package/dist/core/policy/types.d.ts +3 -3
  215. package/dist/core/policy/types.d.ts.map +1 -1
  216. package/dist/core/work/state.d.ts +12 -4
  217. package/dist/core/work/state.d.ts.map +1 -1
  218. package/dist/core/work/state.js +50 -17
  219. package/dist/core/workflow/adapters.d.ts +17 -0
  220. package/dist/core/workflow/adapters.d.ts.map +1 -0
  221. package/dist/core/workflow/adapters.js +21 -0
  222. package/dist/core/workflow/approvals.js +6 -6
  223. package/dist/core/workflow/billing.d.ts +9 -9
  224. package/dist/core/workflow/billing.js +9 -9
  225. package/dist/core/workflow/child-env.d.ts +10 -2
  226. package/dist/core/workflow/child-env.d.ts.map +1 -1
  227. package/dist/core/workflow/child-env.js +12 -4
  228. package/dist/core/workflow/engine.d.ts +15 -5
  229. package/dist/core/workflow/engine.d.ts.map +1 -1
  230. package/dist/core/workflow/engine.js +138 -83
  231. package/dist/core/workflow/evidence-preflight.d.ts +24 -0
  232. package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
  233. package/dist/core/workflow/evidence-preflight.js +134 -0
  234. package/dist/core/workflow/index.d.ts +1 -1
  235. package/dist/core/workflow/index.d.ts.map +1 -1
  236. package/dist/core/workflow/proof.d.ts +11 -5
  237. package/dist/core/workflow/proof.d.ts.map +1 -1
  238. package/dist/core/workflow/proof.js +45 -36
  239. package/dist/core/workflow/run-state.d.ts +1 -1
  240. package/dist/core/workflow/run-state.js +7 -7
  241. package/dist/core/workflow/sandbox-projection.d.ts +6 -6
  242. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  243. package/dist/core/workflow/sandbox-projection.js +11 -11
  244. package/dist/core/workflow/spawn-claude.d.ts +3 -3
  245. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  246. package/dist/core/workflow/spawn-claude.js +9 -6
  247. package/dist/core/workflow/spawn-codex.d.ts +4 -4
  248. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  249. package/dist/core/workflow/spawn-codex.js +9 -6
  250. package/dist/core/workflow/spawn-cursor.d.ts +4 -4
  251. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  252. package/dist/core/workflow/spawn-cursor.js +9 -6
  253. package/dist/core/workflow/spawn-failure.d.ts +1 -1
  254. package/dist/core/workflow/spawn-failure.js +1 -1
  255. package/dist/core/workflow/specialists.js +2 -2
  256. package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
  257. package/dist/core/workflow/transcript.d.ts.map +1 -0
  258. package/dist/core/workflow/{journal.js → transcript.js} +14 -14
  259. package/dist/core/workflow/types.d.ts +98 -51
  260. package/dist/core/workflow/types.d.ts.map +1 -1
  261. package/dist/core/workflow/types.js +11 -1
  262. package/dist/core/workflow/validate.d.ts +1 -1
  263. package/dist/core/workflow/validate.js +1 -1
  264. package/dist/core/workflow/workspaces/cancellation.js +2 -2
  265. package/dist/core/workflow/workspaces/cleanup.js +2 -2
  266. package/dist/core/workflow/workspaces/execution.js +6 -6
  267. package/dist/core/workflow/workspaces/inspect.js +9 -9
  268. package/dist/core/workflow/workspaces/integration.js +14 -14
  269. package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
  270. package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
  271. package/dist/core/workflow/workspaces/lifecycle.js +9 -9
  272. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  273. package/dist/core/workflow/workspaces/local-git.js +39 -5
  274. package/dist/core/workflow/workspaces/recovery.js +3 -3
  275. package/dist/core/workflow/workspaces/state.d.ts +1 -1
  276. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  277. package/dist/core/workflow/workspaces/state.js +3 -3
  278. package/dist/core/workflow/workspaces/types.d.ts +1 -1
  279. package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
  280. package/dist/lib/browser/asserts.d.ts +28 -0
  281. package/dist/lib/browser/asserts.d.ts.map +1 -0
  282. package/dist/lib/browser/asserts.js +152 -0
  283. package/dist/lib/browser/client.d.ts +49 -0
  284. package/dist/lib/browser/client.d.ts.map +1 -1
  285. package/dist/lib/browser/client.js +155 -14
  286. package/dist/lib/browser/content-checks.d.ts +108 -0
  287. package/dist/lib/browser/content-checks.d.ts.map +1 -0
  288. package/dist/lib/browser/content-checks.js +474 -0
  289. package/dist/lib/browser/critique.d.ts +91 -0
  290. package/dist/lib/browser/critique.d.ts.map +1 -0
  291. package/dist/lib/browser/critique.js +167 -0
  292. package/dist/lib/browser/geometry.d.ts +32 -0
  293. package/dist/lib/browser/geometry.d.ts.map +1 -1
  294. package/dist/lib/browser/geometry.js +244 -1
  295. package/dist/lib/browser/index.d.ts +4 -1
  296. package/dist/lib/browser/index.d.ts.map +1 -1
  297. package/dist/lib/browser/index.js +2 -0
  298. package/dist/lib/claude-desktop.d.ts.map +1 -1
  299. package/dist/lib/claude-desktop.js +4 -1
  300. package/dist/lib/council/index.d.ts +6 -6
  301. package/dist/lib/council/index.d.ts.map +1 -1
  302. package/dist/lib/council/index.js +8 -8
  303. package/dist/lib/identities/assume.d.ts +2 -2
  304. package/dist/lib/identities/assume.js +7 -7
  305. package/dist/lib/instructions/apply.d.ts +3 -3
  306. package/dist/lib/instructions/apply.d.ts.map +1 -1
  307. package/dist/lib/instructions/apply.js +75 -27
  308. package/dist/lib/instructions/host-addendum.d.ts +54 -0
  309. package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
  310. package/dist/lib/instructions/host-addendum.js +107 -0
  311. package/dist/lib/instructions/templates.d.ts +10 -3
  312. package/dist/lib/instructions/templates.d.ts.map +1 -1
  313. package/dist/lib/instructions/templates.js +41 -12
  314. package/dist/lib/machine.d.ts +2 -2
  315. package/dist/lib/machine.js +2 -2
  316. package/package.json +26 -21
  317. package/schemas/config.schema.json +26 -1
  318. package/src/commander.ts +22 -13
  319. package/src/commands/{harness.ts → adapter.ts} +46 -46
  320. package/src/commands/agents.ts +71 -64
  321. package/src/commands/artifacts.ts +168 -0
  322. package/src/commands/backup.ts +47 -33
  323. package/src/commands/browse.ts +438 -2
  324. package/src/commands/checkpoint.ts +196 -0
  325. package/src/commands/context.ts +3 -176
  326. package/src/commands/decision.ts +41 -7
  327. package/src/commands/deinit.ts +16 -16
  328. package/src/commands/doctor.ts +28 -28
  329. package/src/commands/{supervisor.ts → governor.ts} +184 -154
  330. package/src/commands/init.ts +50 -34
  331. package/src/commands/{scratch.ts → journal.ts} +60 -60
  332. package/src/commands/policy.ts +1 -1
  333. package/src/commands/presence.ts +2 -2
  334. package/src/commands/sync.ts +3 -3
  335. package/src/commands/tunnel.ts +237 -34
  336. package/src/commands/web-fetch.ts +1 -1
  337. package/src/commands/web.ts +56 -1
  338. package/src/commands/work.ts +50 -35
  339. package/src/commands/workflow.ts +43 -34
  340. package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
  341. package/src/core/{harnesses → adapters}/attest.ts +27 -27
  342. package/src/core/{harnesses → adapters}/attestation.ts +41 -41
  343. package/src/core/{harnesses → adapters}/bench.ts +34 -43
  344. package/src/core/adapters/index.ts +56 -0
  345. package/src/core/{harnesses → adapters}/profiles.ts +16 -16
  346. package/src/core/{harnesses → adapters}/registry.ts +23 -23
  347. package/src/core/{harnesses → adapters}/types.ts +23 -23
  348. package/src/core/agents/canonical-emit.ts +6 -6
  349. package/src/core/agents/cli-emit.ts +1 -1
  350. package/src/core/agents/cli.ts +30 -28
  351. package/src/core/agents/codex-replay.ts +6 -6
  352. package/src/core/agents/coord-client.ts +119 -28
  353. package/src/core/agents/events/consume.ts +1 -1
  354. package/src/core/agents/events/emit.ts +7 -6
  355. package/src/core/agents/render/prompt-context.ts +107 -31
  356. package/src/core/agents/render/session-context.ts +6 -6
  357. package/src/core/agents/rules/claim-conflict.ts +4 -7
  358. package/src/core/agents/rules/commit-conflict.ts +103 -22
  359. package/src/core/agents/rules/stop-hook.ts +33 -17
  360. package/src/core/agents/rules/verdict.ts +13 -0
  361. package/src/core/agents/session-events.ts +4 -4
  362. package/src/core/agents/state/council.ts +1 -1
  363. package/src/core/agents/state/heartbeat-projector.ts +16 -12
  364. package/src/core/agents/state/heartbeat-writer.ts +12 -12
  365. package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
  366. package/src/core/agents/state/pidmap.ts +130 -15
  367. package/src/core/agents/state/proc-start.ts +208 -0
  368. package/src/core/agents/state/shell-mutation.ts +1 -1
  369. package/src/core/agents/state/stale-sweep.ts +7 -7
  370. package/src/core/artifacts/index.ts +557 -0
  371. package/src/core/config.ts +30 -2
  372. package/src/core/context/index.ts +15 -15
  373. package/src/core/governor/adapter-spread.ts +82 -0
  374. package/src/core/governor/index.ts +91 -0
  375. package/src/core/governor/plan-api.ts +115 -0
  376. package/src/core/{supervisor → governor}/plan-read.ts +112 -97
  377. package/src/core/{supervisor → governor}/plan-types.ts +59 -56
  378. package/src/core/{supervisor → governor}/planning.ts +223 -192
  379. package/src/core/governor/read.ts +59 -0
  380. package/src/core/{supervisor → governor}/runner.ts +39 -39
  381. package/src/core/{supervisor → governor}/service-read.ts +22 -22
  382. package/src/core/{supervisor → governor}/service.ts +117 -119
  383. package/src/core/{supervisor → governor}/state.ts +325 -167
  384. package/src/core/hooks/adapter/detect.ts +30 -0
  385. package/src/core/hooks/{harness → adapter}/events.ts +16 -14
  386. package/src/core/hooks/{harness → adapter}/output.ts +31 -23
  387. package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
  388. package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
  389. package/src/core/hooks/cli.ts +185 -162
  390. package/src/core/hooks/effects/image-capture.ts +6 -6
  391. package/src/core/hooks/effects/index.ts +17 -17
  392. package/src/core/hooks/events/emit.ts +3 -3
  393. package/src/core/hooks/events/rotate.ts +1 -1
  394. package/src/core/hooks/events/schema.ts +15 -7
  395. package/src/core/hooks/guard-path.ts +2 -2
  396. package/src/core/hooks/index.ts +1 -1
  397. package/src/core/hooks/resolve/anchor.ts +63 -8
  398. package/src/core/hooks/resolve/coord-root.ts +4 -4
  399. package/src/core/hooks/resolve/intent.ts +1 -1
  400. package/src/core/hooks/resolve/owner.ts +11 -5
  401. package/src/core/{scratch → journal}/index.ts +53 -53
  402. package/src/core/policy/evaluate.ts +8 -8
  403. package/src/core/policy/types.ts +3 -3
  404. package/src/core/work/state.ts +65 -23
  405. package/src/core/workflow/adapters.ts +33 -0
  406. package/src/core/workflow/approvals.ts +7 -7
  407. package/src/core/workflow/billing.ts +12 -12
  408. package/src/core/workflow/child-env.ts +13 -4
  409. package/src/core/workflow/engine.ts +155 -90
  410. package/src/core/workflow/evidence-preflight.ts +165 -0
  411. package/src/core/workflow/index.ts +3 -3
  412. package/src/core/workflow/proof.ts +60 -50
  413. package/src/core/workflow/run-state.ts +7 -7
  414. package/src/core/workflow/sandbox-projection.ts +16 -16
  415. package/src/core/workflow/spawn-claude.ts +13 -10
  416. package/src/core/workflow/spawn-codex.ts +13 -10
  417. package/src/core/workflow/spawn-cursor.ts +13 -10
  418. package/src/core/workflow/spawn-failure.ts +1 -1
  419. package/src/core/workflow/specialists.ts +2 -2
  420. package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
  421. package/src/core/workflow/types.ts +108 -51
  422. package/src/core/workflow/validate.ts +1 -1
  423. package/src/core/workflow/workspaces/cancellation.ts +2 -2
  424. package/src/core/workflow/workspaces/cleanup.ts +2 -2
  425. package/src/core/workflow/workspaces/execution.ts +6 -6
  426. package/src/core/workflow/workspaces/inspect.ts +14 -11
  427. package/src/core/workflow/workspaces/integration.ts +18 -14
  428. package/src/core/workflow/workspaces/lifecycle.ts +10 -10
  429. package/src/core/workflow/workspaces/local-git.ts +47 -5
  430. package/src/core/workflow/workspaces/recovery.ts +3 -3
  431. package/src/core/workflow/workspaces/state.ts +4 -3
  432. package/src/core/workflow/workspaces/types.ts +1 -1
  433. package/src/lib/browser/asserts.ts +177 -0
  434. package/src/lib/browser/client.ts +189 -14
  435. package/src/lib/browser/content-checks.ts +571 -0
  436. package/src/lib/browser/critique.ts +231 -0
  437. package/src/lib/browser/geometry.ts +297 -1
  438. package/src/lib/browser/index.ts +34 -0
  439. package/src/lib/claude-desktop.ts +4 -1
  440. package/src/lib/council/index.ts +9 -9
  441. package/src/lib/identities/assume.ts +7 -7
  442. package/src/lib/instructions/apply.ts +87 -25
  443. package/src/lib/instructions/host-addendum.ts +126 -0
  444. package/src/lib/instructions/templates.ts +44 -15
  445. package/src/lib/machine.ts +2 -2
  446. package/dist/commands/harness.d.ts +0 -12
  447. package/dist/commands/harness.d.ts.map +0 -1
  448. package/dist/commands/scratch.d.ts +0 -4
  449. package/dist/commands/scratch.d.ts.map +0 -1
  450. package/dist/commands/session.d.ts.map +0 -1
  451. package/dist/commands/session.js +0 -162
  452. package/dist/commands/supervisor.d.ts +0 -4
  453. package/dist/commands/supervisor.d.ts.map +0 -1
  454. package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
  455. package/dist/core/harnesses/attest.d.ts.map +0 -1
  456. package/dist/core/harnesses/attestation.d.ts.map +0 -1
  457. package/dist/core/harnesses/bench.d.ts.map +0 -1
  458. package/dist/core/harnesses/index.d.ts +0 -13
  459. package/dist/core/harnesses/index.d.ts.map +0 -1
  460. package/dist/core/harnesses/index.js +0 -6
  461. package/dist/core/harnesses/profiles.d.ts.map +0 -1
  462. package/dist/core/harnesses/registry.d.ts +0 -15
  463. package/dist/core/harnesses/registry.d.ts.map +0 -1
  464. package/dist/core/harnesses/types.d.ts.map +0 -1
  465. package/dist/core/hooks/harness/detect.d.ts +0 -9
  466. package/dist/core/hooks/harness/events.d.ts.map +0 -1
  467. package/dist/core/hooks/harness/output.d.ts.map +0 -1
  468. package/dist/core/supervisor/index.d.ts +0 -7
  469. package/dist/core/supervisor/index.d.ts.map +0 -1
  470. package/dist/core/supervisor/index.js +0 -6
  471. package/dist/core/supervisor/plan-api.d.ts +0 -23
  472. package/dist/core/supervisor/plan-api.d.ts.map +0 -1
  473. package/dist/core/supervisor/plan-api.js +0 -50
  474. package/dist/core/supervisor/plan-read.d.ts +0 -5
  475. package/dist/core/supervisor/plan-read.d.ts.map +0 -1
  476. package/dist/core/supervisor/plan-types.d.ts.map +0 -1
  477. package/dist/core/supervisor/planning.d.ts +0 -33
  478. package/dist/core/supervisor/planning.d.ts.map +0 -1
  479. package/dist/core/supervisor/read.d.ts +0 -6
  480. package/dist/core/supervisor/read.d.ts.map +0 -1
  481. package/dist/core/supervisor/read.js +0 -4
  482. package/dist/core/supervisor/runner.d.ts +0 -37
  483. package/dist/core/supervisor/runner.d.ts.map +0 -1
  484. package/dist/core/supervisor/service-read.d.ts +0 -6
  485. package/dist/core/supervisor/service-read.d.ts.map +0 -1
  486. package/dist/core/supervisor/service.d.ts +0 -114
  487. package/dist/core/supervisor/service.d.ts.map +0 -1
  488. package/dist/core/supervisor/state.d.ts +0 -95
  489. package/dist/core/supervisor/state.d.ts.map +0 -1
  490. package/dist/core/workflow/harnesses.d.ts +0 -17
  491. package/dist/core/workflow/harnesses.d.ts.map +0 -1
  492. package/dist/core/workflow/harnesses.js +0 -21
  493. package/dist/core/workflow/journal.d.ts.map +0 -1
  494. package/src/commands/session.ts +0 -187
  495. package/src/core/harnesses/index.ts +0 -56
  496. package/src/core/hooks/harness/detect.ts +0 -30
  497. package/src/core/supervisor/index.ts +0 -89
  498. package/src/core/supervisor/plan-api.ts +0 -73
  499. package/src/core/supervisor/read.ts +0 -59
  500. package/src/core/workflow/harnesses.ts +0 -33
  501. /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
@@ -1,19 +1,25 @@
1
1
  /**
2
2
  * UserPromptSubmit UX renderer. Combines the peer-refresh dedup, the
3
- * council-pending hash-dedup, and the Cursor set-task staleness nudge.
3
+ * council-pending hash-dedup, the cross-adapter first-session naming nudge,
4
+ * the Cursor/Codex set-task staleness nudge, and the Codex status-footer
5
+ * reminder.
4
6
  * agent-hook's user_prompt.submit post-emit handler calls this
5
- * and forwards the result as the harness-shaped additionalContext payload.
7
+ * and forwards the result as the adapter-shaped additionalContext payload.
6
8
  *
7
- * Three hash-dedup'd subsections combined into one additionalContext payload:
9
+ * Four subsections combined into one additionalContext payload:
8
10
  * 1. Peer table: semantically-relevant peer fields hashed; only re-emits
9
11
  * when peers change (name + instance_id + session_id + kind + started_at
10
12
  * + sorted(files_touched) + platform, sorted by instance_id).
11
13
  * 2. Council pending: pending open-council IDs hashed; re-emits when the
12
14
  * ID set changes.
13
- * 3. Task staleness nudge (cursor only, since CC enforces via the Stop hook
14
- * transcript scan). Fires when `task` is null or `task_updated_at` is
15
- * older than HARNERY_TASK_STALE_SECONDS (default 1800 = 30 min). Hash-deduped
16
- * against the previous nudge state.
15
+ * 3. Focus nudge. Before the first set-task in a human-facing session, tells
16
+ * every adapter to reproduce set-task's suggested session name in a fenced
17
+ * code block. Cursor/Codex additionally get the existing unset/stale-task
18
+ * reminder because their Stop hooks do not enforce task declarations as
19
+ * reliably as Claude Code's.
20
+ * 4. Codex status footer: a non-deduplicated reminder to put the live status
21
+ * box after the substantive answer. Stop stays observe-only, so missing the
22
+ * footer can never trigger a replacement response.
17
23
  *
18
24
  * Hash files live at:
19
25
  * .harnery/.last-peer-hash.<instance_id>
@@ -36,6 +42,7 @@ import {
36
42
  import { join } from "node:path";
37
43
 
38
44
  import { coordEnv } from "../../../lib/env.ts";
45
+ import { resolveBinName } from "../../config.ts";
39
46
  import { type RemoteMachine, readRemoteMachines } from "../../presence/index.ts";
40
47
  import { formatPendingCouncils } from "./session-context.ts";
41
48
 
@@ -51,7 +58,9 @@ interface HeartbeatRow {
51
58
  files_touched?: string[];
52
59
  platform?: string;
53
60
  task?: string;
61
+ task_updated_at?: string;
54
62
  turn_summary?: string;
63
+ workflow_run_id?: string;
55
64
  }
56
65
 
57
66
  export interface PromptContextOpts {
@@ -59,9 +68,15 @@ export interface PromptContextOpts {
59
68
  instanceId: string;
60
69
  sessionId: string;
61
70
  agentName?: string;
62
- /** When true, run the task-staleness nudge check (cursor only; CC has the
63
- * Stop-hook transcript-scan enforcement). */
71
+ /** When true, remind a human-facing session to print the session name before
72
+ * its first set-task. UserPromptSubmit enables this for every adapter. */
73
+ sessionNameNudge?: boolean;
74
+ /** When true, run the unset/stale-task check after the first set-task.
75
+ * Cursor/Codex use this; Claude Code has Stop-hook transcript enforcement. */
64
76
  taskNudge?: boolean;
77
+ /** When true, append the non-deduplicated Codex status-footer reminder.
78
+ * The caller enables this only for human-facing Codex sessions. */
79
+ statusFooterNudge?: boolean;
65
80
  }
66
81
 
67
82
  /**
@@ -73,7 +88,15 @@ export interface PromptContextOpts {
73
88
  * councils are pending, matching the bash behavior).
74
89
  */
75
90
  export function renderPromptContext(opts: PromptContextOpts): string {
76
- const { coordRoot, instanceId, sessionId, agentName, taskNudge } = opts;
91
+ const {
92
+ coordRoot,
93
+ instanceId,
94
+ sessionId,
95
+ agentName,
96
+ sessionNameNudge,
97
+ taskNudge,
98
+ statusFooterNudge,
99
+ } = opts;
77
100
  const sections: string[] = [];
78
101
 
79
102
  // 1. Peer table with hash dedup.
@@ -86,62 +109,107 @@ export function renderPromptContext(opts: PromptContextOpts): string {
86
109
  if (councilMsg) sections.push(councilMsg);
87
110
  }
88
111
 
89
- // 3. Task staleness nudge (cursor only).
90
- if (taskNudge) {
91
- const nudgeMsg = computeTaskNudgeIfChanged(coordRoot, instanceId);
112
+ // 3. First-session naming for every adapter, plus unset/stale-task reminders
113
+ // for Cursor/Codex. One state machine avoids a second generic "task unset"
114
+ // reminder immediately after the first-session reminder has been deduped.
115
+ if (sessionNameNudge || taskNudge) {
116
+ const nudgeMsg = computeFocusNudgeIfChanged(coordRoot, instanceId, {
117
+ sessionNameNudge: sessionNameNudge ?? false,
118
+ taskNudge: taskNudge ?? false,
119
+ });
92
120
  if (nudgeMsg) sections.push(nudgeMsg);
93
121
  }
94
122
 
123
+ // 4. Codex gets this on every prompt. It is intentionally not hash-deduped:
124
+ // the reminder must be fresh in the model's context when it writes the reply.
125
+ // Missing it is harmless because Codex Stop enforcement remains observe-only.
126
+ if (statusFooterNudge) {
127
+ const statusMsg = renderStatusFooterReminder(coordRoot, instanceId);
128
+ if (statusMsg) sections.push(statusMsg);
129
+ }
130
+
95
131
  return sections.join("\n\n");
96
132
  }
97
133
 
98
- /** The set-task staleness nudge for Cursor.
99
- * Emits a one-line reminder when `task` is null or `task_updated_at` is older
100
- * than HARNERY_TASK_STALE_SECONDS (default 1800). Hash-deduped against previous
101
- * nudge state. */
102
- function computeTaskNudgeIfChanged(coordRoot: string, selfInstanceId: string): string {
134
+ function renderStatusFooterReminder(coordRoot: string, selfInstanceId: string): string {
135
+ const hb = readHeartbeat(join(coordRoot, ".harnery", "active", `${selfInstanceId}.json`));
136
+ if (!hb || hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id) return "";
137
+
138
+ const bin = resolveBinName(coordRoot);
139
+ return (
140
+ "Codex status footer: complete the user's request first. " +
141
+ `Then run \`${bin} agents status\` as your final shell call and append its stdout verbatim in a fenced code block at the bottom of the same substantive reply. ` +
142
+ "Keep the answer intact. If the footer is missed, do not retry or replace the reply; the Stop hook is observe-only."
143
+ );
144
+ }
145
+
146
+ /**
147
+ * First-session naming for every adapter, followed by Cursor/Codex task
148
+ * staleness checks. The absence of `task_updated_at` is the same invariant
149
+ * `agents set-task` uses for `first_of_session`; clears still stamp the field.
150
+ */
151
+ function computeFocusNudgeIfChanged(
152
+ coordRoot: string,
153
+ selfInstanceId: string,
154
+ opts: { sessionNameNudge: boolean; taskNudge: boolean },
155
+ ): string {
103
156
  const hbPath = join(coordRoot, ".harnery", "active", `${selfInstanceId}.json`);
104
157
  if (!existsSync(hbPath)) return "";
105
- let hb: { task?: string; task_updated_at?: string };
158
+ let hb: HeartbeatRow;
106
159
  try {
107
160
  hb = JSON.parse(readFileSync(hbPath, "utf8"));
108
161
  } catch {
109
162
  return "";
110
163
  }
111
164
 
165
+ const hashFile = join(coordRoot, ".harnery", `.last-task-nudge-hash.${selfInstanceId}`);
166
+ // Subagents and workflow children have no human-owned session/tab to rename.
167
+ if (hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id) {
168
+ clearHashFile(hashFile);
169
+ return "";
170
+ }
171
+
172
+ const bin = resolveBinName(coordRoot);
112
173
  const threshold = Number.parseInt(coordEnv("TASK_STALE_SECONDS") ?? "1800", 10);
113
174
  const taskValue = hb.task ?? "";
114
175
  let needsNudge = false;
115
176
  let message = "";
177
+ let nudgeKind = "";
116
178
 
117
- if (!taskValue) {
179
+ if (opts.sessionNameNudge && !hb.task_updated_at) {
180
+ needsNudge = true;
181
+ nudgeKind = "session-name";
182
+ message =
183
+ `New session: run \`${bin} agents set-task "<2-5 word session topic>"\` as your first tool call. ` +
184
+ "When it returns `first_of_session: true`, reproduce its `suggested_session_name` value by itself inside a fenced code block at the very top of your reply so the operator can one-click-copy it as the session/tab title. " +
185
+ "Then continue with the task; that first `set-task` also satisfies this turn's focus declaration.";
186
+ } else if (opts.taskNudge && !taskValue) {
118
187
  needsNudge = true;
188
+ nudgeKind = "task-unset";
119
189
  message =
120
- "Heads up: your `task` field is unset. Run `agents set-task \"<short focus>\"` so peers + the coord dashboard can see what you're working on. (Cursor sessions can't enforce this from the Stop hook the way Claude Code does, so this is a one-time soft reminder per staleness state.)";
121
- } else if (hb.task_updated_at) {
190
+ `Heads up: your \`task\` field is unset. Run \`${bin} agents set-task "<short focus>"\` so peers + the coord dashboard can see what you're working on. ` +
191
+ "(This adapter cannot enforce the declaration from its Stop hook as reliably as Claude Code, so this is a one-time soft reminder per staleness state.)";
192
+ } else if (opts.taskNudge && hb.task_updated_at) {
122
193
  const updatedSec = Math.floor(Date.parse(hb.task_updated_at) / 1000);
123
194
  const nowSec = Math.floor(Date.now() / 1000);
124
195
  if (Number.isFinite(updatedSec) && updatedSec > 0) {
125
196
  const ageSec = nowSec - updatedSec;
126
197
  if (ageSec > threshold) {
127
198
  needsNudge = true;
128
- message = `Heads up: your \`task\` field hasn't changed in ${ageSec}s (threshold ${threshold}s). If you've moved on from "${taskValue.slice(0, 60)}", update via \`agents set-task "<new focus>"\`. Pass an empty string to clear.`;
199
+ nudgeKind = "task-stale";
200
+ message = `Heads up: your \`task\` field hasn't changed in ${ageSec}s (threshold ${threshold}s). If you've moved on from "${taskValue.slice(0, 60)}", update via \`${bin} agents set-task "<new focus>"\`. Pass an empty string to clear.`;
129
201
  }
130
202
  }
131
203
  }
132
204
 
133
- const hashFile = join(coordRoot, ".harnery", `.last-task-nudge-hash.${selfInstanceId}`);
134
205
  if (!needsNudge) {
135
- try {
136
- if (existsSync(hashFile)) rmSync(hashFile, { force: true });
137
- } catch {
138
- /* swallow */
139
- }
206
+ clearHashFile(hashFile);
140
207
  return "";
141
208
  }
142
209
 
143
- // Dedup on a state-hash (task value + threshold) so same-state turns don't re-nudge.
144
- const state = `${taskValue}|stale=1|threshold=${threshold}`;
210
+ // Dedup on kind + task state so the first-session reminder does not turn into
211
+ // a generic "task unset" reminder on the next prompt before set-task runs.
212
+ const state = `${nudgeKind}|task=${taskValue}|updated=${hb.task_updated_at ?? ""}|threshold=${threshold}`;
145
213
  const newHash = sha256Hex16(state);
146
214
  const oldHash = safeRead(hashFile);
147
215
  if (oldHash && oldHash === newHash) return "";
@@ -325,6 +393,14 @@ function safeRead(path: string): string {
325
393
  }
326
394
  }
327
395
 
396
+ function clearHashFile(path: string): void {
397
+ try {
398
+ if (existsSync(path)) rmSync(path, { force: true });
399
+ } catch {
400
+ /* swallow */
401
+ }
402
+ }
403
+
328
404
  function sha256Hex16(input: string): string {
329
405
  return createHash("sha256").update(input).digest("hex").slice(0, 16);
330
406
  }
@@ -11,7 +11,7 @@ import { spawnSync } from "node:child_process";
11
11
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
12
12
  import { join } from "node:path";
13
13
  import { resolveBinName, resolveHooksSetupHint } from "../../config.ts";
14
- import { harneryVersion, loadHarnessWiring } from "../../hooks/harness/wiring.ts";
14
+ import { harneryVersion, loadAdapterWiring } from "../../hooks/adapter/wiring.ts";
15
15
  import { readRemoteMachines } from "../../presence/index.ts";
16
16
 
17
17
  interface HeartbeatRow {
@@ -34,7 +34,7 @@ export interface RenderOpts {
34
34
  instanceId: string;
35
35
  sessionId: string;
36
36
  agentName?: string;
37
- /** Harness label rendered in parens after the self-name (e.g. "Cursor", "Codex").
37
+ /** Adapter label rendered in parens after the self-name (e.g. "Cursor", "Codex").
38
38
  * Claude Code omits it. */
39
39
  platformLabel?: string;
40
40
  }
@@ -84,11 +84,11 @@ export function renderSessionContext(opts: RenderOpts): string {
84
84
  messages.push(wiringSummary);
85
85
  }
86
86
 
87
- // 5. Harness-hook drift: a harnery upgrade changed the hook set, but this
88
- // project's settings file hasn't been re-wired. Only fires for a harness the
87
+ // 5. Adapter-hook drift: a harnery upgrade changed the hook set, but this
88
+ // project's settings file hasn't been re-wired. Only fires for a adapter the
89
89
  // project already opted into (≥1 hook wired), so it never nags a project that
90
90
  // simply has a settings file. Remedy is always `<bin> init` (idempotent).
91
- const drift = loadHarnessWiring(coordRoot);
91
+ const drift = loadAdapterWiring(coordRoot);
92
92
  if (drift.length > 0) {
93
93
  const bin = resolveBinName(coordRoot);
94
94
  const ver = harneryVersion();
@@ -101,7 +101,7 @@ export function renderSessionContext(opts: RenderOpts): string {
101
101
  return ` - ${d.settingsFile} — ${bits.join("; ")}`;
102
102
  });
103
103
  messages.push(
104
- `Harnery hook wiring is out of date${verPart}: an upgrade changed the hook set but the harness ` +
104
+ `Harnery hook wiring is out of date${verPart}: an upgrade changed the hook set but the adapter ` +
105
105
  `settings file hasn't been re-wired, so the new hook(s) won't fire. Run \`${bin} init\` to wire them ` +
106
106
  `(idempotent, additive).\n${lines.join("\n")}`,
107
107
  );
@@ -21,12 +21,9 @@ import { emit } from "../events/emit.ts";
21
21
 
22
22
  const FRESHNESS_SECS = 600;
23
23
 
24
- export type VerdictResult = {
25
- allow: boolean;
26
- exit_code: 0 | 2;
27
- rule: string;
28
- reason?: string;
29
- };
24
+ export type { VerdictResult } from "./verdict.ts";
25
+
26
+ import type { VerdictResult } from "./verdict.ts";
30
27
 
31
28
  interface PeerView {
32
29
  instance_id: string;
@@ -329,7 +326,7 @@ function pruneClaimFromPeer(coordRoot: string, instanceId: string, relPath: stri
329
326
  event_type: "claim.release",
330
327
  instance_id: instanceId,
331
328
  session_id: (body.session_id as string | undefined) ?? instanceId,
332
- harness: platform === "cursor" ? "cursor" : platform === "codex" ? "codex" : "claude-code",
329
+ adapter: platform === "cursor" ? "cursor" : platform === "codex" ? "codex" : "claude-code",
333
330
  source: "agent-coord",
334
331
  data: { path: target, reason: "heal" },
335
332
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -21,8 +21,8 @@
21
21
  */
22
22
 
23
23
  import { spawnSync } from "node:child_process";
24
- import { existsSync, readdirSync, readFileSync } from "node:fs";
25
- import { join } from "node:path";
24
+ import { appendFileSync, existsSync, readdirSync, readFileSync } from "node:fs";
25
+ import { basename, dirname, join } from "node:path";
26
26
  import { coordFreshnessSeconds } from "../../config.ts";
27
27
  import { instanceHasLivePid } from "../state/pidmap.ts";
28
28
 
@@ -198,12 +198,38 @@ function findOverlap(
198
198
  return null;
199
199
  }
200
200
 
201
+ /**
202
+ * Repository-discovery environment that git exports to its own hooks.
203
+ *
204
+ * A hook runs with GIT_DIR (and friends) already pointing at the repository
205
+ * being committed, and every child inherits them. That silently outranks `cwd`,
206
+ * so a probe launched from some other directory still interrogates the hook's
207
+ * repository. Committing inside a submodule therefore asked the submodule about
208
+ * superproject paths and vice versa, and the answers were wrong in whichever
209
+ * direction did not match. Scrubbing these lets git rediscover the repository
210
+ * from `cwd`, which is the whole point of choosing a `cwd` per path.
211
+ */
212
+ const GIT_DISCOVERY_VARS = [
213
+ "GIT_DIR",
214
+ "GIT_WORK_TREE",
215
+ "GIT_COMMON_DIR",
216
+ "GIT_INDEX_FILE",
217
+ "GIT_OBJECT_DIRECTORY",
218
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
219
+ "GIT_PREFIX",
220
+ "GIT_NAMESPACE",
221
+ ] as const;
222
+
223
+ /** Run a read-only git probe in `cwd`, free of the caller's repo environment. */
224
+ function gitProbe(args: string[], cwd: string): { status: number; stdout: string } {
225
+ const env = { ...process.env };
226
+ for (const v of GIT_DISCOVERY_VARS) delete env[v];
227
+ const result = spawnSync("git", args, { cwd, env, encoding: "utf8", timeout: 2000 });
228
+ return { status: result.status ?? 1, stdout: result.stdout ?? "" };
229
+ }
230
+
201
231
  function isGitlinkInIndex(coordRoot: string, path: string): boolean {
202
- const result = spawnSync("git", ["ls-files", "--stage", "--", path], {
203
- cwd: coordRoot,
204
- encoding: "utf8",
205
- timeout: 2000,
206
- });
232
+ const result = gitProbe(["ls-files", "--stage", "--", path], coordRoot);
207
233
  if (result.status !== 0) return false;
208
234
  // ls-files --stage emits "<mode> <sha> <stage>\t<path>"; mode 160000 = gitlink.
209
235
  return result.stdout.trim().startsWith("160000 ");
@@ -216,21 +242,73 @@ function isHolderSelfAttributed(
216
242
  peers: readonly PeerHeartbeat[],
217
243
  ): boolean {
218
244
  // Gate B (cheaper): live foreign pid-map entry blocks self-attribution.
219
- if (holderHasLiveForeignPid(coordRoot, holderId)) return false;
245
+ if (holderHasLiveForeignPid(coordRoot, holderId)) {
246
+ debugGate(coordRoot, { holder: holderId, gate: "B", reason: "live_pid_anchor" });
247
+ return false;
248
+ }
220
249
 
221
250
  // Gate A: every held path is either in the staged set or already clean in HEAD.
222
251
  const holder = peers.find((p) => p.instance_id === holderId);
223
- if (!holder) return false;
252
+ if (!holder) {
253
+ debugGate(coordRoot, { holder: holderId, gate: "A", reason: "holder_not_found" });
254
+ return false;
255
+ }
224
256
  const files = holder.files_touched ?? [];
225
- if (files.length === 0) return false;
257
+ if (files.length === 0) {
258
+ debugGate(coordRoot, { holder: holderId, gate: "A", reason: "holder_holds_nothing" });
259
+ return false;
260
+ }
226
261
  for (const held of files) {
227
262
  if (stagedSet.has(held)) continue;
228
263
  if (isPathCleanInHead(coordRoot, held)) continue;
264
+ if (isPathIgnoredByGit(coordRoot, held)) continue;
265
+ debugGate(coordRoot, { holder: holderId, gate: "A", reason: "path_unverifiable", path: held });
229
266
  return false;
230
267
  }
231
268
  return true;
232
269
  }
233
270
 
271
+ /**
272
+ * Append one line explaining a refused gate, when HARNERY_COORD_DEBUG is set.
273
+ *
274
+ * Worth carrying permanently: the deciding state lives inside a git hook, whose
275
+ * stderr the caller discards and whose process is gone before anyone can look.
276
+ * Reconstructing a refusal afterwards means guessing, because the holder's claim
277
+ * set has moved on by then. This turns that into evidence.
278
+ */
279
+ function debugGate(coordRoot: string, detail: Record<string, string>): void {
280
+ if (!process.env.HARNERY_COORD_DEBUG) return;
281
+ try {
282
+ appendFileSync(
283
+ join(coordRoot, ".harnery", "coord-debug.ndjson"),
284
+ `${JSON.stringify({ event: "self_attribution_refused", pid: process.pid, ...detail })}\n`,
285
+ "utf8",
286
+ );
287
+ } catch {
288
+ /* diagnostics never break a verdict */
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Does git ignore this path?
294
+ *
295
+ * Such a claim is safe to look past. Agents write journal files into ignored
296
+ * directories constantly, and each one used to leave a claim that Gate A could
297
+ * never vouch for: not staged, not tracked, therefore "unknown", therefore
298
+ * blocked. That defeated self-attribution for the rest of a session and taught
299
+ * agents to reach for the bypass. An ignored path cannot enter anyone's commit,
300
+ * so it cannot be work this commit might clobber. A merely untracked path is a
301
+ * different matter and still counts against the holder, since anybody could add
302
+ * it.
303
+ */
304
+ function isPathIgnoredByGit(coordRoot: string, relPath: string): boolean {
305
+ const abs = join(coordRoot, relPath);
306
+ const cwd = dirname(abs);
307
+ if (!existsSync(cwd)) return false;
308
+ // check-ignore exits 0 when the path is ignored, 1 when it is not.
309
+ return gitProbe(["check-ignore", "--quiet", "--", basename(abs)], cwd).status === 0;
310
+ }
311
+
234
312
  function holderHasLiveForeignPid(coordRoot: string, holderId: string): boolean {
235
313
  return instanceHasLivePid(coordRoot, holderId);
236
314
  }
@@ -238,18 +316,21 @@ function holderHasLiveForeignPid(coordRoot: string, holderId: string): boolean {
238
316
  function isPathCleanInHead(coordRoot: string, relPath: string): boolean {
239
317
  // Path is tracked + diff-clean against HEAD = "already committed, holder
240
318
  // hasn't released the claim yet". Counts as self-attributable.
241
- const tracked = spawnSync("git", ["ls-files", "--error-unmatch", "--", relPath], {
242
- cwd: coordRoot,
243
- encoding: "utf8",
244
- timeout: 2000,
245
- });
246
- if (tracked.status !== 0) return false;
247
- const diff = spawnSync("git", ["diff", "--quiet", "HEAD", "--", relPath], {
248
- cwd: coordRoot,
249
- encoding: "utf8",
250
- timeout: 2000,
251
- });
252
- return diff.status === 0;
319
+ //
320
+ // Ask the repository that actually tracks the path, by running git from the
321
+ // file's own directory instead of the coord root. Claims are recorded
322
+ // monorepo-relative, so a submodule file arrives here as `sub/src/x.ts`, and
323
+ // the superproject does not track submodule contents: asking it always
324
+ // answered "not tracked". Gate A therefore failed for every submodule path,
325
+ // self-attribution never suppressed a submodule commit, and the operator paid
326
+ // a HARNERY_AGENT_COORD_BYPASS=1 per commit. Running from the file's directory
327
+ // lands in the submodule's own repo and handles any nesting depth.
328
+ const abs = join(coordRoot, relPath);
329
+ const cwd = dirname(abs);
330
+ const base = basename(abs);
331
+ if (!existsSync(cwd)) return false;
332
+ if (gitProbe(["ls-files", "--error-unmatch", "--", base], cwd).status !== 0) return false;
333
+ return gitProbe(["diff", "--quiet", "HEAD", "--", base], cwd).status === 0;
253
334
  }
254
335
 
255
336
  function readActivePeers(coordRoot: string): PeerHeartbeat[] {
@@ -2,6 +2,10 @@
2
2
  * Stop-hook verdict: rule 1/3, 2/3, 3/3 enforcement evaluated
3
3
  * from the canonical event stream alone (no transcript scan).
4
4
  *
5
+ * Codex is observe-only: its Stop continuation can replace the user-facing
6
+ * answer in clients that retain only the final continuation response.
7
+ *
8
+ * Enforced adapters:
5
9
  * Rule 1/3: `state.status_checked` event with matching turn boundary exists.
6
10
  * Rule 2/3: latest `turn.stop` event has `status_box_present: true` (or the
7
11
  * stop currently firing carries that field via the in-flight event
@@ -17,12 +21,9 @@ import { existsSync, readFileSync } from "node:fs";
17
21
  import { join } from "node:path";
18
22
  import { resolveBinName } from "../../config.ts";
19
23
 
20
- export type VerdictResult = {
21
- allow: boolean;
22
- exit_code: 0 | 2;
23
- rule: string;
24
- reason?: string;
25
- };
24
+ export type { VerdictResult } from "./verdict.ts";
25
+
26
+ import type { VerdictResult } from "./verdict.ts";
26
27
 
27
28
  interface CanonicalEvent {
28
29
  event_id: string;
@@ -31,7 +32,7 @@ interface CanonicalEvent {
31
32
  instance_id: string;
32
33
  session_id: string;
33
34
  turn_id?: string;
34
- harness: string;
35
+ adapter: string;
35
36
  source: string;
36
37
  data: Record<string, unknown>;
37
38
  }
@@ -40,9 +41,9 @@ export interface StopHookRequest {
40
41
  rule: "stop-hook";
41
42
  instance_id: string;
42
43
  session_id?: string;
43
- /** Firing harness. Selects the end-of-turn ack signal (see `ackSignalFor`).
44
+ /** Firing adapter. Selects the end-of-turn ack signal (see `ackSignalFor`).
44
45
  * Undefined → Claude Code semantics (transcript-scanned status box). */
45
- harness?: string;
46
+ adapter?: string;
46
47
  /** Wall-clock cutoff for the current turn; events strictly after this are not yet relevant. */
47
48
  now_ms?: number;
48
49
  /** Override the turn-window discovery (used by tests). */
@@ -51,7 +52,7 @@ export interface StopHookRequest {
51
52
  bypass?: boolean;
52
53
  /** Headless child spawned by `harn workflow` (HARNERY_WORKFLOW_CHILD=1).
53
54
  * The end-of-turn ritual exists to surface status to a HUMAN reader; a
54
- * workflow child reports to the engine's journal instead, so the ritual is
55
+ * workflow child reports to the engine's transcript instead, so the ritual is
55
56
  * meaningless there — worse, blocking burns the child's turn budget on
56
57
  * re-prompts (observed as error_max_turns in the Phase 1 spike). Exempting
57
58
  * here, rather than disabling the child's hooks wholesale, keeps heartbeat +
@@ -61,7 +62,7 @@ export interface StopHookRequest {
61
62
 
62
63
  /**
63
64
  * The end-of-turn "I surfaced my status" signal, detected differently per
64
- * harness because the ritual's *goal* (status visible to the human) is reached
65
+ * adapter because the ritual's *goal* (status visible to the human) is reached
65
66
  * by different means:
66
67
  *
67
68
  * - **Claude Code / Codex** collapse tool calls in the UI, so the human-visible
@@ -78,12 +79,12 @@ export interface StopHookRequest {
78
79
  * This is why the fix is not "relax 2/3 because we can't see it": it's "2/3
79
80
  * and 1/3 are two detections of the same thing, and Cursor's inline UI makes
80
81
  * 1/3 the right one." The enforcement *channel* (exit-2+stderr vs Cursor's
81
- * `followup_message`) is handled separately in hooks/harness/output.ts.
82
+ * `followup_message`) is handled separately in hooks/adapter/output.ts.
82
83
  */
83
84
  type AckSignal = "status_box_present" | "status_checked";
84
85
 
85
- function ackSignalFor(harness?: string): AckSignal {
86
- return harness === "cursor" ? "status_checked" : "status_box_present";
86
+ function ackSignalFor(adapter?: string): AckSignal {
87
+ return adapter === "cursor" ? "status_checked" : "status_box_present";
87
88
  }
88
89
 
89
90
  const RECENT_EVENT_WINDOW_LINES = 5_000;
@@ -112,6 +113,21 @@ export function evaluateStopHook(coordRoot: string, req: StopHookRequest): Verdi
112
113
  };
113
114
  }
114
115
 
116
+ // A blocked Codex Stop does not reject the turn. Codex creates a continuation
117
+ // prompt and asks the model to answer again. Some clients retain only that
118
+ // final continuation response, so a coordination reminder can replace a
119
+ // correct user-facing answer with status output. The Stop path has already
120
+ // emitted turn.stop and projected the coordination state before this verdict
121
+ // runs. Keep that telemetry, but never continue a Codex turn for this ritual.
122
+ if (req.adapter === "codex") {
123
+ return {
124
+ allow: true,
125
+ exit_code: 0,
126
+ rule: "stop-hook.codex_observe_only",
127
+ reason: "Codex Stop continuations must not replace the user-facing answer",
128
+ };
129
+ }
130
+
115
131
  let events: CanonicalEvent[];
116
132
  try {
117
133
  events = readRecentEvents(coordRoot, RECENT_EVENT_WINDOW_LINES);
@@ -160,16 +176,16 @@ export function evaluateStopHook(coordRoot: string, req: StopHookRequest): Verdi
160
176
  const latestTurnStop = [...inTurn].reverse().find((e) => e.event_type === "turn.stop");
161
177
  const boxPresent = latestTurnStop ? Boolean(latestTurnStop.data.status_box_present) : false;
162
178
 
163
- // Harness-aware end-of-turn ack signal (see `ackSignalFor`). On Cursor the
179
+ // Adapter-aware end-of-turn ack signal (see `ackSignalFor`). On Cursor the
164
180
  // ack is `status_checked` (running `harn agents status` shows the box inline);
165
181
  // on Claude Code / Codex it's the transcript-scanned `status_box_present`.
166
182
  // The matching block helper carries the right "how to fix" message.
167
- const ackSignal = ackSignalFor(req.harness);
183
+ const ackSignal = ackSignalFor(req.adapter);
168
184
  const ackPresent = ackSignal === "status_checked" ? statusChecked : boxPresent;
169
185
  const ackBlock = ackSignal === "status_checked" ? rule13Block : rule23Block;
170
186
 
171
187
  // Pure-prose-turn exemption: only the ack signal applies. Parity
172
- // across harnesses: CC requires the box; Cursor requires status_checked.
188
+ // across adapters: CC requires the box; Cursor requires status_checked.
173
189
  if (!toolPreUseInTurn) {
174
190
  if (!ackPresent) {
175
191
  return ackBlock();
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The shape every coordination rule returns.
3
+ *
4
+ * Declared here once. The claim-conflict and stop-hook rules each carried a
5
+ * byte-identical private copy, which is how two rules drift into disagreeing
6
+ * about what a verdict is.
7
+ */
8
+ export type VerdictResult = {
9
+ allow: boolean;
10
+ exit_code: 0 | 2;
11
+ rule: string;
12
+ reason?: string;
13
+ };
@@ -12,7 +12,7 @@ import { randomBytes } from "node:crypto";
12
12
  import { existsSync, readFileSync } from "node:fs";
13
13
  import { dirname, resolve } from "node:path";
14
14
  // Kept dependency-light: vendored verbatim into a downstream consumer, so no coordEnv import.
15
- import { normalizeHarness, resolveEmitRoot } from "./canonical-emit.ts";
15
+ import { normalizeAdapter, resolveEmitRoot } from "./canonical-emit.ts";
16
16
  import { emit } from "./events/emit.ts";
17
17
 
18
18
  /** Event types accepted by `writeSessionEvent`. Only the command stream +
@@ -98,7 +98,7 @@ const CANONICAL_TYPE: Partial<Record<SessionEventType, string>> = {
98
98
 
99
99
  interface HeartbeatEnrichment {
100
100
  session_id: string;
101
- harness: "claude-code" | "cursor" | "codex";
101
+ adapter: "claude-code" | "cursor" | "codex";
102
102
  at: number;
103
103
  }
104
104
  /** Cache heartbeat-derived envelope fields per instance_id so a burst of
@@ -121,7 +121,7 @@ function enrichFromHeartbeat(coordRoot: string, instanceId: string): HeartbeatEn
121
121
  };
122
122
  const enrichment: HeartbeatEnrichment = {
123
123
  session_id: hb.session_id || instanceId,
124
- harness: normalizeHarness(hb.platform),
124
+ adapter: normalizeAdapter(hb.platform),
125
125
  at: now,
126
126
  };
127
127
  enrichCache.set(instanceId, enrichment);
@@ -173,7 +173,7 @@ function emitCanonicalCommand(type: SessionEventType, fields: Record<string, unk
173
173
  event_type: eventType,
174
174
  instance_id: instanceId,
175
175
  session_id: enrich.session_id,
176
- harness: enrich.harness,
176
+ adapter: enrich.adapter,
177
177
  data: canonicalData(type, fields),
178
178
  });
179
179
  } catch {
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * This module owns: lifecycle (advance, close, archive, unarchive, delete)
9
9
  * and steward reassignment. Council CREATION + contribution writes stay
10
- * separate (operator-side, harness-agnostic).
10
+ * separate (operator-side, adapter-agnostic).
11
11
  */
12
12
 
13
13
  import {