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
@@ -7,7 +7,6 @@
7
7
  * string.
8
8
  */
9
9
  import { statSync } from "node:fs";
10
- import { type MailboxMessageV1 } from "../mailbox.js";
11
10
  export interface RenderOpts {
12
11
  coordRoot: string;
13
12
  instanceId: string;
@@ -16,19 +15,14 @@ export interface RenderOpts {
16
15
  /** Adapter label rendered in parens after the self-name (e.g. "Cursor", "Codex").
17
16
  * Claude Code omits it. */
18
17
  platformLabel?: string;
18
+ /** The emitter acknowledges exactly these IDs only after output succeeds. */
19
+ onInboxPrepared?: (messageIds: readonly string[]) => void;
19
20
  }
20
21
  /**
21
22
  * Build the combined SessionStart systemMessage. Returns the additionalContext
22
23
  * string (or "" if there's nothing to say).
23
24
  */
24
25
  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;
32
26
  /**
33
27
  * Returns a list of commit-guard wiring issues (empty when wired). Portable
34
28
  * 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;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,CAiG7D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GACpC,IAAI,CAUN;AA6LD;;;;;;;;;;;;;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;AAyB1E,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;IACvB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;CAC3D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA8F7D;AA6LD;;;;;;;;;;;;;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,9 +11,8 @@ 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
+ import { formatPeerInbox, peerInbox } from "../../inbox/peer-messages.js";
15
15
  import { readRemoteMachines } from "../../presence/index.js";
16
- import { drainMailbox, formatMailboxDelivery } from "../mailbox.js";
17
16
  import { readLiveCoordinationRows } from "../state/live-coordination-view.js";
18
17
  import { forkRelationLabels, readForkState } from "../state/names.js";
19
18
  /**
@@ -56,15 +55,12 @@ export function renderSessionContext(opts) {
56
55
  if (isLinkedWorktree(coordRoot)) {
57
56
  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.`);
58
57
  }
59
- // 3. Peer messages queued for this name while no session held it. Draining
60
- // here is what makes `agents ping` safe to use on a dormant agent: the sender
61
- // never has to know whether the recipient is running.
62
- if (agentName) {
63
- const delivered = drainMailbox(coordRoot, agentName);
64
- if (delivered.length > 0) {
65
- recordDeliveredMessages(instanceId, delivered);
66
- messages.push(formatMailboxDelivery(coordRoot, delivered));
67
- }
58
+ // 3. Rendering never consumes a message. The native emitter acknowledges the
59
+ // prepared instance-bound batch only after it has flushed the output.
60
+ const delivered = peerInbox(coordRoot).prepareSurface(instanceId);
61
+ if (delivered.length > 0) {
62
+ opts.onInboxPrepared?.(delivered.map(({ message_id }) => message_id));
63
+ messages.push(formatPeerInbox(coordRoot, delivered));
68
64
  }
69
65
  // 4. Council invites
70
66
  if (agentName) {
@@ -107,25 +103,6 @@ export function renderSessionContext(opts) {
107
103
  }
108
104
  return messages.join("\n\n");
109
105
  }
110
- /**
111
- * Write delivered peer messages into the recipient's own journal, so they
112
- * survive context compaction the way any other coordination breadcrumb does.
113
- * A message the sender already journaled (the recipient was live when it was
114
- * sent) is skipped to avoid a duplicate entry.
115
- */
116
- export function recordDeliveredMessages(instanceId, messages) {
117
- for (const m of messages) {
118
- if (m.journaled)
119
- continue;
120
- try {
121
- appendEntry(instanceId, "handoff", `from agent-${m.from_name}: ${m.body}`);
122
- }
123
- catch {
124
- // A journal write failure must never cost the delivery itself; the
125
- // message is already in the rendered context the model reads.
126
- }
127
- }
128
- }
129
106
  /** One-line-per-agent view of sessions on other machines (presence refs). */
130
107
  function formatRemoteMachines(coordRoot) {
131
108
  try {
@@ -331,9 +331,9 @@ function sessionNameBlock(coordRoot, instanceId) {
331
331
  catch {
332
332
  // The ledger evidence still authorizes the block; the cache is UX only.
333
333
  }
334
- const instruction = name
335
- ? `Reproduce it by itself in a fenced code block: ${name}`
336
- : `Run \`${resolveBinName(coordRoot)} agents suggest-name\`, then reproduce its output by itself in a fenced code block.`;
334
+ // The first post-mint reply was already rejected. Repeating its title later
335
+ // cannot change that evidence; the JSON retry result opens a new window.
336
+ const instruction = `Run \`${resolveBinName(coordRoot)} agents suggest-name --json\` as your only tool call to create a fresh verification boundary, then display its suggested_session_name verbatim by itself in a fenced code block${name ? `: ${name}` : "."}`;
337
337
  return {
338
338
  allow: false,
339
339
  exit_code: 2,
@@ -1 +1 @@
1
- {"version":3,"file":"heartbeat-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"heartbeat-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-writer.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,8 @@
15
15
  */
16
16
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
17
17
  import { dirname, join } from "node:path";
18
+ import { assertCurrentGenerationV3, withGenerationCustodyV3, } from "../../events/v3/generation-custody.js";
19
+ import { liveInstanceIdV3 } from "../../events/v3/live-routing.js";
18
20
  import { canonicalClaimPath } from "../claim-path.js";
19
21
  import { heartbeatPath, readHeartbeat } from "./heartbeat-reader.js";
20
22
  export { readHeartbeat } from "./heartbeat-reader.js";
@@ -38,13 +40,16 @@ export function setLifecycleCache(coordRoot, instanceId, state, reason, suggeste
38
40
  }));
39
41
  }
40
42
  function mutate(coordRoot, instanceId, fn) {
41
- const hb = readHeartbeat(coordRoot, instanceId);
42
- if (!hb)
43
- return null;
44
- const next = fn(hb);
45
- next.last_heartbeat = nowIsoSeconds();
46
- atomicWrite(heartbeatPath(coordRoot, instanceId), JSON.stringify(next, null, 2));
47
- return next;
43
+ return withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () => {
44
+ const hb = readHeartbeat(coordRoot, instanceId);
45
+ if (!hb)
46
+ return null;
47
+ const next = fn(hb);
48
+ next.last_heartbeat = nowIsoSeconds();
49
+ assertCurrentGenerationV3(coordRoot, liveInstanceIdV3(instanceId), hb.v3_generation_id);
50
+ atomicWrite(heartbeatPath(coordRoot, instanceId), JSON.stringify(next, null, 2));
51
+ return next;
52
+ });
48
53
  }
49
54
  export function setTask(coordRoot, instanceId, task) {
50
55
  return mutate(coordRoot, instanceId, (hb) => {
@@ -68,13 +73,16 @@ export function setTask(coordRoot, instanceId, task) {
68
73
  }
69
74
  /** Update the disposable identity cache after a canonical V3 attestation. */
70
75
  export function setIdentityCache(coordRoot, instanceId, name, agentId) {
71
- const heartbeat = readHeartbeat(coordRoot, instanceId);
72
- if (!heartbeat)
73
- return null;
74
- heartbeat.name = name;
75
- heartbeat.agent_id = agentId;
76
- atomicWrite(heartbeatPath(coordRoot, instanceId), JSON.stringify(heartbeat, null, 2));
77
- return heartbeat;
76
+ return withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () => {
77
+ const heartbeat = readHeartbeat(coordRoot, instanceId);
78
+ if (!heartbeat)
79
+ return null;
80
+ heartbeat.name = name;
81
+ heartbeat.agent_id = agentId;
82
+ assertCurrentGenerationV3(coordRoot, liveInstanceIdV3(instanceId), heartbeat.v3_generation_id);
83
+ atomicWrite(heartbeatPath(coordRoot, instanceId), JSON.stringify(heartbeat, null, 2));
84
+ return heartbeat;
85
+ });
78
86
  }
79
87
  /** Restore the pool-assigned display name on a generation that was
80
88
  * materialized before the adapter's normal SessionStart naming path ran.
@@ -248,6 +256,9 @@ export function clearCoordinationCache(coordRoot, instanceId) {
248
256
  }
249
257
  }
250
258
  export function healPidmap(coordRoot, instanceId, pid) {
259
+ withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () => healPidmapUnderCustody(coordRoot, instanceId, pid));
260
+ }
261
+ function healPidmapUnderCustody(coordRoot, instanceId, pid) {
251
262
  const dir = join(coordRoot, ".harnery", "pid-map");
252
263
  mkdirSync(dir, { recursive: true });
253
264
  const hb = readHeartbeat(coordRoot, instanceId);
@@ -1 +1 @@
1
- {"version":3,"file":"live-coordination-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/live-coordination-writer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAMhD,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AAUtE;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,GAAG,IAAI,CASlB;AAED,wFAAwF;AACxF,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,OAAO,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,GAAG,IAAI,CASlB"}
1
+ {"version":3,"file":"live-coordination-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/live-coordination-writer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAUhD,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AAUtE;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,GAAG,IAAI,CASlB;AAED,wFAAwF;AACxF,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,OAAO,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,GAAG,IAAI,CASlB"}
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync, renameSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from "../../events/v3/coordination-view.js";
4
+ import { assertCurrentGenerationV3, withGenerationCustodyV3, } from "../../events/v3/generation-custody.js";
4
5
  import { liveInstanceIdV3, resolveLiveEventLedgerRouteV3 } from "../../events/v3/live-routing.js";
5
6
  import { readHeartbeat } from "./heartbeat-reader.js";
6
7
  import { isAdapterV3, isCurrentV3HeartbeatCache, observedAdapterV3, projectHeartbeatV3, } from "./live-coordination-view.js";
@@ -19,6 +20,9 @@ export function repairLiveCoordinationHeartbeat(coordRoot, nativeInstanceId, nat
19
20
  return materializeLiveCoordinationHeartbeat(coordRoot, nativeInstanceId, nativeSessionId, adapter, model, true);
20
21
  }
21
22
  function materializeLiveCoordinationHeartbeat(coordRoot, nativeInstanceId, nativeSessionId, _adapter, model, force) {
23
+ return withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(nativeInstanceId)], () => materializeHeartbeatUnderCustody(coordRoot, nativeInstanceId, nativeSessionId, _adapter, model, force));
24
+ }
25
+ function materializeHeartbeatUnderCustody(coordRoot, nativeInstanceId, nativeSessionId, _adapter, model, force) {
22
26
  const route = resolveLiveEventLedgerRouteV3(coordRoot);
23
27
  if (route.state === "blocked")
24
28
  throw new Error(`event_v3_coordination_view:${route.reason}`);
@@ -85,6 +89,7 @@ function materializeLiveCoordinationHeartbeat(coordRoot, nativeInstanceId, nativ
85
89
  v3_projection_event_id: generation.last_event_id,
86
90
  v3_task_state: generation.task_state === "set" ? "set" : "cleared",
87
91
  };
92
+ assertCurrentGenerationV3(coordRoot, liveInstanceIdV3(nativeInstanceId), generation.generation_id);
88
93
  writeHeartbeatCache(coordRoot, nativeInstanceId, materialized);
89
94
  return materialized;
90
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pidmap.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/pidmap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAyBH,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAGA;AAwCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA8B7D;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CA6BN;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAmBjF;AAED,yFAAyF;AACzF,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBzF"}
1
+ {"version":3,"file":"pidmap.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/pidmap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA2BH,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAGA;AAwCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA8B7D;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CAIN;AAsCD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAmBjF;AAED,yFAAyF;AACzF,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBzF"}
@@ -15,6 +15,8 @@
15
15
  */
16
16
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
17
17
  import { dirname, join } from "node:path";
18
+ import { withGenerationCustodyV3 } from "../../events/v3/generation-custody.js";
19
+ import { liveInstanceIdV3 } from "../../events/v3/live-routing.js";
18
20
  import { checkPidToken, processStartToken } from "./proc-start.js";
19
21
  /**
20
22
  * Row count that triggers a dead-row sweep on the next write.
@@ -110,6 +112,9 @@ export function prunePidmapDeadRows(coordRoot) {
110
112
  return removed;
111
113
  }
112
114
  export function writePidmapRow(coordRoot, pid, instanceId, platform) {
115
+ withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () => writePidmapRowUnderCustody(coordRoot, pid, instanceId, platform));
116
+ }
117
+ function writePidmapRowUnderCustody(coordRoot, pid, instanceId, platform) {
113
118
  const dir = join(coordRoot, ".harnery", "pid-map");
114
119
  const path = join(dir, String(pid));
115
120
  // Stamped at write time, when the pid provably belongs to the process we
@@ -30,8 +30,9 @@
30
30
  * through the caller's locale and timezone, so both are pinned; without that,
31
31
  * two callers reading the same live process disagree.
32
32
  */
33
+ import { signalProcessWithHandle } from "./process-handle.js";
33
34
  /** Which probe answers "when did this process start?" on this machine. */
34
- export type StartTokenProbe = "procfs" | "ps";
35
+ export type StartTokenProbe = "procfs" | "ps" | "windows";
35
36
  /**
36
37
  * Which probe this machine uses, decided once.
37
38
  *
@@ -68,6 +69,22 @@ export declare function psStartToken(pid: number, run?: (pid: number) => {
68
69
  * will not say.
69
70
  */
70
71
  export declare function processStartToken(pid: number): string | null;
72
+ export type ProcessGenerationVerdict = "same" | "different" | "unknown";
73
+ type ReadProcessStartToken = (pid: number) => string | null;
74
+ /** A missing or unreadable token never establishes process identity. */
75
+ export declare function verifyProcessGeneration(pid: number, recordedToken: string | null | undefined, readToken?: ReadProcessStartToken): ProcessGenerationVerdict;
76
+ export interface ProcessGenerationSignalDependencies {
77
+ readToken?: ReadProcessStartToken;
78
+ signalProcess?: typeof signalProcessWithHandle;
79
+ }
80
+ /**
81
+ * Observations may refuse a signal, but only the retained native process handle
82
+ * can authorize it. No numeric-PID effect follows the preliminary token read.
83
+ */
84
+ export declare function signalIfSameGeneration(pid: number, recordedToken: string | null | undefined, signal: NodeJS.Signals | number, dependencies?: ProcessGenerationSignalDependencies): {
85
+ sent: boolean;
86
+ verdict: ProcessGenerationVerdict;
87
+ };
71
88
  /**
72
89
  * Do two tokens describe the same run?
73
90
  *
@@ -92,4 +109,5 @@ export declare function tokensAgree(recorded: string, current: string): boolean;
92
109
  * treated every row previously: trust it if the pid is alive.
93
110
  */
94
111
  export declare function checkPidToken(pid: number, recorded: string | undefined): "match" | "mismatch" | "unverified";
112
+ export {};
95
113
  //# sourceMappingURL=proc-start.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"proc-start.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/proc-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,IAAI,CAAC;AAc9C;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAOjD;AAyBD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc3D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAiB,GAC7E,MAAM,GAAG,IAAI,CAUf;AAWD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,GAAG,UAAU,GAAG,YAAY,CAKrC"}
1
+ {"version":3,"file":"proc-start.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/proc-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,EAAE,uBAAuB,EAA4B,MAAM,qBAAqB,CAAC;AAExF,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC;AAc1D;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAQjD;AAyBD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc3D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAiB,GAC7E,MAAM,GAAG,IAAI,CAUf;AAWD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ5D;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;AAExE,KAAK,qBAAqB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAE5D,wEAAwE;AACxE,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,SAAS,GAAE,qBAAyC,GACnD,wBAAwB,CAkB1B;AAED,MAAM,WAAW,mCAAmC;IAClD,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,uBAAuB,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,EAC/B,YAAY,GAAE,mCAAwC,GACrD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,wBAAwB,CAAA;CAAE,CAQtD;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,GAAG,UAAU,GAAG,YAAY,CAKrC"}
@@ -33,6 +33,7 @@
33
33
  import { spawnSync } from "node:child_process";
34
34
  import { existsSync, readFileSync } from "node:fs";
35
35
  import { coordEnv } from "../../../lib/env.js";
36
+ import { signalProcessWithHandle, windowsProcessStartToken } from "./process-handle.js";
36
37
  /**
37
38
  * The `ps` probe is text formatted by `strftime("%c", localtime(...))`, so its
38
39
  * output moves with `TZ` and `LC_TIME`. A hook running under `LC_ALL=C` (git
@@ -54,6 +55,8 @@ export function startTokenProbe() {
54
55
  const override = coordEnv("PID_PROBE");
55
56
  if (override === "procfs" || override === "ps")
56
57
  return override;
58
+ if (process.platform === "win32")
59
+ return "windows";
57
60
  if (cachedProbe === undefined) {
58
61
  cachedProbe = existsSync("/proc/self/stat") ? "procfs" : "ps";
59
62
  }
@@ -149,7 +152,49 @@ function defaultPsRun(pid) {
149
152
  export function processStartToken(pid) {
150
153
  if (!Number.isFinite(pid) || pid <= 0)
151
154
  return null;
152
- return startTokenProbe() === "procfs" ? procfsStartToken(pid) : psStartToken(pid);
155
+ const probe = startTokenProbe();
156
+ return probe === "windows"
157
+ ? windowsProcessStartToken(pid)
158
+ : probe === "procfs"
159
+ ? procfsStartToken(pid)
160
+ : psStartToken(pid);
161
+ }
162
+ /** A missing or unreadable token never establishes process identity. */
163
+ export function verifyProcessGeneration(pid, recordedToken, readToken = processStartToken) {
164
+ if (!Number.isInteger(pid) ||
165
+ pid <= 0 ||
166
+ typeof recordedToken !== "string" ||
167
+ !recordedToken.trim())
168
+ return "unknown";
169
+ // A bootless Linux token cannot identify a process across machine boots.
170
+ if (/^l\d+$/.test(recordedToken))
171
+ return "unknown";
172
+ try {
173
+ const currentToken = readToken(pid);
174
+ if (typeof currentToken !== "string" || !currentToken.trim())
175
+ return "unknown";
176
+ if (/^l\d+$/.test(currentToken))
177
+ return "unknown";
178
+ return currentToken === recordedToken ? "same" : "different";
179
+ }
180
+ catch {
181
+ return "unknown";
182
+ }
183
+ }
184
+ /**
185
+ * Observations may refuse a signal, but only the retained native process handle
186
+ * can authorize it. No numeric-PID effect follows the preliminary token read.
187
+ */
188
+ export function signalIfSameGeneration(pid, recordedToken, signal, dependencies = {}) {
189
+ const verdict = verifyProcessGeneration(pid, recordedToken, dependencies.readToken);
190
+ if (verdict !== "same")
191
+ return { sent: false, verdict };
192
+ try {
193
+ return (dependencies.signalProcess ?? signalProcessWithHandle)(pid, recordedToken, signal);
194
+ }
195
+ catch {
196
+ return { sent: false, verdict: "unknown" };
197
+ }
153
198
  }
154
199
  /**
155
200
  * Do two tokens describe the same run?
@@ -0,0 +1,26 @@
1
+ import type { ProcessGenerationVerdict } from "./proc-start.js";
2
+ export interface ProcessHandleSignalResult {
3
+ sent: boolean;
4
+ verdict: ProcessGenerationVerdict;
5
+ }
6
+ export type NativeProcessHelperRunner = (command: string, args: string[], options: {
7
+ input: string;
8
+ encoding: "utf8";
9
+ timeout: number;
10
+ maxBuffer: number;
11
+ windowsHide: boolean;
12
+ }) => {
13
+ status: number | null;
14
+ stdout: string | Buffer | null;
15
+ error?: Error;
16
+ };
17
+ export declare const LINUX_PROCESS_HANDLE_SCRIPT = "\nimport json, os, pathlib, signal, sys\nresult = {\"sent\": False, \"verdict\": \"unknown\"}\nfd = None\ntry:\n request = json.loads(sys.stdin.readline())\n if not hasattr(os, \"pidfd_open\") or not hasattr(signal, \"pidfd_send_signal\"):\n raise RuntimeError(\"pidfd support unavailable\")\n fd = os.pidfd_open(request[\"pid\"])\n stat = pathlib.Path(\"/proc/%d/stat\" % request[\"pid\"]).read_text()\n ticks = stat[stat.rfind(\") \") + 2:].split()[19]\n boot = pathlib.Path(\"/proc/sys/kernel/random/boot_id\").read_text().strip().replace(\"-\", \"\")[:8]\n token = \"l%s.%s\" % (boot, ticks)\n result[\"token\"] = token\n recorded = request.get(\"token\")\n result[\"verdict\"] = \"same\" if token == recorded else \"different\"\n if request[\"mode\"] == \"signal\" and result[\"verdict\"] == \"same\":\n print(json.dumps({\"phase\": \"verified\"}), flush=True)\n if sys.stdin.readline().strip() == \"signal\":\n signal.pidfd_send_signal(fd, request[\"signal\"])\n result[\"sent\"] = True\nexcept Exception:\n pass\nfinally:\n if fd is not None:\n os.close(fd)\nprint(json.dumps(result), flush=True)\n";
18
+ export declare const WINDOWS_PROCESS_HANDLE_SCRIPT = "\n$ErrorActionPreference = 'Stop'\n$result = @{ sent = $false; verdict = 'unknown' }\n$handle = [IntPtr]::Zero\ntry {\n Add-Type -TypeDefinition @'\nusing System;\nusing System.Runtime.InteropServices;\npublic static class HarneryProcessHandle {\n [DllImport(\"kernel32.dll\", SetLastError=true)] public static extern IntPtr OpenProcess(uint access, bool inherit, uint processId);\n [DllImport(\"kernel32.dll\", SetLastError=true)] public static extern bool GetProcessTimes(IntPtr handle, out long creation, out long exit, out long kernel, out long user);\n [DllImport(\"kernel32.dll\", SetLastError=true)] public static extern bool TerminateProcess(IntPtr handle, uint exitCode);\n [DllImport(\"kernel32.dll\")] public static extern uint WaitForSingleObject(IntPtr handle, uint milliseconds);\n [DllImport(\"kernel32.dll\")] public static extern bool CloseHandle(IntPtr handle);\n}\n'@\n $request = [Console]::In.ReadLine() | ConvertFrom-Json\n $access = [uint32]0x101000\n if ($request.mode -eq 'signal') { $access = $access -bor 1 }\n $handle = [HarneryProcessHandle]::OpenProcess($access, $false, [uint32]$request.pid)\n if ($handle -eq [IntPtr]::Zero) { throw 'process handle unavailable' }\n [long]$creation = 0; [long]$exitTime = 0; [long]$kernel = 0; [long]$userTime = 0\n if (-not [HarneryProcessHandle]::GetProcessTimes($handle, [ref]$creation, [ref]$exitTime, [ref]$kernel, [ref]$userTime)) { throw 'process time unavailable' }\n $token = 'w' + $creation.ToString([Globalization.CultureInfo]::InvariantCulture)\n $result.token = $token\n $result.verdict = if ($token -ceq $request.token) { 'same' } else { 'different' }\n if ($request.mode -eq 'signal' -and $result.verdict -eq 'same') {\n [Console]::Out.WriteLine('{\"phase\":\"verified\"}')\n [Console]::Out.Flush()\n if ([Console]::In.ReadLine() -eq 'signal' -and [HarneryProcessHandle]::WaitForSingleObject($handle, 0) -eq 258) {\n $result.sent = [HarneryProcessHandle]::TerminateProcess($handle, 1)\n }\n }\n} catch {} finally {\n if ($handle -ne [IntPtr]::Zero) { [void][HarneryProcessHandle]::CloseHandle($handle) }\n}\n$result | ConvertTo-Json -Compress\n";
19
+ /** Windows creation FILETIME, read from an OpenProcess handle. */
20
+ export declare function windowsProcessStartToken(pid: number, run?: NativeProcessHelperRunner): string | null;
21
+ /** No numeric-PID signaling fallback is permitted when an exact handle fails. */
22
+ export declare function signalProcessWithHandle(pid: number, recordedToken: string, signal: NodeJS.Signals | number, options?: {
23
+ platform?: NodeJS.Platform;
24
+ run?: NativeProcessHelperRunner;
25
+ }): ProcessHandleSignalResult;
26
+ //# sourceMappingURL=process-handle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-handle.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/process-handle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAEhE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,wBAAwB,CAAC;CACnC;AAMD,MAAM,MAAM,yBAAyB,GAAG,CACtC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IACP,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB,KACE;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAI9E,eAAO,MAAM,2BAA2B,+qCA2BvC,CAAC;AAEF,eAAO,MAAM,6BAA6B,qnEAqCzC,CAAC;AAwCF,kEAAkE;AAClE,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,yBAAqC,GACzC,MAAM,GAAG,IAAI,CAGf;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,EAC/B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,yBAAyB,CAAA;CAAO,GAC5E,yBAAyB,CAgB3B"}
@@ -0,0 +1,121 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { constants } from "node:os";
3
+ // The helper owns the descriptor throughout observation and effect. The second
4
+ // input line authorizes the effect; EOF releases the handle without signaling.
5
+ export const LINUX_PROCESS_HANDLE_SCRIPT = `
6
+ import json, os, pathlib, signal, sys
7
+ result = {"sent": False, "verdict": "unknown"}
8
+ fd = None
9
+ try:
10
+ request = json.loads(sys.stdin.readline())
11
+ if not hasattr(os, "pidfd_open") or not hasattr(signal, "pidfd_send_signal"):
12
+ raise RuntimeError("pidfd support unavailable")
13
+ fd = os.pidfd_open(request["pid"])
14
+ stat = pathlib.Path("/proc/%d/stat" % request["pid"]).read_text()
15
+ ticks = stat[stat.rfind(") ") + 2:].split()[19]
16
+ boot = pathlib.Path("/proc/sys/kernel/random/boot_id").read_text().strip().replace("-", "")[:8]
17
+ token = "l%s.%s" % (boot, ticks)
18
+ result["token"] = token
19
+ recorded = request.get("token")
20
+ result["verdict"] = "same" if token == recorded else "different"
21
+ if request["mode"] == "signal" and result["verdict"] == "same":
22
+ print(json.dumps({"phase": "verified"}), flush=True)
23
+ if sys.stdin.readline().strip() == "signal":
24
+ signal.pidfd_send_signal(fd, request["signal"])
25
+ result["sent"] = True
26
+ except Exception:
27
+ pass
28
+ finally:
29
+ if fd is not None:
30
+ os.close(fd)
31
+ print(json.dumps(result), flush=True)
32
+ `;
33
+ export const WINDOWS_PROCESS_HANDLE_SCRIPT = `
34
+ $ErrorActionPreference = 'Stop'
35
+ $result = @{ sent = $false; verdict = 'unknown' }
36
+ $handle = [IntPtr]::Zero
37
+ try {
38
+ Add-Type -TypeDefinition @'
39
+ using System;
40
+ using System.Runtime.InteropServices;
41
+ public static class HarneryProcessHandle {
42
+ [DllImport("kernel32.dll", SetLastError=true)] public static extern IntPtr OpenProcess(uint access, bool inherit, uint processId);
43
+ [DllImport("kernel32.dll", SetLastError=true)] public static extern bool GetProcessTimes(IntPtr handle, out long creation, out long exit, out long kernel, out long user);
44
+ [DllImport("kernel32.dll", SetLastError=true)] public static extern bool TerminateProcess(IntPtr handle, uint exitCode);
45
+ [DllImport("kernel32.dll")] public static extern uint WaitForSingleObject(IntPtr handle, uint milliseconds);
46
+ [DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr handle);
47
+ }
48
+ '@
49
+ $request = [Console]::In.ReadLine() | ConvertFrom-Json
50
+ $access = [uint32]0x101000
51
+ if ($request.mode -eq 'signal') { $access = $access -bor 1 }
52
+ $handle = [HarneryProcessHandle]::OpenProcess($access, $false, [uint32]$request.pid)
53
+ if ($handle -eq [IntPtr]::Zero) { throw 'process handle unavailable' }
54
+ [long]$creation = 0; [long]$exitTime = 0; [long]$kernel = 0; [long]$userTime = 0
55
+ if (-not [HarneryProcessHandle]::GetProcessTimes($handle, [ref]$creation, [ref]$exitTime, [ref]$kernel, [ref]$userTime)) { throw 'process time unavailable' }
56
+ $token = 'w' + $creation.ToString([Globalization.CultureInfo]::InvariantCulture)
57
+ $result.token = $token
58
+ $result.verdict = if ($token -ceq $request.token) { 'same' } else { 'different' }
59
+ if ($request.mode -eq 'signal' -and $result.verdict -eq 'same') {
60
+ [Console]::Out.WriteLine('{"phase":"verified"}')
61
+ [Console]::Out.Flush()
62
+ if ([Console]::In.ReadLine() -eq 'signal' -and [HarneryProcessHandle]::WaitForSingleObject($handle, 0) -eq 258) {
63
+ $result.sent = [HarneryProcessHandle]::TerminateProcess($handle, 1)
64
+ }
65
+ }
66
+ } catch {} finally {
67
+ if ($handle -ne [IntPtr]::Zero) { [void][HarneryProcessHandle]::CloseHandle($handle) }
68
+ }
69
+ $result | ConvertTo-Json -Compress
70
+ `;
71
+ function runHelper(request, platform, run) {
72
+ const unknown = { sent: false, verdict: "unknown" };
73
+ if (!Number.isInteger(request.pid) || request.pid <= 0)
74
+ return unknown;
75
+ if (platform !== "linux" && platform !== "win32")
76
+ return unknown;
77
+ try {
78
+ const command = platform === "linux" ? "python3" : "powershell.exe";
79
+ const args = platform === "linux"
80
+ ? ["-I", "-c", LINUX_PROCESS_HANDLE_SCRIPT]
81
+ : ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", WINDOWS_PROCESS_HANDLE_SCRIPT];
82
+ const output = run(command, args, {
83
+ input: `${JSON.stringify(request)}\nsignal\n`,
84
+ encoding: "utf8",
85
+ timeout: 10_000,
86
+ maxBuffer: 64 * 1024,
87
+ windowsHide: true,
88
+ });
89
+ if (output.error || output.status !== 0)
90
+ return unknown;
91
+ const lines = String(output.stdout ?? "")
92
+ .trim()
93
+ .split(/\r?\n/);
94
+ const parsed = JSON.parse(lines.at(-1) ?? "");
95
+ if (typeof parsed.sent !== "boolean" ||
96
+ !["same", "different", "unknown"].includes(parsed.verdict) ||
97
+ (parsed.sent && parsed.verdict !== "same"))
98
+ return unknown;
99
+ return parsed;
100
+ }
101
+ catch {
102
+ return unknown;
103
+ }
104
+ }
105
+ /** Windows creation FILETIME, read from an OpenProcess handle. */
106
+ export function windowsProcessStartToken(pid, run = spawnSync) {
107
+ const result = runHelper({ mode: "probe", pid }, "win32", run);
108
+ return typeof result.token === "string" && /^w\d+$/.test(result.token) ? result.token : null;
109
+ }
110
+ /** No numeric-PID signaling fallback is permitted when an exact handle fails. */
111
+ export function signalProcessWithHandle(pid, recordedToken, signal, options = {}) {
112
+ const platform = options.platform ?? process.platform;
113
+ const number = typeof signal === "number" ? signal : constants.signals[signal];
114
+ if (!Number.isInteger(number) ||
115
+ number <= 0 ||
116
+ (platform === "linux" && !/^l[0-9a-f]{8}\.\d+$/.test(recordedToken)) ||
117
+ (platform === "win32" && (!/^w\d+$/.test(recordedToken) || ![9, 15].includes(number))))
118
+ return { sent: false, verdict: "unknown" };
119
+ const result = runHelper({ mode: "signal", pid, token: recordedToken, signal: number }, platform, options.run ?? spawnSync);
120
+ return { sent: result.sent, verdict: result.verdict };
121
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"stale-sweep.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/stale-sweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkEH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG;IAC7C,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAyJA"}
1
+ {"version":3,"file":"stale-sweep.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/stale-sweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyIH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG;IAC7C,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAuPA"}