harnery 0.36.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/dist/commander.d.ts +9 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +10 -1
  4. package/dist/commands/admission.d.ts +21 -0
  5. package/dist/commands/admission.d.ts.map +1 -0
  6. package/dist/commands/admission.js +565 -0
  7. package/dist/commands/agents.d.ts +7 -0
  8. package/dist/commands/agents.d.ts.map +1 -1
  9. package/dist/commands/agents.js +148 -47
  10. package/dist/commands/artifacts.d.ts.map +1 -1
  11. package/dist/commands/artifacts.js +73 -2
  12. package/dist/commands/browse-ai.d.ts +2 -2
  13. package/dist/commands/browse-ai.d.ts.map +1 -1
  14. package/dist/commands/browse-ai.js +6 -4
  15. package/dist/commands/browse.d.ts.map +1 -1
  16. package/dist/commands/browse.js +360 -21
  17. package/dist/commands/diagnostics.d.ts +4 -0
  18. package/dist/commands/diagnostics.d.ts.map +1 -0
  19. package/dist/commands/diagnostics.js +229 -0
  20. package/dist/commands/fetch.js +1 -0
  21. package/dist/commands/files.d.ts +12 -0
  22. package/dist/commands/files.d.ts.map +1 -1
  23. package/dist/commands/files.js +54 -1
  24. package/dist/commands/governor.d.ts.map +1 -1
  25. package/dist/commands/governor.js +4 -0
  26. package/dist/commands/ledger-v3.d.ts.map +1 -1
  27. package/dist/commands/ledger-v3.js +71 -2
  28. package/dist/commands/qa-record.d.ts +144 -0
  29. package/dist/commands/qa-record.d.ts.map +1 -0
  30. package/dist/commands/qa-record.js +0 -0
  31. package/dist/commands/qa-run.d.ts +8 -0
  32. package/dist/commands/qa-run.d.ts.map +1 -0
  33. package/dist/commands/qa-run.js +396 -0
  34. package/dist/commands/qa-status.d.ts +71 -0
  35. package/dist/commands/qa-status.d.ts.map +1 -0
  36. package/dist/commands/qa-status.js +490 -0
  37. package/dist/commands/qa-verify.d.ts +40 -0
  38. package/dist/commands/qa-verify.d.ts.map +1 -0
  39. package/dist/commands/qa-verify.js +180 -0
  40. package/dist/commands/resources.d.ts +4 -0
  41. package/dist/commands/resources.d.ts.map +1 -0
  42. package/dist/commands/resources.js +29 -0
  43. package/dist/commands/review-pack.d.ts +4 -0
  44. package/dist/commands/review-pack.d.ts.map +1 -0
  45. package/dist/commands/review-pack.js +1001 -0
  46. package/dist/commands/supervisor.d.ts +4 -0
  47. package/dist/commands/supervisor.d.ts.map +1 -0
  48. package/dist/commands/supervisor.js +101 -0
  49. package/dist/commands/web.d.ts.map +1 -1
  50. package/dist/commands/web.js +17 -0
  51. package/dist/commands/workflow.d.ts.map +1 -1
  52. package/dist/commands/workflow.js +4 -0
  53. package/dist/core/agents/cli.js +7 -3
  54. package/dist/core/agents/health.d.ts +17 -0
  55. package/dist/core/agents/health.d.ts.map +1 -0
  56. package/dist/core/agents/health.js +109 -0
  57. package/dist/core/agents/mailbox.d.ts +92 -0
  58. package/dist/core/agents/mailbox.d.ts.map +1 -0
  59. package/dist/core/agents/mailbox.js +325 -0
  60. package/dist/core/agents/qa-signal.d.ts +111 -0
  61. package/dist/core/agents/qa-signal.d.ts.map +1 -0
  62. package/dist/core/agents/qa-signal.js +231 -0
  63. package/dist/core/agents/reconcile-coordination-v3.d.ts +44 -0
  64. package/dist/core/agents/reconcile-coordination-v3.d.ts.map +1 -0
  65. package/dist/core/agents/reconcile-coordination-v3.js +58 -0
  66. package/dist/core/agents/render/prompt-context.d.ts +5 -0
  67. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  68. package/dist/core/agents/render/prompt-context.js +34 -13
  69. package/dist/core/agents/render/session-context.d.ts +8 -0
  70. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  71. package/dist/core/agents/render/session-context.js +34 -3
  72. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  73. package/dist/core/agents/rules/commit-conflict.js +50 -1
  74. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  75. package/dist/core/agents/rules/stop-hook.js +6 -10
  76. package/dist/core/agents/session-finalizer-v3.js +4 -1
  77. package/dist/core/agents/session-name-display.d.ts +20 -5
  78. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  79. package/dist/core/agents/session-name-display.js +67 -7
  80. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  81. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  82. package/dist/core/agents/state/heartbeat-writer.d.ts +11 -0
  83. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  84. package/dist/core/agents/state/heartbeat-writer.js +17 -0
  85. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  86. package/dist/core/agents/state/live-coordination-view.js +1 -0
  87. package/dist/core/agents/state/live-coordination-writer.js +5 -0
  88. package/dist/core/artifacts/constants.d.ts +1 -1
  89. package/dist/core/artifacts/constants.js +1 -1
  90. package/dist/core/artifacts/index.d.ts +82 -6
  91. package/dist/core/artifacts/index.d.ts.map +1 -1
  92. package/dist/core/artifacts/index.js +410 -15
  93. package/dist/core/config.d.ts +20 -0
  94. package/dist/core/config.d.ts.map +1 -1
  95. package/dist/core/config.js +59 -0
  96. package/dist/core/diagnostics/advice.d.ts +9 -0
  97. package/dist/core/diagnostics/advice.d.ts.map +1 -0
  98. package/dist/core/diagnostics/advice.js +112 -0
  99. package/dist/core/diagnostics/bundle.d.ts +51 -0
  100. package/dist/core/diagnostics/bundle.d.ts.map +1 -0
  101. package/dist/core/diagnostics/bundle.js +968 -0
  102. package/dist/core/diagnostics/comparison.d.ts +5 -0
  103. package/dist/core/diagnostics/comparison.d.ts.map +1 -0
  104. package/dist/core/diagnostics/comparison.js +444 -0
  105. package/dist/core/diagnostics/contract.d.ts +245 -0
  106. package/dist/core/diagnostics/contract.d.ts.map +1 -0
  107. package/dist/core/diagnostics/contract.js +18 -0
  108. package/dist/core/diagnostics/identity.d.ts +3 -0
  109. package/dist/core/diagnostics/identity.d.ts.map +1 -0
  110. package/dist/core/diagnostics/identity.js +10 -0
  111. package/dist/core/diagnostics/index.d.ts +8 -0
  112. package/dist/core/diagnostics/index.d.ts.map +1 -0
  113. package/dist/core/diagnostics/index.js +7 -0
  114. package/dist/core/diagnostics/replay.d.ts +8 -0
  115. package/dist/core/diagnostics/replay.d.ts.map +1 -0
  116. package/dist/core/diagnostics/replay.js +213 -0
  117. package/dist/core/diagnostics/sanitize.d.ts +9 -0
  118. package/dist/core/diagnostics/sanitize.d.ts.map +1 -0
  119. package/dist/core/diagnostics/sanitize.js +84 -0
  120. package/dist/core/events/legacy-storage/compression.d.ts +13 -0
  121. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -0
  122. package/dist/core/events/legacy-storage/compression.js +107 -0
  123. package/dist/core/events/legacy-storage/index.d.ts +1 -0
  124. package/dist/core/events/legacy-storage/index.d.ts.map +1 -1
  125. package/dist/core/events/legacy-storage/index.js +1 -0
  126. package/dist/core/events/v3/archive-retention.d.ts +33 -0
  127. package/dist/core/events/v3/archive-retention.d.ts.map +1 -0
  128. package/dist/core/events/v3/archive-retention.js +195 -0
  129. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  130. package/dist/core/events/v3/bootstrap.js +10 -0
  131. package/dist/core/events/v3/capabilities.d.ts +1 -1
  132. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  133. package/dist/core/events/v3/capabilities.js +1 -0
  134. package/dist/core/events/v3/coordination-view.d.ts +3 -0
  135. package/dist/core/events/v3/coordination-view.d.ts.map +1 -1
  136. package/dist/core/events/v3/coordination-view.js +69 -8
  137. package/dist/core/events/v3/index.d.ts +1 -0
  138. package/dist/core/events/v3/index.d.ts.map +1 -1
  139. package/dist/core/events/v3/index.js +1 -0
  140. package/dist/core/events/v3/live-routing.d.ts.map +1 -1
  141. package/dist/core/events/v3/live-routing.js +1 -0
  142. package/dist/core/events/v3/producers/hook-base.d.ts +1 -1
  143. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  144. package/dist/core/events/v3/producers/hook-base.js +13 -2
  145. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  146. package/dist/core/events/v3/producers/intake.js +10 -2
  147. package/dist/core/events/v3/producers/recorder.d.ts +39 -0
  148. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  149. package/dist/core/events/v3/producers/recorder.js +350 -17
  150. package/dist/core/hooks/adapter/output.d.ts +8 -0
  151. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  152. package/dist/core/hooks/adapter/output.js +11 -1
  153. package/dist/core/hooks/cli.js +303 -219
  154. package/dist/core/hooks/health.d.ts +59 -0
  155. package/dist/core/hooks/health.d.ts.map +1 -0
  156. package/dist/core/hooks/health.js +84 -0
  157. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  158. package/dist/core/hooks/resolve/transcript.js +10 -3
  159. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  160. package/dist/core/hooks/session-name-presence.js +4 -1
  161. package/dist/core/qa-artifacts.d.ts +20 -0
  162. package/dist/core/qa-artifacts.d.ts.map +1 -0
  163. package/dist/core/qa-artifacts.js +110 -0
  164. package/dist/core/resources/contract.d.ts +96 -0
  165. package/dist/core/resources/contract.d.ts.map +1 -0
  166. package/dist/core/resources/contract.js +2 -0
  167. package/dist/core/resources/index.d.ts +6 -0
  168. package/dist/core/resources/index.d.ts.map +1 -0
  169. package/dist/core/resources/index.js +5 -0
  170. package/dist/core/resources/sampler.d.ts +34 -0
  171. package/dist/core/resources/sampler.d.ts.map +1 -0
  172. package/dist/core/resources/sampler.js +458 -0
  173. package/dist/core/resources/service-status.d.ts +3 -0
  174. package/dist/core/resources/service-status.d.ts.map +1 -0
  175. package/dist/core/resources/service-status.js +48 -0
  176. package/dist/core/resources/service.d.ts +26 -0
  177. package/dist/core/resources/service.d.ts.map +1 -0
  178. package/dist/core/resources/service.js +292 -0
  179. package/dist/core/resources/storage.d.ts +13 -0
  180. package/dist/core/resources/storage.d.ts.map +1 -0
  181. package/dist/core/resources/storage.js +30 -0
  182. package/dist/core/storage/atomic-json.d.ts +3 -0
  183. package/dist/core/storage/atomic-json.d.ts.map +1 -0
  184. package/dist/core/storage/atomic-json.js +15 -0
  185. package/dist/core/storage/builtins.d.ts.map +1 -1
  186. package/dist/core/storage/builtins.js +45 -6
  187. package/dist/core/storage/logger.d.ts +2 -0
  188. package/dist/core/storage/logger.d.ts.map +1 -1
  189. package/dist/core/storage/logger.js +2 -0
  190. package/dist/core/storage/query.d.ts +14 -0
  191. package/dist/core/storage/query.d.ts.map +1 -1
  192. package/dist/core/storage/query.js +51 -0
  193. package/dist/core/supervisor/activity.d.ts +4 -0
  194. package/dist/core/supervisor/activity.d.ts.map +1 -0
  195. package/dist/core/supervisor/activity.js +141 -0
  196. package/dist/core/supervisor/contract.d.ts +268 -0
  197. package/dist/core/supervisor/contract.d.ts.map +1 -0
  198. package/dist/core/supervisor/contract.js +35 -0
  199. package/dist/core/supervisor/explanations.d.ts +4 -0
  200. package/dist/core/supervisor/explanations.d.ts.map +1 -0
  201. package/dist/core/supervisor/explanations.js +71 -0
  202. package/dist/core/supervisor/findings.d.ts +20 -0
  203. package/dist/core/supervisor/findings.d.ts.map +1 -0
  204. package/dist/core/supervisor/findings.js +399 -0
  205. package/dist/core/supervisor/history.d.ts +9 -0
  206. package/dist/core/supervisor/history.d.ts.map +1 -0
  207. package/dist/core/supervisor/history.js +46 -0
  208. package/dist/core/supervisor/hook-health-alerts.d.ts +14 -0
  209. package/dist/core/supervisor/hook-health-alerts.d.ts.map +1 -0
  210. package/dist/core/supervisor/hook-health-alerts.js +70 -0
  211. package/dist/core/supervisor/hook-health-storage.d.ts +5 -0
  212. package/dist/core/supervisor/hook-health-storage.d.ts.map +1 -0
  213. package/dist/core/supervisor/hook-health-storage.js +24 -0
  214. package/dist/core/supervisor/hook-health.d.ts +72 -0
  215. package/dist/core/supervisor/hook-health.d.ts.map +1 -0
  216. package/dist/core/supervisor/hook-health.js +203 -0
  217. package/dist/core/supervisor/hooks.d.ts +10 -0
  218. package/dist/core/supervisor/hooks.d.ts.map +1 -0
  219. package/dist/core/supervisor/hooks.js +28 -0
  220. package/dist/core/supervisor/index.d.ts +15 -0
  221. package/dist/core/supervisor/index.d.ts.map +1 -0
  222. package/dist/core/supervisor/index.js +14 -0
  223. package/dist/core/supervisor/log-feed.d.ts +9 -0
  224. package/dist/core/supervisor/log-feed.d.ts.map +1 -0
  225. package/dist/core/supervisor/log-feed.js +103 -0
  226. package/dist/core/supervisor/service.d.ts +29 -0
  227. package/dist/core/supervisor/service.d.ts.map +1 -0
  228. package/dist/core/supervisor/service.js +450 -0
  229. package/dist/core/supervisor/services.d.ts +11 -0
  230. package/dist/core/supervisor/services.d.ts.map +1 -0
  231. package/dist/core/supervisor/services.js +155 -0
  232. package/dist/core/supervisor/status.d.ts +3 -0
  233. package/dist/core/supervisor/status.d.ts.map +1 -0
  234. package/dist/core/supervisor/status.js +48 -0
  235. package/dist/core/supervisor/storage.d.ts +30 -0
  236. package/dist/core/supervisor/storage.d.ts.map +1 -0
  237. package/dist/core/supervisor/storage.js +73 -0
  238. package/dist/core/supervisor/timeline.d.ts +3 -0
  239. package/dist/core/supervisor/timeline.d.ts.map +1 -0
  240. package/dist/core/supervisor/timeline.js +87 -0
  241. package/dist/core/workflow/admission.d.ts +23 -0
  242. package/dist/core/workflow/admission.d.ts.map +1 -0
  243. package/dist/core/workflow/admission.js +136 -0
  244. package/dist/core/workflow/engine.d.ts.map +1 -1
  245. package/dist/core/workflow/engine.js +77 -2
  246. package/dist/core/workflow/index.d.ts +3 -2
  247. package/dist/core/workflow/index.d.ts.map +1 -1
  248. package/dist/core/workflow/index.js +2 -1
  249. package/dist/core/workflow/proof.d.ts +2 -1
  250. package/dist/core/workflow/proof.d.ts.map +1 -1
  251. package/dist/core/workflow/proof.js +39 -1
  252. package/dist/core/workflow/run-state.d.ts +3 -2
  253. package/dist/core/workflow/run-state.d.ts.map +1 -1
  254. package/dist/core/workflow/run-state.js +8 -1
  255. package/dist/core/workflow/types.d.ts +33 -1
  256. package/dist/core/workflow/types.d.ts.map +1 -1
  257. package/dist/core/workflow/types.js +2 -1
  258. package/dist/lib/admission.d.ts +71 -0
  259. package/dist/lib/admission.d.ts.map +1 -0
  260. package/dist/lib/admission.js +264 -0
  261. package/dist/lib/agent-browser/client.d.ts +1 -1
  262. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  263. package/dist/lib/agent-browser/client.js +1 -5
  264. package/dist/lib/browser/capture-fidelity.d.ts +39 -0
  265. package/dist/lib/browser/capture-fidelity.d.ts.map +1 -0
  266. package/dist/lib/browser/capture-fidelity.js +84 -0
  267. package/dist/lib/browser/client.d.ts +46 -1
  268. package/dist/lib/browser/client.d.ts.map +1 -1
  269. package/dist/lib/browser/client.js +168 -9
  270. package/dist/lib/browser/critique.d.ts +38 -1
  271. package/dist/lib/browser/critique.d.ts.map +1 -1
  272. package/dist/lib/browser/critique.js +34 -6
  273. package/dist/lib/browser/index.d.ts +5 -1
  274. package/dist/lib/browser/index.d.ts.map +1 -1
  275. package/dist/lib/browser/index.js +4 -0
  276. package/dist/lib/browser/page-review-judge.d.ts +64 -0
  277. package/dist/lib/browser/page-review-judge.d.ts.map +1 -0
  278. package/dist/lib/browser/page-review-judge.js +270 -0
  279. package/dist/lib/browser/page-review-pack.d.ts +613 -0
  280. package/dist/lib/browser/page-review-pack.d.ts.map +1 -0
  281. package/dist/lib/browser/page-review-pack.js +1751 -0
  282. package/dist/lib/browser/qa-run-contracts.d.ts +377 -0
  283. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -0
  284. package/dist/lib/browser/qa-run-contracts.js +361 -0
  285. package/dist/lib/browser/qa-run.d.ts +141 -0
  286. package/dist/lib/browser/qa-run.d.ts.map +1 -0
  287. package/dist/lib/browser/qa-run.js +1283 -0
  288. package/dist/lib/browser/request-diagnostics.d.ts +13 -0
  289. package/dist/lib/browser/request-diagnostics.d.ts.map +1 -0
  290. package/dist/lib/browser/request-diagnostics.js +18 -0
  291. package/dist/lib/browser/tiling.d.ts +19 -0
  292. package/dist/lib/browser/tiling.d.ts.map +1 -1
  293. package/dist/lib/browser/tiling.js +28 -0
  294. package/dist/lib/cookies/client.d.ts +9 -0
  295. package/dist/lib/cookies/client.d.ts.map +1 -1
  296. package/dist/lib/cookies/client.js +197 -44
  297. package/dist/lib/cookies/extra.d.ts +18 -0
  298. package/dist/lib/cookies/extra.d.ts.map +1 -0
  299. package/dist/lib/cookies/extra.js +14 -0
  300. package/dist/lib/cookies/index.d.ts +2 -1
  301. package/dist/lib/cookies/index.d.ts.map +1 -1
  302. package/dist/lib/cookies/index.js +2 -1
  303. package/dist/lib/coord-root-id.d.ts +9 -0
  304. package/dist/lib/coord-root-id.d.ts.map +1 -0
  305. package/dist/lib/coord-root-id.js +17 -0
  306. package/dist/lib/durable-job.d.ts +124 -0
  307. package/dist/lib/durable-job.d.ts.map +1 -0
  308. package/dist/lib/durable-job.js +296 -0
  309. package/dist/lib/http/client.d.ts +7 -1
  310. package/dist/lib/http/client.d.ts.map +1 -1
  311. package/dist/lib/http/client.js +2 -0
  312. package/dist/lib/instructions/apply.d.ts +3 -1
  313. package/dist/lib/instructions/apply.d.ts.map +1 -1
  314. package/dist/lib/instructions/apply.js +74 -3
  315. package/dist/lib/instructions/templates.d.ts.map +1 -1
  316. package/dist/lib/instructions/templates.js +11 -2
  317. package/package.json +19 -2
  318. package/src/commander.ts +75 -1
  319. package/src/commands/admission.ts +699 -0
  320. package/src/commands/agents.ts +189 -50
  321. package/src/commands/artifacts.ts +132 -18
  322. package/src/commands/browse-ai.ts +10 -5
  323. package/src/commands/browse.ts +496 -20
  324. package/src/commands/diagnostics.ts +271 -0
  325. package/src/commands/fetch.ts +1 -0
  326. package/src/commands/files.ts +102 -2
  327. package/src/commands/governor.ts +8 -0
  328. package/src/commands/ledger-v3.ts +82 -0
  329. package/src/commands/qa-record.ts +682 -0
  330. package/src/commands/qa-run.ts +503 -0
  331. package/src/commands/qa-status.ts +608 -0
  332. package/src/commands/qa-verify.ts +238 -0
  333. package/src/commands/resources.ts +38 -0
  334. package/src/commands/review-pack.ts +1281 -0
  335. package/src/commands/supervisor.ts +141 -0
  336. package/src/commands/web.ts +19 -0
  337. package/src/commands/workflow.ts +8 -0
  338. package/src/core/agents/cli.ts +7 -3
  339. package/src/core/agents/health.ts +142 -0
  340. package/src/core/agents/mailbox.ts +373 -0
  341. package/src/core/agents/qa-signal.ts +261 -0
  342. package/src/core/agents/reconcile-coordination-v3.ts +77 -0
  343. package/src/core/agents/render/prompt-context.ts +42 -12
  344. package/src/core/agents/render/session-context.ts +37 -3
  345. package/src/core/agents/rules/commit-conflict.ts +58 -1
  346. package/src/core/agents/rules/stop-hook.ts +6 -12
  347. package/src/core/agents/session-finalizer-v3.ts +4 -1
  348. package/src/core/agents/session-name-display.ts +78 -7
  349. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  350. package/src/core/agents/state/heartbeat-writer.ts +23 -0
  351. package/src/core/agents/state/live-coordination-view.ts +1 -0
  352. package/src/core/agents/state/live-coordination-writer.ts +5 -0
  353. package/src/core/artifacts/constants.ts +1 -1
  354. package/src/core/artifacts/index.ts +565 -30
  355. package/src/core/config.ts +134 -2
  356. package/src/core/diagnostics/advice.ts +147 -0
  357. package/src/core/diagnostics/bundle.ts +1250 -0
  358. package/src/core/diagnostics/comparison.ts +565 -0
  359. package/src/core/diagnostics/contract.ts +311 -0
  360. package/src/core/diagnostics/identity.ts +11 -0
  361. package/src/core/diagnostics/index.ts +7 -0
  362. package/src/core/diagnostics/replay.ts +314 -0
  363. package/src/core/diagnostics/sanitize.ts +97 -0
  364. package/src/core/events/legacy-storage/compression.ts +130 -0
  365. package/src/core/events/legacy-storage/index.ts +1 -0
  366. package/src/core/events/v3/archive-retention.ts +251 -0
  367. package/src/core/events/v3/bootstrap.ts +10 -0
  368. package/src/core/events/v3/capabilities.ts +2 -0
  369. package/src/core/events/v3/coordination-view.ts +100 -11
  370. package/src/core/events/v3/index.ts +9 -0
  371. package/src/core/events/v3/live-routing.ts +1 -0
  372. package/src/core/events/v3/producers/hook-base.ts +14 -2
  373. package/src/core/events/v3/producers/intake.ts +10 -2
  374. package/src/core/events/v3/producers/recorder.ts +420 -17
  375. package/src/core/hooks/adapter/output.ts +12 -1
  376. package/src/core/hooks/cli.ts +332 -247
  377. package/src/core/hooks/health.ts +146 -0
  378. package/src/core/hooks/resolve/transcript.ts +10 -3
  379. package/src/core/hooks/session-name-presence.ts +6 -1
  380. package/src/core/qa-artifacts.ts +126 -0
  381. package/src/core/resources/contract.ts +105 -0
  382. package/src/core/resources/index.ts +5 -0
  383. package/src/core/resources/sampler.ts +590 -0
  384. package/src/core/resources/service-status.ts +62 -0
  385. package/src/core/resources/service.ts +337 -0
  386. package/src/core/resources/storage.ts +44 -0
  387. package/src/core/storage/atomic-json.ts +15 -0
  388. package/src/core/storage/builtins.ts +62 -6
  389. package/src/core/storage/logger.ts +2 -0
  390. package/src/core/storage/query.ts +58 -0
  391. package/src/core/supervisor/activity.ts +193 -0
  392. package/src/core/supervisor/contract.ts +312 -0
  393. package/src/core/supervisor/explanations.ts +104 -0
  394. package/src/core/supervisor/findings.ts +788 -0
  395. package/src/core/supervisor/history.ts +61 -0
  396. package/src/core/supervisor/hook-health-alerts.ts +108 -0
  397. package/src/core/supervisor/hook-health-storage.ts +25 -0
  398. package/src/core/supervisor/hook-health.ts +301 -0
  399. package/src/core/supervisor/hooks.ts +35 -0
  400. package/src/core/supervisor/index.ts +14 -0
  401. package/src/core/supervisor/log-feed.ts +131 -0
  402. package/src/core/supervisor/service.ts +511 -0
  403. package/src/core/supervisor/services.ts +203 -0
  404. package/src/core/supervisor/status.ts +59 -0
  405. package/src/core/supervisor/storage.ts +126 -0
  406. package/src/core/supervisor/timeline.ts +133 -0
  407. package/src/core/workflow/admission.ts +215 -0
  408. package/src/core/workflow/engine.ts +93 -1
  409. package/src/core/workflow/index.ts +10 -0
  410. package/src/core/workflow/proof.ts +54 -1
  411. package/src/core/workflow/run-state.ts +13 -1
  412. package/src/core/workflow/types.ts +39 -1
  413. package/src/lib/admission.ts +347 -0
  414. package/src/lib/agent-browser/client.ts +2 -10
  415. package/src/lib/browser/capture-fidelity.ts +98 -0
  416. package/src/lib/browser/client.ts +212 -10
  417. package/src/lib/browser/critique.ts +62 -7
  418. package/src/lib/browser/index.ts +65 -0
  419. package/src/lib/browser/page-review-judge.ts +360 -0
  420. package/src/lib/browser/page-review-pack.ts +2384 -0
  421. package/src/lib/browser/qa-run-contracts.ts +743 -0
  422. package/src/lib/browser/qa-run.ts +1462 -0
  423. package/src/lib/browser/request-diagnostics.ts +27 -0
  424. package/src/lib/browser/tiling.ts +32 -0
  425. package/src/lib/cookies/client.ts +228 -42
  426. package/src/lib/cookies/extra.ts +28 -0
  427. package/src/lib/cookies/index.ts +2 -0
  428. package/src/lib/coord-root-id.ts +23 -0
  429. package/src/lib/durable-job.ts +407 -0
  430. package/src/lib/http/client.ts +13 -1
  431. package/src/lib/instructions/apply.ts +81 -2
  432. package/src/lib/instructions/templates.ts +11 -2
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The canonical supervisor reconcile pass.
3
+ *
4
+ * Two things have to happen together, in this order:
5
+ *
6
+ * 1. `staleSweep` prunes the disposable coordination cache under
7
+ * `.harnery/active` (plus orphaned pid-map and `.last-peer-hash` files)
8
+ * and leaves a durable lifecycle observation for every deletion.
9
+ * 2. `reconcileSessionFinalizationV3` turns those observations, along with
10
+ * archive/idle/run/host signals, into session finalization requests.
11
+ *
12
+ * Running the sweep first is what makes a single pass self-contained: the
13
+ * `lifecycle.sweep_observed` rows it writes are visible to the finalizer in
14
+ * the same pass, so a stale session is both reaped and finalized without
15
+ * waiting for a later invocation.
16
+ *
17
+ * ADR 0077 already specified that `agents reconcile` and session start
18
+ * discover stale-sweep in the same pass. Both entry points had drifted to
19
+ * calling only the finalizer, so the sweep never ran outside its own
20
+ * subcommand. This module is the single composition both now share; adding a
21
+ * caller means calling this, not re-composing the two halves.
22
+ */
23
+ import { type ReconcileSessionFinalizationOptionsV3, type ReconcileSessionFinalizationResultV3 } from "./session-finalizer-v3.js";
24
+ export interface ReconcileCoordinationResultV3 extends ReconcileSessionFinalizationResultV3 {
25
+ /** Heartbeat cache files deleted from `.harnery/active` by the sweep. */
26
+ swept_heartbeats: number;
27
+ /** Orphaned `.harnery/pid-map` entries deleted by the sweep. */
28
+ swept_pidmaps: number;
29
+ /** Dead-owner `.last-peer-hash.*` files deleted by the sweep. */
30
+ swept_peer_hashes: number;
31
+ }
32
+ /**
33
+ * Sweep the disposable coordination cache, then reconcile session
34
+ * finalization, and report both sets of counts.
35
+ *
36
+ * The sweep owns its own safety contract (configured coordination freshness
37
+ * for valid rows, an additional stale-mtime gate for malformed or missing
38
+ * timestamps, and a durable audit record before any deletion, failing closed
39
+ * when neither can be written). Nothing here loosens it, and a sweep failure
40
+ * never blocks finalization: the cache is a projection that heals on the
41
+ * owning agent's next tool call, while the V3 ledger is authoritative.
42
+ */
43
+ export declare function reconcileCoordinationV3(coordRoot: string, options?: ReconcileSessionFinalizationOptionsV3): ReconcileCoordinationResultV3;
44
+ //# sourceMappingURL=reconcile-coordination-v3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile-coordination-v3.d.ts","sourceRoot":"","sources":["../../../src/core/agents/reconcile-coordination-v3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,KAAK,qCAAqC,EAC1C,KAAK,oCAAoC,EAE1C,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,6BAA8B,SAAQ,oCAAoC;IACzF,yEAAyE;IACzE,gBAAgB,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qCAA0C,GAClD,6BAA6B,CAuB/B"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The canonical supervisor reconcile pass.
3
+ *
4
+ * Two things have to happen together, in this order:
5
+ *
6
+ * 1. `staleSweep` prunes the disposable coordination cache under
7
+ * `.harnery/active` (plus orphaned pid-map and `.last-peer-hash` files)
8
+ * and leaves a durable lifecycle observation for every deletion.
9
+ * 2. `reconcileSessionFinalizationV3` turns those observations, along with
10
+ * archive/idle/run/host signals, into session finalization requests.
11
+ *
12
+ * Running the sweep first is what makes a single pass self-contained: the
13
+ * `lifecycle.sweep_observed` rows it writes are visible to the finalizer in
14
+ * the same pass, so a stale session is both reaped and finalized without
15
+ * waiting for a later invocation.
16
+ *
17
+ * ADR 0077 already specified that `agents reconcile` and session start
18
+ * discover stale-sweep in the same pass. Both entry points had drifted to
19
+ * calling only the finalizer, so the sweep never ran outside its own
20
+ * subcommand. This module is the single composition both now share; adding a
21
+ * caller means calling this, not re-composing the two halves.
22
+ */
23
+ import { reconcileSessionFinalizationV3, } from "./session-finalizer-v3.js";
24
+ import { staleSweep } from "./state/stale-sweep.js";
25
+ /**
26
+ * Sweep the disposable coordination cache, then reconcile session
27
+ * finalization, and report both sets of counts.
28
+ *
29
+ * The sweep owns its own safety contract (configured coordination freshness
30
+ * for valid rows, an additional stale-mtime gate for malformed or missing
31
+ * timestamps, and a durable audit record before any deletion, failing closed
32
+ * when neither can be written). Nothing here loosens it, and a sweep failure
33
+ * never blocks finalization: the cache is a projection that heals on the
34
+ * owning agent's next tool call, while the V3 ledger is authoritative.
35
+ */
36
+ export function reconcileCoordinationV3(coordRoot, options = {}) {
37
+ let sweptHeartbeats = 0;
38
+ let sweptPidmaps = 0;
39
+ let sweptPeerHashes = 0;
40
+ const sweepDiagnostics = [];
41
+ try {
42
+ const sweep = staleSweep(coordRoot);
43
+ sweptHeartbeats = sweep.heartbeatsRemoved.length;
44
+ sweptPidmaps = sweep.pidmapsRemoved;
45
+ sweptPeerHashes = sweep.peerHashesRemoved;
46
+ }
47
+ catch (error) {
48
+ sweepDiagnostics.push(`stale_sweep_failed:${error instanceof Error ? error.message : String(error)}`);
49
+ }
50
+ const result = reconcileSessionFinalizationV3(coordRoot, options);
51
+ return {
52
+ ...result,
53
+ diagnostics: [...sweepDiagnostics, ...result.diagnostics],
54
+ swept_heartbeats: sweptHeartbeats,
55
+ swept_pidmaps: sweptPidmaps,
56
+ swept_peer_hashes: sweptPeerHashes,
57
+ };
58
+ }
@@ -49,6 +49,11 @@ export interface PromptContextOpts {
49
49
  /** When true, append the non-deduplicated Codex status-footer reminder.
50
50
  * The caller enables this only for human-facing Codex sessions. */
51
51
  statusFooterNudge?: boolean;
52
+ /** Adapter id this prompt is being rendered for. Gates the mailbox drain:
53
+ * an adapter that cannot receive UserPromptSubmit context must not consume
54
+ * messages here, or they are removed from the queue and never shown. When
55
+ * omitted, delivery is attempted (the default route can receive). */
56
+ adapter?: string;
52
57
  /** Adapter id whose Stop hook ENFORCES the end-of-turn ritual (task_set +
53
58
  * status check). When set, append a fresh per-prompt reminder of that
54
59
  * contract so the model satisfies it before ending the turn instead of
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AA4CH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;8EAC0E;IAC1E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;kFAC8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;kFAC8E;IAC9E,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;uEACmE;IACnE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;wBAIoB;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CA8DnE"}
1
+ {"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AA+CH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;8EAC0E;IAC1E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;kFAC8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;kFAC8E;IAC9E,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;uEACmE;IACnE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;yEAGqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;wBAIoB;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAqFnE"}
@@ -37,10 +37,12 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, w
37
37
  import { join } from "node:path";
38
38
  import { coordEnv } from "../../../lib/env.js";
39
39
  import { endOfTurnStatusCommand, hostPromptReminder, resolveBinName } from "../../config.js";
40
+ import { canReceiveContext } from "../../hooks/adapter/output.js";
40
41
  import { readRemoteMachines } from "../../presence/index.js";
42
+ import { drainMailbox, formatMailboxDelivery } from "../mailbox.js";
41
43
  import { sessionNameDisplayBlock, sessionNameDisplayPending } from "../session-name-display.js";
42
44
  import { readLiveCoordinationRow, readLiveCoordinationRows, } from "../state/live-coordination-view.js";
43
- import { formatPendingCouncils } from "./session-context.js";
45
+ import { formatPendingCouncils, recordDeliveredMessages } from "./session-context.js";
44
46
  /**
45
47
  * Build the combined UserPromptSubmit additionalContext string. Returns "" when
46
48
  * nothing has changed since the last call (so the caller can skip the JSON emit).
@@ -50,19 +52,39 @@ import { formatPendingCouncils } from "./session-context.js";
50
52
  * councils are pending, matching the bash behavior).
51
53
  */
52
54
  export function renderPromptContext(opts) {
53
- const { coordRoot, instanceId, sessionId, agentName, sessionNameNudge, taskNudge, hostPromptReminder: promptReminderEnabled, statusFooterNudge, turnRitualNudge, } = opts;
55
+ const { coordRoot, instanceId, sessionId, agentName, sessionNameNudge, taskNudge, hostPromptReminder: promptReminderEnabled, statusFooterNudge, turnRitualNudge, adapter, } = opts;
54
56
  const sections = [];
55
- // 1. Peer table with hash dedup.
57
+ // The hook route does not pass --name, so resolve it from the heartbeat.
58
+ // Every name-addressed section below (messages, councils) is silently
59
+ // skipped without it.
60
+ const resolvedName = agentName ?? readLiveCoordinationRow(coordRoot, instanceId)?.name;
61
+ // 1. Peer messages. Drained first and never deduped: a message is delivered
62
+ // exactly once, and it is the one section here that carries new information
63
+ // from another agent rather than a reminder.
64
+ //
65
+ // Draining is gated on the adapter being able to receive this event's
66
+ // context. Cursor cannot, so draining there would empty the queue into output
67
+ // that is discarded; those sessions get their messages at SessionStart, which
68
+ // Cursor does deliver.
69
+ const canDeliverHere = !adapter || canReceiveContext(adapter, "UserPromptSubmit");
70
+ if (resolvedName && canDeliverHere) {
71
+ const delivered = drainMailbox(coordRoot, resolvedName);
72
+ if (delivered.length > 0) {
73
+ recordDeliveredMessages(instanceId, delivered);
74
+ sections.push(formatMailboxDelivery(coordRoot, delivered));
75
+ }
76
+ }
77
+ // 2. Peer table with hash dedup.
56
78
  const peerTable = computePeerTableIfChanged(coordRoot, instanceId, sessionId);
57
79
  if (peerTable)
58
80
  sections.push(peerTable);
59
- // 2. Council pending with hash dedup.
60
- if (agentName) {
61
- const councilMsg = computeCouncilPendingIfChanged(coordRoot, instanceId, agentName);
81
+ // 3. Council pending with hash dedup.
82
+ if (resolvedName) {
83
+ const councilMsg = computeCouncilPendingIfChanged(coordRoot, instanceId, resolvedName);
62
84
  if (councilMsg)
63
85
  sections.push(councilMsg);
64
86
  }
65
- // 3. First-session naming for every adapter, plus unset/stale-task reminders
87
+ // 4. First-session naming for every adapter, plus unset/stale-task reminders
66
88
  // for Cursor/Codex. One state machine avoids a second generic "task unset"
67
89
  // reminder immediately after the first-session reminder has been deduped.
68
90
  if (sessionNameNudge || taskNudge) {
@@ -73,7 +95,7 @@ export function renderPromptContext(opts) {
73
95
  if (nudgeMsg)
74
96
  sections.push(nudgeMsg);
75
97
  }
76
- // 4. A supported adapter gets the host reminder on every prompt. The text is
98
+ // 5. A supported adapter gets the host reminder on every prompt. The text is
77
99
  // intentionally not hash-deduped or persisted into runtime state: freshness
78
100
  // is the feature, and the project config remains its only durable copy.
79
101
  if (promptReminderEnabled) {
@@ -81,7 +103,7 @@ export function renderPromptContext(opts) {
81
103
  if (reminder)
82
104
  sections.push(reminder);
83
105
  }
84
- // 5. Codex gets this on every prompt. It is intentionally not hash-deduped:
106
+ // 6. Codex gets this on every prompt. It is intentionally not hash-deduped:
85
107
  // the reminder must be fresh in the model's context when it writes the reply.
86
108
  // Missing it is harmless because Codex Stop enforcement remains observe-only.
87
109
  if (statusFooterNudge) {
@@ -89,7 +111,7 @@ export function renderPromptContext(opts) {
89
111
  if (statusMsg)
90
112
  sections.push(statusMsg);
91
113
  }
92
- // 6. Adapters whose Stop hook ENFORCES the turn ritual get a fresh reminder
114
+ // 7. Adapters whose Stop hook ENFORCES the turn ritual get a fresh reminder
93
115
  // on every prompt. Reminding before the reply is drafted is far cheaper than
94
116
  // the bounce-and-retry the Stop hook otherwise forces: enforcement-only
95
117
  // compliance was measured missing on a double-digit share of turns. Not
@@ -113,9 +135,8 @@ function renderTurnRitualReminder(coordRoot, selfInstanceId, adapter) {
113
135
  return "";
114
136
  const bin = resolveBinName(coordRoot);
115
137
  const statusCommand = endOfTurnStatusCommand(coordRoot);
116
- // Claude Code renders the status box only if the reply carries it; Cursor
117
- // shows the box inline on its own, so it is never asked for a second copy.
118
- const paste = adapter === "claude-code"
138
+ // Claude Code and Cursor can both enforce completed assistant reply text.
139
+ const paste = adapter === "claude-code" || adapter === "cursor"
119
140
  ? " and paste its output verbatim in a fenced code block at the end of your reply"
120
141
  : "";
121
142
  return (`Turn ritual (Stop-enforced): on any turn that uses tools, run \`${bin} agents set-task "<short focus>"\` at some point during the turn (required every turn, even when the focus is unchanged; pass an empty string when there is no meaningful focus), then make \`${statusCommand}\` your final tool call${paste}. ` +
@@ -7,6 +7,7 @@
7
7
  * string.
8
8
  */
9
9
  import { statSync } from "node:fs";
10
+ import { type MailboxMessageV1 } from "../mailbox.js";
10
11
  export interface RenderOpts {
11
12
  coordRoot: string;
12
13
  instanceId: string;
@@ -21,6 +22,13 @@ export interface RenderOpts {
21
22
  * string (or "" if there's nothing to say).
22
23
  */
23
24
  export declare function renderSessionContext(opts: RenderOpts): string;
25
+ /**
26
+ * Write delivered peer messages into the recipient's own journal, so they
27
+ * survive context compaction the way any other coordination breadcrumb does.
28
+ * A message the sender already journaled (the recipient was live when it was
29
+ * sent) is skipped to avoid a duplicate entry.
30
+ */
31
+ export declare function recordDeliveredMessages(instanceId: string, messages: readonly MailboxMessageV1[]): void;
24
32
  /**
25
33
  * Returns a list of commit-guard wiring issues (empty when wired). Portable
26
34
  * across host projects: it asserts the FUNCTIONAL property ("does this repo's
@@ -1 +1 @@
1
- {"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/session-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAyC,QAAQ,EAAE,MAAM,SAAS,CAAC;AAwB1E,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;+BAC2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA6E7D;AAkID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBvD;AA0DD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAiDlF;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,iBAAW,CAAC"}
1
+ {"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/session-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAyC,QAAQ,EAAE,MAAM,SAAS,CAAC;AAM1E,OAAO,EAAuC,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAoB3F,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;+BAC2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAwF7D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GACpC,IAAI,CAUN;AAkID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBvD;AA0DD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAiDlF;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,iBAAW,CAAC"}
@@ -11,7 +11,9 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
  import { resolveBinName, resolveHooksSetupHint } from "../../config.js";
13
13
  import { harneryVersion, loadAdapterWiring } from "../../hooks/adapter/wiring.js";
14
+ import { appendEntry } from "../../journal/index.js";
14
15
  import { readRemoteMachines } from "../../presence/index.js";
16
+ import { drainMailbox, formatMailboxDelivery } from "../mailbox.js";
15
17
  import { readLiveCoordinationRows } from "../state/live-coordination-view.js";
16
18
  import { readForkParent } from "../state/names.js";
17
19
  /**
@@ -50,13 +52,23 @@ export function renderSessionContext(opts) {
50
52
  if (isLinkedWorktree(coordRoot)) {
51
53
  messages.push(`Running inside worktree ${process.cwd()}. The coord layer is scoped to this worktree only; use \`${resolveBinName(coordRoot)} worktree diff\` to check for conflicts against sibling worktrees.`);
52
54
  }
53
- // 3. Council invites
55
+ // 3. Peer messages queued for this name while no session held it. Draining
56
+ // here is what makes `agents ping` safe to use on a dormant agent: the sender
57
+ // never has to know whether the recipient is running.
58
+ if (agentName) {
59
+ const delivered = drainMailbox(coordRoot, agentName);
60
+ if (delivered.length > 0) {
61
+ recordDeliveredMessages(instanceId, delivered);
62
+ messages.push(formatMailboxDelivery(coordRoot, delivered));
63
+ }
64
+ }
65
+ // 4. Council invites
54
66
  if (agentName) {
55
67
  const councilMsg = formatPendingCouncils(coordRoot, agentName);
56
68
  if (councilMsg)
57
69
  messages.push(councilMsg);
58
70
  }
59
- // 4. Commit-guard wiring check
71
+ // 5. Commit-guard wiring check
60
72
  const wiringIssues = checkWiring(coordRoot);
61
73
  if (wiringIssues.length > 0) {
62
74
  const hint = resolveHooksSetupHint(coordRoot);
@@ -66,7 +78,7 @@ export function renderSessionContext(opts) {
66
78
  const wiringSummary = `Coordination hooks are NOT wired: the E-guard will not block conflicting commits, and post-commit claim pruning will not run. ${fix} Detected:\n${wiringIssues.map((i) => ` - ${i}`).join("\n")}`;
67
79
  messages.push(wiringSummary);
68
80
  }
69
- // 5. Adapter-hook drift: a harnery upgrade changed the hook set, but this
81
+ // 6. Adapter-hook drift: a harnery upgrade changed the hook set, but this
70
82
  // project's settings file hasn't been re-wired. Only fires for a adapter the
71
83
  // project already opted into (≥1 hook wired), so it never nags a project that
72
84
  // simply has a settings file. Remedy is always `<bin> init` (idempotent).
@@ -89,6 +101,25 @@ export function renderSessionContext(opts) {
89
101
  }
90
102
  return messages.join("\n\n");
91
103
  }
104
+ /**
105
+ * Write delivered peer messages into the recipient's own journal, so they
106
+ * survive context compaction the way any other coordination breadcrumb does.
107
+ * A message the sender already journaled (the recipient was live when it was
108
+ * sent) is skipped to avoid a duplicate entry.
109
+ */
110
+ export function recordDeliveredMessages(instanceId, messages) {
111
+ for (const m of messages) {
112
+ if (m.journaled)
113
+ continue;
114
+ try {
115
+ appendEntry(instanceId, "handoff", `from agent-${m.from_name}: ${m.body}`);
116
+ }
117
+ catch {
118
+ // A journal write failure must never cost the delivery itself; the
119
+ // message is already in the rendered context the model reads.
120
+ }
121
+ }
122
+ }
92
123
  /** One-line-per-agent view of sessions on other machines (presence refs). */
93
124
  function formatRemoteMachines(coordRoot) {
94
125
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"commit-conflict.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/commit-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAkBH,UAAU,QAAQ;IAChB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;wEACoE;IACpE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,mBAAmB,CAuHhG"}
1
+ {"version":3,"file":"commit-conflict.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/commit-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAoBH,UAAU,QAAQ;IAChB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;wEACoE;IACpE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,mBAAmB,CAgIhG"}
@@ -24,6 +24,7 @@ import { appendFileSync, existsSync } from "node:fs";
24
24
  import { basename, dirname, join } from "node:path";
25
25
  import { coordFreshnessSeconds } from "../../config.js";
26
26
  import { resolveOwner } from "../../hooks/resolve/owner.js";
27
+ import { recordLiveClaimChangeV3 } from "../live-authority-v3.js";
27
28
  import { readLiveCoordinationRows } from "../state/live-coordination-view.js";
28
29
  import { instanceHasLivePid } from "../state/pidmap.js";
29
30
  export function evaluateCommit(coordRoot, req) {
@@ -120,6 +121,14 @@ export function evaluateCommit(coordRoot, req) {
120
121
  suppressed_self_attribution: true,
121
122
  };
122
123
  }
124
+ // A blocked commit proves this session has work in every staged path, even
125
+ // when the edit hook never acquired a claim (for example, a shell edit made
126
+ // just before another session claimed the same file). Keep those paths on
127
+ // the blocked session's durable claim set. The original holder's later
128
+ // commit will then see the overlap instead of silently sweeping this
129
+ // session's working-tree hunks. A successful later commit releases clean
130
+ // paths through the existing post-commit hook.
131
+ retainBlockedStagedPaths(coordRoot, me, mySession, req.staged_paths, peers);
123
132
  return {
124
133
  allow: false,
125
134
  exit_code: 1,
@@ -132,6 +141,37 @@ export function evaluateCommit(coordRoot, req) {
132
141
  " active agents:",
133
142
  };
134
143
  }
144
+ function retainBlockedStagedPaths(coordRoot, instanceId, sessionId, stagedPaths, peers) {
145
+ if (instanceId === "__unattributed__")
146
+ return;
147
+ const self = peers.find((peer) => peer.instance_id === instanceId);
148
+ if (!self)
149
+ return;
150
+ for (const path of stagedPaths) {
151
+ try {
152
+ recordLiveClaimChangeV3({
153
+ coordRoot,
154
+ owner: instanceId,
155
+ nativeSessionId: self.session_id ?? sessionId,
156
+ adapter: adapterFromPlatform(self.platform),
157
+ operation: "acquired",
158
+ path,
159
+ access: "write",
160
+ });
161
+ }
162
+ catch {
163
+ // The commit is already blocked. Coordination remains fail-open for
164
+ // authority-write failures so a damaged cache cannot brick Git.
165
+ }
166
+ }
167
+ }
168
+ function adapterFromPlatform(platform) {
169
+ if (platform === "cursor")
170
+ return "cursor";
171
+ if (platform === "codex")
172
+ return "codex";
173
+ return "claude-code";
174
+ }
135
175
  function findOverlap(staged, files, coordRoot, stagedIsGitlink) {
136
176
  for (const claimed of files) {
137
177
  if (claimed === staged)
@@ -188,13 +228,22 @@ function isGitlinkInIndex(coordRoot, path) {
188
228
  return result.stdout.trim().startsWith("160000 ");
189
229
  }
190
230
  function isHolderSelfAttributed(coordRoot, holderId, stagedSet, peers) {
231
+ const holder = peers.find((p) => p.instance_id === holderId);
232
+ // Windows-hosted Codex sessions cross a process boundary into WSL, so they
233
+ // deliberately have no usable PID anchor. Their canonical V3 session row is
234
+ // the foreign-identity evidence instead. Treating that row as a transient
235
+ // self identity would suppress the symmetric claim retained after a blocked
236
+ // commit and reopen the working-tree sweep this guard is meant to stop.
237
+ if (holder?.platform === "codex" && holder.session_id) {
238
+ debugGate(coordRoot, { holder: holderId, gate: "B", reason: "canonical_codex_session" });
239
+ return false;
240
+ }
191
241
  // Gate B (cheaper): live foreign pid-map entry blocks self-attribution.
192
242
  if (holderHasLiveForeignPid(coordRoot, holderId)) {
193
243
  debugGate(coordRoot, { holder: holderId, gate: "B", reason: "live_pid_anchor" });
194
244
  return false;
195
245
  }
196
246
  // Gate A: every held path is either in the staged set or already clean in HEAD.
197
- const holder = peers.find((p) => p.instance_id === holderId);
198
247
  if (!holder) {
199
248
  debugGate(coordRoot, { holder: holderId, gate: "A", reason: "holder_not_found" });
200
249
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"stop-hook.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/stop-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAM3D,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAInE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,aAAa,CA6BvF;AAED,oFAAoF;AACpF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,OAAO,EAAE,GACzB,aAAa,CAuIf"}
1
+ {"version":3,"file":"stop-hook.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/stop-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAM3D,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAInE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,aAAa,CA6BvF;AAED,oFAAoF;AACpF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,OAAO,EAAE,GACzB,aAAa,CAiIf"}
@@ -91,17 +91,13 @@ export function evaluateStopHookV3Events(coordRoot, req, events) {
91
91
  .map((event) => observedBoolean(event.payload.ritual?.status_box_present_strict))
92
92
  .filter((value) => value !== undefined);
93
93
  const currentStrictBox = claudeContinuation ? req.status_box_present_strict : undefined;
94
- if (req.adapter !== "cursor" && latestBox === undefined && currentStrictBox === undefined) {
94
+ if (latestBox === undefined && currentStrictBox === undefined) {
95
95
  return evidenceUnavailable("turn.completed has no observed V3 assistant-text status-box result; Stop remains fail-open");
96
96
  }
97
- const ackPresent = req.adapter === "cursor"
98
- ? requiredStatusChecked
99
- : claudeContinuation
100
- ? strictBoxObservations.includes(true) || currentStrictBox === true
101
- : latestBox === true;
102
- const ackBlock = req.adapter === "cursor"
103
- ? () => rule13Block(coordRoot, req.session_id)
104
- : () => rule23Block(coordRoot, req.session_id);
97
+ const ackPresent = claudeContinuation
98
+ ? strictBoxObservations.includes(true) || currentStrictBox === true
99
+ : latestBox === true;
100
+ const ackBlock = () => rule23Block(coordRoot, req.session_id);
105
101
  // Pure prose turns keep the human-visible acknowledgement but do not require
106
102
  // task or final-status mutations. This preserves the pre-V3 policy exactly.
107
103
  if (!usedTool) {
@@ -260,7 +256,7 @@ function sessionIdSuffix(sessionId) {
260
256
  * The verdict keeps the first failure's rule id, so the `blocked_rule`
261
257
  * telemetry and the adapter's `rule=` stderr contract are unchanged; the
262
258
  * reason enumerates the rest so a single continuation can repair all of them.
263
- * Cursor maps the acknowledgement onto rule 1/3, so identical rules dedupe.
259
+ * Duplicate rules are folded so one continuation repairs every missing signal.
264
260
  */
265
261
  function mergeStopBlocks(blocks) {
266
262
  let primary;
@@ -450,7 +450,10 @@ function observeCascades(coordRoot, route, records, events, lastByGeneration, no
450
450
  }
451
451
  }
452
452
  if (event.event_type === "lifecycle.sweep_observed" &&
453
- event.payload.observation === "stale_sweep") {
453
+ // stale-sweep.ts emits the observation as `stale_heartbeat`; the
454
+ // finalization request it produces is the one named `stale_sweep`.
455
+ // Matching the request name here made this branch unreachable.
456
+ event.payload.observation === "stale_heartbeat") {
454
457
  const record = records.find(({ state }) => !state.terminal && state.instance_id === event.payload.subject_instance_id);
455
458
  const last = record ? lastByGeneration.get(record.state.generation_id) : undefined;
456
459
  if (!record?.state.last_event_id || !last)
@@ -7,9 +7,29 @@
7
7
  export interface SessionNameDisplayState {
8
8
  suggested_session_name?: string;
9
9
  session_name_seen_for?: string;
10
+ session_name_display_requested_for?: string;
10
11
  }
11
12
  export declare const SESSION_NAME_DISPLAY_NOTE = "Next, before any prose or another tool call, send suggested_session_name verbatim by itself in a fenced code block.";
12
13
  export declare function sessionNameDisplayPending(row: SessionNameDisplayState | null | undefined): string | null;
14
+ /**
15
+ * Every title whose exact display satisfies the current latch.
16
+ *
17
+ * The pending title always counts. The title the agent was last instructed to
18
+ * display also counts, because the harness — not the agent — is what changed
19
+ * the target after asking. Accepting it costs nothing the operator can see: a
20
+ * clean one-line block did open that reply. Refusing it left Cursor sessions
21
+ * permanently latched, since no Cursor surface can re-open a missed window.
22
+ */
23
+ export declare function sessionNameDisplayAcceptedNames(row: SessionNameDisplayState | null | undefined): string[];
24
+ /**
25
+ * Match assistant text against any accepted title and report which one closed
26
+ * the latch, so a drifted display can be stamped against the pending title and
27
+ * logged as drift rather than silently discarded.
28
+ */
29
+ export declare function matchSessionNameDisplay(row: SessionNameDisplayState | null | undefined, text: string | undefined, startsWithBlock?: (text: string, expectedName: string) => boolean): {
30
+ pending: string;
31
+ displayed: string;
32
+ } | null;
13
33
  export declare function sessionNameDisplayBlock(name: string): string;
14
34
  export declare function sessionNameDisplayInstruction(name: string): string;
15
35
  export declare function sessionNameDisplayRecoveryInstruction(name: string, binName: string): string;
@@ -26,11 +46,6 @@ export declare function sessionNameDisplayRecoveryInstruction(name: string, binN
26
46
  * activating the display ritual.
27
47
  */
28
48
  export declare function toolResponseMintedSessionName(response: unknown, name: string): boolean;
29
- /**
30
- * Accept the exact unlabelled block Harnery requests. `text` and `plaintext`
31
- * are tolerated for older prompts, but the code block may contain only the
32
- * suggested name and must be the first non-whitespace user-facing content.
33
- */
34
49
  export declare function assistantTextStartsWithSessionNameBlock(text: string, name: string): boolean;
35
50
  /**
36
51
  * The display latch must not block the two coordination commands that can
@@ -1 +1 @@
1
- {"version":3,"file":"session-name-display.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-name-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,yBAAyB,wHACiF,CAAC;AAExH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,GAAG,IAAI,CAIf;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlE;AAED,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtF;AAgDD;;;;GAIG;AACH,wBAAgB,uCAAuC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAK3F;AAUD;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAmBT"}
1
+ {"version":3,"file":"session-name-display.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-name-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kCAAkC,CAAC,EAAE,MAAM,CAAC;CAC7C;AAED,eAAO,MAAM,yBAAyB,wHACiF,CAAC;AAExH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,GAAG,IAAI,CAIf;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,EAAE,CAKV;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,EAC/C,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,eAAe,GAAE,CACf,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,KACjB,OAAiD,GACrD;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO/C;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlE;AAED,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtF;AAqFD,wBAAgB,uCAAuC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAI3F;AAUD;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAmBT"}
@@ -11,6 +11,37 @@ export function sessionNameDisplayPending(row) {
11
11
  return null;
12
12
  return name;
13
13
  }
14
+ /**
15
+ * Every title whose exact display satisfies the current latch.
16
+ *
17
+ * The pending title always counts. The title the agent was last instructed to
18
+ * display also counts, because the harness — not the agent — is what changed
19
+ * the target after asking. Accepting it costs nothing the operator can see: a
20
+ * clean one-line block did open that reply. Refusing it left Cursor sessions
21
+ * permanently latched, since no Cursor surface can re-open a missed window.
22
+ */
23
+ export function sessionNameDisplayAcceptedNames(row) {
24
+ const pending = sessionNameDisplayPending(row);
25
+ if (!pending)
26
+ return [];
27
+ const requested = row?.session_name_display_requested_for;
28
+ return requested && requested !== pending ? [pending, requested] : [pending];
29
+ }
30
+ /**
31
+ * Match assistant text against any accepted title and report which one closed
32
+ * the latch, so a drifted display can be stamped against the pending title and
33
+ * logged as drift rather than silently discarded.
34
+ */
35
+ export function matchSessionNameDisplay(row, text, startsWithBlock = assistantTextStartsWithSessionNameBlock) {
36
+ if (!text)
37
+ return null;
38
+ const accepted = sessionNameDisplayAcceptedNames(row);
39
+ const pending = accepted[0];
40
+ if (!pending)
41
+ return null;
42
+ const displayed = accepted.find((candidate) => startsWithBlock(text, candidate));
43
+ return displayed ? { pending, displayed } : null;
44
+ }
14
45
  export function sessionNameDisplayBlock(name) {
15
46
  return `\`\`\`\n${name}\n\`\`\``;
16
47
  }
@@ -62,10 +93,16 @@ function responseContainsSessionNameMint(value, name, seen, depth) {
62
93
  }
63
94
  }
64
95
  catch {
65
- // Wrapper prose and partial JSON are not authoritative mint evidence.
96
+ // Fall through to the textual check below.
66
97
  }
67
98
  }
68
- return false;
99
+ // The mint command's stdout often reaches the transcript mangled: cut
100
+ // short by `cut`/`head -c`, or with a tail-truncated first line. Whole-JSON
101
+ // parsing then fails even though the name and the mint flag are both
102
+ // legible, and a session that did mint can never latch. Accept the text
103
+ // when the exact quoted name sits under its key AND a mint flag is true;
104
+ // ordinary status output carries the name without any flag.
105
+ return textCarriesSessionNameMint(value, name);
69
106
  }
70
107
  if (typeof value !== "object")
71
108
  return false;
@@ -82,17 +119,40 @@ function responseContainsSessionNameMint(value, name, seen, depth) {
82
119
  }
83
120
  return Object.values(row).some((item) => responseContainsSessionNameMint(item, name, seen, depth + 1));
84
121
  }
122
+ const MINT_FLAG_PATTERN = /"(?:first_of_session|name_reminted|session_name_retry)"\s*:\s*true\b/;
123
+ function textCarriesSessionNameMint(text, name) {
124
+ const quotedName = JSON.stringify(name);
125
+ const keyIndex = text.indexOf('"suggested_session_name"');
126
+ if (keyIndex < 0)
127
+ return false;
128
+ const afterKey = text.slice(keyIndex + '"suggested_session_name"'.length);
129
+ const valueMatch = /^\s*:\s*("(?:[^"\\]|\\.)*")/.exec(afterKey);
130
+ if (!valueMatch || valueMatch[1] !== quotedName)
131
+ return false;
132
+ return MINT_FLAG_PATTERN.test(text);
133
+ }
85
134
  /**
86
- * Accept the exact unlabelled block Harnery requests. `text` and `plaintext`
87
- * are tolerated for older prompts, but the code block may contain only the
88
- * suggested name and must be the first non-whitespace user-facing content.
135
+ * A leading fenced block whose only line is the suggested name.
136
+ *
137
+ * What the contract protects is what the operator sees: the reply opens with a
138
+ * code block containing exactly the title, so it renders as one clean
139
+ * copy-pasteable line. The fence's info string changes none of that, so any
140
+ * single-word language tag passes. Restricting it to `text`/`plaintext` meant a
141
+ * model that wrote ```txt or ```markdown produced a display the operator could
142
+ * read perfectly while the latch refused to close — and since nothing else can
143
+ * close a Cursor latch, that mismatch stranded the whole session.
144
+ *
145
+ * Still exact in every load-bearing way: the block is the first user-facing
146
+ * content, holds one line, and that line equals the name. The backreference
147
+ * requires the closing fence to match the opening run, so a longer fence cannot
148
+ * be closed by a shorter one.
89
149
  */
150
+ const LEADING_SESSION_NAME_BLOCK = /^(`{3,})[ \t]*[A-Za-z0-9_.+#-]*[ \t]*\n([^\n]*)\n\1[ \t]*(?:\n|$)/;
90
151
  export function assistantTextStartsWithSessionNameBlock(text, name) {
91
152
  if (!text || !name)
92
153
  return false;
93
154
  const normalized = text.replace(/\r\n?/g, "\n").trimStart();
94
- const match = /^```(?:text|plaintext)?[ \t]*\n([^\n]*)\n```(?:[ \t]*(?:\n|$))/.exec(normalized);
95
- return match?.[1] === name;
155
+ return LEADING_SESSION_NAME_BLOCK.exec(normalized)?.[2] === name;
96
156
  }
97
157
  /**
98
158
  * A trailing stream redirect to `/dev/null` or another descriptor. Agents append