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
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { spawnSync } from "node:child_process";
11
11
  import { existsSync, readdirSync, readFileSync } from "node:fs";
12
- import { dirname, resolve, sep } from "node:path";
12
+ import { dirname, join, resolve, sep } from "node:path";
13
13
 
14
14
  // NOTE: kept dependency-free (node builtins only); this file is vendored verbatim into
15
15
  // a downstream consumer, so it cannot import the coordEnv helper.
@@ -73,7 +73,7 @@ export interface Heartbeat {
73
73
  last_tool_target?: string;
74
74
  /** Free-form task/intent string set via `harn agents set-task`. Phase 2. */
75
75
  task?: string;
76
- /** UTC ISO-8601 timestamp when task was last set/cleared. Used by harnesses without Stop enforcement to compute staleness. 2026-05-24. */
76
+ /** UTC ISO-8601 timestamp when task was last set/cleared. Used by adapters without Stop enforcement to compute staleness. 2026-05-24. */
77
77
  task_updated_at?: string | null;
78
78
  /** Auto-generated per-turn summary written by the Stop hook via Haiku. 2026-05-23. */
79
79
  turn_summary?: string | null;
@@ -86,57 +86,173 @@ export interface Heartbeat {
86
86
  /**
87
87
  * Resolve the monorepo root for coord-state purposes.
88
88
  *
89
- * Resolves the coordination root,
90
- * including the `--git-common-dir` fallback that strips
91
- * `<superproject>/.git/modules/<name>/` to recover the superproject for
92
- * submodule worktrees. Without that fallback, a TS coord caller running
93
- * inside a `harn worktree add-submodule` checkout would resolve a different
94
- * `.harnery/` than the bash hooks and the two layers would silently diverge.
89
+ * Thin alias for `resolveCoordRoot()`; kept because it is the name the CLI
90
+ * command modules and the vendored downstream consumer already import.
95
91
  */
96
92
  export function monorepoRoot(): string | null {
97
- // Test-only override (matches bash HARNERY_COORD_ROOT_OVERRIDE). Lets tests
98
- // seed `.harnery/pid-map/<pid>` + `.harnery/active/<owner>.json` fixtures
99
- // in a tmpdir without needing a real git repo.
93
+ return resolveCoordRoot();
94
+ }
95
+
96
+ /**
97
+ * THE coordination-root resolution. Every surface — the hooks, the CLI's reads,
98
+ * and the CLI's canonical emits — resolves through this one function, because a
99
+ * root the two layers disagree about is a root that silently breaks the
100
+ * end-of-turn rules: the hook evaluates `state.status_checked` from the stream
101
+ * it reads, so an emit into a different `.harnery/events.ndjson` is invisible
102
+ * and rule 1/3 blocks a turn that did run `agents status`, with no sequence of
103
+ * CLI commands able to satisfy it.
104
+ *
105
+ * Precedence:
106
+ * 1. `HARNERY_COORD_ROOT_OVERRIDE` — explicit pin (tests, git hooks, and the
107
+ * root every coord-helper spawn pins for its child).
108
+ * 2. `CLAUDE_PROJECT_DIR` — the adapter stating which project it opened. Hook
109
+ * processes inherit the session's *shell* cwd, which follows `cd` into a
110
+ * subdirectory or submodule that may carry a `.harnery/` of its own (or
111
+ * none at all), so the adapter's own statement outranks the cwd walk.
112
+ * 3. The candidate root that already holds THIS session's heartbeat.
113
+ * 4. The nearest enclosing `.harnery/`, then a git-derived root.
114
+ *
115
+ * Step 3 is what makes CLI/hook disagreement structurally impossible rather
116
+ * than a coin flip. Choosing either root unconditionally is wrong in one
117
+ * direction each: preferring the git superproject strands a session whose
118
+ * adapter opened the submodule itself (its heartbeat lives in the submodule,
119
+ * so status/set-task wrote events the hook never read), while walking up from
120
+ * cwd alone strands the opposite case, a session opened on the superproject
121
+ * whose shell has cd'd into a submodule that carries its own `.harnery/`
122
+ * (regression-tested in tests/unit/coord-helper-root-pin.test.ts). The session's
123
+ * own heartbeat settles it: whichever root the hook registered this session in
124
+ * is the root the CLI must use, and the adapter-exported session id needed to
125
+ * recognize it is available to a plain tool-call subprocess even though
126
+ * `CLAUDE_PROJECT_DIR` is not.
127
+ */
128
+ export function resolveCoordRoot(start: string = process.cwd()): string | null {
100
129
  const rootOverride = process.env.HARNERY_COORD_ROOT_OVERRIDE;
101
- if (rootOverride) {
102
- return rootOverride;
130
+ if (rootOverride) return rootOverride;
131
+
132
+ const projectDir = process.env.CLAUDE_PROJECT_DIR;
133
+ if (projectDir) {
134
+ const fromProject = nearestCoordRoot(projectDir);
135
+ if (fromProject) return fromProject;
103
136
  }
104
137
 
105
- // 1. Superproject working tree (when running from inside a submodule).
106
- const sup = spawnSync("git", ["rev-parse", "--show-superproject-working-tree"], {
107
- encoding: "utf8",
108
- });
109
- if (sup.status === 0 && sup.stdout.trim() !== "") {
110
- return sup.stdout.trim();
138
+ // Nearest-first, so the fallback at the end keeps the historical cwd-walk
139
+ // behavior for a shell that has no registered session at all.
140
+ const ancestors = ancestorCoordRoots(start);
141
+ for (const candidate of ancestors) {
142
+ if (rootKnowsSession(candidate)) return candidate;
111
143
  }
112
144
 
113
- // 2. --git-common-dir fallback for submodule worktrees. `git worktree add`
114
- // inside a submodule produces a worktree whose --show-superproject-working-
115
- // tree is empty (the worktree has no submodule relationship of its own),
116
- // but --git-common-dir points at <superproject>/.git/modules/<name>/, so
117
- // we recover the superproject by stripping that suffix.
118
- const common = spawnSync("git", ["rev-parse", "--git-common-dir"], {
119
- encoding: "utf8",
120
- });
121
- if (common.status === 0) {
122
- const cd = common.stdout.trim();
123
- const idx = cd.indexOf("/.git/modules/");
124
- if (idx !== -1) {
125
- return cd.substring(0, idx);
126
- }
145
+ // Only reached when no enclosing root knows this session: a submodule
146
+ // worktree (`harn worktree add-submodule`) has no ancestor relationship with
147
+ // the superproject that registered the session, so ask git for it.
148
+ const gitRoots = gitCoordRoots(start).filter((r) => !ancestors.includes(r));
149
+ for (const candidate of gitRoots) {
150
+ if (rootKnowsSession(candidate)) return candidate;
151
+ }
152
+
153
+ if (ancestors.length > 0) return ancestors[0] as string;
154
+ if (gitRoots.length > 0) return gitRoots[0] as string;
155
+ // Nothing carries `.harnery/` yet. Hand back the enclosing checkout so a
156
+ // first run (`harn init`, agent-hook's session.start) has somewhere to
157
+ // create it.
158
+ return gitToplevel(start);
159
+ }
160
+
161
+ /** Nearest enclosing directory carrying `.harnery/`, or null. */
162
+ export function nearestCoordRoot(start: string): string | null {
163
+ return ancestorCoordRoots(start)[0] ?? null;
164
+ }
165
+
166
+ /** Every enclosing directory carrying `.harnery/`, nearest first. */
167
+ function ancestorCoordRoots(start: string): string[] {
168
+ const found: string[] = [];
169
+ let dir = resolve(start);
170
+ while (true) {
171
+ if (existsSync(join(dir, ".harnery"))) found.push(dir);
172
+ const parent = dirname(dir);
173
+ if (parent === dir) break;
174
+ dir = parent;
127
175
  }
176
+ return found;
177
+ }
178
+
179
+ /**
180
+ * Does this root's `.harnery/` already know the process asking?
181
+ *
182
+ * Two discriminators, both genuinely about *this* session: the adapter-exported
183
+ * session id matching a live heartbeat, and a pid-map row on our own ppid
184
+ * chain. Deliberately NOT the single-live-agent fallback that owner resolution
185
+ * ends with — a lone stranger in the wrong root is exactly how `whoami` came to
186
+ * report another agent's name and task as its own.
187
+ */
188
+ export function rootKnowsSession(root: string): boolean {
189
+ if (resolveOwnerBySessionEnv(root)) return true;
190
+ return resolveOwnerByPidmap(root).owner !== null;
191
+ }
128
192
 
129
- // 3. Top-level fallback (regular checkout).
130
- const top = spawnSync("git", ["rev-parse", "--show-toplevel"], {
131
- encoding: "utf8",
132
- });
133
- if (top.status === 0 && top.stdout.trim() !== "") {
134
- return top.stdout.trim();
193
+ /**
194
+ * Git-derived candidate roots for `start`, in preference order, filtered to
195
+ * those that actually carry `.harnery/`.
196
+ *
197
+ * Spawns run with `cwd: start` rather than the process cwd so an injected
198
+ * start dir resolves its own repository — without that, a call about some
199
+ * unrelated directory inherits this process's repo and can resolve a root that
200
+ * has nothing to do with the question asked.
201
+ */
202
+ function gitCoordRoots(start: string): string[] {
203
+ const roots: string[] = [];
204
+ const push = (value: string | null) => {
205
+ if (value && existsSync(join(value, ".harnery")) && !roots.includes(value)) roots.push(value);
206
+ };
207
+
208
+ // Superproject working tree (running from inside a submodule).
209
+ push(gitRevParse(start, "--show-superproject-working-tree"));
210
+
211
+ // `--git-common-dir` fallback for submodule worktrees: `git worktree add`
212
+ // inside a submodule produces a worktree whose superproject working tree is
213
+ // empty (the worktree has no submodule relationship of its own), but the
214
+ // common dir points at `<superproject>/.git/modules/<name>/`, so the
215
+ // superproject is recoverable by stripping that suffix.
216
+ const common = gitRevParse(start, "--git-common-dir");
217
+ if (common) {
218
+ const idx = common.indexOf("/.git/modules/");
219
+ if (idx !== -1) push(common.substring(0, idx));
135
220
  }
136
221
 
137
- return null;
222
+ // Top-level (regular checkout).
223
+ push(gitRevParse(start, "--show-toplevel"));
224
+ return roots;
138
225
  }
139
226
 
227
+ function gitToplevel(start: string): string | null {
228
+ return gitRevParse(start, "--show-toplevel");
229
+ }
230
+
231
+ function gitRevParse(cwd: string, flag: string): string | null {
232
+ const key = `${cwd}${flag}`;
233
+ const cached = gitRevParseCache.get(key);
234
+ if (cached !== undefined) return cached;
235
+ let value: string | null = null;
236
+ try {
237
+ // `cwd` must exist or the spawn itself fails; callers pass paths that may
238
+ // not (a project dir from a stale env var), so treat any failure as "no
239
+ // answer" rather than letting it throw.
240
+ const r = spawnSync("git", ["rev-parse", flag], { encoding: "utf8", cwd });
241
+ if (r.status === 0) value = r.stdout.trim() || null;
242
+ } catch {
243
+ value = null;
244
+ }
245
+ gitRevParseCache.set(key, value);
246
+ return value;
247
+ }
248
+
249
+ /**
250
+ * Memoized because git spawns are the expensive part of resolution and every
251
+ * CLI command resolves the root many times per invocation. Keyed by (cwd, flag);
252
+ * a repository's identity does not change under a running process.
253
+ */
254
+ const gitRevParseCache = new Map<string, string | null>();
255
+
140
256
  /** Parse owner from a pid-map row (`owner` or `owner\tplatform`). */
141
257
  export function parsePidmapRowOwner(row: string): string {
142
258
  const trimmed = row.trim();
@@ -146,9 +262,93 @@ export function parsePidmapRowOwner(row: string): string {
146
262
 
147
263
  /** Parse platform from a pid-map row; legacy rows default to `claude_code`. */
148
264
  export function parsePidmapRowPlatform(row: string): string {
149
- const trimmed = row.trim();
150
- const tab = trimmed.indexOf("\t");
151
- return tab >= 0 ? trimmed.slice(tab + 1).trim() : "claude_code";
265
+ const platform = row.trim().split("\t")[1]?.trim();
266
+ return platform || "claude_code";
267
+ }
268
+
269
+ /** Parse the start token from a pid-map row; rows written before it carry none. */
270
+ export function parsePidmapRowStartToken(row: string): string | undefined {
271
+ return row.trim().split("\t")[2]?.trim() || undefined;
272
+ }
273
+
274
+ /**
275
+ * Is the process now holding `pid` the one this row was written for?
276
+ *
277
+ * A pid is a number the OS re-issues, and quickly: a `pid_max` of 99999 against
278
+ * ~100 new processes a second recycles the whole space about every quarter
279
+ * hour. Believing a row past that point resolves this session to whichever
280
+ * agent last held the number — which is what made `whoami` report a stranger's
281
+ * name and files. The start token settles it, since two processes may share a
282
+ * pid but never a pid and a start instant.
283
+ *
284
+ * Deliberately inlined rather than imported from `state/proc-start.ts`: this
285
+ * file is vendored verbatim into a downstream consumer and stays on node
286
+ * builtins only. The token is a wire format shared with that module and with
287
+ * the host's commit guard, so the copies must agree byte for byte; exported so
288
+ * a test can hold this one against `processStartToken` and fail on drift.
289
+ */
290
+ export function pidStartToken(pid: number): string | null {
291
+ const forced = process.env.HARNERY_PID_PROBE;
292
+ const useProcfs = forced === "procfs" || (forced !== "ps" && existsSync("/proc/self/stat"));
293
+ // One machine, one probe. Falling back to the other on a read failure would
294
+ // answer in the wrong dialect and read as a recycled pid.
295
+ if (useProcfs) {
296
+ try {
297
+ const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
298
+ const afterComm = stat.slice(stat.lastIndexOf(") ") + 2);
299
+ // Fields after comm, 0-based: 0 is state (field 3), starttime (22) is 19.
300
+ const ticks = afterComm.split(" ")[19];
301
+ if (!ticks || !/^\d+$/.test(ticks)) return null;
302
+ // Ticks count from boot, so they repeat across reboots; the boot id scopes
303
+ // them. Rows written before it carry ticks alone and still compare.
304
+ let boot = "";
305
+ try {
306
+ const raw = readFileSync("/proc/sys/kernel/random/boot_id", "utf8")
307
+ .trim()
308
+ .replace(/-/g, "");
309
+ if (/^[0-9a-f]{8,}$/.test(raw)) boot = `${raw.slice(0, 8)}.`;
310
+ } catch {
311
+ /* unnamed boot: fall back to the tick-only shape */
312
+ }
313
+ return `l${boot}${ticks}`;
314
+ } catch {
315
+ return null;
316
+ }
317
+ }
318
+ try {
319
+ // TZ and locale are pinned because `ps` renders the date through them, and
320
+ // two callers with different environments must not disagree about one
321
+ // process.
322
+ const out = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], {
323
+ encoding: "utf8",
324
+ timeout: 2000,
325
+ env: { ...process.env, TZ: "UTC", LC_ALL: "C" },
326
+ });
327
+ if (out.status !== 0) return null;
328
+ const lstart = (out.stdout ?? "").split("\n")[0]?.trim().replace(/\s+/g, " ");
329
+ return lstart ? `p${lstart}` : null;
330
+ } catch {
331
+ return null;
332
+ }
333
+ }
334
+
335
+ function pidWasRecycled(pid: number, row: string): boolean {
336
+ const recorded = parsePidmapRowStartToken(row);
337
+ if (!recorded) return false; // pre-token row: unverifiable, behave as before
338
+ const current = pidStartToken(pid);
339
+ if (!current) return false;
340
+ if (current === recorded) return false;
341
+ // A row predating the boot segment recorded ticks alone; compare it on what
342
+ // it recorded rather than pruning every live row on the first upgraded run.
343
+ if (
344
+ recorded[0] === "l" &&
345
+ current[0] === "l" &&
346
+ recorded.includes(".") !== current.includes(".")
347
+ ) {
348
+ const ticks = (t: string) => (t.includes(".") ? t.slice(t.indexOf(".") + 1) : t.slice(1));
349
+ return ticks(recorded) !== ticks(current);
350
+ }
351
+ return true;
152
352
  }
153
353
 
154
354
  function readPidmapRow(pidmapDir: string, pid: number): string | null {
@@ -197,9 +397,14 @@ export function resolveOwnerWithSource(): {
197
397
  const root = monorepoRoot();
198
398
  if (!root) return { owner: null, source: "none" };
199
399
 
200
- // Cursor's Glass/Agents UI can run several chats under one long-lived node
201
- // process, so the pid-map row for that shared ancestor is last-writer-wins.
202
- // Prefer the per-chat session id when Cursor exposes one in the tool env.
400
+ // Every supported adapter exports its session id into the env of the
401
+ // subprocess it spawns for a tool call, and each heartbeat records the
402
+ // session id it was minted under. Matching the two names us outright, even
403
+ // with many live agents, so it goes ahead of the ppid walk rather than
404
+ // catching what the walk drops. Cursor needed this first because its
405
+ // Glass/Agents UI runs several chats under one node ancestor, making that row
406
+ // last-writer-wins; pid recycling generalises the same hazard to every
407
+ // adapter.
203
408
  if (shouldPreferSessionEnv()) {
204
409
  const bySession = resolveOwnerBySessionEnv(root);
205
410
  if (bySession) {
@@ -207,6 +412,34 @@ export function resolveOwnerWithSource(): {
207
412
  }
208
413
  }
209
414
 
415
+ if (!existsSync(resolve(root, ".harnery", "pid-map"))) return { owner: null, source: "none" };
416
+
417
+ const byPidmap = resolveOwnerByPidmap(root);
418
+ if (byPidmap.owner) return byPidmap;
419
+
420
+ // Last resort: if exactly one agent is live in this coord root, it's
421
+ // unambiguously us — resolve to it. This is what lets the bare `agents
422
+ // status` / `set-task` the stop hook recommends work without a `--session-id`
423
+ // flag in the common single-agent case. With 0 or 2+ live agents it would be
424
+ // a guess, so we stay null and require the explicit flag.
425
+ const singleton = resolveSingleActiveOwner(root);
426
+ if (singleton) {
427
+ return { owner: singleton, source: "active_singleton" };
428
+ }
429
+
430
+ return { owner: null, source: "none" };
431
+ }
432
+
433
+ /**
434
+ * Walk our own ppid chain for a pid-map row in ONE given root.
435
+ *
436
+ * Root-parameterized (rather than resolving the root itself) so root resolution
437
+ * can use it as a discriminator without recursing back into itself.
438
+ */
439
+ export function resolveOwnerByPidmap(root: string): {
440
+ owner: string | null;
441
+ source: "pidmap" | "pidmap_fallback" | "none";
442
+ } {
210
443
  const pidmapDir = resolve(root, ".harnery", "pid-map");
211
444
  if (!existsSync(pidmapDir)) return { owner: null, source: "none" };
212
445
 
@@ -217,7 +450,7 @@ export function resolveOwnerWithSource(): {
217
450
  for (let hop = 0; hop < 20; hop++) {
218
451
  if (pid === null) break;
219
452
  const row = readPidmapRow(pidmapDir, pid);
220
- if (row) {
453
+ if (row && !pidWasRecycled(pid, row)) {
221
454
  const rowOwner = parsePidmapRowOwner(row);
222
455
  const rowPlat = parsePidmapRowPlatform(row);
223
456
  if (rowPlat === prefer) {
@@ -230,35 +463,12 @@ export function resolveOwnerWithSource(): {
230
463
  if (fallbackOwner) {
231
464
  return { owner: fallbackOwner, source: "pidmap_fallback" };
232
465
  }
233
-
234
- // The ppid walk found nothing (e.g. a Bash-tool subshell whose process tree
235
- // doesn't climb back to the harness anchor). Before guessing, try the
236
- // harness-provided session id from the environment: every supported harness
237
- // exports its session id into the tool subprocess env, and each heartbeat
238
- // records the `session_id` it was minted under. Matching the two resolves us
239
- // unambiguously even with multiple live agents, which is the case the
240
- // singleton fallback below cannot handle.
241
- const bySession = resolveOwnerBySessionEnv(root);
242
- if (bySession) {
243
- return { owner: bySession, source: "session_env" };
244
- }
245
-
246
- // Last resort: if exactly one agent is live in this coord root, it's
247
- // unambiguously us — resolve to it. This is what lets the bare `agents
248
- // status` / `set-task` the stop hook recommends work without a `--session-id`
249
- // flag in the common single-agent case. With 0 or 2+ live agents it would be
250
- // a guess, so we stay null and require the explicit flag.
251
- const singleton = resolveSingleActiveOwner(root);
252
- if (singleton) {
253
- return { owner: singleton, source: "active_singleton" };
254
- }
255
-
256
466
  return { owner: null, source: "none" };
257
467
  }
258
468
 
259
469
  /**
260
- * Harness-exported session-id environment variables, in precedence order. Each
261
- * supported harness propagates its session id into the env of the subprocess it
470
+ * Adapter-exported session-id environment variables, in precedence order. Each
471
+ * supported adapter propagates its session id into the env of the subprocess it
262
472
  * spawns for a tool call (Claude Code's Bash tool, Cursor's terminal, Codex's
263
473
  * shell). A coord CLI invoked as such a tool can therefore recover its own
264
474
  * identity from the env even when the ppid walk misses.
@@ -274,7 +484,7 @@ const SESSION_ID_ENV_VARS = [
274
484
  "CODEX_SESSION_ID",
275
485
  ] as const;
276
486
 
277
- /** Read normalized candidates from the first non-empty harness session-id env var. */
487
+ /** Read normalized candidates from the first non-empty adapter session-id env var. */
278
488
  function sessionIdsFromEnv(): string[] {
279
489
  for (const key of SESSION_ID_ENV_VARS) {
280
490
  const v = process.env[key]?.trim();
@@ -287,19 +497,33 @@ function sessionIdsFromEnv(): string[] {
287
497
  return [];
288
498
  }
289
499
 
290
- /** Read the first non-empty harness session-id env var, or null. */
500
+ /** Read the first non-empty adapter session-id env var, or null. */
291
501
  function sessionIdFromEnv(): string | null {
292
502
  return sessionIdsFromEnv()[0] ?? null;
293
503
  }
294
504
 
505
+ /**
506
+ * Should the adapter-exported session id be consulted before the ppid walk?
507
+ *
508
+ * Yes, whenever one is exported. The env var is the adapter stating its own
509
+ * identity; the walk is a guess over a namespace the OS recycles. On a box with
510
+ * `pid_max` of 99999 and ~100 pids allocated per second the whole pid space
511
+ * turns over about every quarter hour, so a row written before that can name a
512
+ * pid some unrelated process now holds. Pruning cannot save the walk here:
513
+ * it removes rows whose pid is dead, and a recycled pid is alive. Letting a
514
+ * guess outrank a statement of fact is what made `agents whoami` report another
515
+ * agent's name and file list.
516
+ *
517
+ * This only reorders the two. Session-env resolution still requires a live
518
+ * heartbeat carrying that session id, so when it does not match, the walk runs
519
+ * exactly as before.
520
+ */
295
521
  function shouldPreferSessionEnv(): boolean {
296
- if (!sessionIdFromEnv()) return false;
297
- const platform = process.env.HARNERY_AGENT_COORD_PLATFORM?.trim();
298
- return process.env.CURSOR_AGENT === "1" || platform === "cursor";
522
+ return sessionIdFromEnv() !== null;
299
523
  }
300
524
 
301
525
  /**
302
- * Resolve the owner by matching the harness session-id env var against the
526
+ * Resolve the owner by matching the adapter session-id env var against the
303
527
  * `session_id` of a live heartbeat in this coord root. Returns the matching
304
528
  * `instance_id`, or null if there's no session-id env var or no live heartbeat
305
529
  * carries it. "Live" reuses the same 10-minute freshness window the singleton
@@ -56,7 +56,7 @@ export interface CanonicalEvent {
56
56
  parent_session_id?: string;
57
57
  turn_id?: string;
58
58
  parent_turn_id?: string;
59
- harness: string;
59
+ adapter: string;
60
60
  source: string;
61
61
  data: Record<string, unknown>;
62
62
  }
@@ -5,7 +5,7 @@
5
5
  * event/schema types).
6
6
  *
7
7
  * Phase 4: agent-coord uses this from CLI handlers (state.task_set,
8
- * state.status_checked, state.scratch_append, council.*, decision.*,
8
+ * state.status_checked, state.journal_append, council.*, decision.*,
9
9
  * presence.*).
10
10
  */
11
11
 
@@ -19,8 +19,9 @@ const STREAM_FILE = ".harnery/events.ndjson";
19
19
  const LOCK_FILE = ".harnery/events.ndjson.lock";
20
20
  const MAX_LINE_BYTES = 64 * 1024;
21
21
 
22
- export type Harness = "claude-code" | "cursor" | "codex";
23
- export type Source = "agent-hooks" | "agent-coord" | "user" | "system";
22
+ export type { Adapter, Source } from "../../hooks/events/schema.ts";
23
+
24
+ import type { Adapter, Source } from "../../hooks/events/schema.ts";
24
25
 
25
26
  export interface Envelope {
26
27
  schema_version: typeof SCHEMA_VERSION;
@@ -32,7 +33,7 @@ export interface Envelope {
32
33
  parent_session_id?: string;
33
34
  turn_id?: string;
34
35
  parent_turn_id?: string;
35
- harness: Harness;
36
+ adapter: Adapter;
36
37
  source: Source;
37
38
  data: Record<string, unknown>;
38
39
  }
@@ -41,7 +42,7 @@ export interface EmitInput {
41
42
  event_type: string;
42
43
  instance_id: string;
43
44
  session_id: string;
44
- harness: Harness;
45
+ adapter: Adapter;
45
46
  source?: Source;
46
47
  parent_session_id?: string;
47
48
  turn_id?: string;
@@ -61,7 +62,7 @@ export function buildEnvelope(input: EmitInput): Envelope {
61
62
  parent_session_id: input.parent_session_id,
62
63
  turn_id: input.turn_id,
63
64
  parent_turn_id: input.parent_turn_id,
64
- harness: input.harness,
65
+ adapter: input.adapter,
65
66
  source: input.source ?? "agent-coord",
66
67
  data: input.data,
67
68
  };
@@ -1,16 +1,12 @@
1
- import { existsSync } from "node:fs";
2
- import { dirname, join, resolve } from "node:path";
1
+ import { resolveCoordRoot } from "./coord-client.ts";
3
2
 
4
3
  /**
5
- * Walk up from `start` looking for a directory containing `.harnery/`, so
6
- * adapter + legacy code agree on the same monorepo root.
4
+ * Resolve the coordination root.
5
+ *
6
+ * Delegates to `resolveCoordRoot()`. This was a third, independent copy of the
7
+ * walk-up — the kind of duplication that let the hooks and the CLI drift onto
8
+ * different roots in the first place; keep resolution in one place.
7
9
  */
8
10
  export function findCoordRoot(start: string = process.cwd()): string | null {
9
- let dir = resolve(start);
10
- while (true) {
11
- if (existsSync(join(dir, ".harnery"))) return dir;
12
- const parent = dirname(dir);
13
- if (parent === dir) return null;
14
- dir = parent;
15
- }
11
+ return resolveCoordRoot(start);
16
12
  }