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
@@ -67,7 +67,7 @@ function seed(ev, coordRoot) {
67
67
  const hb = {
68
68
  instance_id: ev.instance_id,
69
69
  session_id: ev.session_id,
70
- harness: ev.harness,
70
+ adapter: ev.adapter,
71
71
  last_heartbeat: ev.ts,
72
72
  last_event_id: ev.event_id,
73
73
  events_applied: 0,
@@ -112,12 +112,12 @@ function apply(hb, ev, coordRoot) {
112
112
  switch (ev.event_type) {
113
113
  case "session.start":
114
114
  hb.started_at = pickStr(d, "started_at") ?? ev.ts;
115
- hb.harness = ev.harness;
115
+ hb.adapter = ev.adapter;
116
116
  {
117
117
  const model = pickStr(d, "model");
118
118
  if (model)
119
119
  hb.model = model;
120
- const platform = pickStr(d, "platform") ?? harnessToPlatform(ev.harness);
120
+ const platform = pickStr(d, "platform") ?? adapterToPlatform(ev.adapter);
121
121
  hb.platform = platform;
122
122
  const name = pickStr(d, "name");
123
123
  if (name)
@@ -141,6 +141,9 @@ function apply(hb, ev, coordRoot) {
141
141
  const workflowRunId = pickStr(d, "workflow_run_id");
142
142
  if (workflowRunId)
143
143
  hb.workflow_run_id = workflowRunId;
144
+ const workflowAgentId = pickStr(d, "workflow_agent_id");
145
+ if (workflowAgentId)
146
+ hb.workflow_agent_id = workflowAgentId;
144
147
  if (!hb.files_touched)
145
148
  hb.files_touched = [];
146
149
  }
@@ -164,7 +167,7 @@ function apply(hb, ev, coordRoot) {
164
167
  hb.started_at = ev.ts;
165
168
  if (!hb.files_touched)
166
169
  hb.files_touched = [];
167
- hb.platform = harnessToPlatform(ev.harness);
170
+ hb.platform = adapterToPlatform(ev.adapter);
168
171
  break;
169
172
  }
170
173
  case "subagent.stop":
@@ -183,7 +186,7 @@ function apply(hb, ev, coordRoot) {
183
186
  hb.turn_summary = summary;
184
187
  hb.turn_summary_updated_at = ev.ts;
185
188
  }
186
- // Backfill model for harnesses that omit it at session.start (Claude
189
+ // Backfill model for adapters that omit it at session.start (Claude
187
190
  // Code). The Stop hook resolves it from the transcript by this point;
188
191
  // only set when present so we never clobber a known model.
189
192
  const model = pickStr(d, "model");
@@ -270,14 +273,14 @@ function apply(hb, ev, coordRoot) {
270
273
  }
271
274
  }
272
275
  }
273
- function harnessToPlatform(harness) {
274
- if (harness === "claude-code")
276
+ function adapterToPlatform(adapter) {
277
+ if (adapter === "claude-code")
275
278
  return "claude_code";
276
- if (harness === "cursor")
279
+ if (adapter === "cursor")
277
280
  return "cursor";
278
- if (harness === "codex")
281
+ if (adapter === "codex")
279
282
  return "codex";
280
- return harness;
283
+ return adapter;
281
284
  }
282
285
  function extractFilePath(data) {
283
286
  const raw = data.tool_input;
@@ -411,6 +414,7 @@ function writeHeartbeat(coordRoot, instanceId, hb) {
411
414
  setIfDefined(merged, "agent_id", hb.agent_id);
412
415
  setIfDefined(merged, "subagent_call_id", hb.subagent_call_id);
413
416
  setIfDefined(merged, "workflow_run_id", hb.workflow_run_id);
417
+ setIfDefined(merged, "workflow_agent_id", hb.workflow_agent_id);
414
418
  setIfDefined(merged, "model", hb.model);
415
419
  setIfDefined(merged, "platform", hb.platform);
416
420
  setIfDefined(merged, "started_at", hb.started_at);
@@ -69,7 +69,7 @@ export interface GroupUnclaimHit {
69
69
  export declare function groupUnclaim(coordRoot: string, groupId: string, path: string): GroupUnclaimHit[];
70
70
  export declare function killHeartbeat(coordRoot: string, instanceId: string): boolean;
71
71
  export declare function healPidmap(coordRoot: string, instanceId: string, pid: number): void;
72
- export declare function healHeartbeat(coordRoot: string, instanceId: string, sessionId?: string, model?: string, harness?: string): Heartbeat | null;
72
+ export declare function healHeartbeat(coordRoot: string, instanceId: string, sessionId?: string, model?: string, adapter?: string): Heartbeat | null;
73
73
  /**
74
74
  * Stamp the heartbeat with the most-recent tool name + target. Written from
75
75
  * the post-tool-use hook.
@@ -15,23 +15,23 @@
15
15
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
16
16
  import { dirname, join } from "node:path";
17
17
  import { emit } from "../events/emit.js";
18
- /** Inline harness normalizer (mirrors canonical-emit.normalizeHarness), kept
18
+ /** Inline adapter normalizer (mirrors canonical-emit.normalizeAdapter), kept
19
19
  * local so the writer's only cross-module dep is the in-process emit(). */
20
- function harnessOf(platform) {
20
+ function adapterOf(platform) {
21
21
  if (platform === "cursor")
22
22
  return "cursor";
23
23
  if (platform === "codex")
24
24
  return "codex";
25
25
  return "claude-code";
26
26
  }
27
- /** Inverse of harnessOf: maps the canonical harness ("claude-code"/"cursor"/
27
+ /** Inverse of adapterOf: maps the canonical adapter ("claude-code"/"cursor"/
28
28
  * "codex") to the legacy underscore platform label stored on the heartbeat.
29
- * Mirrors heartbeat-projector.harnessToPlatform so a healed heartbeat carries
29
+ * Mirrors heartbeat-projector.adapterToPlatform so a healed heartbeat carries
30
30
  * the same platform value projection would have written. */
31
- function harnessToPlatform(harness) {
32
- if (harness === "cursor")
31
+ function adapterToPlatform(adapter) {
32
+ if (adapter === "cursor")
33
33
  return "cursor";
34
- if (harness === "codex")
34
+ if (adapter === "codex")
35
35
  return "codex";
36
36
  return "claude_code";
37
37
  }
@@ -49,7 +49,7 @@ function emitHealthHeal(coordRoot, type, instanceId, hb, data) {
49
49
  event_type: type,
50
50
  instance_id: instanceId,
51
51
  session_id: hb?.session_id ?? instanceId,
52
- harness: harnessOf(hb?.platform),
52
+ adapter: adapterOf(hb?.platform),
53
53
  source: "agent-coord",
54
54
  data,
55
55
  });
@@ -226,7 +226,7 @@ export function healPidmap(coordRoot, instanceId, pid) {
226
226
  ...(existingOwner ? { prior: existingOwner.slice(0, 8) } : {}),
227
227
  });
228
228
  }
229
- export function healHeartbeat(coordRoot, instanceId, sessionId, model, harness) {
229
+ export function healHeartbeat(coordRoot, instanceId, sessionId, model, adapter) {
230
230
  const path = heartbeatPath(coordRoot, instanceId);
231
231
  if (existsSync(path)) {
232
232
  // Already alive, return it.
@@ -264,11 +264,11 @@ export function healHeartbeat(coordRoot, instanceId, sessionId, model, harness)
264
264
  started_at: now,
265
265
  last_heartbeat: now,
266
266
  files_touched: [],
267
- // Default to claude_code only when the caller can't tell us the harness
267
+ // Default to claude_code only when the caller can't tell us the adapter
268
268
  // (e.g. manual `harn agents heal`). The live tool.pre_use heal threads the
269
- // detected harness so a pruned Cursor/Codex heartbeat is recreated with
269
+ // detected adapter so a pruned Cursor/Codex heartbeat is recreated with
270
270
  // the correct platform instead of being mislabeled claude_code.
271
- platform: harnessToPlatform(harness),
271
+ platform: adapterToPlatform(adapter),
272
272
  };
273
273
  atomicWrite(path, JSON.stringify(hb, null, 2));
274
274
  // Write-only telemetry: only the actual-recreate branch reaches here (the
@@ -1,27 +1,27 @@
1
1
  /**
2
- * Scratchpad mutations: append-only timestamped journal at
3
- * `.harnery/scratch/<instance_id>.md`.
2
+ * Journal mutations: append-only timestamped transcript at
3
+ * `.harnery/journal/<instance_id>.md`.
4
4
  *
5
5
  * Used by the agents-coord web UI route handlers for both operator-nudge
6
6
  * appends and inline edits.
7
7
  */
8
8
  /**
9
- * Append a timestamped entry to the owner's scratchpad.
9
+ * Append a timestamped entry to the owner's journal.
10
10
  * Returns true on success, false if validation failed.
11
11
  */
12
- export declare function appendScratch(coordRoot: string, instanceId: string, category: string, body: string): {
12
+ export declare function appendJournal(coordRoot: string, instanceId: string, category: string, body: string): {
13
13
  ok: boolean;
14
14
  reason?: string;
15
15
  path?: string;
16
16
  };
17
17
  /**
18
- * Replace the scratchpad with new body, archiving prior contents and
18
+ * Replace the journal with new body, archiving prior contents and
19
19
  * appending an "(edited via UI by the operator)" audit-marker note.
20
20
  */
21
- export declare function editScratchpad(coordRoot: string, instanceId: string, newBody: string, summary: string): {
21
+ export declare function editJournal(coordRoot: string, instanceId: string, newBody: string, summary: string): {
22
22
  ok: boolean;
23
23
  reason?: string;
24
24
  archivePath?: string;
25
25
  path?: string;
26
26
  };
27
- //# sourceMappingURL=scratch.d.ts.map
27
+ //# sourceMappingURL=journal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scratch.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/scratch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBjD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA8BvE"}
1
+ {"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/journal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA8BvE"}
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Scratchpad mutations: append-only timestamped journal at
3
- * `.harnery/scratch/<instance_id>.md`.
2
+ * Journal mutations: append-only timestamped transcript at
3
+ * `.harnery/journal/<instance_id>.md`.
4
4
  *
5
5
  * Used by the agents-coord web UI route handlers for both operator-nudge
6
6
  * appends and inline edits.
@@ -20,11 +20,11 @@ const APPEND_BODY_CAP = 4096;
20
20
  function nowIsoSeconds() {
21
21
  return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
22
22
  }
23
- function scratchPath(coordRoot, instanceId) {
24
- return join(coordRoot, ".harnery", "scratch", `${instanceId}.md`);
23
+ function journalPath(coordRoot, instanceId) {
24
+ return join(coordRoot, ".harnery", "journal", `${instanceId}.md`);
25
25
  }
26
26
  function archivePath(coordRoot, instanceId, suffix) {
27
- return join(coordRoot, ".harnery", "scratch", "archived", `${instanceId}-${suffix}.md`);
27
+ return join(coordRoot, ".harnery", "journal", "archived", `${instanceId}-${suffix}.md`);
28
28
  }
29
29
  function atomicWriteText(path, content) {
30
30
  mkdirSync(dirname(path), { recursive: true });
@@ -33,10 +33,10 @@ function atomicWriteText(path, content) {
33
33
  renameSync(tmp, path);
34
34
  }
35
35
  /**
36
- * Append a timestamped entry to the owner's scratchpad.
36
+ * Append a timestamped entry to the owner's journal.
37
37
  * Returns true on success, false if validation failed.
38
38
  */
39
- export function appendScratch(coordRoot, instanceId, category, body) {
39
+ export function appendJournal(coordRoot, instanceId, category, body) {
40
40
  if (!ALLOWED_CATEGORIES.has(category)) {
41
41
  return { ok: false, reason: `invalid category "${category}"` };
42
42
  }
@@ -44,7 +44,7 @@ export function appendScratch(coordRoot, instanceId, category, body) {
44
44
  return { ok: false, reason: "body required" };
45
45
  }
46
46
  const trimmedBody = body.length > APPEND_BODY_CAP ? `${body.slice(0, APPEND_BODY_CAP - 3)}...` : body;
47
- const target = scratchPath(coordRoot, instanceId);
47
+ const target = journalPath(coordRoot, instanceId);
48
48
  const ts = nowIsoSeconds();
49
49
  let prior = "";
50
50
  if (existsSync(target)) {
@@ -57,14 +57,14 @@ export function appendScratch(coordRoot, instanceId, category, body) {
57
57
  return { ok: true, path: target };
58
58
  }
59
59
  /**
60
- * Replace the scratchpad with new body, archiving prior contents and
60
+ * Replace the journal with new body, archiving prior contents and
61
61
  * appending an "(edited via UI by the operator)" audit-marker note.
62
62
  */
63
- export function editScratchpad(coordRoot, instanceId, newBody, summary) {
63
+ export function editJournal(coordRoot, instanceId, newBody, summary) {
64
64
  if (typeof newBody !== "string") {
65
65
  return { ok: false, reason: "newBody required" };
66
66
  }
67
- const target = scratchPath(coordRoot, instanceId);
67
+ const target = journalPath(coordRoot, instanceId);
68
68
  const ts = nowIsoSeconds();
69
69
  const archiveSuffix = `pre-ui-${ts.replace(/:/g, "-")}`;
70
70
  const archive = archivePath(coordRoot, instanceId, archiveSuffix);
@@ -84,7 +84,7 @@ export function editScratchpad(coordRoot, instanceId, newBody, summary) {
84
84
  const summaryText = summary && summary.length > 0 ? summary : "(no summary)";
85
85
  const auditMarker = `## [${ts}] note (edited via UI by the operator)\n` +
86
86
  `${summaryText}\n` +
87
- `Pre-edit archived at .harnery/scratch/archived/${instanceId}-${archiveSuffix}.md\n\n`;
87
+ `Pre-edit archived at .harnery/journal/archived/${instanceId}-${archiveSuffix}.md\n\n`;
88
88
  atomicWriteText(target, prior + auditMarker + newBody + (newBody.endsWith("\n") ? "" : "\n"));
89
89
  return { ok: true, archivePath: archive, path: target };
90
90
  }
@@ -1,14 +1,38 @@
1
1
  /**
2
2
  * Pid-map writer + liveness helpers.
3
3
  *
4
- * Per-harness pid-map at `.harnery/pid-map/<pid>` containing
5
- * `<instance_id>\t<platform>`. `harn agents whoami` walks ppid up 20 hops looking
6
- * for a matching entry (preferring the harness, falling back to any platform).
4
+ * Per-adapter pid-map at `.harnery/pid-map/<pid>` containing
5
+ * `<instance_id>\t<platform>\t<start_token>`. `harn agents whoami` walks ppid up
6
+ * 20 hops looking for a matching entry (preferring the adapter, falling back to
7
+ * any platform).
8
+ *
9
+ * The start token pins each row to one *run* of that pid, because a pid on its
10
+ * own is a number the OS re-issues — see `proc-start.ts` for how fast. Rows
11
+ * written before tokens existed carry two fields and read as unverified, which
12
+ * behaves exactly as they always did.
7
13
  *
8
14
  * Atomic temp+rename. Idempotent: re-writing the same row is a no-op.
9
15
  */
16
+ /** Split a row into its fields, tolerating the pre-token two-field shape. */
17
+ export declare function parsePidmapRow(row: string): {
18
+ instanceId: string;
19
+ platform: string;
20
+ startToken: string | undefined;
21
+ };
22
+ /**
23
+ * Delete every pid-map row that no longer names its process — exited, or its
24
+ * pid re-issued to something else. Returns the number removed.
25
+ */
26
+ export declare function prunePidmapDeadRows(coordRoot: string): number;
10
27
  export declare function writePidmapRow(coordRoot: string, pid: number, instanceId: string, platform: string): void;
11
- /** True when any pid-map row for `instanceId` still belongs to a live process. */
28
+ /**
29
+ * True when any pid-map row for `instanceId` still belongs to a live process.
30
+ *
31
+ * Callers spend this answer on consequential things — whether a commit guard
32
+ * treats a heartbeat as a live peer, whether a name can be reclaimed from an
33
+ * abandoned session — so a re-issued pid must not read as that agent still
34
+ * running.
35
+ */
12
36
  export declare function instanceHasLivePid(coordRoot: string, instanceId: string): boolean;
13
37
  /** Drop every pid-map row owned by `instanceId`. Returns how many files were removed. */
14
38
  export declare function removePidmapRowsForInstance(coordRoot: string, instanceId: string): number;
@@ -1 +1 @@
1
- {"version":3,"file":"pidmap.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/pidmap.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CAYN;AAED,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAsBjF;AAED,yFAAyF;AACzF,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAqBzF"}
1
+ {"version":3,"file":"pidmap.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/pidmap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAyBH,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAGA;AAwCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA8B7D;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CA6BN;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAmBjF;AAED,yFAAyF;AACzF,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBzF"}
@@ -1,23 +1,124 @@
1
1
  /**
2
2
  * Pid-map writer + liveness helpers.
3
3
  *
4
- * Per-harness pid-map at `.harnery/pid-map/<pid>` containing
5
- * `<instance_id>\t<platform>`. `harn agents whoami` walks ppid up 20 hops looking
6
- * for a matching entry (preferring the harness, falling back to any platform).
4
+ * Per-adapter pid-map at `.harnery/pid-map/<pid>` containing
5
+ * `<instance_id>\t<platform>\t<start_token>`. `harn agents whoami` walks ppid up
6
+ * 20 hops looking for a matching entry (preferring the adapter, falling back to
7
+ * any platform).
8
+ *
9
+ * The start token pins each row to one *run* of that pid, because a pid on its
10
+ * own is a number the OS re-issues — see `proc-start.ts` for how fast. Rows
11
+ * written before tokens existed carry two fields and read as unverified, which
12
+ * behaves exactly as they always did.
7
13
  *
8
14
  * Atomic temp+rename. Idempotent: re-writing the same row is a no-op.
9
15
  */
10
16
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
11
17
  import { dirname, join } from "node:path";
18
+ import { checkPidToken, processStartToken } from "./proc-start.js";
19
+ /**
20
+ * Row count that triggers a dead-row sweep on the next write.
21
+ *
22
+ * Rows are written per hook shell, and those shells exit immediately, so an
23
+ * unattended map only grows. One repo was observed holding 512 rows of which 510
24
+ * were dead. That matters beyond disk: pids get recycled, so a stale row whose
25
+ * number is later reused makes an identity walk resolve to a long-gone agent,
26
+ * and `agents whoami` starts reporting somebody else's session.
27
+ */
28
+ const PRUNE_AT_ROWS = 200;
29
+ /** Split a row into its fields, tolerating the pre-token two-field shape. */
30
+ export function parsePidmapRow(row) {
31
+ const [instanceId = "", platform = "", startToken = ""] = row.trim().split("\t");
32
+ return { instanceId, platform, startToken: startToken || undefined };
33
+ }
34
+ /**
35
+ * Is this row still about the process it was written for?
36
+ *
37
+ * Two ways to fail. The pid may have exited, which a liveness probe catches.
38
+ * Or the pid may have been re-issued to an unrelated process, which a liveness
39
+ * probe cannot catch — that pid is alive — and which the start token does.
40
+ * Getting the second one wrong resolves an identity walk to a long-gone agent
41
+ * and keeps a dead agent's heartbeat looking live, so the check belongs
42
+ * everywhere a row is believed, not only where it is swept.
43
+ */
44
+ function rowStillNamesItsProcess(pid, startToken) {
45
+ if (!pidIsAlive(pid))
46
+ return false;
47
+ return checkPidToken(pid, startToken) !== "mismatch";
48
+ }
12
49
  function atomicWrite(path, content) {
13
50
  mkdirSync(dirname(path), { recursive: true });
14
51
  const tmp = `${path}.tmp.${process.pid}`;
15
52
  writeFileSync(tmp, content, "utf8");
16
53
  renameSync(tmp, path);
17
54
  }
55
+ /**
56
+ * Is this pid a running process?
57
+ *
58
+ * `EPERM` means the process exists but belongs to another user, which is still
59
+ * alive. Treating every failure as "dead" would drop a live foreign row and
60
+ * weaken the checks that depend on knowing a claim is anchored.
61
+ */
62
+ function pidIsAlive(pid) {
63
+ try {
64
+ process.kill(pid, 0); // signal 0 = liveness probe
65
+ return true;
66
+ }
67
+ catch (error) {
68
+ return error.code === "EPERM";
69
+ }
70
+ }
71
+ /**
72
+ * Delete every pid-map row that no longer names its process — exited, or its
73
+ * pid re-issued to something else. Returns the number removed.
74
+ */
75
+ export function prunePidmapDeadRows(coordRoot) {
76
+ const dir = join(coordRoot, ".harnery", "pid-map");
77
+ if (!existsSync(dir))
78
+ return 0;
79
+ let removed = 0;
80
+ let names;
81
+ try {
82
+ names = readdirSync(dir);
83
+ }
84
+ catch {
85
+ return 0;
86
+ }
87
+ for (const f of names) {
88
+ const pid = Number.parseInt(f, 10);
89
+ // Only touch files whose whole name is a pid, so a stray note or a
90
+ // half-written `.tmp.<pid>` file is left where it is.
91
+ if (!Number.isFinite(pid) || String(pid) !== f)
92
+ continue;
93
+ let token;
94
+ try {
95
+ token = parsePidmapRow(readFileSync(join(dir, f), "utf8")).startToken;
96
+ }
97
+ catch {
98
+ /* unreadable row: fall back to the liveness check alone */
99
+ }
100
+ if (rowStillNamesItsProcess(pid, token))
101
+ continue;
102
+ try {
103
+ unlinkSync(join(dir, f));
104
+ removed += 1;
105
+ }
106
+ catch {
107
+ /* best-effort cleanup */
108
+ }
109
+ }
110
+ return removed;
111
+ }
18
112
  export function writePidmapRow(coordRoot, pid, instanceId, platform) {
19
- const path = join(coordRoot, ".harnery", "pid-map", String(pid));
20
- const row = `${instanceId}\t${platform}`;
113
+ const dir = join(coordRoot, ".harnery", "pid-map");
114
+ const path = join(dir, String(pid));
115
+ // Stamped at write time, when the pid provably belongs to the process we
116
+ // mean. A platform that will not say gets no third field rather than an empty
117
+ // one, so an unverifiable row stays byte-identical to what came before.
118
+ const startToken = processStartToken(pid);
119
+ const row = startToken
120
+ ? `${instanceId}\t${platform}\t${startToken}`
121
+ : `${instanceId}\t${platform}`;
21
122
  // Read-then-write idempotency: skip the rename churn when already current.
22
123
  if (existsSync(path)) {
23
124
  try {
@@ -28,9 +129,27 @@ export function writePidmapRow(coordRoot, pid, instanceId, platform) {
28
129
  /* fall through to write */
29
130
  }
30
131
  }
132
+ // Sweep on the way past the threshold. Writes are the only event guaranteed
133
+ // to happen while agents are active, so hanging the sweep here keeps the map
134
+ // bounded without a scheduled job, and the common write stays a single stat.
135
+ try {
136
+ if (existsSync(dir) && readdirSync(dir).length > PRUNE_AT_ROWS) {
137
+ prunePidmapDeadRows(coordRoot);
138
+ }
139
+ }
140
+ catch {
141
+ /* pruning is best-effort; never fail a write over it */
142
+ }
31
143
  atomicWrite(path, row);
32
144
  }
33
- /** True when any pid-map row for `instanceId` still belongs to a live process. */
145
+ /**
146
+ * True when any pid-map row for `instanceId` still belongs to a live process.
147
+ *
148
+ * Callers spend this answer on consequential things — whether a commit guard
149
+ * treats a heartbeat as a live peer, whether a name can be reclaimed from an
150
+ * abandoned session — so a re-issued pid must not read as that agent still
151
+ * running.
152
+ */
34
153
  export function instanceHasLivePid(coordRoot, instanceId) {
35
154
  const dir = join(coordRoot, ".harnery", "pid-map");
36
155
  if (!existsSync(dir))
@@ -43,19 +162,16 @@ export function instanceHasLivePid(coordRoot, instanceId) {
43
162
  catch {
44
163
  continue;
45
164
  }
46
- const owner = row.split("\t")[0]?.trim() ?? "";
165
+ const { instanceId: owner, startToken } = parsePidmapRow(row);
47
166
  if (owner !== instanceId)
48
167
  continue;
49
168
  const pid = Number.parseInt(f, 10);
50
169
  if (!Number.isFinite(pid))
51
170
  continue;
52
- try {
53
- process.kill(pid, 0); // signal 0 = liveness probe
171
+ // Rows that no longer name their process keep the scan going; one that does
172
+ // settles it.
173
+ if (rowStillNamesItsProcess(pid, startToken))
54
174
  return true;
55
- }
56
- catch {
57
- // ESRCH (no such process): pid-map entry is stale, keep scanning
58
- }
59
175
  }
60
176
  return false;
61
177
  }
@@ -73,8 +189,7 @@ export function removePidmapRowsForInstance(coordRoot, instanceId) {
73
189
  catch {
74
190
  continue;
75
191
  }
76
- const owner = row.split("\t")[0]?.trim() ?? "";
77
- if (owner !== instanceId)
192
+ if (parsePidmapRow(row).instanceId !== instanceId)
78
193
  continue;
79
194
  try {
80
195
  unlinkSync(join(dir, f));
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Process start-token probe: the "is this still the same process?" half of pid
3
+ * identity.
4
+ *
5
+ * A pid alone does not identify a process. Operating systems hand numbers back
6
+ * out, and faster than intuition suggests: one development machine runs a
7
+ * `pid_max` of 99999 against roughly 100 new processes a second, turning the
8
+ * whole space over about every quarter hour. Anything that remembers a pid for
9
+ * longer than that is remembering a number, not a process.
10
+ *
11
+ * A start token pins the pair down. Two processes can share a pid but never a
12
+ * pid *and* a start instant, so a recorded `(pid, token)` either still names the
13
+ * process that was recorded or plainly does not.
14
+ *
15
+ * The token is opaque on purpose. Callers compare it for equality and nothing
16
+ * else, which is why no clock arithmetic appears here: Linux reports ticks since
17
+ * boot and BSD reports a formatted date, and neither needs converting to be
18
+ * compared with itself. A platform that will not answer returns null, meaning
19
+ * "unverifiable" — callers fall back to a plain liveness check, which is where
20
+ * they were before this existed.
21
+ *
22
+ * Two rules keep comparison honest, and both exist because getting them wrong
23
+ * reports a *false* mismatch, which prunes a live row and hands the identity
24
+ * walk the same wrong answer this module was written to stop:
25
+ *
26
+ * 1. One machine uses one probe. The probe is chosen by capability and locked
27
+ * in, never fallen back from, so a transient read failure cannot answer in
28
+ * the other probe's dialect and read as a recycled pid.
29
+ * 2. A probe's output must not depend on who asked. The `ps` text is formatted
30
+ * through the caller's locale and timezone, so both are pinned; without that,
31
+ * two callers reading the same live process disagree.
32
+ */
33
+ /** Which probe answers "when did this process start?" on this machine. */
34
+ export type StartTokenProbe = "procfs" | "ps";
35
+ /**
36
+ * Which probe this machine uses, decided once.
37
+ *
38
+ * `HARNERY_PID_PROBE` forces the choice. That exists so the `ps` path can be
39
+ * exercised on a procfs machine — it is the same code on either OS, and running
40
+ * it only on hardware we happen to own is how a branch rots.
41
+ */
42
+ export declare function startTokenProbe(): StartTokenProbe;
43
+ /** Forget the cached probe and boot id. Tests only. */
44
+ export declare function resetStartTokenCaches(): void;
45
+ /**
46
+ * `l[<boot>.]<ticks since boot>` from `/proc/<pid>/stat`, with no subprocess.
47
+ *
48
+ * The tick count is field 22, counted from the end of the comm field rather
49
+ * than by splitting the whole line, because comm is the executable name in
50
+ * parentheses and may itself contain spaces and parens. The boot segment is
51
+ * dropped rather than faked when the kernel will not name its boot, which
52
+ * yields the pre-boot-id shape that `tokensAgree` still accepts.
53
+ */
54
+ export declare function procfsStartToken(pid: number): string | null;
55
+ /**
56
+ * `p<start date>` from one `ps -o lstart=`, for machines without procfs.
57
+ *
58
+ * Second resolution is ample: telling two runs of one pid apart would need the
59
+ * entire pid space to recycle inside a second. The runner is injectable so the
60
+ * parsing and the env pinning can be tested without a process to look at.
61
+ */
62
+ export declare function psStartToken(pid: number, run?: (pid: number) => {
63
+ status: number | null;
64
+ stdout: string;
65
+ }): string | null;
66
+ /**
67
+ * An opaque token identifying *this run* of `pid`, or null when the platform
68
+ * will not say.
69
+ */
70
+ export declare function processStartToken(pid: number): string | null;
71
+ /**
72
+ * Do two tokens describe the same run?
73
+ *
74
+ * Equality, plus one concession: a row written before tokens carried a boot
75
+ * segment recorded the tick count alone, so it is compared on the part it
76
+ * actually recorded. Refusing that would call every pre-upgrade row a recycled
77
+ * pid and prune a working machine's live rows the first time the new code ran.
78
+ * Such a row loses its reboot protection until the next write rewrites it,
79
+ * which is a strictly better position than the one it came from.
80
+ */
81
+ export declare function tokensAgree(recorded: string, current: string): boolean;
82
+ /**
83
+ * Does `pid` still name the process a row recorded?
84
+ *
85
+ * Three answers, and the difference between the last two matters:
86
+ *
87
+ * - `"match"` — recorded token, current token, same run. The row is trustworthy.
88
+ * - `"mismatch"` — both present and different. The pid was recycled. This is
89
+ * the case a liveness check cannot see, because a recycled pid is alive.
90
+ * - `"unverified"` — no recorded token (a row written before tokens existed) or
91
+ * no current one (an unsupported platform). Callers treat this the way they
92
+ * treated every row previously: trust it if the pid is alive.
93
+ */
94
+ export declare function checkPidToken(pid: number, recorded: string | undefined): "match" | "mismatch" | "unverified";
95
+ //# sourceMappingURL=proc-start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proc-start.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/proc-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,IAAI,CAAC;AAc9C;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAOjD;AAyBD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc3D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAiB,GAC7E,MAAM,GAAG,IAAI,CAUf;AAWD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,GAAG,UAAU,GAAG,YAAY,CAKrC"}