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
@@ -152,7 +152,7 @@ export declare const SemanticUsageReceiptV1Schema: import("@sinclair/typebox").T
152
152
  scope: import("@sinclair/typebox").TLiteral<"unreported">;
153
153
  }>]>;
154
154
  export declare const SemanticEvidenceObservationV1Schema: import("@sinclair/typebox").TObject<{
155
- kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"error" | "recovery" | "artifact" | "operation" | "action" | "terminal" | "progress" | "task" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
155
+ kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
156
156
  event_id: import("@sinclair/typebox").TString;
157
157
  observed_at: import("@sinclair/typebox").TString;
158
158
  label: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -193,7 +193,7 @@ export declare const SemanticEvidenceV1Schema: import("@sinclair/typebox").TObje
193
193
  started_at: import("@sinclair/typebox").TString;
194
194
  }>>;
195
195
  recent: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
196
- kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"error" | "recovery" | "artifact" | "operation" | "action" | "terminal" | "progress" | "task" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
196
+ kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
197
197
  event_id: import("@sinclair/typebox").TString;
198
198
  observed_at: import("@sinclair/typebox").TString;
199
199
  label: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -201,7 +201,7 @@ export declare const SemanticEvidenceV1Schema: import("@sinclair/typebox").TObje
201
201
  error_class: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
202
202
  }>>;
203
203
  attention: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
204
- kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"error" | "recovery" | "artifact" | "operation" | "action" | "terminal" | "progress" | "task" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
204
+ kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
205
205
  event_id: import("@sinclair/typebox").TString;
206
206
  observed_at: import("@sinclair/typebox").TString;
207
207
  label: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -628,7 +628,7 @@ export declare const SemanticInvalidReadModelV2Schema: import("@sinclair/typebox
628
628
  }>;
629
629
  receipt: import("@sinclair/typebox").TObject<{
630
630
  reason_code: import("@sinclair/typebox").TLiteral<"invalid_output">;
631
- validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "schema" | "privacy" | "confidence" | "identity" | "basis" | "citation" | "unsupported_claim">[]>>;
631
+ validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "confidence" | "schema" | "privacy" | "identity" | "basis" | "citation" | "unsupported_claim">[]>>;
632
632
  usage: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
633
633
  schema_version: import("@sinclair/typebox").TLiteral<1>;
634
634
  source: import("@sinclair/typebox").TLiteral<"native">;
@@ -954,7 +954,7 @@ export declare const SemanticAgentReadModelV2Schema: import("@sinclair/typebox")
954
954
  }>;
955
955
  receipt: import("@sinclair/typebox").TObject<{
956
956
  reason_code: import("@sinclair/typebox").TLiteral<"invalid_output">;
957
- validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "schema" | "privacy" | "confidence" | "identity" | "basis" | "citation" | "unsupported_claim">[]>>;
957
+ validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "confidence" | "schema" | "privacy" | "identity" | "basis" | "citation" | "unsupported_claim">[]>>;
958
958
  usage: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
959
959
  schema_version: import("@sinclair/typebox").TLiteral<1>;
960
960
  source: import("@sinclair/typebox").TLiteral<"native">;
@@ -14,11 +14,12 @@
14
14
  * for `gc` once its starting agent has ended or been unobserved for a long
15
15
  * time AND nobody has used the server for the idle window.
16
16
  *
17
- * Fail-closed rules: a record whose pid was recycled is treated as dead, never
18
- * signalled; an unreadable coordination view makes every owner `unknown`,
17
+ * Fail-closed rules: a record whose pid was recycled is never signalled;
18
+ * an unreadable start token or coordination view is `unknown`,
19
19
  * which `gc` never stops; a record without an owner is never stopped
20
20
  * automatically.
21
21
  */
22
+ import { type ProcessGenerationSignalDependencies, type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
22
23
  import { type ScanSupport } from "./net.js";
23
24
  export { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, lsofConnectionCounts, lsofListeningSockets, parseElapsed, parseLsofListen, parseProcNetTcp, processStartedAt, psProcessInfo, readProcessInfo, scanSupport, } from "./net.js";
24
25
  export { tunnelServerId, tunnelServerInput } from "./tunnels.js";
@@ -40,13 +41,13 @@ export interface ServerRecord {
40
41
  start_token?: string;
41
42
  /** Further processes that belong to this server (helpers, children). */
42
43
  pids?: number[];
44
+ /** Generation witnesses captured when the further processes registered. */
45
+ pids_start_tokens?: Record<string, string>;
43
46
  host: string;
44
47
  /** What the server serves, usually a directory. */
45
48
  scope?: string;
46
49
  cwd?: string;
47
50
  log?: string;
48
- /** Graceful stop command; signals are the fallback. */
49
- stop_argv?: string[];
50
51
  owner?: {
51
52
  instance_id: string;
52
53
  name?: string;
@@ -63,11 +64,12 @@ export interface RegisterServerInput {
63
64
  url?: string;
64
65
  port?: number;
65
66
  pid?: number;
67
+ /** Preserve an observed witness when adopting, rather than probing a new generation. */
68
+ start_token?: string;
66
69
  pids?: number[];
67
70
  scope?: string;
68
71
  cwd?: string;
69
72
  log?: string;
70
- stop_argv?: string[];
71
73
  started_at?: string;
72
74
  /** `null` records no owner; omitted resolves the calling agent session. */
73
75
  owner?: {
@@ -78,7 +80,7 @@ export interface RegisterServerInput {
78
80
  export interface ServerOptions {
79
81
  coordRoot?: string | null;
80
82
  }
81
- export type ServerState = "running" | "dead" | "other-host";
83
+ export type ServerState = "running" | "dead" | "other-host" | "unknown";
82
84
  export type OwnerState = "live" | "abandoned" | "ended" | "unknown" | "none";
83
85
  export interface ServerView {
84
86
  record: ServerRecord;
@@ -90,6 +92,8 @@ export interface ServerView {
90
92
  }
91
93
  export interface UnregisteredListener {
92
94
  pid: number;
95
+ /** Captured during the scan, before a stop can be authorized. */
96
+ start_token?: string;
93
97
  port: number;
94
98
  address: string;
95
99
  command: string;
@@ -134,7 +138,7 @@ export declare function touchServer(id: string, options?: ServerOptions & {
134
138
  export declare function createServerToucher(id: string, options?: ServerOptions): () => void;
135
139
  export declare function readServerRecords(options?: ServerOptions): ServerRecord[];
136
140
  export declare function pidAlive(pid: number): boolean;
137
- export declare function serverState(record: ServerRecord): ServerState;
141
+ export declare function serverState(record: ServerRecord, readToken?: (pid: number) => string | null, probePid?: (pid: number) => boolean): ServerState;
138
142
  export interface ListServersOptions extends ServerOptions {
139
143
  /** Look for listening processes inside the project that never registered. */
140
144
  scan?: boolean;
@@ -147,8 +151,6 @@ export interface ListServersOptions extends ServerOptions {
147
151
  }
148
152
  /** Read every record, observe it, and optionally find unregistered listeners. */
149
153
  export declare function listServers(options?: ListServersOptions): ServersReport;
150
- /** Register running tunnels that have no current record. Returns adopted ids. */
151
- export declare function adoptRunningTunnels(coordRoot: string): string[];
152
154
  /**
153
155
  * Listening processes that run inside the project (working directory or
154
156
  * command line within the coordination root) but are not covered by any
@@ -175,16 +177,21 @@ export declare function planServerGc(views: ServerView[], policy?: GcPolicy): Gc
175
177
  export interface StopResult {
176
178
  id: string;
177
179
  stopped: boolean;
178
- method: "not-running" | "stop-command" | "signal" | "refused";
180
+ method: "not-running" | "signal" | "refused";
181
+ generation?: ProcessGenerationVerdict;
179
182
  detail?: string;
180
183
  }
184
+ export interface ServerProcessDependencies extends ProcessGenerationSignalDependencies {
185
+ probePid?: (pid: number) => boolean;
186
+ }
181
187
  /**
182
- * Stop one server: its graceful stop command first when recorded, then
183
- * SIGTERM to its process group, then SIGKILL after the grace period. A pid
184
- * whose start token no longer matches is never signalled.
188
+ * Stop a verified server with SIGTERM, then SIGKILL after the grace period.
189
+ * Each signal, including the process group and auxiliary processes, requires
190
+ * a fresh generation check. Unknown or different generations retain the record.
185
191
  */
186
192
  export declare function stopServer(record: ServerRecord, options?: ServerOptions & {
187
193
  graceMs?: number;
194
+ signalDependencies?: ServerProcessDependencies;
188
195
  }): Promise<StopResult>;
189
196
  /**
190
197
  * The first free port in the configured range for `type`
@@ -200,6 +207,10 @@ export interface AdoptOptions extends ServerOptions {
200
207
  scope?: string;
201
208
  /** `self` (default) records the adopting agent as owner; `none` records no owner. */
202
209
  owner?: "self" | "none";
210
+ dependencies?: {
211
+ listServers?: typeof listServers;
212
+ readToken?: (pid: number) => string | null;
213
+ };
203
214
  }
204
215
  /**
205
216
  * Register a running server that never registered itself. Only a pid the scan
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/servers/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/servers/index.ts"],"names":[],"mappings":"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"}
@@ -14,27 +14,24 @@
14
14
  * for `gc` once its starting agent has ended or been unobserved for a long
15
15
  * time AND nobody has used the server for the idle window.
16
16
  *
17
- * Fail-closed rules: a record whose pid was recycled is treated as dead, never
18
- * signalled; an unreadable coordination view makes every owner `unknown`,
17
+ * Fail-closed rules: a record whose pid was recycled is never signalled;
18
+ * an unreadable start token or coordination view is `unknown`,
19
19
  * which `gc` never stops; a record without an owner is never stopped
20
20
  * automatically.
21
21
  */
22
- import { spawnSync } from "node:child_process";
23
22
  import { createHash } from "node:crypto";
24
23
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
25
24
  import { hostname } from "node:os";
26
25
  import { basename, isAbsolute, join, relative, resolve } from "node:path";
27
- import { listStates } from "../../lib/tunnel/state.js";
28
26
  import { resolveCoordRoot } from "../agents/coord-client.js";
29
27
  import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
30
- import { checkPidToken, processStartToken } from "../agents/state/proc-start.js";
28
+ import { processStartToken, signalIfSameGeneration, verifyProcessGeneration, } from "../agents/state/proc-start.js";
31
29
  import { serverPortRange } from "../config.js";
32
30
  import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from "../events/v3/coordination-view.js";
33
31
  import { liveInstanceIdV3 } from "../events/v3/live-route-observer.js";
34
32
  import { resolveOwner } from "../hooks/resolve/owner.js";
35
33
  import { writePrivateJsonAtomic } from "../storage/atomic-json.js";
36
34
  import { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, processStartedAt, readProcessInfo, scanSupport, } from "./net.js";
37
- import { tunnelServerId, tunnelServerInput } from "./tunnels.js";
38
35
  export { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, lsofConnectionCounts, lsofListeningSockets, parseElapsed, parseLsofListen, parseProcNetTcp, processStartedAt, psProcessInfo, readProcessInfo, scanSupport, } from "./net.js";
39
36
  export { tunnelServerId, tunnelServerInput } from "./tunnels.js";
40
37
  export const SERVER_RECORD_SCHEMA_VERSION = 1;
@@ -87,7 +84,14 @@ export function registerServer(input, options) {
87
84
  throw new Error(`invalid server port: ${input.port}`);
88
85
  const id = input.id ?? serverId(input.type, input.scope);
89
86
  const now = new Date().toISOString();
90
- const startToken = processStartToken(pid);
87
+ const startToken = input.start_token ?? processStartToken(pid);
88
+ const pids = [...new Set(input.pids?.filter((p) => Number.isSafeInteger(p) && p > 0))];
89
+ const pidsStartTokens = {};
90
+ for (const auxiliaryPid of pids) {
91
+ const token = processStartToken(auxiliaryPid);
92
+ if (token)
93
+ pidsStartTokens[auxiliaryPid] = token;
94
+ }
91
95
  const owner = input.owner === undefined ? callingOwner(coordRoot) : (input.owner ?? undefined);
92
96
  const record = {
93
97
  schema_version: SERVER_RECORD_SCHEMA_VERSION,
@@ -99,19 +103,18 @@ export function registerServer(input, options) {
99
103
  ...(input.port ? { port: input.port } : {}),
100
104
  pid,
101
105
  ...(startToken ? { start_token: startToken } : {}),
102
- ...(input.pids?.length
103
- ? { pids: input.pids.filter((p) => Number.isSafeInteger(p) && p > 0) }
104
- : {}),
106
+ ...(pids.length ? { pids, pids_start_tokens: pidsStartTokens } : {}),
105
107
  host: hostname(),
106
108
  ...(input.scope ? { scope: resolve(input.scope) } : {}),
107
109
  ...(input.cwd ? { cwd: resolve(input.cwd) } : {}),
108
110
  ...(input.log ? { log: resolve(input.log) } : {}),
109
- ...(input.stop_argv?.length ? { stop_argv: input.stop_argv } : {}),
110
111
  ...(owner ? { owner } : {}),
111
112
  started_at: input.started_at ?? now,
112
113
  registered_at: now,
113
114
  last_active_at: now,
114
115
  };
116
+ if (input.start_token !== undefined && verifyProcessGeneration(pid, input.start_token) !== "same")
117
+ throw new Error(`pid ${pid} generation could not be verified before registration`);
115
118
  writePrivateJsonAtomic(recordPath(coordRoot, id), record);
116
119
  return record;
117
120
  }
@@ -224,20 +227,33 @@ export function readServerRecords(options) {
224
227
  .sort((a, b) => a.kind.localeCompare(b.kind) || a.type.localeCompare(b.type) || a.id.localeCompare(b.id));
225
228
  }
226
229
  export function pidAlive(pid) {
230
+ return processPresence(pid) !== "absent";
231
+ }
232
+ function processPresence(pid, probePid) {
227
233
  try {
234
+ if (probePid)
235
+ return probePid(pid) ? "present" : "absent";
228
236
  process.kill(pid, 0);
229
- return true;
237
+ return "present";
230
238
  }
231
239
  catch (error) {
232
- return error.code === "EPERM";
240
+ const code = error.code;
241
+ return code === "ESRCH" ? "absent" : code === "EPERM" ? "present" : "unknown";
233
242
  }
234
243
  }
235
- export function serverState(record) {
244
+ function allProcessesAbsent(record, probePid) {
245
+ return [record.pid, ...(record.pids ?? [])].every((pid) => processPresence(pid, probePid) === "absent");
246
+ }
247
+ export function serverState(record, readToken, probePid) {
236
248
  if (record.host !== hostname())
237
249
  return "other-host";
238
- if (!pidAlive(record.pid))
239
- return "dead";
240
- return checkPidToken(record.pid, record.start_token) === "mismatch" ? "dead" : "running";
250
+ const presence = processPresence(record.pid, probePid);
251
+ if (presence === "absent")
252
+ return allProcessesAbsent(record, probePid) ? "dead" : "unknown";
253
+ if (presence === "unknown")
254
+ return "unknown";
255
+ const verdict = verifyProcessGeneration(record.pid, record.start_token, readToken);
256
+ return verdict === "same" ? "running" : verdict === "different" ? "dead" : "unknown";
241
257
  }
242
258
  function ownerReader(coordRoot, ownerStaleMs, nowMs) {
243
259
  let view = null;
@@ -268,7 +284,6 @@ export function listServers(options) {
268
284
  const now = options?.now ?? new Date();
269
285
  const nowMs = now.getTime();
270
286
  const ownerStaleMs = hours(options?.ownerStaleHours ?? SERVER_DEFAULT_OWNER_STALE_HOURS);
271
- adoptRunningTunnels(coordRoot);
272
287
  const records = readServerRecords({ coordRoot });
273
288
  const ownerOf = ownerReader(coordRoot, ownerStaleMs, nowMs);
274
289
  const states = records.map((record) => serverState(record));
@@ -278,7 +293,7 @@ export function listServers(options) {
278
293
  const servers = [];
279
294
  records.forEach((record, i) => {
280
295
  const state = states[i];
281
- if (state === "dead" && options?.prune !== false) {
296
+ if (state === "dead" && allProcessesAbsent(record) && options?.prune !== false) {
282
297
  unregisterServer(record.id, { coordRoot, pid: record.pid });
283
298
  pruned.push(record.id);
284
299
  return;
@@ -316,27 +331,6 @@ export function listServers(options) {
316
331
  pruned,
317
332
  };
318
333
  }
319
- /** Register running tunnels that have no current record. Returns adopted ids. */
320
- export function adoptRunningTunnels(coordRoot) {
321
- const adopted = [];
322
- let states;
323
- try {
324
- states = listStates(coordRoot);
325
- }
326
- catch {
327
- return adopted;
328
- }
329
- for (const state of states) {
330
- if (!state.gate_pid || !pidAlive(state.gate_pid))
331
- continue;
332
- const id = tunnelServerId(state.name);
333
- if (readServer(id, { coordRoot })?.pid === state.gate_pid)
334
- continue;
335
- if (tryRegisterServer(tunnelServerInput(state, coordRoot), { coordRoot }))
336
- adopted.push(id);
337
- }
338
- return adopted;
339
- }
340
334
  /** Processes that browser automation and editors start; never project servers. */
341
335
  const IGNORED_LISTENER_COMMANDS = [
342
336
  /(^|\/)(chrome|chromium|chrome-headless-shell|headless_shell|msedge|firefox)( |$)/i,
@@ -350,10 +344,10 @@ const IGNORED_LISTENER_COMMANDS = [
350
344
  * a recorded port.
351
345
  */
352
346
  export function findUnregisteredListeners(coordRoot, servers) {
353
- const running = servers.filter((server) => server.state === "running");
347
+ const registered = servers.filter((server) => server.record.host === hostname());
354
348
  const covered = new Set();
355
349
  const ports = new Set();
356
- for (const { record } of running) {
350
+ for (const { record } of registered) {
357
351
  covered.add(record.pid);
358
352
  for (const pid of record.pids ?? [])
359
353
  covered.add(pid);
@@ -363,8 +357,9 @@ export function findUnregisteredListeners(coordRoot, servers) {
363
357
  const results = [];
364
358
  const seenPids = new Map();
365
359
  for (const socket of listListeningSockets()) {
366
- if (ports.has(socket.port) || socket.pid === process.pid)
360
+ if (ports.has(socket.port) || covered.has(socket.pid) || socket.pid === process.pid)
367
361
  continue;
362
+ const startToken = processStartToken(socket.pid);
368
363
  let inside = seenPids.get(socket.pid);
369
364
  const info = readProcessInfo(socket.pid);
370
365
  if (!info)
@@ -380,6 +375,7 @@ export function findUnregisteredListeners(coordRoot, servers) {
380
375
  continue;
381
376
  results.push({
382
377
  pid: socket.pid,
378
+ ...(startToken ? { start_token: startToken } : {}),
383
379
  port: socket.port,
384
380
  address: socket.address,
385
381
  command: info.command.slice(0, 300),
@@ -430,72 +426,111 @@ export function planServerGc(views, policy) {
430
426
  });
431
427
  }
432
428
  /**
433
- * Stop one server: its graceful stop command first when recorded, then
434
- * SIGTERM to its process group, then SIGKILL after the grace period. A pid
435
- * whose start token no longer matches is never signalled.
429
+ * Stop a verified server with SIGTERM, then SIGKILL after the grace period.
430
+ * Each signal, including the process group and auxiliary processes, requires
431
+ * a fresh generation check. Unknown or different generations retain the record.
436
432
  */
437
433
  export async function stopServer(record, options) {
438
434
  const coordRoot = rootOf(options);
439
435
  const graceMs = options?.graceMs ?? 5_000;
436
+ const dependencies = options?.signalDependencies ?? {};
437
+ const targets = [
438
+ { pid: record.pid, token: record.start_token },
439
+ ...[...new Set(record.pids ?? [])]
440
+ .filter((pid) => pid !== record.pid)
441
+ .map((pid) => ({ pid, token: record.pids_start_tokens?.[pid] })),
442
+ ];
443
+ const refuse = (pid, generation) => ({
444
+ id: record.id,
445
+ stopped: false,
446
+ method: "refused",
447
+ generation,
448
+ detail: `pid ${pid} generation is ${generation}; left the server record in place`,
449
+ });
440
450
  const finish = (result) => {
441
451
  if (result.stopped || result.method === "not-running")
442
452
  unregisterServer(record.id, { coordRoot, pid: record.pid });
443
453
  return result;
444
454
  };
445
- const state = serverState(record);
446
- if (state === "other-host")
455
+ if (record.host !== hostname())
447
456
  return { id: record.id, stopped: false, method: "refused", detail: `runs on ${record.host}` };
448
- if (state === "dead")
449
- return finish({ id: record.id, stopped: false, method: "not-running" });
450
- if (record.stop_argv?.length) {
451
- const [command, ...args] = record.stop_argv;
452
- spawnSync(command, args, { stdio: "ignore", timeout: 30_000, cwd: record.cwd });
453
- if (await exited(record, graceMs))
454
- return finish({ id: record.id, stopped: true, method: "stop-command" });
457
+ const leaderPresence = processPresence(record.pid, dependencies.probePid);
458
+ if (leaderPresence === "unknown" ||
459
+ (leaderPresence === "absent" && !allProcessesAbsent(record, dependencies.probePid)))
460
+ return refuse(record.pid, "unknown");
461
+ // Check every recorded live process before sending the first signal.
462
+ for (const target of targets) {
463
+ const presence = processPresence(target.pid, dependencies.probePid);
464
+ if (presence === "absent")
465
+ continue;
466
+ if (presence === "unknown")
467
+ return refuse(target.pid, "unknown");
468
+ const verdict = verifyProcessGeneration(target.pid, target.token, dependencies.readToken);
469
+ if (verdict !== "same")
470
+ return refuse(target.pid, verdict);
455
471
  }
456
- signal(record.pid, "SIGTERM");
457
- for (const pid of record.pids ?? [])
458
- signal(pid, "SIGTERM");
459
- if (await exited(record, graceMs))
460
- return finish({ id: record.id, stopped: true, method: "signal" });
461
- if (serverState(record) === "running") {
462
- signal(record.pid, "SIGKILL");
463
- for (const pid of record.pids ?? [])
464
- signal(pid, "SIGKILL");
472
+ if (targets.every((target) => processPresence(target.pid, dependencies.probePid) === "absent"))
473
+ return finish({ id: record.id, stopped: false, method: "not-running" });
474
+ for (const name of ["SIGTERM", "SIGKILL"]) {
475
+ for (const target of targets) {
476
+ const presence = processPresence(target.pid, dependencies.probePid);
477
+ if (presence === "absent")
478
+ continue;
479
+ if (presence === "unknown")
480
+ return refuse(target.pid, "unknown");
481
+ const result = target.pid === record.pid
482
+ ? signalServerLeader(target.pid, target.token, name, dependencies)
483
+ : signalIfSameGeneration(target.pid, target.token, name, dependencies);
484
+ if (result.verdict !== "same")
485
+ return refuse(target.pid, result.verdict);
486
+ }
487
+ const observed = await waitForExit(targets, name === "SIGTERM" ? graceMs : 2_000, dependencies);
488
+ if (observed.refused)
489
+ return refuse(observed.refused.pid, observed.refused.verdict);
490
+ if (observed.exited)
491
+ return finish({ id: record.id, stopped: true, method: "signal", generation: "same" });
465
492
  }
466
- const stopped = await exited(record, 2_000);
467
- return finish({
493
+ return {
468
494
  id: record.id,
469
- stopped,
495
+ stopped: false,
470
496
  method: "signal",
471
- ...(stopped ? {} : { detail: "process survived SIGKILL" }),
472
- });
497
+ generation: "same",
498
+ detail: "process survived SIGKILL",
499
+ };
473
500
  }
474
- function signal(pid, name) {
475
- // Detached servers lead their own process group; signal the group so a
476
- // wrapper (npm, bunx) does not orphan the real listener.
477
- try {
478
- process.kill(-pid, name);
479
- return;
480
- }
481
- catch {
482
- // Not a group leader, or already gone.
483
- }
484
- try {
485
- process.kill(pid, name);
486
- }
487
- catch {
488
- // Already gone.
489
- }
501
+ function signalServerLeader(pid, token, name, dependencies) {
502
+ // The leader's generation authorizes its group. A failed group signal must
503
+ // not reuse that observation for the direct-PID fallback.
504
+ const kill = dependencies.kill ?? process.kill;
505
+ const group = signalIfSameGeneration(pid, token, name, {
506
+ readToken: dependencies.readToken,
507
+ kill: (leader, signal) => kill(-leader, signal),
508
+ });
509
+ return group.sent || group.verdict !== "same"
510
+ ? group
511
+ : signalIfSameGeneration(pid, token, name, dependencies);
490
512
  }
491
- async function exited(record, waitMs) {
513
+ async function waitForExit(targets, waitMs, dependencies) {
492
514
  const deadline = Date.now() + waitMs;
493
- while (Date.now() < deadline) {
494
- if (serverState(record) !== "running")
495
- return true;
515
+ while (true) {
516
+ let live = false;
517
+ for (const target of targets) {
518
+ const presence = processPresence(target.pid, dependencies.probePid);
519
+ if (presence === "absent")
520
+ continue;
521
+ if (presence === "unknown")
522
+ return { exited: false, refused: { pid: target.pid, verdict: "unknown" } };
523
+ const verdict = verifyProcessGeneration(target.pid, target.token, dependencies.readToken);
524
+ if (verdict !== "same")
525
+ return { exited: false, refused: { pid: target.pid, verdict } };
526
+ live = true;
527
+ }
528
+ if (!live)
529
+ return { exited: true };
530
+ if (Date.now() >= deadline)
531
+ return { exited: false };
496
532
  await new Promise((r) => setTimeout(r, 100));
497
533
  }
498
- return serverState(record) !== "running";
499
534
  }
500
535
  /**
501
536
  * The first free port in the configured range for `type`
@@ -510,7 +545,8 @@ export function allocateServerPort(type, options) {
510
545
  return null;
511
546
  const taken = listListeningPorts();
512
547
  for (const record of readServerRecords({ coordRoot })) {
513
- if (record.port && serverState(record) === "running")
548
+ const state = serverState(record);
549
+ if (record.port && (state === "running" || state === "unknown"))
514
550
  taken.add(record.port);
515
551
  }
516
552
  for (let port = range[0]; port <= range[1]; port++)
@@ -526,12 +562,15 @@ export function adoptServer(pid, options) {
526
562
  const coordRoot = rootOf(options);
527
563
  if (!coordRoot)
528
564
  throw new Error("no Harnery project found (coordination root did not resolve)");
529
- const report = listServers({ coordRoot, sample: false });
565
+ const report = (options?.dependencies?.listServers ?? listServers)({ coordRoot, sample: false });
530
566
  const listeners = report.unregistered.filter((item) => item.pid === pid);
531
567
  if (!listeners.length)
532
568
  throw new Error(`pid ${pid} is not an unregistered listener inside this project`);
533
569
  const ports = listeners.map((item) => item.port).sort((a, b) => a - b);
534
570
  const first = listeners[0];
571
+ const generation = verifyProcessGeneration(pid, first.start_token, options?.dependencies?.readToken);
572
+ if (generation !== "same")
573
+ throw new Error(`pid ${pid} generation is ${generation}; adoption refused`);
535
574
  const type = options?.type ?? "adopted";
536
575
  const scope = options?.scope ?? first.cwd ?? undefined;
537
576
  const started = processStartedAt(pid);
@@ -543,6 +582,7 @@ export function adoptServer(pid, options) {
543
582
  url: `http://127.0.0.1:${ports[0]}/`,
544
583
  port: ports[0],
545
584
  pid,
585
+ start_token: first.start_token,
546
586
  ...(scope ? { scope } : {}),
547
587
  ...(first.cwd ? { cwd: first.cwd } : {}),
548
588
  ...(options?.owner === "none" ? { owner: null } : {}),
@@ -1,14 +1,12 @@
1
1
  /**
2
2
  * Tunnels as registry services.
3
3
  *
4
- * `tunnel up` and `tunnel reload` register the instance; `tunnel down`
5
- * removes it. Tunnels started before the registry existed are adopted the next
6
- * time the registry is read, so a long-lived tunnel never shows as an
7
- * unregistered listener.
4
+ * `tunnel up` and `tunnel reload` register the new gate; `tunnel down` removes
5
+ * its record. Older tunnels need an explicit adoption of an observed listener.
8
6
  */
9
7
  import { type TunnelState } from "../../lib/tunnel/state.js";
10
8
  import type { RegisterServerInput } from "./index.js";
11
9
  export declare function tunnelServerId(name: string): string;
12
- /** The registry input for one tunnel instance. */
10
+ /** Register the new gate; saved helper PIDs have no generation witnesses. */
13
11
  export declare function tunnelServerInput(state: TunnelState, coordRoot: string): RegisterServerInput;
14
12
  //# sourceMappingURL=tunnels.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tunnels.d.ts","sourceRoot":"","sources":["../../../src/core/servers/tunnels.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,kDAAkD;AAClD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,mBAAmB,CAmB5F"}
1
+ {"version":3,"file":"tunnels.d.ts","sourceRoot":"","sources":["../../../src/core/servers/tunnels.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,mBAAmB,CAc5F"}
@@ -1,20 +1,16 @@
1
1
  /**
2
2
  * Tunnels as registry services.
3
3
  *
4
- * `tunnel up` and `tunnel reload` register the instance; `tunnel down`
5
- * removes it. Tunnels started before the registry existed are adopted the next
6
- * time the registry is read, so a long-lived tunnel never shows as an
7
- * unregistered listener.
4
+ * `tunnel up` and `tunnel reload` register the new gate; `tunnel down` removes
5
+ * its record. Older tunnels need an explicit adoption of an observed listener.
8
6
  */
9
7
  import { resolve } from "node:path";
10
8
  import { gateLogFile } from "../../lib/tunnel/state.js";
11
- import { resolveBinName } from "../config.js";
12
9
  export function tunnelServerId(name) {
13
10
  return `tunnel-${name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-")}`;
14
11
  }
15
- /** The registry input for one tunnel instance. */
12
+ /** Register the new gate; saved helper PIDs have no generation witnesses. */
16
13
  export function tunnelServerInput(state, coordRoot) {
17
- const helpers = [state.cloudflared_pid, state.provider_pid].filter((pid) => typeof pid === "number" && pid > 0);
18
14
  return {
19
15
  id: tunnelServerId(state.name),
20
16
  kind: "service",
@@ -23,10 +19,8 @@ export function tunnelServerInput(state, coordRoot) {
23
19
  url: state.url,
24
20
  port: state.gate_port,
25
21
  pid: state.gate_pid,
26
- ...(helpers.length ? { pids: helpers } : {}),
27
22
  log: resolve(coordRoot, ".cache", "tunnel", gateLogFile(state.name)),
28
23
  cwd: coordRoot,
29
- stop_argv: [resolveBinName(coordRoot), "tunnel", "down", "--name", state.name],
30
24
  started_at: state.started_at,
31
25
  owner: null,
32
26
  };