harnery 0.43.0 → 0.45.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 (323) hide show
  1. package/README.md +3 -3
  2. package/dist/commander.js +1 -0
  3. package/dist/commands/agents.d.ts.map +1 -1
  4. package/dist/commands/agents.js +41 -48
  5. package/dist/commands/browse.d.ts.map +1 -1
  6. package/dist/commands/browse.js +38 -11
  7. package/dist/commands/diagnostics.d.ts.map +1 -1
  8. package/dist/commands/diagnostics.js +10 -0
  9. package/dist/commands/doctor.d.ts +12 -0
  10. package/dist/commands/doctor.d.ts.map +1 -1
  11. package/dist/commands/doctor.js +22 -7
  12. package/dist/commands/files.d.ts +2 -2
  13. package/dist/commands/files.d.ts.map +1 -1
  14. package/dist/commands/files.js +7 -4
  15. package/dist/commands/governor.js +22 -10
  16. package/dist/commands/inbox.d.ts +1 -1
  17. package/dist/commands/inbox.d.ts.map +1 -1
  18. package/dist/commands/inbox.js +55 -2
  19. package/dist/commands/rm.d.ts.map +1 -1
  20. package/dist/commands/rm.js +28 -4
  21. package/dist/commands/servers.d.ts.map +1 -1
  22. package/dist/commands/servers.js +26 -17
  23. package/dist/commands/supervisor.d.ts.map +1 -1
  24. package/dist/commands/supervisor.js +7 -2
  25. package/dist/commands/toc.d.ts +14 -0
  26. package/dist/commands/toc.d.ts.map +1 -1
  27. package/dist/commands/toc.js +7 -4
  28. package/dist/commands/tunnel.d.ts +15 -0
  29. package/dist/commands/tunnel.d.ts.map +1 -1
  30. package/dist/commands/tunnel.js +453 -153
  31. package/dist/commands/turn-rollback.d.ts +5 -0
  32. package/dist/commands/turn-rollback.d.ts.map +1 -0
  33. package/dist/commands/turn-rollback.js +76 -0
  34. package/dist/commands/workflow.d.ts.map +1 -1
  35. package/dist/commands/workflow.js +68 -30
  36. package/dist/core/agents/coord-client.d.ts.map +1 -1
  37. package/dist/core/agents/coord-client.js +18 -0
  38. package/dist/core/agents/live-authority-v3.d.ts +1 -0
  39. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  40. package/dist/core/agents/live-authority-v3.js +11 -0
  41. package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
  42. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  43. package/dist/core/agents/live-lifecycle-v3.js +28 -7
  44. package/dist/core/agents/mailbox.d.ts +9 -8
  45. package/dist/core/agents/mailbox.d.ts.map +1 -1
  46. package/dist/core/agents/mailbox.js +24 -17
  47. package/dist/core/agents/render/prompt-context.d.ts +2 -0
  48. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  49. package/dist/core/agents/render/prompt-context.js +8 -12
  50. package/dist/core/agents/render/session-context.d.ts +2 -8
  51. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  52. package/dist/core/agents/render/session-context.js +7 -30
  53. package/dist/core/agents/rules/stop-hook.js +3 -3
  54. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  55. package/dist/core/agents/state/heartbeat-writer.js +25 -14
  56. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
  57. package/dist/core/agents/state/live-coordination-writer.js +5 -0
  58. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  59. package/dist/core/agents/state/pidmap.js +5 -0
  60. package/dist/core/agents/state/proc-start.d.ts +19 -1
  61. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  62. package/dist/core/agents/state/proc-start.js +46 -1
  63. package/dist/core/agents/state/process-handle.d.ts +26 -0
  64. package/dist/core/agents/state/process-handle.d.ts.map +1 -0
  65. package/dist/core/agents/state/process-handle.js +121 -0
  66. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  67. package/dist/core/agents/state/stale-sweep.js +180 -70
  68. package/dist/core/agents/state/unchanged-file.d.ts +44 -0
  69. package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
  70. package/dist/core/agents/state/unchanged-file.js +390 -0
  71. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  72. package/dist/core/artifacts/delivery-card.js +2 -1
  73. package/dist/core/context/index.d.ts +22 -10
  74. package/dist/core/context/index.d.ts.map +1 -1
  75. package/dist/core/context/index.js +381 -119
  76. package/dist/core/context/observation.d.ts +26 -0
  77. package/dist/core/context/observation.d.ts.map +1 -0
  78. package/dist/core/context/observation.js +11 -0
  79. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  80. package/dist/core/diagnostics/bundle.js +25 -14
  81. package/dist/core/diagnostics/path-identity.d.ts +3 -0
  82. package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
  83. package/dist/core/diagnostics/path-identity.js +12 -0
  84. package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
  85. package/dist/core/diagnostics/sanitize.js +4 -3
  86. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  87. package/dist/core/events/v3/authority-outbox.js +29 -0
  88. package/dist/core/events/v3/generation-custody.d.ts +5 -0
  89. package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
  90. package/dist/core/events/v3/generation-custody.js +50 -0
  91. package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
  92. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  93. package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
  94. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  95. package/dist/core/events/v3/producers/recorder.js +77 -64
  96. package/dist/core/governor/planning.d.ts.map +1 -1
  97. package/dist/core/governor/planning.js +14 -1
  98. package/dist/core/governor/service-read.d.ts +10 -2
  99. package/dist/core/governor/service-read.d.ts.map +1 -1
  100. package/dist/core/governor/service-read.js +105 -22
  101. package/dist/core/governor/service.d.ts +20 -2
  102. package/dist/core/governor/service.d.ts.map +1 -1
  103. package/dist/core/governor/service.js +72 -47
  104. package/dist/core/hooks/cli.js +33 -5
  105. package/dist/core/inbox/contract.d.ts +2 -1
  106. package/dist/core/inbox/contract.d.ts.map +1 -1
  107. package/dist/core/inbox/index.d.ts +2 -0
  108. package/dist/core/inbox/index.d.ts.map +1 -1
  109. package/dist/core/inbox/index.js +2 -0
  110. package/dist/core/inbox/peer-messages.d.ts +37 -0
  111. package/dist/core/inbox/peer-messages.d.ts.map +1 -0
  112. package/dist/core/inbox/peer-messages.js +72 -0
  113. package/dist/core/inbox/service.d.ts +8 -1
  114. package/dist/core/inbox/service.d.ts.map +1 -1
  115. package/dist/core/inbox/service.js +67 -21
  116. package/dist/core/inbox/targets.d.ts +22 -0
  117. package/dist/core/inbox/targets.d.ts.map +1 -0
  118. package/dist/core/inbox/targets.js +108 -0
  119. package/dist/core/inbox/watcher.js +7 -3
  120. package/dist/core/journal/index.d.ts.map +1 -1
  121. package/dist/core/journal/index.js +17 -2
  122. package/dist/core/resources/contract.d.ts +3 -0
  123. package/dist/core/resources/contract.d.ts.map +1 -1
  124. package/dist/core/resources/service-status.d.ts +14 -1
  125. package/dist/core/resources/service-status.d.ts.map +1 -1
  126. package/dist/core/resources/service-status.js +23 -13
  127. package/dist/core/resources/service.d.ts +7 -13
  128. package/dist/core/resources/service.d.ts.map +1 -1
  129. package/dist/core/resources/service.js +34 -91
  130. package/dist/core/resources/status.d.ts +2 -5
  131. package/dist/core/resources/status.d.ts.map +1 -1
  132. package/dist/core/resources/status.js +36 -7
  133. package/dist/core/semantic/service.d.ts.map +1 -1
  134. package/dist/core/semantic/service.js +2 -9
  135. package/dist/core/servers/index.d.ts +34 -22
  136. package/dist/core/servers/index.d.ts.map +1 -1
  137. package/dist/core/servers/index.js +297 -144
  138. package/dist/core/servers/tunnels.d.ts +7 -7
  139. package/dist/core/servers/tunnels.d.ts.map +1 -1
  140. package/dist/core/servers/tunnels.js +47 -10
  141. package/dist/core/supervisor/contract.d.ts +12 -0
  142. package/dist/core/supervisor/contract.d.ts.map +1 -1
  143. package/dist/core/supervisor/explanations.d.ts.map +1 -1
  144. package/dist/core/supervisor/explanations.js +2 -1
  145. package/dist/core/supervisor/findings.d.ts.map +1 -1
  146. package/dist/core/supervisor/findings.js +54 -9
  147. package/dist/core/supervisor/service.d.ts +7 -1
  148. package/dist/core/supervisor/service.d.ts.map +1 -1
  149. package/dist/core/supervisor/service.js +59 -29
  150. package/dist/core/supervisor/services.d.ts.map +1 -1
  151. package/dist/core/supervisor/services.js +22 -1
  152. package/dist/core/supervisor/status.d.ts +17 -2
  153. package/dist/core/supervisor/status.d.ts.map +1 -1
  154. package/dist/core/supervisor/status.js +70 -12
  155. package/dist/core/supervisor/timeline.d.ts.map +1 -1
  156. package/dist/core/supervisor/timeline.js +2 -1
  157. package/dist/core/turn-rollback/content.d.ts +53 -0
  158. package/dist/core/turn-rollback/content.d.ts.map +1 -0
  159. package/dist/core/turn-rollback/content.js +396 -0
  160. package/dist/core/turn-rollback/index.d.ts +72 -0
  161. package/dist/core/turn-rollback/index.d.ts.map +1 -0
  162. package/dist/core/turn-rollback/index.js +443 -0
  163. package/dist/core/work/state.js +7 -7
  164. package/dist/core/workflow/engine.d.ts.map +1 -1
  165. package/dist/core/workflow/engine.js +105 -32
  166. package/dist/core/workflow/failure-evidence.d.ts +16 -0
  167. package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
  168. package/dist/core/workflow/failure-evidence.js +114 -0
  169. package/dist/core/workflow/index.d.ts +5 -3
  170. package/dist/core/workflow/index.d.ts.map +1 -1
  171. package/dist/core/workflow/index.js +3 -2
  172. package/dist/core/workflow/live-session-v3.d.ts +6 -0
  173. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  174. package/dist/core/workflow/live-session-v3.js +126 -5
  175. package/dist/core/workflow/proof.d.ts +5 -5
  176. package/dist/core/workflow/proof.d.ts.map +1 -1
  177. package/dist/core/workflow/proof.js +115 -20
  178. package/dist/core/workflow/recovery-audit.d.ts +30 -0
  179. package/dist/core/workflow/recovery-audit.d.ts.map +1 -0
  180. package/dist/core/workflow/recovery-audit.js +116 -0
  181. package/dist/core/workflow/run-state.d.ts +20 -1
  182. package/dist/core/workflow/run-state.d.ts.map +1 -1
  183. package/dist/core/workflow/run-state.js +141 -32
  184. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  185. package/dist/core/workflow/spawn-claude.js +19 -0
  186. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  187. package/dist/core/workflow/spawn-codex.js +13 -0
  188. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  189. package/dist/core/workflow/spawn-cursor.js +19 -0
  190. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  191. package/dist/core/workflow/spawn-opencode.js +14 -0
  192. package/dist/core/workflow/types.d.ts +64 -17
  193. package/dist/core/workflow/types.d.ts.map +1 -1
  194. package/dist/core/workflow/workspaces/index.d.ts +0 -1
  195. package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
  196. package/dist/core/workflow/workspaces/index.js +0 -1
  197. package/dist/core/workflow/workspaces/leases.d.ts +3 -1
  198. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  199. package/dist/core/workflow/workspaces/leases.js +30 -16
  200. package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
  201. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  202. package/dist/core/workflow/workspaces/local-git.js +328 -8
  203. package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
  204. package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
  205. package/dist/core/workflow/workspaces/ownership.js +194 -0
  206. package/dist/core/workflow/workspaces/reclaim.d.ts +40 -0
  207. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
  208. package/dist/core/workflow/workspaces/reclaim.js +14 -0
  209. package/dist/core/workflow/workspaces/recovery.d.ts +2 -0
  210. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
  211. package/dist/core/workflow/workspaces/recovery.js +42 -10
  212. package/dist/core/workflow/workspaces/state.d.ts +1 -0
  213. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  214. package/dist/core/workflow/workspaces/state.js +21 -1
  215. package/dist/lib/browser/windows-chrome.d.ts +100 -12
  216. package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
  217. package/dist/lib/browser/windows-chrome.js +262 -73
  218. package/dist/lib/docs-links.d.ts.map +1 -1
  219. package/dist/lib/docs-links.js +45 -2
  220. package/dist/lib/guarded-remove.d.ts +7 -0
  221. package/dist/lib/guarded-remove.d.ts.map +1 -1
  222. package/dist/lib/guarded-remove.js +106 -12
  223. package/dist/lib/identities/assume.d.ts.map +1 -1
  224. package/dist/lib/identities/assume.js +4 -0
  225. package/dist/lib/tunnel/state.d.ts +13 -6
  226. package/dist/lib/tunnel/state.d.ts.map +1 -1
  227. package/dist/lib/tunnel/state.js +58 -12
  228. package/dist/lib/zip/index.d.ts +41 -0
  229. package/dist/lib/zip/index.d.ts.map +1 -0
  230. package/dist/lib/zip/index.js +398 -0
  231. package/package.json +12 -1
  232. package/src/commander.ts +10 -0
  233. package/src/commands/agents.ts +54 -63
  234. package/src/commands/browse.ts +50 -14
  235. package/src/commands/diagnostics.ts +10 -0
  236. package/src/commands/doctor.ts +26 -7
  237. package/src/commands/files.ts +13 -5
  238. package/src/commands/governor.ts +23 -10
  239. package/src/commands/inbox.ts +52 -2
  240. package/src/commands/rm.ts +43 -4
  241. package/src/commands/servers.ts +27 -26
  242. package/src/commands/supervisor.ts +7 -2
  243. package/src/commands/toc.ts +14 -6
  244. package/src/commands/tunnel.ts +533 -166
  245. package/src/commands/turn-rollback.ts +111 -0
  246. package/src/commands/workflow.ts +89 -30
  247. package/src/core/agents/coord-client.ts +20 -0
  248. package/src/core/agents/live-authority-v3.ts +17 -0
  249. package/src/core/agents/live-lifecycle-v3.ts +52 -14
  250. package/src/core/agents/mailbox.ts +26 -17
  251. package/src/core/agents/render/prompt-context.ts +10 -12
  252. package/src/core/agents/render/session-context.ts +9 -32
  253. package/src/core/agents/rules/stop-hook.ts +3 -3
  254. package/src/core/agents/state/heartbeat-writer.ts +29 -12
  255. package/src/core/agents/state/live-coordination-writer.ts +29 -0
  256. package/src/core/agents/state/pidmap.ts +13 -0
  257. package/src/core/agents/state/proc-start.ts +62 -2
  258. package/src/core/agents/state/process-handle.ts +165 -0
  259. package/src/core/agents/state/stale-sweep.ts +247 -82
  260. package/src/core/agents/state/unchanged-file.ts +456 -0
  261. package/src/core/artifacts/delivery-card.ts +7 -1
  262. package/src/core/context/index.ts +446 -136
  263. package/src/core/context/observation.ts +29 -0
  264. package/src/core/diagnostics/bundle.ts +23 -18
  265. package/src/core/diagnostics/path-identity.ts +15 -0
  266. package/src/core/diagnostics/sanitize.ts +4 -3
  267. package/src/core/events/v3/authority-outbox.ts +56 -0
  268. package/src/core/events/v3/generation-custody.ts +59 -0
  269. package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
  270. package/src/core/events/v3/producers/recorder.ts +94 -68
  271. package/src/core/governor/planning.ts +20 -8
  272. package/src/core/governor/service-read.ts +128 -20
  273. package/src/core/governor/service.ts +119 -42
  274. package/src/core/hooks/cli.ts +33 -5
  275. package/src/core/inbox/contract.ts +2 -1
  276. package/src/core/inbox/index.ts +2 -0
  277. package/src/core/inbox/peer-messages.ts +94 -0
  278. package/src/core/inbox/service.ts +88 -22
  279. package/src/core/inbox/targets.ts +148 -0
  280. package/src/core/inbox/watcher.ts +8 -9
  281. package/src/core/journal/index.ts +19 -2
  282. package/src/core/resources/contract.ts +4 -0
  283. package/src/core/resources/service-status.ts +35 -12
  284. package/src/core/resources/service.ts +57 -95
  285. package/src/core/resources/status.ts +57 -8
  286. package/src/core/semantic/service.ts +2 -8
  287. package/src/core/servers/index.ts +376 -141
  288. package/src/core/servers/tunnels.ts +57 -13
  289. package/src/core/supervisor/contract.ts +13 -0
  290. package/src/core/supervisor/explanations.ts +4 -1
  291. package/src/core/supervisor/findings.ts +68 -10
  292. package/src/core/supervisor/service.ts +87 -26
  293. package/src/core/supervisor/services.ts +26 -8
  294. package/src/core/supervisor/status.ts +89 -12
  295. package/src/core/supervisor/timeline.ts +2 -1
  296. package/src/core/turn-rollback/content.ts +499 -0
  297. package/src/core/turn-rollback/index.ts +580 -0
  298. package/src/core/work/state.ts +7 -7
  299. package/src/core/workflow/engine.ts +131 -49
  300. package/src/core/workflow/failure-evidence.ts +141 -0
  301. package/src/core/workflow/index.ts +12 -2
  302. package/src/core/workflow/live-session-v3.ts +146 -5
  303. package/src/core/workflow/proof.ts +134 -26
  304. package/src/core/workflow/recovery-audit.ts +181 -0
  305. package/src/core/workflow/run-state.ts +165 -34
  306. package/src/core/workflow/spawn-claude.ts +18 -0
  307. package/src/core/workflow/spawn-codex.ts +13 -0
  308. package/src/core/workflow/spawn-cursor.ts +18 -0
  309. package/src/core/workflow/spawn-opencode.ts +14 -0
  310. package/src/core/workflow/types.ts +51 -17
  311. package/src/core/workflow/workspaces/index.ts +0 -1
  312. package/src/core/workflow/workspaces/leases.ts +48 -17
  313. package/src/core/workflow/workspaces/local-git.ts +388 -7
  314. package/src/core/workflow/workspaces/ownership.ts +232 -0
  315. package/src/core/workflow/workspaces/reclaim.ts +62 -0
  316. package/src/core/workflow/workspaces/recovery.ts +45 -10
  317. package/src/core/workflow/workspaces/state.ts +20 -0
  318. package/src/lib/browser/windows-chrome.ts +346 -86
  319. package/src/lib/docs-links.ts +49 -2
  320. package/src/lib/guarded-remove.ts +132 -14
  321. package/src/lib/identities/assume.ts +4 -0
  322. package/src/lib/tunnel/state.ts +70 -15
  323. package/src/lib/zip/index.ts +483 -0
@@ -0,0 +1,5 @@
1
+ import type { Command } from "commander";
2
+ import type { EmitContext, HarneryProgramContext } from "../commander.js";
3
+ /** Explicit turn boundaries; hooks cannot infer provider ownership or path scope. */
4
+ export declare function registerTurnRollbackCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
5
+ //# sourceMappingURL=turn-rollback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-rollback.d.ts","sourceRoot":"","sources":["../../src/commands/turn-rollback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAa1E,qFAAqF;AACrF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA2FN"}
@@ -0,0 +1,76 @@
1
+ import { monorepoRoot } from "../core/agents/index.js";
2
+ import { applyTurnRollback, beginTurnRollback, inspectTurnRollback, prepareTurnRollback, sealTurnRollback, } from "../core/turn-rollback/index.js";
3
+ import { readJsonRecord } from "../core/workflow/durable-record.js";
4
+ import { readWorkspaceBinding } from "../core/workflow/workspaces/state.js";
5
+ /** Explicit turn boundaries; hooks cannot infer provider ownership or path scope. */
6
+ export function registerTurnRollbackCommand(program, emit, context) {
7
+ const command = program
8
+ .command("turn-rollback")
9
+ .description("Capture and review exact-path content rollback in an owned workspace");
10
+ const run = (operation) => async (options) => {
11
+ try {
12
+ const coordRoot = context?.resolveCoordRoot?.() ?? context?.repoRoot ?? monorepoRoot();
13
+ if (!coordRoot)
14
+ throw new Error("project coordination root is unavailable");
15
+ emit.config({ format: "json" });
16
+ const result = await operation(coordRoot, options);
17
+ emit.data(result);
18
+ if (result &&
19
+ typeof result === "object" &&
20
+ "status" in result &&
21
+ result.status === "unknown")
22
+ emit.setExitCode(1);
23
+ }
24
+ catch (error) {
25
+ emit.error({ code: "turn_rollback_refused", message: String(error) });
26
+ emit.setExitCode(1);
27
+ }
28
+ };
29
+ const binding = (root, options) => {
30
+ const value = readWorkspaceBinding(root, "local-git-worktree", options.binding);
31
+ if (!value)
32
+ throw new Error("immutable workspace binding was not found");
33
+ return value;
34
+ };
35
+ command
36
+ .command("begin")
37
+ .description("Capture exact literal paths before a turn")
38
+ .requiredOption("--binding <id>", "Owned workspace binding ID")
39
+ .requiredOption("--turn <id>", "Caller turn identity")
40
+ .requiredOption("--generation <number>", "Increasing turn generation")
41
+ .requiredOption("--path <file...>", "Exact relative file paths, including planned new files")
42
+ .action(run((root, options) => beginTurnRollback(root, {
43
+ binding: binding(root, options),
44
+ turn: { turn_id: options.turn, generation: Number(options.generation) },
45
+ paths: options.path,
46
+ })));
47
+ for (const operation of ["seal", "prepare", "show"]) {
48
+ command
49
+ .command(`${operation} <capture-id>`)
50
+ .description(`${operation} an exact turn capture`)
51
+ .requiredOption("--binding <id>", "Owned workspace binding ID")
52
+ .action((id, options) => run((root, opts) => {
53
+ const input = { binding: binding(root, opts), capture_id: id };
54
+ return operation === "seal"
55
+ ? sealTurnRollback(root, input)
56
+ : operation === "prepare"
57
+ ? prepareTurnRollback(root, input)
58
+ : inspectTurnRollback(root, input);
59
+ })(options));
60
+ }
61
+ command
62
+ .command("apply")
63
+ .description("Preview a saved proposal, or apply it after exact confirmation")
64
+ .requiredOption("--binding <id>", "Owned workspace binding ID")
65
+ .requiredOption("--plan <file>", "Saved JSON proposal returned by prepare")
66
+ .option("--yes", "Apply this exact proposal; preview is the default")
67
+ .option("--actor <name>", "Who authorized the restore")
68
+ .option("--reason <text>", "Why this turn is being restored")
69
+ .action(run((root, options) => applyTurnRollback(root, {
70
+ binding: binding(root, options),
71
+ plan: readJsonRecord(options.plan, "rollback proposal"),
72
+ yes: options.yes,
73
+ actor: options.actor,
74
+ reason: options.reason,
75
+ })));
76
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA0EnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAmvBjF"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAmFnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAqyBjF"}
@@ -472,10 +472,13 @@ export function registerWorkflowCommand(program, emit) {
472
472
  });
473
473
  workflow
474
474
  .command("reclaim <run-id>")
475
- .description("Resolve a workspace preserved because it was dirty: salvage the work to its branch, then release.")
475
+ .description("Preview reclaim of an inactive owned workspace, or confirm salvage or discard and release.")
476
476
  .option("--yes", "Confirm that this commits or discards uncommitted work, then releases")
477
477
  .option("--discard", "Throw the uncommitted work away instead of salvaging it")
478
- .option("--json", "Emit the reclaim preparation and cleanup result as JSON")
478
+ .option("--unknown-liveness <sha256>", "Recover unknown evidence pinned by the preview digest; active evidence still refuses")
479
+ .option("--actor <id>", "Actor responsible for an audited recovery")
480
+ .option("--reason <text>", "Reason for an audited recovery")
481
+ .option("--json", "Emit the reclaim preview or confirmed result as JSON")
479
482
  .action(async (runId, opts) => {
480
483
  const coordRoot = resolveCoordRoot();
481
484
  if (!coordRoot) {
@@ -485,50 +488,85 @@ export function registerWorkflowCommand(program, emit) {
485
488
  });
486
489
  process.exit(1);
487
490
  }
488
- if (!opts.yes) {
489
- emit.error({
490
- code: "reclaim_confirmation_required",
491
- message: opts.discard
492
- ? "reclaim --discard permanently destroys uncommitted work; pass --yes to confirm"
493
- : "workspace reclaim commits uncommitted work to its branch and then releases the worktree; pass --yes to confirm",
494
- });
495
- process.exit(1);
496
- }
497
491
  try {
498
- const { inspectWorkflowWorkspace, prepareReclaim, cleanupWorkspace } = await import("../core/workflow/index.js");
499
- const inspection = inspectWorkflowWorkspace(coordRoot, runId);
500
- if (!inspection.ok) {
501
- emit.error({ code: "workflow_reclaim_failed", message: inspection.error });
502
- process.exit(1);
503
- }
504
- const allocation = inspection.value.allocation;
505
- if (!allocation) {
492
+ const { createLocalGitWorktreeProvider, readWorkflowRunManifest } = await import("../core/workflow/index.js");
493
+ const binding = readWorkflowRunManifest(coordRoot, runId).execution.workspace_binding;
494
+ if (binding?.provider.id !== "local-git-worktree") {
506
495
  emit.error({
507
496
  code: "workflow_reclaim_unavailable",
508
497
  message: `run ${runId} has no provider-owned Git workspace to reclaim`,
509
498
  });
510
499
  process.exit(1);
511
500
  }
512
- const preparation = prepareReclaim({
513
- worktreePath: allocation.active_root,
501
+ const provider = createLocalGitWorktreeProvider({ coordRoot });
502
+ const result = await provider.reclaim(binding, {
514
503
  mode: opts.discard ? "discard" : "salvage",
515
- runId,
504
+ yes: opts.yes,
505
+ unknownLiveness: opts.unknownLiveness
506
+ ? {
507
+ evidenceSha256: opts.unknownLiveness,
508
+ actor: opts.actor,
509
+ reason: opts.reason,
510
+ }
511
+ : undefined,
516
512
  });
517
- // Cleanup still owns removal. Reclaim only changes whether the tree it
518
- // finds is dirty, so there is one audited release path, not two.
519
- const provider = await builtInProviderForRun(coordRoot, runId);
520
- const cleanup = await cleanupWorkspace({ coordRoot, runId, provider });
521
513
  if (opts.json) {
522
514
  emit.config({ format: "json" });
523
- emit.data({ preparation, cleanup });
515
+ emit.data(result);
516
+ return;
517
+ }
518
+ if (result.action === "preview") {
519
+ const holders = result.lease_holder.map((holder) => `${holder.scope}: ${holder.owner_id ?? "unknown"}${holder.pid ? ` (pid ${holder.pid})` : ""}${holder.detail ? `; ${holder.detail}` : ""}`);
520
+ emit.text(`reclaim preview: ${result.run_id}\n` +
521
+ `mode: ${result.mode}\nliveness: ${result.liveness}\nownership: ${result.ownership}\n` +
522
+ `evidence sha256: ${result.evidence_sha256}\n` +
523
+ `lease holder: ${holders.length > 0 ? holders.join(", ") : "none observed"}\n` +
524
+ `dirty paths: ${result.dirty_paths === null ? "unknown" : result.dirty_paths.length > 0 ? result.dirty_paths.join(", ") : "none"}\n` +
525
+ (result.ownership_detail ? `ownership detail: ${result.ownership_detail}\n` : "") +
526
+ `Pass --yes to confirm ${result.mode} and release after all checks pass.\n`);
527
+ return;
528
+ }
529
+ emit.text(`reclaim ${result.preparation.action}: ${result.preparation.detail}\n` +
530
+ `workspace cleanup ${result.cleanup.status}: ${result.cleanup.binding_id}\n`);
531
+ }
532
+ catch (error) {
533
+ emit.error({
534
+ code: error instanceof Error && "code" in error && typeof error.code === "string"
535
+ ? error.code
536
+ : "workflow_reclaim_failed",
537
+ message: error instanceof Error ? error.message : String(error),
538
+ });
539
+ process.exit(1);
540
+ }
541
+ });
542
+ workflow
543
+ .command("unlock <run-id>")
544
+ .description("Preview or explicitly retire one exact resume lock whose local owner is proven dead.")
545
+ .option("--lock-sha256 <sha256>", "Exact lock identity digest returned by the preview")
546
+ .option("--actor <id>", "Actor responsible for the recovery")
547
+ .option("--reason <text>", "Reason for the recovery")
548
+ .option("--yes", "Confirm removal after dead-owner and exact-lock checks")
549
+ .option("--json", "Emit the recovery preview or result as JSON")
550
+ .action(async (runId, opts) => {
551
+ try {
552
+ const coordRoot = resolveCoordRoot();
553
+ if (!coordRoot)
554
+ throw new Error("no .harnery/ coordination root found");
555
+ const { recoverWorkflowResumeLease } = await import("../core/workflow/index.js");
556
+ const result = recoverWorkflowResumeLease(coordRoot, runId, opts);
557
+ if (opts.json) {
558
+ emit.config({ format: "json" });
559
+ emit.data(result);
524
560
  return;
525
561
  }
526
- emit.text(`reclaim ${preparation.action}: ${preparation.detail}\n` +
527
- `workspace cleanup ${cleanup.status}: ${cleanup.binding_id}\n`);
562
+ emit.text(`resume lock ${result.action}: ${runId}\nowner: ${result.owner_state}\nlock sha256: ${result.lock_sha256 ?? "absent"}\n` +
563
+ (result.action === "preview"
564
+ ? "Pass --lock-sha256, --actor, --reason and --yes to confirm exact dead-owner recovery.\n"
565
+ : `recovery: ${result.recovery_id}\n`));
528
566
  }
529
567
  catch (error) {
530
568
  emit.error({
531
- code: "workflow_reclaim_failed",
569
+ code: "workflow_resume_unlock_failed",
532
570
  message: error instanceof Error ? error.message : String(error),
533
571
  });
534
572
  process.exit(1);
@@ -1 +1 @@
1
- {"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOhF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CA+B7E;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE7D;AAeD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAiED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2CxD;AAgCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAsEA;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsB5C;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG;IAClD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,QAAQ,GAAG,iBAAiB,GAAG,MAAM,CAAC;CAC/C,CAyBA;AAyBD,sFAAsF;AACtF;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,GAAG,IAAI,CAEtD;AAuCD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAmB9C;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE"}
1
+ {"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOhF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CA+B7E;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE7D;AAeD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAiED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+DxD;AAgCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAsEA;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsB5C;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG;IAClD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,QAAQ,GAAG,iBAAiB,GAAG,MAAM,CAAC;CAC/C,CAyBA;AAyBD,sFAAsF;AACtF;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,GAAG,IAAI,CAEtD;AAuCD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAmB9C;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE"}
@@ -251,7 +251,25 @@ export function parsePidmapRowStartToken(row) {
251
251
  * a test can hold this one against `processStartToken` and fail on drift.
252
252
  */
253
253
  export function pidStartToken(pid) {
254
+ if (!Number.isInteger(pid) || pid <= 0)
255
+ return null;
254
256
  const forced = process.env.HARNERY_PID_PROBE;
257
+ if (process.platform === "win32" && forced !== "procfs" && forced !== "ps") {
258
+ try {
259
+ const out = spawnSync("powershell.exe", [
260
+ "-NoLogo",
261
+ "-NoProfile",
262
+ "-NonInteractive",
263
+ "-Command",
264
+ `$process = [Diagnostics.Process]::GetProcessById(${pid}); try { 'w' + $process.StartTime.ToFileTimeUtc().ToString([Globalization.CultureInfo]::InvariantCulture) } finally { $process.Dispose() }`,
265
+ ], { encoding: "utf8", timeout: 10_000, windowsHide: true });
266
+ const token = String(out.stdout ?? "").trim();
267
+ return out.status === 0 && /^w\d+$/.test(token) ? token : null;
268
+ }
269
+ catch {
270
+ return null;
271
+ }
272
+ }
255
273
  const useProcfs = forced === "procfs" || (forced !== "ps" && existsSync("/proc/self/stat"));
256
274
  // One machine, one probe. Falling back to the other on a read failure would
257
275
  // answer in the wrong dialect and read as a recycled pid.
@@ -38,6 +38,7 @@ interface LiveAuthorityBaseV3 {
38
38
  nativeSessionId: string;
39
39
  adapter: Adapter;
40
40
  observationId?: string;
41
+ expectedGenerationId?: `gen_${string}`;
41
42
  }
42
43
  /**
43
44
  * Establish the missing generation authority for one adapter-native session,
@@ -1 +1 @@
1
- {"version":3,"file":"live-authority-v3.d.ts","sourceRoot":"","sources":["../../../src/core/agents/live-authority-v3.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"live-authority-v3.d.ts","sourceRoot":"","sources":["../../../src/core/agents/live-authority-v3.ts"],"names":[],"mappings":"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"}
@@ -419,6 +419,10 @@ function recordLiveAuthority(input, signal, observation, expected, desired, appl
419
419
  platform: livePlatformV3(),
420
420
  expected_prior_state_digest: coordinationAuthorityStateDigestV3(expected),
421
421
  desired_state_digest: coordinationAuthorityStateDigestV3(desired),
422
+ expected_generation_id: input.expectedGenerationId ??
423
+ (subject === input.owner
424
+ ? expected.v3_generation_id
425
+ : readLiveCoordinationRow(input.coordRoot, input.owner)?.v3_generation_id),
422
426
  reconciler: {
423
427
  readStateDigest: () => {
424
428
  const heartbeat = readHeartbeat(input.coordRoot, subject);
@@ -448,9 +452,16 @@ export function liveCoordinationWriteModeV3(coordRoot) {
448
452
  return route.mode;
449
453
  }
450
454
  function requireHeartbeat(input, owner) {
455
+ if (input.expectedGenerationId &&
456
+ readLiveCoordinationRow(input.coordRoot, owner)?.v3_generation_id !== input.expectedGenerationId) {
457
+ throw new LiveCoordinationAuthorityV3Error("expected_generation_changed");
458
+ }
451
459
  const heartbeat = ensureLiveCoordinationHeartbeat(input.coordRoot, owner, owner === input.owner ? input.nativeSessionId : owner, input.adapter);
452
460
  if (!heartbeat)
453
461
  throw new LiveCoordinationAuthorityV3Error(`heartbeat_missing:${owner}`);
462
+ if (input.expectedGenerationId && heartbeat.v3_generation_id !== input.expectedGenerationId) {
463
+ throw new LiveCoordinationAuthorityV3Error("expected_generation_changed");
464
+ }
454
465
  return heartbeat;
455
466
  }
456
467
  export function coordinationAuthorityStateDigestV3(heartbeat) {
@@ -11,10 +11,12 @@ interface LiveLifecycleObservationBaseV3 {
11
11
  adapter: EventAdapterIdV3;
12
12
  observedAt?: string;
13
13
  }
14
- export declare function recordLiveSweepObservationV3(input: LiveLifecycleObservationBaseV3 & {
14
+ interface LiveSweepObservationV3Input extends LiveLifecycleObservationBaseV3 {
15
+ expectedGenerationId: `gen_${string}` | null;
15
16
  observation: "stale_heartbeat" | "killed" | "unparseable_heartbeat" | "missing_timestamp";
16
17
  ageMs: number;
17
- }): LiveLifecycleObservationV3Result;
18
+ }
19
+ export declare function recordLiveSweepObservationV3(input: LiveSweepObservationV3Input): LiveLifecycleObservationV3Result;
18
20
  export declare function recordLiveResumeObservationV3(input: LiveLifecycleObservationBaseV3): LiveLifecycleObservationV3Result;
19
21
  export {};
20
22
  //# sourceMappingURL=live-lifecycle-v3.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"live-lifecycle-v3.d.ts","sourceRoot":"","sources":["../../../src/core/agents/live-lifecycle-v3.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAGnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAaxD,MAAM,MAAM,gCAAgC,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAErF,UAAU,8BAA8B;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,8BAA8B,GAAG;IACtC,WAAW,EAAE,iBAAiB,GAAG,QAAQ,GAAG,uBAAuB,GAAG,mBAAmB,CAAC;IAC1F,KAAK,EAAE,MAAM,CAAC;CACf,GACA,gCAAgC,CAOlC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,8BAA8B,GACpC,gCAAgC,CAQlC"}
1
+ {"version":3,"file":"live-lifecycle-v3.d.ts","sourceRoot":"","sources":["../../../src/core/agents/live-lifecycle-v3.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAGnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAcxD,MAAM,MAAM,gCAAgC,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAErF,UAAU,8BAA8B;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,2BAA4B,SAAQ,8BAA8B;IAC1E,oBAAoB,EAAE,OAAO,MAAM,EAAE,GAAG,IAAI,CAAC;IAC7C,WAAW,EAAE,iBAAiB,GAAG,QAAQ,GAAG,uBAAuB,GAAG,mBAAmB,CAAC;IAC1F,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,2BAA2B,GACjC,gCAAgC,CAqBlC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,8BAA8B,GACpC,gCAAgC,CAalC"}
@@ -3,17 +3,27 @@ import { buildEventV3 } from "../events/v3/builder.js";
3
3
  import { normalizeNativeIdV3 } from "../events/v3/canonical.js";
4
4
  import { readEventV3ControlState } from "../events/v3/control.js";
5
5
  import { fingerprintContextV3 } from "../events/v3/fingerprint-keys.js";
6
+ import { withGenerationCustodyV3 } from "../events/v3/generation-custody.js";
6
7
  import { liveInstanceIdV3, livePlatformV3, resolveLiveEventLedgerRouteV3, } from "../events/v3/live-routing.js";
7
8
  import { readJoinableHookProducerStateV3 } from "../events/v3/producers/recorder.js";
8
9
  import { writeEventV3 } from "../events/v3/writer.js";
9
10
  import { LiveCoordinationAuthorityV3Error } from "./live-authority-v3.js";
10
11
  import { liveCoordinationAdapterV3 } from "./state/live-coordination-view.js";
11
12
  export function recordLiveSweepObservationV3(input) {
12
- return recordObservation(input, "lifecycle.sweep_observed", {
13
- subject_instance_id: liveInstanceIdV3(input.owner),
14
- observation: input.observation,
15
- provisional: true,
16
- age_ms: Math.max(0, Math.floor(input.ageMs)),
13
+ input = { ...input };
14
+ // Reject stale observations before preparing durable custody state. The
15
+ // in-custody checks cover replacement while acquiring custody and reentry.
16
+ requireSweepHookState(input);
17
+ return withGenerationCustodyV3(input.coordRoot, [liveInstanceIdV3(input.owner)], () => {
18
+ requireSweepHookState(input);
19
+ return recordObservation(input, "lifecycle.sweep_observed", {
20
+ subject_instance_id: liveInstanceIdV3(input.owner),
21
+ observation: input.observation,
22
+ provisional: true,
23
+ age_ms: Math.max(0, Math.floor(input.ageMs)),
24
+ }, () => {
25
+ requireSweepHookState(input);
26
+ });
17
27
  });
18
28
  }
19
29
  export function recordLiveResumeObservationV3(input) {
@@ -23,9 +33,9 @@ export function recordLiveResumeObservationV3(input) {
23
33
  prior_generation_id: hook.generation_id,
24
34
  continuity: "native",
25
35
  evidence_reference: "heartbeat_recreated",
26
- });
36
+ }, () => { });
27
37
  }
28
- function recordObservation(input, eventType, payload) {
38
+ function recordObservation(input, eventType, payload, beforeWrite) {
29
39
  const route = resolveLiveEventLedgerRouteV3(input.coordRoot);
30
40
  if (route.state === "blocked")
31
41
  throw new LiveCoordinationAuthorityV3Error(route.reason);
@@ -73,6 +83,7 @@ function recordObservation(input, eventType, payload) {
73
83
  monotonic_ns: process.hrtime.bigint().toString(),
74
84
  };
75
85
  const event = buildEventV3(eventType, { ...common, payload });
86
+ beforeWrite();
76
87
  const durability = writeEventV3(input.coordRoot, event, {
77
88
  expectedGenesisId: control.genesis.event.payload.genesis_id,
78
89
  });
@@ -81,6 +92,16 @@ function recordObservation(input, eventType, payload) {
81
92
  }
82
93
  return { state: "recorded", event };
83
94
  }
95
+ function requireSweepHookState(input) {
96
+ if (typeof input.expectedGenerationId !== "string" ||
97
+ !/^gen_[a-zA-Z0-9._-]+$/.test(input.expectedGenerationId)) {
98
+ throw new LiveCoordinationAuthorityV3Error("hook_generation_not_joinable");
99
+ }
100
+ const hook = requireHookState(withAttestedAdapter(input));
101
+ if (hook.generation_id !== input.expectedGenerationId) {
102
+ throw new LiveCoordinationAuthorityV3Error("expected_generation_changed");
103
+ }
104
+ }
84
105
  function withAttestedAdapter(input) {
85
106
  const adapter = liveCoordinationAdapterV3(input.coordRoot, input.owner);
86
107
  if (!adapter)
@@ -9,13 +9,14 @@
9
9
  * shown to them, because nothing surfaces another session's journal.
10
10
  *
11
11
  * This module addresses the durable persona name instead of the instance, so a
12
- * message can be queued before its recipient exists. Delivery happens on the
13
- * recipient's side: their next SessionStart or prompt drains the queue, shows
14
- * the messages, and records them in their journal.
12
+ * message can be queued before its recipient exists. The recipient's next
13
+ * SessionStart or prompt can drain the queue for injection. A drain does not
14
+ * confirm that the adapter emitted the context or that anyone read it.
15
15
  *
16
16
  * Storage: `.harnery/mailbox/<slug>.jsonl`, one JSON record per line, appended
17
- * by senders and removed by an atomic claim on the recipient's side. Delivered
18
- * records are appended to `.harnery/mailbox/delivered/<slug>.jsonl` for audit.
17
+ * by senders and removed by an atomic claim on the recipient's side. Drained
18
+ * records are appended to `.harnery/mailbox/delivered/<slug>.jsonl` for audit,
19
+ * with injection explicitly unconfirmed.
19
20
  *
20
21
  * Related but distinct: `core/inbox` is an instance-keyed transport with a
21
22
  * storage-framework service behind it. This mailbox is name-keyed, file-only,
@@ -81,10 +82,10 @@ export declare function peekMailbox(coordRoot: string, name: string): MailboxMes
81
82
  /**
82
83
  * Claim and return every pending message for a name.
83
84
  *
84
- * The claim is an atomic rename, so two sessions sharing a name cannot deliver
85
+ * The claim is an atomic rename, so two sessions sharing a name cannot claim
85
86
  * the same message twice: whichever rename wins owns the batch. Claimed records
86
- * are appended to the delivered archive before the claim file is removed, so a
87
- * crash between the two leaves an auditable copy rather than a silent loss.
87
+ * are archived before the claim file is removed. The archive records only the
88
+ * drain; output, recipient read, and turn completion remain unconfirmed.
88
89
  */
89
90
  export declare function drainMailbox(coordRoot: string, name: string): MailboxMessageV1[];
90
91
  /** Render a drained batch as the context block a recipient reads. */
@@ -1 +1 @@
1
- {"version":3,"file":"mailbox.d.ts","sourceRoot":"","sources":["../../../src/core/agents/mailbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAqBH,eAAO,MAAM,sBAAsB,EAAG,4BAAqC,CAAC;AAE5E;mCACmC;AACnC,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,qEAAqE;AACrE,eAAO,MAAM,WAAW,KAAK,CAAC;AAI9B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,sBAAsB,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb;qEACiE;IACjE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,eAAe,CAAC;AAEnE,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,WAAW,EAAE,qBAAqB;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAFb,WAAW,EAAE,qBAAqB,EAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM;CAOzB;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD;AAYD,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAqBD;;+DAE+D;AAC/D,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAU1E;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAiBjF;AAiBD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CA6BlE;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAa/E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAmDhF;AA8CD,qEAAqE;AACrE,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GACpC,MAAM,CAcR"}
1
+ {"version":3,"file":"mailbox.d.ts","sourceRoot":"","sources":["../../../src/core/agents/mailbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAqBH,eAAO,MAAM,sBAAsB,EAAG,4BAAqC,CAAC;AAE5E;mCACmC;AACnC,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,qEAAqE;AACrE,eAAO,MAAM,WAAW,KAAK,CAAC;AAI9B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,sBAAsB,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb;qEACiE;IACjE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,eAAe,CAAC;AAEnE,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,WAAW,EAAE,qBAAqB;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAFb,WAAW,EAAE,qBAAqB,EAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM;CAOzB;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD;AAYD,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAqBD;;+DAE+D;AAC/D,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAU1E;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAiBjF;AAiBD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CA6BlE;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAa/E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,EAAE,CA2DhF;AA8CD,qEAAqE;AACrE,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GACpC,MAAM,CAcR"}
@@ -9,13 +9,14 @@
9
9
  * shown to them, because nothing surfaces another session's journal.
10
10
  *
11
11
  * This module addresses the durable persona name instead of the instance, so a
12
- * message can be queued before its recipient exists. Delivery happens on the
13
- * recipient's side: their next SessionStart or prompt drains the queue, shows
14
- * the messages, and records them in their journal.
12
+ * message can be queued before its recipient exists. The recipient's next
13
+ * SessionStart or prompt can drain the queue for injection. A drain does not
14
+ * confirm that the adapter emitted the context or that anyone read it.
15
15
  *
16
16
  * Storage: `.harnery/mailbox/<slug>.jsonl`, one JSON record per line, appended
17
- * by senders and removed by an atomic claim on the recipient's side. Delivered
18
- * records are appended to `.harnery/mailbox/delivered/<slug>.jsonl` for audit.
17
+ * by senders and removed by an atomic claim on the recipient's side. Drained
18
+ * records are appended to `.harnery/mailbox/delivered/<slug>.jsonl` for audit,
19
+ * with injection explicitly unconfirmed.
19
20
  *
20
21
  * Related but distinct: `core/inbox` is an instance-keyed transport with a
21
22
  * storage-framework service behind it. This mailbox is name-keyed, file-only,
@@ -34,8 +35,8 @@ export const MAILBOX_MESSAGE_SCHEMA = "harnery.mailbox-message/v1";
34
35
  export const MAX_BODY_BYTES = 4000;
35
36
  /** Pending messages held for one name before senders are refused. */
36
37
  export const MAX_PENDING = 25;
37
- /** Delivered-archive ceiling; the file is truncated from the head past this. */
38
- const MAX_DELIVERED_BYTES = 256 * 1024;
38
+ /** Drain-archive ceiling; the file is truncated from the head past this. */
39
+ const MAX_ARCHIVE_BYTES = 256 * 1024;
39
40
  export class MailboxCapacityError extends Error {
40
41
  reason_code;
41
42
  current;
@@ -68,7 +69,7 @@ export function mailboxDir(coordRoot) {
68
69
  export function mailboxPath(coordRoot, name) {
69
70
  return join(mailboxDir(coordRoot), `${slugFor(name)}.jsonl`);
70
71
  }
71
- function deliveredPath(coordRoot, name) {
72
+ function archivePath(coordRoot, name) {
72
73
  return join(mailboxDir(coordRoot), "delivered", `${slugFor(name)}.jsonl`);
73
74
  }
74
75
  function parseLines(content) {
@@ -196,10 +197,10 @@ export function peekMailbox(coordRoot, name) {
196
197
  /**
197
198
  * Claim and return every pending message for a name.
198
199
  *
199
- * The claim is an atomic rename, so two sessions sharing a name cannot deliver
200
+ * The claim is an atomic rename, so two sessions sharing a name cannot claim
200
201
  * the same message twice: whichever rename wins owns the batch. Claimed records
201
- * are appended to the delivered archive before the claim file is removed, so a
202
- * crash between the two leaves an auditable copy rather than a silent loss.
202
+ * are archived before the claim file is removed. The archive records only the
203
+ * drain; output, recipient read, and turn completion remain unconfirmed.
203
204
  */
204
205
  export function drainMailbox(coordRoot, name) {
205
206
  let path;
@@ -233,10 +234,16 @@ export function drainMailbox(coordRoot, name) {
233
234
  }
234
235
  if (messages.length > 0) {
235
236
  try {
236
- const archive = deliveredPath(coordRoot, name);
237
+ const archive = archivePath(coordRoot, name);
237
238
  mkdirSync(join(mailboxDir(coordRoot), "delivered"), { recursive: true });
238
- const deliveredAt = new Date().toISOString();
239
- appendFileSync(archive, `${messages.map((m) => JSON.stringify({ ...m, delivered_at: deliveredAt })).join("\n")}\n`, "utf8");
239
+ const drainedAt = new Date().toISOString();
240
+ appendFileSync(archive, `${messages
241
+ .map((m) => JSON.stringify({
242
+ ...m,
243
+ drained_at: drainedAt,
244
+ delivery_status: "injection-unconfirmed",
245
+ }))
246
+ .join("\n")}\n`, "utf8");
240
247
  trimArchive(archive);
241
248
  }
242
249
  catch {
@@ -291,7 +298,7 @@ function sweepStaleClaims(coordRoot) {
291
298
  }
292
299
  function trimArchive(path) {
293
300
  try {
294
- if (statSync(path).size <= MAX_DELIVERED_BYTES)
301
+ if (statSync(path).size <= MAX_ARCHIVE_BYTES)
295
302
  return;
296
303
  const lines = readFileSync(path, "utf8").split("\n").filter(Boolean);
297
304
  const keep = lines.slice(Math.floor(lines.length / 2));
@@ -307,8 +314,8 @@ export function formatMailboxDelivery(coordRoot, messages) {
307
314
  return "";
308
315
  const bin = resolveBinName(coordRoot);
309
316
  const header = messages.length === 1
310
- ? `Message from a peer agent (delivered now, recorded in your journal):`
311
- : `${messages.length} messages from peer agents (delivered now, recorded in your journal):`;
317
+ ? `Message from a peer agent (prepared for injection, recorded in your journal):`
318
+ : `${messages.length} messages from peer agents (prepared for injection, recorded in your journal):`;
312
319
  const body = messages
313
320
  .map((m) => {
314
321
  const when = m.created_at.replace("T", " ").replace(/\.\d+Z$/, "Z");
@@ -55,6 +55,8 @@ export interface PromptContextOpts {
55
55
  * being bounced into a retry. Not hash-deduped, same rationale as the
56
56
  * status footer. */
57
57
  turnRitualNudge?: string;
58
+ /** The emitter acknowledges exactly these IDs only after output succeeds. */
59
+ onInboxPrepared?: (messageIds: readonly string[]) => void;
58
60
  }
59
61
  /**
60
62
  * Build the combined UserPromptSubmit additionalContext string. Returns "" when
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAiDH,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,CAkFnE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA6CtF"}
1
+ {"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAiDH,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;IACzB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;CAC3D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CA8EnE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA6CtF"}
@@ -37,14 +37,14 @@ 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 { formatPeerInbox, peerInbox } from "../../inbox/peer-messages.js";
40
41
  import { readRemoteMachines } from "../../presence/index.js";
41
42
  import { readResourceStatus } from "../../resources/status.js";
42
- import { drainMailbox, formatMailboxDelivery } from "../mailbox.js";
43
43
  import { sessionNameDisplayBlock, sessionNameDisplayPending } from "../session-name-display.js";
44
44
  import { heartbeatPath } from "../state/heartbeat-reader.js";
45
45
  import { readLiveCoordinationRow, readLiveCoordinationRows, } from "../state/live-coordination-view.js";
46
46
  import { forkRelationLabels } from "../state/names.js";
47
- import { formatPendingCouncils, recordDeliveredMessages } from "./session-context.js";
47
+ import { formatPendingCouncils } from "./session-context.js";
48
48
  /**
49
49
  * Build the combined UserPromptSubmit additionalContext string. Returns "" when
50
50
  * nothing has changed since the last call (so the caller can skip the JSON emit).
@@ -60,16 +60,12 @@ export function renderPromptContext(opts) {
60
60
  // Every name-addressed section below (messages, councils) is silently
61
61
  // skipped without it.
62
62
  const resolvedName = agentName ?? readLiveCoordinationRow(coordRoot, instanceId)?.name;
63
- // 1. Peer messages. Drained first and never deduped: a message is delivered
64
- // exactly once, and it is the one section here that carries new information
65
- // from another agent rather than a reminder.
66
- //
67
- if (resolvedName) {
68
- const delivered = drainMailbox(coordRoot, resolvedName);
69
- if (delivered.length > 0) {
70
- recordDeliveredMessages(instanceId, delivered);
71
- sections.push(formatMailboxDelivery(coordRoot, delivered));
72
- }
63
+ // 1. Instance-bound messages. Rendering is read-only; failed output leaves
64
+ // this same batch pending rather than losing it before a prompt sees it.
65
+ const delivered = peerInbox(coordRoot).prepareSurface(instanceId);
66
+ if (delivered.length > 0) {
67
+ opts.onInboxPrepared?.(delivered.map(({ message_id }) => message_id));
68
+ sections.push(formatPeerInbox(coordRoot, delivered));
73
69
  }
74
70
  // 2. Peer table with hash dedup.
75
71
  const peerTable = computePeerTableIfChanged(coordRoot, instanceId, sessionId);