harnery 0.42.0 → 0.44.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 (343) hide show
  1. package/README.md +3 -3
  2. package/dist/commander.d.ts +16 -0
  3. package/dist/commander.d.ts.map +1 -1
  4. package/dist/commander.js +18 -0
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +95 -52
  7. package/dist/commands/browse.d.ts.map +1 -1
  8. package/dist/commands/browse.js +38 -11
  9. package/dist/commands/diagnostics.d.ts.map +1 -1
  10. package/dist/commands/diagnostics.js +10 -0
  11. package/dist/commands/doctor.d.ts +12 -0
  12. package/dist/commands/doctor.d.ts.map +1 -1
  13. package/dist/commands/doctor.js +22 -7
  14. package/dist/commands/governor.js +22 -10
  15. package/dist/commands/inbox.d.ts +1 -1
  16. package/dist/commands/inbox.d.ts.map +1 -1
  17. package/dist/commands/inbox.js +55 -2
  18. package/dist/commands/rm.d.ts.map +1 -1
  19. package/dist/commands/rm.js +28 -4
  20. package/dist/commands/servers.d.ts.map +1 -1
  21. package/dist/commands/servers.js +16 -1
  22. package/dist/commands/supervisor.d.ts.map +1 -1
  23. package/dist/commands/supervisor.js +7 -2
  24. package/dist/commands/toc.d.ts +14 -0
  25. package/dist/commands/toc.d.ts.map +1 -1
  26. package/dist/commands/toc.js +7 -4
  27. package/dist/commands/tunnel.d.ts +65 -0
  28. package/dist/commands/tunnel.d.ts.map +1 -1
  29. package/dist/commands/tunnel.js +290 -101
  30. package/dist/commands/web-fetch.d.ts +3 -2
  31. package/dist/commands/web-fetch.d.ts.map +1 -1
  32. package/dist/commands/web-fetch.js +25 -19
  33. package/dist/commands/web.d.ts +7 -1
  34. package/dist/commands/web.d.ts.map +1 -1
  35. package/dist/commands/web.js +9 -3
  36. package/dist/commands/workflow.js +24 -30
  37. package/dist/core/agents/live-authority-v3.d.ts +1 -0
  38. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  39. package/dist/core/agents/live-authority-v3.js +11 -0
  40. package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
  41. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  42. package/dist/core/agents/live-lifecycle-v3.js +28 -7
  43. package/dist/core/agents/mailbox.d.ts +9 -8
  44. package/dist/core/agents/mailbox.d.ts.map +1 -1
  45. package/dist/core/agents/mailbox.js +24 -17
  46. package/dist/core/agents/render/prompt-context.d.ts +2 -0
  47. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  48. package/dist/core/agents/render/prompt-context.js +19 -17
  49. package/dist/core/agents/render/session-context.d.ts +2 -8
  50. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  51. package/dist/core/agents/render/session-context.js +64 -43
  52. package/dist/core/agents/rules/stop-hook.js +3 -3
  53. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  54. package/dist/core/agents/state/heartbeat-writer.js +25 -14
  55. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
  56. package/dist/core/agents/state/live-coordination-writer.js +5 -0
  57. package/dist/core/agents/state/names.d.ts +43 -3
  58. package/dist/core/agents/state/names.d.ts.map +1 -1
  59. package/dist/core/agents/state/names.js +87 -3
  60. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  61. package/dist/core/agents/state/pidmap.js +5 -0
  62. package/dist/core/agents/state/proc-start.d.ts +19 -0
  63. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  64. package/dist/core/agents/state/proc-start.js +41 -0
  65. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  66. package/dist/core/agents/state/stale-sweep.js +180 -70
  67. package/dist/core/agents/state/unchanged-file.d.ts +44 -0
  68. package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
  69. package/dist/core/agents/state/unchanged-file.js +390 -0
  70. package/dist/core/context/index.d.ts +22 -10
  71. package/dist/core/context/index.d.ts.map +1 -1
  72. package/dist/core/context/index.js +381 -119
  73. package/dist/core/context/observation.d.ts +26 -0
  74. package/dist/core/context/observation.d.ts.map +1 -0
  75. package/dist/core/context/observation.js +11 -0
  76. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  77. package/dist/core/diagnostics/bundle.js +25 -14
  78. package/dist/core/diagnostics/path-identity.d.ts +3 -0
  79. package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
  80. package/dist/core/diagnostics/path-identity.js +12 -0
  81. package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
  82. package/dist/core/diagnostics/sanitize.js +4 -3
  83. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  84. package/dist/core/events/v3/authority-outbox.js +29 -0
  85. package/dist/core/events/v3/generation-custody.d.ts +5 -0
  86. package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
  87. package/dist/core/events/v3/generation-custody.js +50 -0
  88. package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
  89. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  90. package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
  91. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  92. package/dist/core/events/v3/producers/recorder.js +77 -64
  93. package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
  94. package/dist/core/events/v3/support-storage/pack-reader.js +42 -16
  95. package/dist/core/governor/planning.d.ts.map +1 -1
  96. package/dist/core/governor/planning.js +14 -1
  97. package/dist/core/governor/service-read.d.ts +10 -2
  98. package/dist/core/governor/service-read.d.ts.map +1 -1
  99. package/dist/core/governor/service-read.js +105 -22
  100. package/dist/core/governor/service.d.ts +20 -2
  101. package/dist/core/governor/service.d.ts.map +1 -1
  102. package/dist/core/governor/service.js +72 -47
  103. package/dist/core/hooks/cli.js +120 -23
  104. package/dist/core/hooks/fork-lineage.d.ts +40 -0
  105. package/dist/core/hooks/fork-lineage.d.ts.map +1 -0
  106. package/dist/core/hooks/fork-lineage.js +62 -0
  107. package/dist/core/hooks/resolve/transcript.d.ts +16 -0
  108. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  109. package/dist/core/hooks/resolve/transcript.js +84 -0
  110. package/dist/core/hooks/shell-rm.d.ts +2 -0
  111. package/dist/core/hooks/shell-rm.d.ts.map +1 -0
  112. package/dist/core/hooks/shell-rm.js +105 -0
  113. package/dist/core/hooks/shell-tokens.d.ts +12 -0
  114. package/dist/core/hooks/shell-tokens.d.ts.map +1 -0
  115. package/dist/core/hooks/shell-tokens.js +97 -0
  116. package/dist/core/hooks/shell-waiter.d.ts.map +1 -1
  117. package/dist/core/hooks/shell-waiter.js +3 -93
  118. package/dist/core/inbox/contract.d.ts +2 -1
  119. package/dist/core/inbox/contract.d.ts.map +1 -1
  120. package/dist/core/inbox/index.d.ts +2 -0
  121. package/dist/core/inbox/index.d.ts.map +1 -1
  122. package/dist/core/inbox/index.js +2 -0
  123. package/dist/core/inbox/peer-messages.d.ts +37 -0
  124. package/dist/core/inbox/peer-messages.d.ts.map +1 -0
  125. package/dist/core/inbox/peer-messages.js +72 -0
  126. package/dist/core/inbox/service.d.ts +8 -1
  127. package/dist/core/inbox/service.d.ts.map +1 -1
  128. package/dist/core/inbox/service.js +67 -21
  129. package/dist/core/inbox/targets.d.ts +22 -0
  130. package/dist/core/inbox/targets.d.ts.map +1 -0
  131. package/dist/core/inbox/targets.js +108 -0
  132. package/dist/core/inbox/watcher.js +7 -3
  133. package/dist/core/journal/index.d.ts.map +1 -1
  134. package/dist/core/journal/index.js +17 -2
  135. package/dist/core/resources/contract.d.ts +3 -0
  136. package/dist/core/resources/contract.d.ts.map +1 -1
  137. package/dist/core/resources/service-status.d.ts +14 -1
  138. package/dist/core/resources/service-status.d.ts.map +1 -1
  139. package/dist/core/resources/service-status.js +23 -13
  140. package/dist/core/resources/service.d.ts +9 -1
  141. package/dist/core/resources/service.d.ts.map +1 -1
  142. package/dist/core/resources/service.js +47 -27
  143. package/dist/core/resources/status.d.ts +2 -5
  144. package/dist/core/resources/status.d.ts.map +1 -1
  145. package/dist/core/resources/status.js +36 -7
  146. package/dist/core/semantic/contract.d.ts +5 -5
  147. package/dist/core/servers/index.d.ts +24 -13
  148. package/dist/core/servers/index.d.ts.map +1 -1
  149. package/dist/core/servers/index.js +132 -92
  150. package/dist/core/servers/tunnels.d.ts +3 -5
  151. package/dist/core/servers/tunnels.d.ts.map +1 -1
  152. package/dist/core/servers/tunnels.js +3 -9
  153. package/dist/core/storage/process-log.d.ts +12 -0
  154. package/dist/core/storage/process-log.d.ts.map +1 -1
  155. package/dist/core/storage/process-log.js +10 -2
  156. package/dist/core/supervisor/contract.d.ts +12 -0
  157. package/dist/core/supervisor/contract.d.ts.map +1 -1
  158. package/dist/core/supervisor/explanations.d.ts.map +1 -1
  159. package/dist/core/supervisor/explanations.js +2 -1
  160. package/dist/core/supervisor/findings.d.ts.map +1 -1
  161. package/dist/core/supervisor/findings.js +54 -9
  162. package/dist/core/supervisor/service.d.ts +9 -1
  163. package/dist/core/supervisor/service.d.ts.map +1 -1
  164. package/dist/core/supervisor/service.js +59 -29
  165. package/dist/core/supervisor/services.d.ts.map +1 -1
  166. package/dist/core/supervisor/services.js +22 -1
  167. package/dist/core/supervisor/status.d.ts +17 -2
  168. package/dist/core/supervisor/status.d.ts.map +1 -1
  169. package/dist/core/supervisor/status.js +70 -12
  170. package/dist/core/supervisor/timeline.d.ts.map +1 -1
  171. package/dist/core/supervisor/timeline.js +2 -1
  172. package/dist/core/work/state.js +7 -7
  173. package/dist/core/workflow/engine.d.ts.map +1 -1
  174. package/dist/core/workflow/engine.js +105 -32
  175. package/dist/core/workflow/failure-evidence.d.ts +16 -0
  176. package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
  177. package/dist/core/workflow/failure-evidence.js +114 -0
  178. package/dist/core/workflow/index.d.ts +1 -1
  179. package/dist/core/workflow/index.d.ts.map +1 -1
  180. package/dist/core/workflow/index.js +1 -1
  181. package/dist/core/workflow/live-session-v3.d.ts +6 -0
  182. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  183. package/dist/core/workflow/live-session-v3.js +126 -5
  184. package/dist/core/workflow/proof.d.ts +5 -5
  185. package/dist/core/workflow/proof.d.ts.map +1 -1
  186. package/dist/core/workflow/proof.js +115 -20
  187. package/dist/core/workflow/run-state.d.ts +3 -1
  188. package/dist/core/workflow/run-state.d.ts.map +1 -1
  189. package/dist/core/workflow/run-state.js +31 -27
  190. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  191. package/dist/core/workflow/spawn-claude.js +19 -0
  192. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  193. package/dist/core/workflow/spawn-codex.js +13 -0
  194. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  195. package/dist/core/workflow/spawn-cursor.js +19 -0
  196. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  197. package/dist/core/workflow/spawn-opencode.js +14 -0
  198. package/dist/core/workflow/types.d.ts +64 -17
  199. package/dist/core/workflow/types.d.ts.map +1 -1
  200. package/dist/core/workflow/workspaces/index.d.ts +0 -1
  201. package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
  202. package/dist/core/workflow/workspaces/index.js +0 -1
  203. package/dist/core/workflow/workspaces/leases.d.ts +3 -1
  204. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  205. package/dist/core/workflow/workspaces/leases.js +30 -16
  206. package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
  207. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  208. package/dist/core/workflow/workspaces/local-git.js +270 -6
  209. package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
  210. package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
  211. package/dist/core/workflow/workspaces/ownership.js +194 -0
  212. package/dist/core/workflow/workspaces/reclaim.d.ts +35 -0
  213. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
  214. package/dist/core/workflow/workspaces/reclaim.js +14 -0
  215. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
  216. package/dist/core/workflow/workspaces/recovery.js +21 -13
  217. package/dist/core/workflow/workspaces/state.d.ts +1 -0
  218. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  219. package/dist/core/workflow/workspaces/state.js +21 -1
  220. package/dist/lib/browser/windows-chrome.d.ts +100 -12
  221. package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
  222. package/dist/lib/browser/windows-chrome.js +262 -73
  223. package/dist/lib/docs-links.d.ts.map +1 -1
  224. package/dist/lib/docs-links.js +45 -2
  225. package/dist/lib/guarded-remove.d.ts +7 -0
  226. package/dist/lib/guarded-remove.d.ts.map +1 -1
  227. package/dist/lib/guarded-remove.js +106 -12
  228. package/dist/lib/identities/assume.d.ts.map +1 -1
  229. package/dist/lib/identities/assume.js +4 -0
  230. package/dist/lib/tunnel/allowlist.d.ts +44 -0
  231. package/dist/lib/tunnel/allowlist.d.ts.map +1 -0
  232. package/dist/lib/tunnel/allowlist.js +204 -0
  233. package/dist/lib/tunnel/current-address.d.ts +41 -0
  234. package/dist/lib/tunnel/current-address.d.ts.map +1 -0
  235. package/dist/lib/tunnel/current-address.js +123 -0
  236. package/dist/lib/tunnel/gate.js +13 -6
  237. package/dist/lib/tunnel/processes.d.ts +39 -0
  238. package/dist/lib/tunnel/processes.d.ts.map +1 -0
  239. package/dist/lib/tunnel/processes.js +121 -0
  240. package/dist/lib/tunnel/state.d.ts +28 -3
  241. package/dist/lib/tunnel/state.d.ts.map +1 -1
  242. package/dist/lib/tunnel/state.js +98 -27
  243. package/dist/lib/zip/index.d.ts +41 -0
  244. package/dist/lib/zip/index.d.ts.map +1 -0
  245. package/dist/lib/zip/index.js +398 -0
  246. package/package.json +8 -1
  247. package/src/commander.ts +32 -0
  248. package/src/commands/agents.ts +151 -74
  249. package/src/commands/browse.ts +50 -14
  250. package/src/commands/diagnostics.ts +10 -0
  251. package/src/commands/doctor.ts +26 -7
  252. package/src/commands/governor.ts +23 -10
  253. package/src/commands/inbox.ts +52 -2
  254. package/src/commands/rm.ts +43 -4
  255. package/src/commands/servers.ts +16 -4
  256. package/src/commands/supervisor.ts +7 -2
  257. package/src/commands/toc.ts +14 -6
  258. package/src/commands/tunnel.ts +394 -116
  259. package/src/commands/web-fetch.ts +30 -19
  260. package/src/commands/web.ts +9 -3
  261. package/src/commands/workflow.ts +30 -30
  262. package/src/core/agents/live-authority-v3.ts +17 -0
  263. package/src/core/agents/live-lifecycle-v3.ts +52 -14
  264. package/src/core/agents/mailbox.ts +26 -17
  265. package/src/core/agents/render/prompt-context.ts +35 -20
  266. package/src/core/agents/render/session-context.ts +92 -48
  267. package/src/core/agents/rules/stop-hook.ts +3 -3
  268. package/src/core/agents/state/heartbeat-writer.ts +29 -12
  269. package/src/core/agents/state/live-coordination-writer.ts +29 -0
  270. package/src/core/agents/state/names.ts +117 -6
  271. package/src/core/agents/state/pidmap.ts +13 -0
  272. package/src/core/agents/state/proc-start.ts +57 -0
  273. package/src/core/agents/state/stale-sweep.ts +247 -82
  274. package/src/core/agents/state/unchanged-file.ts +456 -0
  275. package/src/core/context/index.ts +446 -136
  276. package/src/core/context/observation.ts +29 -0
  277. package/src/core/diagnostics/bundle.ts +23 -18
  278. package/src/core/diagnostics/path-identity.ts +15 -0
  279. package/src/core/diagnostics/sanitize.ts +4 -3
  280. package/src/core/events/v3/authority-outbox.ts +56 -0
  281. package/src/core/events/v3/generation-custody.ts +59 -0
  282. package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
  283. package/src/core/events/v3/producers/recorder.ts +94 -68
  284. package/src/core/events/v3/support-storage/pack-reader.ts +45 -19
  285. package/src/core/governor/planning.ts +20 -8
  286. package/src/core/governor/service-read.ts +128 -20
  287. package/src/core/governor/service.ts +119 -42
  288. package/src/core/hooks/cli.ts +139 -22
  289. package/src/core/hooks/fork-lineage.ts +82 -0
  290. package/src/core/hooks/resolve/transcript.ts +88 -0
  291. package/src/core/hooks/shell-rm.ts +103 -0
  292. package/src/core/hooks/shell-tokens.ts +96 -0
  293. package/src/core/hooks/shell-waiter.ts +4 -91
  294. package/src/core/inbox/contract.ts +2 -1
  295. package/src/core/inbox/index.ts +2 -0
  296. package/src/core/inbox/peer-messages.ts +94 -0
  297. package/src/core/inbox/service.ts +88 -22
  298. package/src/core/inbox/targets.ts +148 -0
  299. package/src/core/inbox/watcher.ts +8 -9
  300. package/src/core/journal/index.ts +19 -2
  301. package/src/core/resources/contract.ts +4 -0
  302. package/src/core/resources/service-status.ts +35 -12
  303. package/src/core/resources/service.ts +71 -24
  304. package/src/core/resources/status.ts +57 -8
  305. package/src/core/servers/index.ts +177 -91
  306. package/src/core/servers/tunnels.ts +3 -11
  307. package/src/core/storage/process-log.ts +17 -2
  308. package/src/core/supervisor/contract.ts +13 -0
  309. package/src/core/supervisor/explanations.ts +4 -1
  310. package/src/core/supervisor/findings.ts +68 -10
  311. package/src/core/supervisor/service.ts +88 -26
  312. package/src/core/supervisor/services.ts +26 -8
  313. package/src/core/supervisor/status.ts +89 -12
  314. package/src/core/supervisor/timeline.ts +2 -1
  315. package/src/core/work/state.ts +7 -7
  316. package/src/core/workflow/engine.ts +131 -49
  317. package/src/core/workflow/failure-evidence.ts +141 -0
  318. package/src/core/workflow/index.ts +0 -1
  319. package/src/core/workflow/live-session-v3.ts +146 -5
  320. package/src/core/workflow/proof.ts +134 -26
  321. package/src/core/workflow/run-state.ts +31 -29
  322. package/src/core/workflow/spawn-claude.ts +18 -0
  323. package/src/core/workflow/spawn-codex.ts +13 -0
  324. package/src/core/workflow/spawn-cursor.ts +18 -0
  325. package/src/core/workflow/spawn-opencode.ts +14 -0
  326. package/src/core/workflow/types.ts +51 -17
  327. package/src/core/workflow/workspaces/index.ts +0 -1
  328. package/src/core/workflow/workspaces/leases.ts +48 -17
  329. package/src/core/workflow/workspaces/local-git.ts +315 -5
  330. package/src/core/workflow/workspaces/ownership.ts +232 -0
  331. package/src/core/workflow/workspaces/reclaim.ts +58 -0
  332. package/src/core/workflow/workspaces/recovery.ts +25 -13
  333. package/src/core/workflow/workspaces/state.ts +20 -0
  334. package/src/lib/browser/windows-chrome.ts +346 -86
  335. package/src/lib/docs-links.ts +49 -2
  336. package/src/lib/guarded-remove.ts +132 -14
  337. package/src/lib/identities/assume.ts +4 -0
  338. package/src/lib/tunnel/allowlist.ts +208 -0
  339. package/src/lib/tunnel/current-address.ts +158 -0
  340. package/src/lib/tunnel/gate.ts +16 -8
  341. package/src/lib/tunnel/processes.ts +149 -0
  342. package/src/lib/tunnel/state.ts +123 -27
  343. package/src/lib/zip/index.ts +483 -0
@@ -0,0 +1,108 @@
1
+ import { resolveName } from "../agents/state/names.js";
2
+ import { coordFreshnessSeconds } from "../config.js";
3
+ import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from "../events/v3/coordination-view.js";
4
+ import { liveInstanceIdV3, nativeInstanceIdV3, observeLiveEventLedgerRouteV3, } from "../events/v3/live-route-observer.js";
5
+ /** Resolve one immutable recipient without extending presence or claiming attention. */
6
+ export function resolveMessageTarget(coordRoot, input) {
7
+ const requestedName = normalizeName(input.name);
8
+ const requestedInstance = input.instanceId?.trim();
9
+ if (!requestedName && !requestedInstance) {
10
+ return { ok: false, code: "no_peer", message: "Supply an exact agent name or instance id." };
11
+ }
12
+ const now = input.now?.getTime() ?? Date.now();
13
+ if (!Number.isFinite(now))
14
+ return unsafe("The message addressing clock is invalid.");
15
+ let view;
16
+ try {
17
+ if (observeLiveEventLedgerRouteV3(coordRoot).state === "blocked") {
18
+ return unsafe("The event ledger cannot establish message recipient authority.");
19
+ }
20
+ view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(coordRoot));
21
+ if (!view.source_complete)
22
+ return unsafe("The event ledger read is incomplete.");
23
+ }
24
+ catch {
25
+ return unsafe("The event ledger cannot establish message recipient authority.");
26
+ }
27
+ // Address historical instances without reopening them. A current generation
28
+ // wins over its own terminal history; otherwise use its last ledger position.
29
+ const byInstance = new Map();
30
+ for (const generation of Object.values(view.terminal_generations)) {
31
+ const prior = byInstance.get(generation.instance_id);
32
+ if (!prior || laterPosition(generation, prior))
33
+ byInstance.set(generation.instance_id, generation);
34
+ }
35
+ for (const generation of Object.values(view.instances))
36
+ byInstance.set(generation.instance_id, generation);
37
+ const freshnessMs = coordFreshnessSeconds(coordRoot) * 1_000;
38
+ const candidates = [...byInstance.values()].flatMap((generation) => {
39
+ const instanceId = nativeInstanceIdV3(generation.instance_id);
40
+ const name = resolveName(coordRoot, instanceId)?.name;
41
+ if (!name)
42
+ return [];
43
+ if (requestedInstance && liveInstanceIdV3(requestedInstance) !== generation.instance_id)
44
+ return [];
45
+ if (requestedName && normalizeName(name) !== requestedName)
46
+ return [];
47
+ const age = now - Date.parse(generation.last_observed_at);
48
+ const diagnostics = view.diagnostics_by_generation[generation.generation_id] ?? [];
49
+ return [
50
+ {
51
+ instanceId,
52
+ name,
53
+ safe: !diagnostics.some((diagnostic) => diagnostic.authority_blocking),
54
+ fresh: generation.phase === "live" && Number.isFinite(age) && age >= 0 && age <= freshnessMs,
55
+ },
56
+ ];
57
+ });
58
+ if (candidates.length === 0) {
59
+ return {
60
+ ok: false,
61
+ code: "no_peer",
62
+ message: "No known agent matches that exact name and instance.",
63
+ };
64
+ }
65
+ if (candidates.some((candidate) => !candidate.safe)) {
66
+ return unsafe("A matching agent generation does not have safe coordination authority.");
67
+ }
68
+ const fresh = candidates.filter((candidate) => candidate.fresh);
69
+ const selected = requestedInstance ? candidates : fresh.length > 0 ? fresh : candidates;
70
+ if (selected.length !== 1) {
71
+ return {
72
+ ok: false,
73
+ code: "ambiguous_peer",
74
+ message: "Several agent instances hold that exact name; specify --instance-id.",
75
+ candidates: selected.map(({ instanceId }) => instanceId).sort(),
76
+ };
77
+ }
78
+ const target = selected[0];
79
+ if (input.requireLive && !target.fresh) {
80
+ return {
81
+ ok: false,
82
+ code: "not_live",
83
+ message: "That agent is known but outside live presence; omit --require-live to queue its message.",
84
+ };
85
+ }
86
+ return {
87
+ ok: true,
88
+ target: {
89
+ instance_id: target.instanceId,
90
+ name: target.name,
91
+ delivery: target.fresh ? "written-live" : "queued",
92
+ },
93
+ };
94
+ }
95
+ function laterPosition(left, right) {
96
+ return (left.source_position.segment_ordinal > right.source_position.segment_ordinal ||
97
+ (left.source_position.segment_ordinal === right.source_position.segment_ordinal &&
98
+ left.source_position.byte_offset > right.source_position.byte_offset));
99
+ }
100
+ function normalizeName(name) {
101
+ return (name ?? "")
102
+ .trim()
103
+ .replace(/^agent-/i, "")
104
+ .toLowerCase();
105
+ }
106
+ function unsafe(message) {
107
+ return { ok: false, code: "authority_unsafe", message };
108
+ }
@@ -17,10 +17,14 @@ export async function* watchInbox(service, recipientInstanceId, options) {
17
17
  }
18
18
  function delay(ms, signal) {
19
19
  return new Promise((resolve) => {
20
- const timer = setTimeout(resolve, ms);
21
- signal.addEventListener("abort", () => {
20
+ const finish = () => {
22
21
  clearTimeout(timer);
22
+ signal.removeEventListener("abort", finish);
23
23
  resolve();
24
- }, { once: true });
24
+ };
25
+ const timer = setTimeout(finish, ms);
26
+ signal.addEventListener("abort", finish, { once: true });
27
+ if (signal.aborted)
28
+ finish();
25
29
  });
26
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/journal/index.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,kBAAkB,iFAQrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,QACyF,CAAC;AAEtH,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,UAAU,IAAI,MAAM,CAMnC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtD;AAeD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAW1D;AAID,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAwEtE;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAkBxD;AAmBD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,SAAoB,GAAG,SAAS,EAAE,CA2DrF;AAID,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,GACX,UAAU,CAiEZ;AAID,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,IAAI,SAAyB,GAAG,MAAM,CAkBnE;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAc9C;AAED,qDAAqD;AACrD,wBAAgB,YAAY,IAAI;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,CAWF;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAIjE;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/journal/index.ts"],"names":[],"mappings":"AA+BA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,kBAAkB,iFAQrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,QACyF,CAAC;AAEtH,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,UAAU,IAAI,MAAM,CAMnC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtD;AAeD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAW1D;AAID,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAwEtE;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAkBxD;AAmBD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,SAAoB,GAAG,SAAS,EAAE,CA2DrF;AAID,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,GACX,UAAU,CAiEZ;AAID,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,IAAI,SAAyB,GAAG,MAAM,CAkBnE;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAuB9C;AAED,qDAAqD;AACrD,wBAAgB,YAAY,IAAI;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,CAWF;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAIjE;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ9D"}
@@ -3,6 +3,8 @@ import { dirname, resolve } from "node:path";
3
3
  import { resolveMachineLabel } from "../../lib/machine.js";
4
4
  import { assertSafeInstanceId, monorepoRoot, resolveContainedFile, resolveOwner, } from "../agents/index.js";
5
5
  import { readLiveCoordinationRow, readLiveCoordinationRows, } from "../agents/state/live-coordination-view.js";
6
+ import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from "../events/v3/coordination-view.js";
7
+ import { liveInstanceIdV3, observeLiveEventLedgerRouteV3, } from "../events/v3/live-route-observer.js";
6
8
  /**
7
9
  * Agent journal: per-agent markdown journal at `.harnery/journal/<instance_id>.md`.
8
10
  *
@@ -340,12 +342,25 @@ export function sweepOrphanJournals() {
340
342
  const dir = journalDir();
341
343
  const root = monorepoRoot();
342
344
  const archived = [];
345
+ if (!root)
346
+ return archived;
347
+ let view;
348
+ try {
349
+ if (observeLiveEventLedgerRouteV3(root).state === "blocked")
350
+ return archived;
351
+ view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(root));
352
+ if (!view.source_complete)
353
+ return archived;
354
+ }
355
+ catch {
356
+ // A missing or unsafe read does not prove that an owner has ended.
357
+ return archived;
358
+ }
343
359
  for (const f of readdirSync(dir)) {
344
360
  if (!f.endsWith(".md"))
345
361
  continue;
346
362
  const instanceId = f.replace(/\.md$/, "");
347
- const hb = root ? readLiveCoordinationRow(root, instanceId) : null;
348
- if (!hb) {
363
+ if (!view.instances[liveInstanceIdV3(instanceId)]) {
349
364
  const dest = archiveJournal(instanceId);
350
365
  if (dest)
351
366
  archived.push(dest);
@@ -1,3 +1,4 @@
1
+ import type { ProcessGenerationVerdict } from "../agents/state/proc-start.js";
1
2
  export declare const RESOURCE_SNAPSHOT_SCHEMA_VERSION = 2;
2
3
  export declare const RESOURCE_SERVICE_STATUS_SCHEMA_VERSION = 1;
3
4
  export type ResourceSupportState = "supported" | "partial" | "unsupported" | "error";
@@ -140,6 +141,8 @@ export interface ResourceServiceStatusRecord {
140
141
  export interface ResourceServiceStatus {
141
142
  running: boolean;
142
143
  stale: boolean;
144
+ generation: ProcessGenerationVerdict;
145
+ liveness: "running" | "stopped" | "unknown";
143
146
  record?: ResourceServiceStatusRecord;
144
147
  status_path: string;
145
148
  snapshot_path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/core/resources/contract.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/core/resources/contract.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,16 @@
1
+ import { type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
1
2
  import { type ResourceServiceStatus } from "./contract.js";
2
- export declare function readResourceServiceStatus(coordRoot: string, nowMs?: number): ResourceServiceStatus;
3
+ export interface ResourceProcessDependencies {
4
+ readToken?: (pid: number) => string | null;
5
+ probePid?: (pid: number) => unknown;
6
+ }
7
+ export declare function readResourceServiceStatus(coordRoot: string, nowMs?: number, dependencies?: ResourceProcessDependencies): ResourceServiceStatus;
8
+ export declare function resourceProcessLiveness(owner: {
9
+ pid: number;
10
+ host: string;
11
+ start_token?: string;
12
+ }, dependencies?: ResourceProcessDependencies): {
13
+ generation: ProcessGenerationVerdict;
14
+ liveness: ResourceServiceStatus["liveness"];
15
+ };
3
16
  //# sourceMappingURL=service-status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service-status.d.ts","sourceRoot":"","sources":["../../../src/core/resources/service-status.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,qBAAqB,EAE3B,MAAM,eAAe,CAAC;AAKvB,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,SAAa,GACjB,qBAAqB,CA0BvB"}
1
+ {"version":3,"file":"service-status.d.ts","sourceRoot":"","sources":["../../../src/core/resources/service-status.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,wBAAwB,EAE9B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,qBAAqB,EAE3B,MAAM,eAAe,CAAC;AAKvB,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACrC;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,SAAa,EAClB,YAAY,GAAE,2BAAgC,GAC7C,qBAAqB,CA2BvB;AAcD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1D,YAAY,GAAE,2BAAgC,GAC7C;IAAE,UAAU,EAAE,wBAAwB,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAA;CAAE,CAgBvF"}
@@ -1,29 +1,31 @@
1
+ import { existsSync } from "node:fs";
1
2
  import { hostname } from "node:os";
2
- import { checkPidToken } from "../agents/state/proc-start.js";
3
+ import { verifyProcessGeneration, } from "../agents/state/proc-start.js";
3
4
  import { RESOURCE_SERVICE_STATUS_SCHEMA_VERSION, } from "./contract.js";
4
5
  import { readResourceServiceRecord, resourcePaths } from "./storage.js";
5
6
  const STALE_HEARTBEAT_MS = 15_000;
6
- export function readResourceServiceStatus(coordRoot, nowMs = Date.now()) {
7
+ export function readResourceServiceStatus(coordRoot, nowMs = Date.now(), dependencies = {}) {
7
8
  const paths = resourcePaths(coordRoot);
8
9
  const record = readResourceServiceRecord(coordRoot);
9
10
  if (!validRecord(record)) {
10
11
  return {
11
12
  running: false,
12
13
  stale: false,
14
+ generation: "unknown",
15
+ liveness: existsSync(paths.service) ? "unknown" : "stopped",
13
16
  status_path: paths.service,
14
17
  snapshot_path: paths.snapshot,
15
18
  };
16
19
  }
17
20
  const heartbeatAt = Date.parse(record.heartbeat_at);
18
21
  const stale = !Number.isFinite(heartbeatAt) || nowMs - heartbeatAt > STALE_HEARTBEAT_MS;
19
- const local = record.host === hostname();
20
- const running = local &&
21
- !stale &&
22
- (record.state === "starting" || record.state === "running" || record.state === "stopping") &&
23
- pidStillMatches(record.pid, record.start_token);
22
+ const owner = resourceProcessLiveness(record, dependencies);
23
+ const liveness = owner.generation === "same" && record.state === "stopped" ? "stopped" : owner.liveness;
24
24
  return {
25
- running,
25
+ running: liveness === "running" && !stale,
26
26
  stale,
27
+ generation: owner.generation,
28
+ liveness,
27
29
  record,
28
30
  status_path: paths.service,
29
31
  snapshot_path: paths.snapshot,
@@ -36,13 +38,21 @@ function validRecord(value) {
36
38
  typeof value.host === "string" &&
37
39
  typeof value.heartbeat_at === "string");
38
40
  }
39
- function pidStillMatches(pid, startToken) {
41
+ export function resourceProcessLiveness(owner, dependencies = {}) {
42
+ if (owner.host !== hostname())
43
+ return { generation: "unknown", liveness: "unknown" };
40
44
  try {
41
- process.kill(pid, 0);
45
+ (dependencies.probePid ?? ((pid) => process.kill(pid, 0)))(owner.pid);
42
46
  }
43
47
  catch (error) {
44
- if (error.code !== "EPERM")
45
- return false;
48
+ return {
49
+ generation: "unknown",
50
+ liveness: error.code === "ESRCH" ? "stopped" : "unknown",
51
+ };
46
52
  }
47
- return checkPidToken(pid, startToken) !== "mismatch";
53
+ const generation = verifyProcessGeneration(owner.pid, owner.start_token, dependencies.readToken);
54
+ return {
55
+ generation,
56
+ liveness: generation === "same" ? "running" : generation === "different" ? "stopped" : "unknown",
57
+ };
48
58
  }
@@ -1,5 +1,7 @@
1
+ import { type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
1
2
  import { type ResourceServiceStatus, type ResourceServiceStatusRecord } from "./contract.js";
2
3
  import { sampleResources } from "./sampler.js";
4
+ import { type ResourceProcessDependencies } from "./service-status.js";
3
5
  export declare const RESOURCE_SERVICE_DEFAULT_INTERVAL_MS = 2000;
4
6
  export interface RunResourceServiceInput {
5
7
  coordRoot: string;
@@ -21,6 +23,12 @@ export declare function spawnResourceService(coordRootRaw: string, options?: {
21
23
  intervalMs?: number;
22
24
  }): Promise<ResourceServiceStatus>;
23
25
  export declare function ensureResourceServiceRunning(coordRoot: string): Promise<EnsureResourceServiceResult>;
24
- export declare function requestResourceServiceStop(coordRootRaw: string): ResourceServiceStatus;
26
+ export interface ResourceServiceStopResult extends ResourceServiceStatus {
27
+ signal_sent: boolean;
28
+ signal_verdict: ProcessGenerationVerdict;
29
+ }
30
+ export declare function requestResourceServiceStop(coordRootRaw: string, dependencies?: ResourceProcessDependencies & {
31
+ kill?: (pid: number, signal: NodeJS.Signals | number) => unknown;
32
+ }): ResourceServiceStopResult;
25
33
  export declare function runResourceService(input: RunResourceServiceInput): Promise<ResourceServiceStatusRecord>;
26
34
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/resources/service.ts"],"names":[],"mappings":"AAiBA,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EACjC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAI/C,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAY1D,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,eAAe,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAC/D;IAAE,KAAK,EAAE,aAAa,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACpC,OAAO,CAAC,qBAAqB,CAAC,CAwChC;AAED,wBAAsB,4BAA4B,CAChD,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,2BAA2B,CAAC,CAoBtC;AAED,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,qBAAqB,CAiBtF;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,2BAA2B,CAAC,CAwFtC"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/resources/service.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EACjC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,KAAK,2BAA2B,EAGjC,MAAM,qBAAqB,CAAC;AAG7B,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAY1D,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,eAAe,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAC/D;IAAE,KAAK,EAAE,aAAa,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACpC,OAAO,CAAC,qBAAqB,CAAC,CAyChC;AAED,wBAAsB,4BAA4B,CAChD,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,2BAA2B,CAAC,CAoBtC;AAED,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,wBAAwB,CAAC;CAC1C;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,2BAA2B,GAAG;IAC1C,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,OAAO,CAAC;CAC7D,GACL,yBAAyB,CAuB3B;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,2BAA2B,CAAC,CA4FtC"}
@@ -3,12 +3,13 @@ import { randomUUID } from "node:crypto";
3
3
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, unlinkSync, writeFileSync, } from "node:fs";
4
4
  import { hostname, platform } from "node:os";
5
5
  import { resolve } from "node:path";
6
- import { checkPidToken, processStartToken } from "../agents/state/proc-start.js";
6
+ import { processStartToken, signalIfSameGeneration, } from "../agents/state/proc-start.js";
7
+ import { resolveBinName } from "../config.js";
7
8
  import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.js";
8
9
  import { stateDirMode, stateFileMode } from "../storage/modes.js";
9
10
  import { RESOURCE_SERVICE_STATUS_SCHEMA_VERSION, } from "./contract.js";
10
11
  import { sampleResources } from "./sampler.js";
11
- import { readResourceServiceStatus } from "./service-status.js";
12
+ import { readResourceServiceStatus, resourceProcessLiveness, } from "./service-status.js";
12
13
  import { resourcePaths, writePrivateJsonAtomic } from "./storage.js";
13
14
  export const RESOURCE_SERVICE_DEFAULT_INTERVAL_MS = 2_000;
14
15
  const RESOURCE_SERVICE_MIN_INTERVAL_MS = 500;
@@ -16,13 +17,15 @@ const RESOURCE_SERVICE_MAX_INTERVAL_MS = 60_000;
16
17
  export async function spawnResourceService(coordRootRaw, options = {}) {
17
18
  const coordRoot = resolve(coordRootRaw);
18
19
  const current = readResourceServiceStatus(coordRoot);
19
- if (current.running) {
20
+ if (current.liveness === "unknown")
21
+ throw unknownResourceOwnerError();
22
+ if (current.liveness === "running") {
20
23
  throw new Error(`resource observer is already running under pid ${current.record?.pid}`);
21
24
  }
22
25
  const intervalMs = normalizeInterval(options.intervalMs);
23
26
  const paths = resourcePaths(coordRoot);
27
+ assertResourceLeaseAvailable(paths.lease);
24
28
  mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
25
- rmSync(paths.stop, { force: true });
26
29
  const harnBin = new URL("../../../bin/harn", import.meta.url).pathname;
27
30
  if (!existsSync(harnBin))
28
31
  throw new Error(`cannot find harn executable at ${harnBin}`);
@@ -77,27 +80,31 @@ export async function ensureResourceServiceRunning(coordRoot) {
77
80
  };
78
81
  }
79
82
  }
80
- export function requestResourceServiceStop(coordRootRaw) {
83
+ export function requestResourceServiceStop(coordRootRaw, dependencies = {}) {
81
84
  const coordRoot = resolve(coordRootRaw);
82
85
  const paths = resourcePaths(coordRoot);
83
- const status = readResourceServiceStatus(coordRoot);
86
+ const status = readResourceServiceStatus(coordRoot, Date.now(), dependencies);
84
87
  mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
85
88
  writePrivateJsonAtomic(paths.stop, {
86
89
  requested_at: new Date().toISOString(),
87
90
  requested_by_pid: process.pid,
88
91
  });
89
- if (status.running && status.record?.host === hostname()) {
90
- try {
91
- process.kill(status.record.pid, "SIGTERM");
92
- }
93
- catch {
94
- // The durable stop file handles a racing exit.
95
- }
96
- }
97
- return readResourceServiceStatus(coordRoot);
92
+ const signal = status.liveness === "running" && status.record?.host === hostname()
93
+ ? signalIfSameGeneration(status.record.pid, status.record.start_token, "SIGTERM", dependencies)
94
+ : { sent: false, verdict: status.generation };
95
+ return {
96
+ ...readResourceServiceStatus(coordRoot, Date.now(), dependencies),
97
+ signal_sent: signal.sent,
98
+ signal_verdict: signal.verdict,
99
+ };
98
100
  }
99
101
  export async function runResourceService(input) {
100
102
  const coordRoot = resolve(input.coordRoot);
103
+ const current = readResourceServiceStatus(coordRoot);
104
+ if (current.liveness === "unknown")
105
+ throw unknownResourceOwnerError();
106
+ if (current.liveness === "running")
107
+ throw new Error(`resource observer is already running under pid ${current.record?.pid}`);
101
108
  const paths = resourcePaths(coordRoot);
102
109
  const intervalMs = normalizeInterval(input.intervalMs);
103
110
  const now = input.now ?? (() => new Date());
@@ -212,7 +219,10 @@ function acquireLease(coordRoot, now) {
212
219
  if (error.code !== "EEXIST")
213
220
  throw error;
214
221
  const existing = readLease(paths.lease);
215
- if (existing && leaseIsLive(existing)) {
222
+ const liveness = existing ? leaseIsLive(existing) : "unknown";
223
+ if (liveness === "unknown")
224
+ throw unknownResourceOwnerError();
225
+ if (existing && liveness === "running") {
216
226
  throw new Error(`resource observer lease is held by pid ${existing.pid}`);
217
227
  }
218
228
  try {
@@ -239,7 +249,13 @@ function releaseLease(path, nonce) {
239
249
  function readLease(path) {
240
250
  try {
241
251
  const value = JSON.parse(readFileSync(path, "utf8"));
242
- return value && Number.isInteger(value.pid) && typeof value.nonce === "string"
252
+ return value &&
253
+ Number.isSafeInteger(value.pid) &&
254
+ value.pid > 0 &&
255
+ typeof value.host === "string" &&
256
+ value.host.length > 0 &&
257
+ typeof value.nonce === "string" &&
258
+ value.nonce.length > 0
243
259
  ? value
244
260
  : undefined;
245
261
  }
@@ -248,16 +264,20 @@ function readLease(path) {
248
264
  }
249
265
  }
250
266
  function leaseIsLive(lease) {
251
- if (lease.host !== hostname())
252
- return true;
253
- try {
254
- process.kill(lease.pid, 0);
255
- }
256
- catch (error) {
257
- if (error.code !== "EPERM")
258
- return false;
259
- }
260
- return checkPidToken(lease.pid, lease.start_token) !== "mismatch";
267
+ return resourceProcessLiveness(lease).liveness;
268
+ }
269
+ function assertResourceLeaseAvailable(path) {
270
+ if (!existsSync(path))
271
+ return;
272
+ const lease = readLease(path);
273
+ const liveness = lease ? leaseIsLive(lease) : "unknown";
274
+ if (liveness === "unknown")
275
+ throw unknownResourceOwnerError();
276
+ if (liveness === "running")
277
+ throw new Error(`resource observer lease is held by pid ${lease?.pid}`);
278
+ }
279
+ function unknownResourceOwnerError() {
280
+ return new Error(`resource observer process identity is unknown; run ${resolveBinName()} supervisor stop to write the durable stop request before starting again`);
261
281
  }
262
282
  function log(coordRoot, event, fields, error) {
263
283
  try {
@@ -1,5 +1,5 @@
1
1
  import { type PressureAssessment, type PressureHysteresisState } from "../diagnostics/contract.js";
2
- import { type SupervisorCapability, type SupervisorPressureRecord } from "../supervisor/contract.js";
2
+ import { type SupervisorCapability, type SupervisorPressureRecord, type SupervisorStatus } from "../supervisor/contract.js";
3
3
  import { type ResourceDiskSample, type ResourceHostSample, type ResourceIoSample, type ResourceMachineSample, type ResourceOomSample, type ResourcePressureSample, type ResourceProcessSample, type ResourceSnapshot } from "./contract.js";
4
4
  export declare const RESOURCE_STATUS_STALE_MS = 15000;
5
5
  export declare const RESOURCE_STATUS_MAX_BYTES: number;
@@ -15,10 +15,7 @@ export interface ResourceStatus {
15
15
  platform: NodeJS.Platform | null;
16
16
  namespace: ResourceSnapshot["namespace"];
17
17
  support: ResourceSnapshot["support"] | null;
18
- writer: {
19
- running: boolean;
20
- stale: boolean;
21
- };
18
+ writer: Pick<SupervisorStatus, "running" | "stale" | "generation" | "liveness" | "running_build" | "installed_build" | "restart_needed" | "build_status">;
22
19
  /**
23
20
  * The one published pressure assessment, read from the observer's record so
24
21
  * this surface, the prompt notice, the dashboard, and a diagnostic bundle all
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/resources/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/resources/status.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { closeSync, fstatSync, openSync, readSync, statSync } from "node:fs";
2
2
  import { unknownPressureAssessment } from "../diagnostics/advice.js";
3
3
  import { PRESSURE_ASSESSMENT_SCHEMA_VERSION, PRESSURE_POLICY, } from "../diagnostics/contract.js";
4
4
  import { SUPERVISOR_PRESSURE_SCHEMA_VERSION, } from "../supervisor/contract.js";
5
- import { readSupervisorStatus } from "../supervisor/status.js";
5
+ import { installedSupervisorBuild, readSupervisorStatus } from "../supervisor/status.js";
6
6
  import { supervisorPaths } from "../supervisor/storage.js";
7
7
  import { RESOURCE_SNAPSHOT_SCHEMA_VERSION, } from "./contract.js";
8
8
  import { resourcePaths } from "./storage.js";
@@ -156,11 +156,13 @@ export function formatResourceSummary(status) {
156
156
  ? "unknown"
157
157
  : `${formatBytes(m.memory_available_bytes)} available`;
158
158
  const diskText = disk ? `${formatBytes(disk.available_bytes)} available` : "unknown";
159
- return `${lead} ${status.namespace}/${status.platform}: CPU ${cpu}, RAM ${ram}, disk ${diskText}; ${Math.round((status.sample_age_ms ?? 0) / 1_000)}s old${status.support?.state === "partial" ? "; partial" : ""}${!status.writer.running ? "; writer stopped" : ""}.`;
159
+ return `${lead} ${status.namespace}/${status.platform}: CPU ${cpu}, RAM ${ram}, disk ${diskText}; ${Math.round((status.sample_age_ms ?? 0) / 1_000)}s old${status.support?.state === "partial" ? "; partial" : ""}${!status.writer.running ? `; writer ${status.writer.liveness === "unknown" ? "unknown" : status.writer.stale ? "stale" : "stopped"}` : ""}.`;
160
160
  }
161
161
  export function formatResourceStatus(status, bin) {
162
162
  const a = status.assessment;
163
163
  const lines = [formatResourceSummary(status)];
164
+ const writer = status.writer;
165
+ lines.push(`Writer: ${writer.liveness}; generation ${writer.generation}; running build ${writer.running_build ? `${writer.running_build.version} (${writer.running_build.revision})` : "unknown"}; installed build ${writer.installed_build.version} (${writer.installed_build.revision}); ${writer.build_status}.`);
164
166
  lines.push(`Assessment: ${a.state} at ${a.scope} scope; limiting resource ${a.limiting_resource}; trend ${a.trend}; evidence ${a.evidence_state}; recommended action ${a.recommended_action}.`);
165
167
  lines.push(`Evidence age: ${a.sample_age_ms === null ? "unknown" : `${Math.round(a.sample_age_ms / 1_000)}s`}; assessment source ${status.assessment_capability.state}${status.assessment_capability.reason_code ? ` (${status.assessment_capability.reason_code})` : ""}.`);
166
168
  for (const item of a.reasons)
@@ -176,7 +178,9 @@ export function formatResourceStatus(status, bin) {
176
178
  }
177
179
  if (a.unattributed_memory_percent !== null)
178
180
  lines.push(`Unattributed memory: ${Math.round(a.unattributed_memory_percent)}% of machine memory has no validated owner.`);
179
- if (status.state !== "fresh" || !status.writer.running)
181
+ if (status.writer.liveness === "unknown")
182
+ lines.push(`Inspect the writer with ${bin} supervisor status. Run ${bin} supervisor stop to write its durable stop request before starting again.`);
183
+ else if (status.state !== "fresh" || !status.writer.running)
180
184
  lines.push(`Start or inspect the writer with ${bin} supervisor start --keep-alive and ${bin} supervisor status.`);
181
185
  if (status.host)
182
186
  lines.push(`Windows host: ${status.host.state}${status.host.machine ? `; CPU ${status.host.machine.cpu_percent === null ? "warming up" : `${Math.round(status.host.machine.cpu_percent)}%`}, RAM ${status.host.machine.memory_available_bytes === null ? "unknown" : `${formatBytes(status.host.machine.memory_available_bytes)} available`}` : `; ${status.host.reason ?? "unavailable"}`}`);
@@ -238,7 +242,11 @@ export function readPublishedPressure(coordRoot, nowMs = Date.now(), writer) {
238
242
  });
239
243
  // A record can only be trusted while its writer is alive. Every surface makes
240
244
  // the same check here so none of them reports a state the others do not.
241
- if (!(writer ?? readWriterState(coordRoot, nowMs)).running) {
245
+ const observer = writer ?? readWriterState(coordRoot, nowMs);
246
+ if (observer.liveness === "unknown") {
247
+ return gap("error", "pressure_observer_unknown", "Local resource pressure cannot be determined because the resource observer's process identity is unknown.");
248
+ }
249
+ if (!observer.running) {
242
250
  return gap("expired", "pressure_observer_not_running", "Local resource pressure cannot be determined because the resource observer is not running.");
243
251
  }
244
252
  const path = supervisorPaths(coordRoot).pressure;
@@ -275,20 +283,41 @@ function readWriterState(coordRoot, nowMs) {
275
283
  // Keep malformed service metadata from making the read expensive. The shared
276
284
  // status reader retains ownership of PID/start-token liveness checks.
277
285
  if (statSync(supervisorPaths(coordRoot).service).size > 65_536) {
278
- return { running: false, stale: false };
286
+ return unknownWriterState();
279
287
  }
280
288
  const writer = readSupervisorStatus(coordRoot, nowMs);
281
289
  const futureHeartbeat = writer.record && Date.parse(writer.record.heartbeat_at) > nowMs + FUTURE_TOLERANCE_MS;
282
290
  return {
283
291
  running: writer.running && !futureHeartbeat,
284
292
  stale: writer.stale || !!futureHeartbeat,
293
+ generation: writer.generation,
294
+ liveness: writer.liveness,
295
+ running_build: writer.running_build,
296
+ installed_build: writer.installed_build,
297
+ restart_needed: writer.restart_needed,
298
+ build_status: writer.build_status,
285
299
  };
286
300
  }
287
- catch {
301
+ catch (error) {
288
302
  // A missing writer must not break normal coordination.
289
- return { running: false, stale: false };
303
+ return {
304
+ ...unknownWriterState(),
305
+ liveness: error.code === "ENOENT" ? "stopped" : "unknown",
306
+ };
290
307
  }
291
308
  }
309
+ function unknownWriterState() {
310
+ return {
311
+ running: false,
312
+ stale: false,
313
+ generation: "unknown",
314
+ liveness: "unknown",
315
+ running_build: null,
316
+ installed_build: installedSupervisorBuild(),
317
+ restart_needed: true,
318
+ build_status: "running build unknown; restart needed",
319
+ };
320
+ }
292
321
  function validPressureRecord(v) {
293
322
  if (!object(v) ||
294
323
  v.schema_version !== SUPERVISOR_PRESSURE_SCHEMA_VERSION ||