@telora/daemon 0.22.146 → 0.22.169

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 (408) hide show
  1. package/build-info.json +15 -3
  2. package/dist/audit/audit-phase.d.ts +11 -1
  3. package/dist/audit/audit-phase.d.ts.map +1 -1
  4. package/dist/audit/audit-phase.js +2 -14
  5. package/dist/audit/audit-phase.js.map +1 -1
  6. package/dist/audit/audit-prompt.d.ts +7 -4
  7. package/dist/audit/audit-prompt.d.ts.map +1 -1
  8. package/dist/audit/audit-prompt.js +34 -23
  9. package/dist/audit/audit-prompt.js.map +1 -1
  10. package/dist/backends/codex/codex-mcp-proxy.d.ts +4 -6
  11. package/dist/backends/codex/codex-mcp-proxy.d.ts.map +1 -1
  12. package/dist/backends/codex/codex-mcp-proxy.js.map +1 -1
  13. package/dist/backends/codex/mcp-preflight.d.ts +2 -2
  14. package/dist/backends/codex/mcp-preflight.d.ts.map +1 -1
  15. package/dist/backends/codex/mcp-preflight.js.map +1 -1
  16. package/dist/completion/advancement-stall-watchdog.d.ts +12 -0
  17. package/dist/completion/advancement-stall-watchdog.d.ts.map +1 -0
  18. package/dist/completion/advancement-stall-watchdog.js +79 -0
  19. package/dist/completion/advancement-stall-watchdog.js.map +1 -0
  20. package/dist/completion/arm-visible-progress-inputs.d.ts +51 -0
  21. package/dist/completion/arm-visible-progress-inputs.d.ts.map +1 -0
  22. package/dist/completion/arm-visible-progress-inputs.js +19 -0
  23. package/dist/completion/arm-visible-progress-inputs.js.map +1 -0
  24. package/dist/completion/completion-phase-registry.d.ts +1 -0
  25. package/dist/completion/completion-phase-registry.d.ts.map +1 -1
  26. package/dist/completion/completion-phase-registry.js +7 -5
  27. package/dist/completion/completion-phase-registry.js.map +1 -1
  28. package/dist/completion/delivery-advancement-guard.d.ts.map +1 -1
  29. package/dist/completion/delivery-advancement-guard.js +1 -0
  30. package/dist/completion/delivery-advancement-guard.js.map +1 -1
  31. package/dist/completion/escalation-provenance.d.ts.map +1 -1
  32. package/dist/completion/escalation-provenance.js +5 -2
  33. package/dist/completion/escalation-provenance.js.map +1 -1
  34. package/dist/completion/event-entry-phases.d.ts.map +1 -1
  35. package/dist/completion/event-entry-phases.js +9 -2
  36. package/dist/completion/event-entry-phases.js.map +1 -1
  37. package/dist/completion/event-phases.d.ts +15 -8
  38. package/dist/completion/event-phases.d.ts.map +1 -1
  39. package/dist/completion/event-phases.js +26 -64
  40. package/dist/completion/event-phases.js.map +1 -1
  41. package/dist/completion/event.d.ts.map +1 -1
  42. package/dist/completion/event.js +7 -1
  43. package/dist/completion/event.js.map +1 -1
  44. package/dist/completion/exit-classification.d.ts +1 -1
  45. package/dist/completion/exit-classification.d.ts.map +1 -1
  46. package/dist/completion/exit-classification.js +7 -1
  47. package/dist/completion/exit-classification.js.map +1 -1
  48. package/dist/completion/review-cycle-cap.d.ts +81 -35
  49. package/dist/completion/review-cycle-cap.d.ts.map +1 -1
  50. package/dist/completion/review-cycle-cap.js +60 -132
  51. package/dist/completion/review-cycle-cap.js.map +1 -1
  52. package/dist/completion/review-exit-deps.d.ts +3 -1
  53. package/dist/completion/review-exit-deps.d.ts.map +1 -1
  54. package/dist/completion/review-exit-deps.js +3 -1
  55. package/dist/completion/review-exit-deps.js.map +1 -1
  56. package/dist/completion/review-exit-phase.d.ts.map +1 -1
  57. package/dist/completion/review-exit-phase.js +5 -4
  58. package/dist/completion/review-exit-phase.js.map +1 -1
  59. package/dist/completion/shutdown-attribution.d.ts +107 -0
  60. package/dist/completion/shutdown-attribution.d.ts.map +1 -0
  61. package/dist/completion/shutdown-attribution.js +106 -0
  62. package/dist/completion/shutdown-attribution.js.map +1 -0
  63. package/dist/completion/state-cascade.d.ts.map +1 -1
  64. package/dist/completion/state-cascade.js +3 -7
  65. package/dist/completion/state-cascade.js.map +1 -1
  66. package/dist/completion/team-lifecycle.d.ts.map +1 -1
  67. package/dist/completion/team-lifecycle.js +16 -0
  68. package/dist/completion/team-lifecycle.js.map +1 -1
  69. package/dist/delivery/delivery-lifecycle.d.ts.map +1 -1
  70. package/dist/delivery/delivery-lifecycle.js +1 -0
  71. package/dist/delivery/delivery-lifecycle.js.map +1 -1
  72. package/dist/directive/directive-assembly.d.ts +19 -1
  73. package/dist/directive/directive-assembly.d.ts.map +1 -1
  74. package/dist/directive/directive-assembly.js +15 -3
  75. package/dist/directive/directive-assembly.js.map +1 -1
  76. package/dist/directive/directive-dispatch.d.ts +67 -2
  77. package/dist/directive/directive-dispatch.d.ts.map +1 -1
  78. package/dist/directive/directive-dispatch.js +188 -45
  79. package/dist/directive/directive-dispatch.js.map +1 -1
  80. package/dist/directive/directive-executor.d.ts +2 -2
  81. package/dist/directive/directive-executor.d.ts.map +1 -1
  82. package/dist/directive/directive-executor.js +49 -12
  83. package/dist/directive/directive-executor.js.map +1 -1
  84. package/dist/directive/directive-queue.d.ts.map +1 -1
  85. package/dist/directive/directive-queue.js +1 -0
  86. package/dist/directive/directive-queue.js.map +1 -1
  87. package/dist/directive/stage-tracker.d.ts +20 -0
  88. package/dist/directive/stage-tracker.d.ts.map +1 -1
  89. package/dist/directive/stage-tracker.js +29 -1
  90. package/dist/directive/stage-tracker.js.map +1 -1
  91. package/dist/focus/focus-engine.d.ts +16 -0
  92. package/dist/focus/focus-engine.d.ts.map +1 -1
  93. package/dist/focus/focus-engine.js +42 -18
  94. package/dist/focus/focus-engine.js.map +1 -1
  95. package/dist/focus/focus-executor.d.ts.map +1 -1
  96. package/dist/focus/focus-executor.js +21 -3
  97. package/dist/focus/focus-executor.js.map +1 -1
  98. package/dist/focus/focus-phase.d.ts +11 -1
  99. package/dist/focus/focus-phase.d.ts.map +1 -1
  100. package/dist/focus/focus-phase.js +12 -2
  101. package/dist/focus/focus-phase.js.map +1 -1
  102. package/dist/focus/focus-spawn-decision.d.ts +5 -3
  103. package/dist/focus/focus-spawn-decision.d.ts.map +1 -1
  104. package/dist/focus/focus-spawn-decision.js.map +1 -1
  105. package/dist/focus/focus-team-state.js +1 -1
  106. package/dist/focus/listener-auto-advance.d.ts.map +1 -1
  107. package/dist/focus/listener-auto-advance.js +1 -0
  108. package/dist/focus/listener-auto-advance.js.map +1 -1
  109. package/dist/focus/listener-spawn.d.ts.map +1 -1
  110. package/dist/focus/listener-spawn.js +37 -13
  111. package/dist/focus/listener-spawn.js.map +1 -1
  112. package/dist/focus/listener.d.ts +15 -0
  113. package/dist/focus/listener.d.ts.map +1 -1
  114. package/dist/focus/listener.js +79 -17
  115. package/dist/focus/listener.js.map +1 -1
  116. package/dist/focus/live-arm-facts.d.ts +10 -0
  117. package/dist/focus/live-arm-facts.d.ts.map +1 -0
  118. package/dist/focus/live-arm-facts.js +47 -0
  119. package/dist/focus/live-arm-facts.js.map +1 -0
  120. package/dist/focus/recorded-new-intent.d.ts +81 -0
  121. package/dist/focus/recorded-new-intent.d.ts.map +1 -0
  122. package/dist/focus/recorded-new-intent.js +147 -0
  123. package/dist/focus/recorded-new-intent.js.map +1 -0
  124. package/dist/focus/review-cycle-cap-hold.d.ts +15 -94
  125. package/dist/focus/review-cycle-cap-hold.d.ts.map +1 -1
  126. package/dist/focus/review-cycle-cap-hold.js +54 -112
  127. package/dist/focus/review-cycle-cap-hold.js.map +1 -1
  128. package/dist/focus/session-health.d.ts +22 -1
  129. package/dist/focus/session-health.d.ts.map +1 -1
  130. package/dist/focus/session-health.js +28 -3
  131. package/dist/focus/session-health.js.map +1 -1
  132. package/dist/focus/session-process-reap.d.ts +3 -1
  133. package/dist/focus/session-process-reap.d.ts.map +1 -1
  134. package/dist/focus/session-process-reap.js +20 -0
  135. package/dist/focus/session-process-reap.js.map +1 -1
  136. package/dist/focus/stage-watermark-seeding.d.ts +39 -0
  137. package/dist/focus/stage-watermark-seeding.d.ts.map +1 -0
  138. package/dist/focus/stage-watermark-seeding.js +60 -0
  139. package/dist/focus/stage-watermark-seeding.js.map +1 -0
  140. package/dist/focus-stage-lifecycle/index.d.ts.map +1 -1
  141. package/dist/focus-stage-lifecycle/index.js +1 -0
  142. package/dist/focus-stage-lifecycle/index.js.map +1 -1
  143. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts +29 -1
  144. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
  145. package/dist/focus-stage-lifecycle/wedge-watchdog.js +46 -6
  146. package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
  147. package/dist/git/focus-integration-sync.d.ts +42 -3
  148. package/dist/git/focus-integration-sync.d.ts.map +1 -1
  149. package/dist/git/focus-integration-sync.js +38 -2
  150. package/dist/git/focus-integration-sync.js.map +1 -1
  151. package/dist/merge/abandoned-merge-recovery.d.ts.map +1 -1
  152. package/dist/merge/abandoned-merge-recovery.js +10 -0
  153. package/dist/merge/abandoned-merge-recovery.js.map +1 -1
  154. package/dist/merge/merge-back-loop.d.ts +1 -0
  155. package/dist/merge/merge-back-loop.d.ts.map +1 -1
  156. package/dist/merge/merge-back-loop.js +3 -3
  157. package/dist/merge/merge-back-loop.js.map +1 -1
  158. package/dist/prompt/focus-prompt-builder.d.ts +2 -1
  159. package/dist/prompt/focus-prompt-builder.d.ts.map +1 -1
  160. package/dist/prompt/focus-prompt-builder.js +10 -1
  161. package/dist/prompt/focus-prompt-builder.js.map +1 -1
  162. package/dist/prompt/team-prompt-base.d.ts +2 -1
  163. package/dist/prompt/team-prompt-base.d.ts.map +1 -1
  164. package/dist/prompt/team-prompt-base.js +8 -20
  165. package/dist/prompt/team-prompt-base.js.map +1 -1
  166. package/dist/prompt-sections/execution-sections.d.ts +22 -1
  167. package/dist/prompt-sections/execution-sections.d.ts.map +1 -1
  168. package/dist/prompt-sections/execution-sections.js +23 -2
  169. package/dist/prompt-sections/execution-sections.js.map +1 -1
  170. package/dist/prompt-sections/integration-reconcile-section.d.ts +45 -0
  171. package/dist/prompt-sections/integration-reconcile-section.d.ts.map +1 -0
  172. package/dist/prompt-sections/integration-reconcile-section.js +69 -0
  173. package/dist/prompt-sections/integration-reconcile-section.js.map +1 -0
  174. package/dist/prompt-sections/persona-sections.d.ts +0 -20
  175. package/dist/prompt-sections/persona-sections.d.ts.map +1 -1
  176. package/dist/prompt-sections/persona-sections.js +4 -49
  177. package/dist/prompt-sections/persona-sections.js.map +1 -1
  178. package/dist/prompt-sections/shared.d.ts +9 -0
  179. package/dist/prompt-sections/shared.d.ts.map +1 -1
  180. package/dist/prompt-sections/shared.js +2 -1
  181. package/dist/prompt-sections/shared.js.map +1 -1
  182. package/dist/prompt-sections/testing-standard.d.ts +51 -0
  183. package/dist/prompt-sections/testing-standard.d.ts.map +1 -0
  184. package/dist/prompt-sections/testing-standard.js +132 -0
  185. package/dist/prompt-sections/testing-standard.js.map +1 -0
  186. package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
  187. package/dist/queries/agent-resolvable-promotion.js +7 -5
  188. package/dist/queries/agent-resolvable-promotion.js.map +1 -1
  189. package/dist/queries/arm-covered-targets.d.ts +84 -0
  190. package/dist/queries/arm-covered-targets.d.ts.map +1 -0
  191. package/dist/queries/arm-covered-targets.js +129 -0
  192. package/dist/queries/arm-covered-targets.js.map +1 -0
  193. package/dist/queries/deliveries.d.ts +3 -0
  194. package/dist/queries/deliveries.d.ts.map +1 -1
  195. package/dist/queries/deliveries.js +10 -3
  196. package/dist/queries/deliveries.js.map +1 -1
  197. package/dist/queries/delivery-gate-state.d.ts +10 -0
  198. package/dist/queries/delivery-gate-state.d.ts.map +1 -1
  199. package/dist/queries/delivery-gate-state.js +1 -1
  200. package/dist/queries/delivery-gate-state.js.map +1 -1
  201. package/dist/queries/delivery-write-discipline.d.ts.map +1 -1
  202. package/dist/queries/delivery-write-discipline.js +2 -1
  203. package/dist/queries/delivery-write-discipline.js.map +1 -1
  204. package/dist/queries/directive-undelivered.d.ts +104 -0
  205. package/dist/queries/directive-undelivered.d.ts.map +1 -0
  206. package/dist/queries/directive-undelivered.js +162 -0
  207. package/dist/queries/directive-undelivered.js.map +1 -0
  208. package/dist/queries/ended-arm.d.ts +47 -0
  209. package/dist/queries/ended-arm.d.ts.map +1 -0
  210. package/dist/queries/ended-arm.js +35 -0
  211. package/dist/queries/ended-arm.js.map +1 -0
  212. package/dist/queries/escalation-consumption.d.ts.map +1 -1
  213. package/dist/queries/escalation-consumption.js +0 -1
  214. package/dist/queries/escalation-consumption.js.map +1 -1
  215. package/dist/queries/focus-discovery.d.ts +30 -0
  216. package/dist/queries/focus-discovery.d.ts.map +1 -1
  217. package/dist/queries/focus-discovery.js +4 -0
  218. package/dist/queries/focus-discovery.js.map +1 -1
  219. package/dist/queries/focus-integration-conflict.d.ts +78 -0
  220. package/dist/queries/focus-integration-conflict.d.ts.map +1 -0
  221. package/dist/queries/focus-integration-conflict.js +136 -0
  222. package/dist/queries/focus-integration-conflict.js.map +1 -0
  223. package/dist/queries/focuses.d.ts +6 -3
  224. package/dist/queries/focuses.d.ts.map +1 -1
  225. package/dist/queries/focuses.js +14 -5
  226. package/dist/queries/focuses.js.map +1 -1
  227. package/dist/queries/incident-correlation.d.ts +1 -2
  228. package/dist/queries/incident-correlation.d.ts.map +1 -1
  229. package/dist/queries/incident-correlation.js +1 -3
  230. package/dist/queries/incident-correlation.js.map +1 -1
  231. package/dist/queries/issues.d.ts.map +1 -1
  232. package/dist/queries/issues.js +1 -0
  233. package/dist/queries/issues.js.map +1 -1
  234. package/dist/queries/local-models.js +1 -1
  235. package/dist/queries/local-models.js.map +1 -1
  236. package/dist/queries/recorded-targets-record.d.ts +46 -0
  237. package/dist/queries/recorded-targets-record.d.ts.map +1 -0
  238. package/dist/queries/recorded-targets-record.js +29 -0
  239. package/dist/queries/recorded-targets-record.js.map +1 -0
  240. package/dist/queries/schemas-local-model.d.ts +47 -0
  241. package/dist/queries/schemas-local-model.d.ts.map +1 -0
  242. package/dist/queries/schemas-local-model.js +57 -0
  243. package/dist/queries/schemas-local-model.js.map +1 -0
  244. package/dist/queries/schemas.d.ts +14 -32
  245. package/dist/queries/schemas.d.ts.map +1 -1
  246. package/dist/queries/schemas.js +24 -41
  247. package/dist/queries/schemas.js.map +1 -1
  248. package/dist/queries/sessions.d.ts +120 -12
  249. package/dist/queries/sessions.d.ts.map +1 -1
  250. package/dist/queries/sessions.js +104 -11
  251. package/dist/queries/sessions.js.map +1 -1
  252. package/dist/queries/shared.d.ts +1 -0
  253. package/dist/queries/shared.d.ts.map +1 -1
  254. package/dist/queries/shared.js +1 -1
  255. package/dist/queries/shared.js.map +1 -1
  256. package/dist/queries/system-session.d.ts.map +1 -1
  257. package/dist/queries/system-session.js +3 -1
  258. package/dist/queries/system-session.js.map +1 -1
  259. package/dist/queries/verification.js +1 -1
  260. package/dist/queries/workflows.d.ts.map +1 -1
  261. package/dist/queries/workflows.js +1 -0
  262. package/dist/queries/workflows.js.map +1 -1
  263. package/dist/resolvers/assembly-engine.d.ts +33 -1
  264. package/dist/resolvers/assembly-engine.d.ts.map +1 -1
  265. package/dist/resolvers/assembly-engine.js +74 -23
  266. package/dist/resolvers/assembly-engine.js.map +1 -1
  267. package/dist/resolvers/compose/loop-delivery-index-compose.d.ts +23 -3
  268. package/dist/resolvers/compose/loop-delivery-index-compose.d.ts.map +1 -1
  269. package/dist/resolvers/compose/loop-delivery-index-compose.js +90 -43
  270. package/dist/resolvers/compose/loop-delivery-index-compose.js.map +1 -1
  271. package/dist/resolvers/delivery-listing.d.ts +27 -0
  272. package/dist/resolvers/delivery-listing.d.ts.map +1 -1
  273. package/dist/resolvers/delivery-listing.js +52 -36
  274. package/dist/resolvers/delivery-listing.js.map +1 -1
  275. package/dist/resolvers/loop-delivery-index.d.ts +8 -0
  276. package/dist/resolvers/loop-delivery-index.d.ts.map +1 -1
  277. package/dist/resolvers/loop-delivery-index.js +68 -7
  278. package/dist/resolvers/loop-delivery-index.js.map +1 -1
  279. package/dist/resolvers/shared/loop.d.ts +7 -0
  280. package/dist/resolvers/shared/loop.d.ts.map +1 -1
  281. package/dist/resolvers/shared/loop.js.map +1 -1
  282. package/dist/resolvers/wiki-topic.d.ts +6 -5
  283. package/dist/resolvers/wiki-topic.d.ts.map +1 -1
  284. package/dist/resolvers/wiki-topic.js +43 -27
  285. package/dist/resolvers/wiki-topic.js.map +1 -1
  286. package/dist/session/session-lineage.d.ts.map +1 -1
  287. package/dist/session/session-lineage.js +1 -0
  288. package/dist/session/session-lineage.js.map +1 -1
  289. package/dist/shell/crash-recovery.d.ts +4 -2
  290. package/dist/shell/crash-recovery.d.ts.map +1 -1
  291. package/dist/shell/crash-recovery.js +15 -14
  292. package/dist/shell/crash-recovery.js.map +1 -1
  293. package/dist/shell/daemon-instance.d.ts +176 -0
  294. package/dist/shell/daemon-instance.d.ts.map +1 -0
  295. package/dist/shell/daemon-instance.js +281 -0
  296. package/dist/shell/daemon-instance.js.map +1 -0
  297. package/dist/shell/graceful-shutdown.d.ts +12 -1
  298. package/dist/shell/graceful-shutdown.d.ts.map +1 -1
  299. package/dist/shell/graceful-shutdown.js +7 -4
  300. package/dist/shell/graceful-shutdown.js.map +1 -1
  301. package/dist/shell/shutdown-ownership.d.ts +34 -0
  302. package/dist/shell/shutdown-ownership.d.ts.map +1 -0
  303. package/dist/shell/shutdown-ownership.js +51 -0
  304. package/dist/shell/shutdown-ownership.js.map +1 -0
  305. package/dist/shell/unified-shell.d.ts.map +1 -1
  306. package/dist/shell/unified-shell.js +12 -5
  307. package/dist/shell/unified-shell.js.map +1 -1
  308. package/dist/shell/worker-pidfile.d.ts +127 -11
  309. package/dist/shell/worker-pidfile.d.ts.map +1 -1
  310. package/dist/shell/worker-pidfile.js +349 -58
  311. package/dist/shell/worker-pidfile.js.map +1 -1
  312. package/dist/spawn/helpers.d.ts.map +1 -1
  313. package/dist/spawn/helpers.js +4 -2
  314. package/dist/spawn/helpers.js.map +1 -1
  315. package/dist/spawn/lead-continuation.d.ts.map +1 -1
  316. package/dist/spawn/lead-continuation.js +8 -2
  317. package/dist/spawn/lead-continuation.js.map +1 -1
  318. package/dist/spawn/lead-launch.d.ts +17 -1
  319. package/dist/spawn/lead-launch.d.ts.map +1 -1
  320. package/dist/spawn/lead-launch.js +56 -7
  321. package/dist/spawn/lead-launch.js.map +1 -1
  322. package/dist/spawn/lead-process-ownership.d.ts +5 -4
  323. package/dist/spawn/lead-process-ownership.d.ts.map +1 -1
  324. package/dist/spawn/lead-process-ownership.js +7 -4
  325. package/dist/spawn/lead-process-ownership.js.map +1 -1
  326. package/dist/spawn/lead-send.d.ts +97 -0
  327. package/dist/spawn/lead-send.d.ts.map +1 -0
  328. package/dist/spawn/lead-send.js +80 -0
  329. package/dist/spawn/lead-send.js.map +1 -0
  330. package/dist/spawn/lifecycle.d.ts +0 -12
  331. package/dist/spawn/lifecycle.d.ts.map +1 -1
  332. package/dist/spawn/lifecycle.js +76 -19
  333. package/dist/spawn/lifecycle.js.map +1 -1
  334. package/dist/spawn/liveness.d.ts.map +1 -1
  335. package/dist/spawn/liveness.js +3 -1
  336. package/dist/spawn/liveness.js.map +1 -1
  337. package/dist/spawn/one-shot-process-ownership.d.ts +46 -2
  338. package/dist/spawn/one-shot-process-ownership.d.ts.map +1 -1
  339. package/dist/spawn/one-shot-process-ownership.js +78 -3
  340. package/dist/spawn/one-shot-process-ownership.js.map +1 -1
  341. package/dist/spawn/process-group-kill.d.ts.map +1 -1
  342. package/dist/spawn/process-group-kill.js +15 -2
  343. package/dist/spawn/process-group-kill.js.map +1 -1
  344. package/dist/spawn/process-spawn.d.ts +5 -15
  345. package/dist/spawn/process-spawn.d.ts.map +1 -1
  346. package/dist/spawn/process-spawn.js +8 -1
  347. package/dist/spawn/process-spawn.js.map +1 -1
  348. package/dist/spawn/prompt-assembly.d.ts +35 -2
  349. package/dist/spawn/prompt-assembly.d.ts.map +1 -1
  350. package/dist/spawn/prompt-assembly.js +67 -44
  351. package/dist/spawn/prompt-assembly.js.map +1 -1
  352. package/dist/spawn/spawn-decision.d.ts +25 -1
  353. package/dist/spawn/spawn-decision.d.ts.map +1 -1
  354. package/dist/spawn/spawn-decision.js +19 -3
  355. package/dist/spawn/spawn-decision.js.map +1 -1
  356. package/dist/spawn/worktree-session.d.ts +49 -2
  357. package/dist/spawn/worktree-session.d.ts.map +1 -1
  358. package/dist/spawn/worktree-session.js +59 -5
  359. package/dist/spawn/worktree-session.js.map +1 -1
  360. package/dist/testing/fixtures/conflicting-repo.d.ts +30 -0
  361. package/dist/testing/fixtures/conflicting-repo.d.ts.map +1 -0
  362. package/dist/testing/fixtures/conflicting-repo.js +89 -0
  363. package/dist/testing/fixtures/conflicting-repo.js.map +1 -0
  364. package/dist/testing/fixtures/index.d.ts.map +1 -1
  365. package/dist/testing/fixtures/index.js +2 -1
  366. package/dist/testing/fixtures/index.js.map +1 -1
  367. package/dist/testing/fixtures/pending-spawn-world.js +2 -2
  368. package/dist/testing/fixtures/pending-spawn-world.js.map +1 -1
  369. package/dist/types/exit-category.d.ts +59 -0
  370. package/dist/types/exit-category.d.ts.map +1 -0
  371. package/dist/types/exit-category.js +133 -0
  372. package/dist/types/exit-category.js.map +1 -0
  373. package/dist/types/focus.d.ts +19 -0
  374. package/dist/types/focus.d.ts.map +1 -1
  375. package/dist/types/index.js +1 -1
  376. package/dist/types/session.d.ts +5 -1
  377. package/dist/types/session.d.ts.map +1 -1
  378. package/dist/types/session.js +1 -1
  379. package/dist/types/session.js.map +1 -1
  380. package/dist/verify/injection-stamp.d.ts +30 -0
  381. package/dist/verify/injection-stamp.d.ts.map +1 -1
  382. package/dist/verify/injection-stamp.js +37 -1
  383. package/dist/verify/injection-stamp.js.map +1 -1
  384. package/dist/verify/recorded-targets.d.ts +103 -0
  385. package/dist/verify/recorded-targets.d.ts.map +1 -0
  386. package/dist/verify/recorded-targets.js +159 -0
  387. package/dist/verify/recorded-targets.js.map +1 -0
  388. package/dist/verify/stall-detector-config.d.ts +5 -1
  389. package/dist/verify/stall-detector-config.d.ts.map +1 -1
  390. package/dist/verify/stall-detector-config.js +5 -1
  391. package/dist/verify/stall-detector-config.js.map +1 -1
  392. package/dist/verify/verification-deps-default.d.ts.map +1 -1
  393. package/dist/verify/verification-deps-default.js +15 -2
  394. package/dist/verify/verification-deps-default.js.map +1 -1
  395. package/dist/verify/verification-deps.d.ts +17 -1
  396. package/dist/verify/verification-deps.d.ts.map +1 -1
  397. package/dist/verify/verification-engine.d.ts.map +1 -1
  398. package/dist/verify/verification-engine.js +51 -47
  399. package/dist/verify/verification-engine.js.map +1 -1
  400. package/dist/verify/verify-restatement.d.ts +27 -33
  401. package/dist/verify/verify-restatement.d.ts.map +1 -1
  402. package/dist/verify/verify-restatement.js +74 -1
  403. package/dist/verify/verify-restatement.js.map +1 -1
  404. package/dist/worktree/state.d.ts +75 -1
  405. package/dist/worktree/state.d.ts.map +1 -1
  406. package/dist/worktree/state.js +89 -17
  407. package/dist/worktree/state.js.map +1 -1
  408. package/package.json +3 -3
@@ -0,0 +1,176 @@
1
+ import type { AgentSession } from '../types/index.js';
2
+ /** The parts of a parsed instance id. */
3
+ export interface DaemonInstance {
4
+ hostname: string;
5
+ pid: number;
6
+ /** Kernel start time of the daemon pid; null when the platform cannot supply one. */
7
+ startIdentity: string | null;
8
+ }
9
+ /**
10
+ * This daemon instance's id. Stamped onto every session it creates, and the
11
+ * identity its own crash recovery compares against.
12
+ */
13
+ export declare function daemonInstanceId(): string;
14
+ /** Reset the memo. Test seam only -- a daemon's identity never changes in life. */
15
+ export declare function resetDaemonInstanceIdCache(): void;
16
+ /**
17
+ * Parse an instance id, or null when it is absent or malformed. A row written
18
+ * by a daemon predating this column parses to null and is treated as
19
+ * unattributable -- never as "gone".
20
+ */
21
+ export declare function parseDaemonInstanceId(id: string | null | undefined): DaemonInstance | null;
22
+ /**
23
+ * What a session row's owning instance is doing now.
24
+ *
25
+ * - `self` -- this daemon owns it. Never reap: the team is ours and live.
26
+ * - `peer_alive` -- another daemon instance on this host is still running. Its
27
+ * sessions are its business; reaping them is the defect.
28
+ * - `gone` -- the owning instance is not running. Its teams stopped with
29
+ * it, so its rows are safe to close.
30
+ * - `unknown` -- no id, a malformed id, or an id from another host whose pid
31
+ * we cannot probe. FAILS SAFE: not reaped.
32
+ */
33
+ export type InstanceLiveness = 'self' | 'peer_alive' | 'gone' | 'unknown';
34
+ /** Probes `classifyOwningInstance` reaches, injectable for tests. */
35
+ export interface InstanceProbeDeps {
36
+ selfId: string;
37
+ selfHostname: string;
38
+ isProcessAlive: (pid: number) => boolean;
39
+ readStartIdentity: (pid: number) => string | null;
40
+ }
41
+ /** Real probes against this host. */
42
+ export declare function defaultInstanceProbes(): InstanceProbeDeps;
43
+ /**
44
+ * Classify the instance that owns a session row.
45
+ *
46
+ * Every branch that is not `gone` leaves the row alone, because only `gone` is
47
+ * positive evidence that the team the row represents has stopped. Recovery
48
+ * would rather leave a stale row than falsify a live one: a stale `running` row
49
+ * is visible and self-correcting on the next sweep, whereas a live session
50
+ * marked `failed` silently tells every reader -- including "is this worktree
51
+ * being written?" -- that a running team is not there.
52
+ */
53
+ export declare function classifyOwningInstance(instanceId: string | null | undefined, deps: InstanceProbeDeps): InstanceLiveness;
54
+ /**
55
+ * What the SESSION's own process is doing -- the second proof, about the team
56
+ * rather than the daemon that spawned it.
57
+ *
58
+ * - `alive` -- this session's recorded lead is still running.
59
+ * - `stopped` -- it is not: gone, or its pid was recycled onto an
60
+ * unrelated process.
61
+ * - `no_process_recorded` -- the host's lead record is readable and names no
62
+ * lead for this session, so none was ever spawned.
63
+ * - `unprovable` -- nothing can be shown: the record could not be read,
64
+ * or it names a process whose identity cannot be
65
+ * verified (a reservation, a pid with no recorded or
66
+ * no readable start identity). An anonymous live pid
67
+ * is NOT an alive team.
68
+ */
69
+ export type SessionProcessLiveness = 'alive' | 'stopped' | 'no_process_recorded' | 'unprovable';
70
+ /** Process probes for the session-liveness check, injectable for tests. */
71
+ export interface SessionProcessProbes {
72
+ isProcessAlive: (pid: number) => boolean;
73
+ isProcessGroupAlive: (pgid: number) => boolean;
74
+ readStartIdentity: (pid: number) => string | null;
75
+ /**
76
+ * The host's durable lead records by session id, or null when they cannot be
77
+ * read. Null is UNKNOWN, never "nothing is running".
78
+ */
79
+ leadRecordsBySession: () => Map<string, {
80
+ pid: number;
81
+ startIdentity?: string;
82
+ pending?: true;
83
+ }> | null;
84
+ }
85
+ /**
86
+ * Real probes against this host. The lead-record read is memoized PER PROBE
87
+ * OBJECT: a sweep classifying many rows reads the file once, and every row in
88
+ * that sweep is judged against the same snapshot rather than a record that may
89
+ * change between rows.
90
+ */
91
+ export declare function defaultSessionProcessProbes(): SessionProcessProbes;
92
+ /**
93
+ * What this record's process is doing: running, gone, or not decidable.
94
+ *
95
+ * The third answer is the one that was missing. A live pid ALONE does not
96
+ * identify our lead, so treating "alive" as proof let an identity-less record
97
+ * -- or one whose identity could not be read -- nominate whatever process
98
+ * happens to hold that number as the session's writer. That is how a stale row
99
+ * keeps reading `running` and invents a writer for a quiescent worktree.
100
+ *
101
+ * So: alive AND verifiably ours -> `alive`. Provably absent -> `gone`. Anything
102
+ * in between -- no recorded identity, an unreadable live identity, a pid we
103
+ * cannot probe -- is `indeterminate`, and each caller applies its OWN safe
104
+ * direction to it rather than inheriting one that only suits recovery.
105
+ */
106
+ export type RecordedProcessState = 'alive' | 'gone' | 'indeterminate';
107
+ export declare function classifyRecordedProcess(record: {
108
+ pid: number;
109
+ startIdentity?: string;
110
+ pending?: true;
111
+ }, probes: SessionProcessProbes): RecordedProcessState;
112
+ /**
113
+ * Classify the session's own process, from the host's DURABLE LEAD RECORD rather
114
+ * than from the session row's pid.
115
+ *
116
+ * The row's pid cannot answer this, for two reasons found in review:
117
+ *
118
+ * 1. It arrives TOO LATE. `launchLeadProcess` spawns the detached lead and
119
+ * records it, and only afterwards does `spawnTeamProcess` persist
120
+ * `{ status: 'running', pid }`. A hard kill or a failed API write in that
121
+ * window leaves a `starting` row with a null pid AND a live team. Reading
122
+ * that as "no process was spawned" closes a row whose team is running.
123
+ *
124
+ * 2. It is UNVERIFIABLE. A bare pid says nothing about WHICH process holds it
125
+ * now, so an unrelated process that recycled the number would keep a stale
126
+ * row `running` -- and `isWorktreeBeingWritten` would report a writer that
127
+ * does not exist.
128
+ *
129
+ * The lead record fixes both: it is written BEFORE the row's pid, and it carries
130
+ * the spawn-time start identity. Its absence is only evidence when the record
131
+ * itself is readable; an unreadable record is `unprovable`, never `stopped`.
132
+ */
133
+ export declare function classifySessionProcess(sessionId: string, probes: SessionProcessProbes): SessionProcessLiveness;
134
+ /**
135
+ * Split the scoped candidates into the ones recovery may close and the ones it
136
+ * must leave alone, with the reason it spared each group.
137
+ *
138
+ * TWO proofs are required, and a row is closed only when both agree.
139
+ *
140
+ * 1. Its OWNER is gone. The host scope answers "whose machine", but a machine
141
+ * is not an owner: two daemon instances share a host. Our own live session,
142
+ * a peer instance still running, an unattributable or remote id -- all spared.
143
+ *
144
+ * 2. Its own PROCESS is gone. A dead owner does NOT imply a stopped team: team
145
+ * leads are spawned detached and survive a hard-killed daemon, and the boot
146
+ * reap that ends them is best-effort by design (a missing or malformed
147
+ * pidfile yields no entries; a failed kill is swallowed). So a live team
148
+ * whose daemon was `kill -9`ed reaches this pass, and closing its row is
149
+ * exactly the falsification the criterion forbids.
150
+ *
151
+ * The asymmetry is deliberate. A stale `running` row is visible and the next
152
+ * sweep re-examines it. A LIVE session marked `failed` is invisible and wrong:
153
+ * it tells every reader -- the dashboard, and `isWorktreeBeingWritten` -- that a
154
+ * running team is not there, which is how a worktree under active write comes to
155
+ * read as quiescent. This pass never kills anything to make its own proof true;
156
+ * ending a surviving orphan belongs to the identity-verified reap paths
157
+ * (`reapAndClearWorkerPidfile`, the untracked-lead sweep), and once they do, the
158
+ * next sweep reads the process as stopped and closes the row.
159
+ */
160
+ export declare function partitionReapableSessions(sessions: readonly AgentSession[], probes: InstanceProbeDeps, processProbes?: SessionProcessProbes): {
161
+ reapable: AgentSession[];
162
+ spared: Map<string, string[]>;
163
+ };
164
+ /** Where the two probe families come from; real host probes when unsupplied. */
165
+ export interface RecoveryProbeSource {
166
+ instanceProbes?: () => InstanceProbeDeps;
167
+ sessionProcessProbes?: () => SessionProcessProbes;
168
+ }
169
+ /**
170
+ * The subset of `sessions` crash recovery may close, logging what it spared and
171
+ * why. The one call recovery makes: everything it returns has a provably
172
+ * stopped owner, and everything it withheld is named in the log rather than
173
+ * silently dropped.
174
+ */
175
+ export declare function reconcileOwnedSessions(sessions: readonly AgentSession[], probeSource?: RecoveryProbeSource): AgentSession[];
176
+ //# sourceMappingURL=daemon-instance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-instance.d.ts","sourceRoot":"","sources":["../../src/shell/daemon-instance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,281 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/queries/session-create-ownership.test.ts,
3
+ // packages/daemon/src/shell/crash-recovery-session-scope.test.ts,
4
+ // packages/daemon/src/shell/daemon-instance.test.ts, packages/daemon/src/worktree/worktree-state.test.ts
5
+ // PINNED-BY-END
6
+ /**
7
+ * Daemon INSTANCE identity -- which running daemon owns a session row.
8
+ *
9
+ * `daemon_hostname` answers "which machine", which is enough to keep one host's
10
+ * crash recovery off another host's live work. It is NOT enough to separate two
11
+ * daemon instances on the SAME host and product: to that scope they are the
12
+ * same owner, so one's recovery sweep would mark the other's running sessions
13
+ * failed. It is also not enough to answer the question recovery actually has to
14
+ * answer before it writes `failed` -- has the team this row represents actually
15
+ * stopped? -- because a hostname says nothing about liveness.
16
+ *
17
+ * An instance id is `<hostname>:<pid>:<startIdentity>`:
18
+ *
19
+ * - `pid` is the daemon process, comparable on its own host.
20
+ * - `startIdentity` is the kernel's start time for that pid (/proc stat field
21
+ * 22 on Linux). It makes the id PID-REUSE SAFE: a new process that happens
22
+ * to land on a dead daemon's pid has a different start time, so it is not
23
+ * mistaken for the instance that owned those rows.
24
+ *
25
+ * The id is therefore the thing crash recovery can probe: "is the daemon that
26
+ * wrote this row still running?" A row whose owner is alive, or whose owner
27
+ * cannot be determined, is left alone.
28
+ *
29
+ * A dead owner is NOT on its own proof that the row's team stopped. Team leads
30
+ * are spawned DETACHED, so they survive a hard-killed daemon; the boot reap
31
+ * that ends them (`reapAndClearWorkerPidfile`, unified-shell) is explicitly
32
+ * best-effort -- a missing or malformed pidfile yields no entries and a failed
33
+ * kill is swallowed. So the destructive pass needs a SECOND proof, about the
34
+ * session's own process: `classifySessionProcess`. Both must agree before a row
35
+ * is closed.
36
+ *
37
+ * @module shell/daemon-instance
38
+ */
39
+ import { hostname } from 'node:os';
40
+ import { createLogger } from './log.js';
41
+ import { isProcessAlive } from '../spawn/liveness.js';
42
+ import { readProcessStartIdentity, isProcessGroupAlive } from '../spawn/process-group-kill.js';
43
+ import { readLeadRecordsBySession } from './worker-pidfile.js';
44
+ const log = createLogger({ module: 'daemon-instance' });
45
+ /** Separator between instance-id parts. Hostnames cannot contain ':'. */
46
+ const SEP = ':';
47
+ /** Memoized so every read within one daemon run returns the same identity. */
48
+ let cachedId = null;
49
+ /**
50
+ * This daemon instance's id. Stamped onto every session it creates, and the
51
+ * identity its own crash recovery compares against.
52
+ */
53
+ export function daemonInstanceId() {
54
+ if (cachedId === null) {
55
+ const start = readProcessStartIdentity(process.pid);
56
+ cachedId = [hostname(), String(process.pid), start ?? 'unknown'].join(SEP);
57
+ }
58
+ return cachedId;
59
+ }
60
+ /** Reset the memo. Test seam only -- a daemon's identity never changes in life. */
61
+ export function resetDaemonInstanceIdCache() {
62
+ cachedId = null;
63
+ }
64
+ /**
65
+ * Parse an instance id, or null when it is absent or malformed. A row written
66
+ * by a daemon predating this column parses to null and is treated as
67
+ * unattributable -- never as "gone".
68
+ */
69
+ export function parseDaemonInstanceId(id) {
70
+ if (typeof id !== 'string' || id.length === 0)
71
+ return null;
72
+ const parts = id.split(SEP);
73
+ if (parts.length !== 3)
74
+ return null;
75
+ const [host, pidText, start] = parts;
76
+ if (!host || !pidText)
77
+ return null;
78
+ // STRICT: Number.parseInt('200junk') is 200, which would have us probe an
79
+ // unintended pid on the strength of a malformed id. Require all digits.
80
+ if (!/^[0-9]+$/.test(pidText))
81
+ return null;
82
+ const pid = Number.parseInt(pidText, 10);
83
+ if (!Number.isInteger(pid) || pid <= 0)
84
+ return null;
85
+ return { hostname: host, pid, startIdentity: start === 'unknown' ? null : (start ?? null) };
86
+ }
87
+ /** Real probes against this host. */
88
+ export function defaultInstanceProbes() {
89
+ return {
90
+ selfId: daemonInstanceId(),
91
+ selfHostname: hostname(),
92
+ isProcessAlive,
93
+ readStartIdentity: readProcessStartIdentity,
94
+ };
95
+ }
96
+ /**
97
+ * Classify the instance that owns a session row.
98
+ *
99
+ * Every branch that is not `gone` leaves the row alone, because only `gone` is
100
+ * positive evidence that the team the row represents has stopped. Recovery
101
+ * would rather leave a stale row than falsify a live one: a stale `running` row
102
+ * is visible and self-correcting on the next sweep, whereas a live session
103
+ * marked `failed` silently tells every reader -- including "is this worktree
104
+ * being written?" -- that a running team is not there.
105
+ */
106
+ export function classifyOwningInstance(instanceId, deps) {
107
+ if (instanceId === deps.selfId)
108
+ return 'self';
109
+ const parsed = parseDaemonInstanceId(instanceId);
110
+ if (parsed === null)
111
+ return 'unknown';
112
+ // A pid from another machine is not a pid we can probe. The host scope should
113
+ // already have excluded it; if one arrives anyway, it is unknown, not gone.
114
+ if (parsed.hostname !== deps.selfHostname)
115
+ return 'unknown';
116
+ if (!deps.isProcessAlive(parsed.pid))
117
+ return 'gone';
118
+ // The pid is alive -- but is it the SAME process, or a reused pid? A start
119
+ // identity that disagrees means the original instance died and its pid was
120
+ // recycled, so the owner IS gone.
121
+ const liveStart = deps.readStartIdentity(parsed.pid);
122
+ if (parsed.startIdentity !== null && liveStart !== null && parsed.startIdentity !== liveStart) {
123
+ return 'gone';
124
+ }
125
+ // Alive, and nothing contradicts that it is the owner. Leave it.
126
+ return 'peer_alive';
127
+ }
128
+ /**
129
+ * Real probes against this host. The lead-record read is memoized PER PROBE
130
+ * OBJECT: a sweep classifying many rows reads the file once, and every row in
131
+ * that sweep is judged against the same snapshot rather than a record that may
132
+ * change between rows.
133
+ */
134
+ export function defaultSessionProcessProbes() {
135
+ let cached;
136
+ return {
137
+ isProcessAlive,
138
+ isProcessGroupAlive,
139
+ readStartIdentity: readProcessStartIdentity,
140
+ leadRecordsBySession: () => {
141
+ if (cached === undefined)
142
+ cached = readLeadRecordsBySession();
143
+ return cached;
144
+ },
145
+ };
146
+ }
147
+ export function classifyRecordedProcess(record, probes) {
148
+ // A RESERVATION: the record exists because a spawn was about to happen. It
149
+ // names no pid yet, so nothing about it is decidable.
150
+ if (record.pending)
151
+ return 'indeterminate';
152
+ const { pid } = record;
153
+ if (!Number.isInteger(pid) || pid <= 1)
154
+ return 'indeterminate';
155
+ // A lead may be detached (its pid is a pgid) or the in-process teammate (not a
156
+ // group leader, only the bare pid answers) -- the same ambiguity
157
+ // spawn/lifecycle.ts handles when it signals a worker. Ask both.
158
+ if (!probes.isProcessGroupAlive(pid) && !probes.isProcessAlive(pid))
159
+ return 'gone';
160
+ // Alive -- but is it the SAME process? Only a matching start identity settles
161
+ // that. Without one recorded, or without one readable now, the pid is alive
162
+ // and anonymous: it may be our lead or may be anything the OS put there.
163
+ if (!record.startIdentity)
164
+ return 'indeterminate';
165
+ const live = probes.readStartIdentity(pid);
166
+ if (live === null)
167
+ return 'indeterminate';
168
+ return live === record.startIdentity ? 'alive' : 'gone';
169
+ }
170
+ /**
171
+ * Classify the session's own process, from the host's DURABLE LEAD RECORD rather
172
+ * than from the session row's pid.
173
+ *
174
+ * The row's pid cannot answer this, for two reasons found in review:
175
+ *
176
+ * 1. It arrives TOO LATE. `launchLeadProcess` spawns the detached lead and
177
+ * records it, and only afterwards does `spawnTeamProcess` persist
178
+ * `{ status: 'running', pid }`. A hard kill or a failed API write in that
179
+ * window leaves a `starting` row with a null pid AND a live team. Reading
180
+ * that as "no process was spawned" closes a row whose team is running.
181
+ *
182
+ * 2. It is UNVERIFIABLE. A bare pid says nothing about WHICH process holds it
183
+ * now, so an unrelated process that recycled the number would keep a stale
184
+ * row `running` -- and `isWorktreeBeingWritten` would report a writer that
185
+ * does not exist.
186
+ *
187
+ * The lead record fixes both: it is written BEFORE the row's pid, and it carries
188
+ * the spawn-time start identity. Its absence is only evidence when the record
189
+ * itself is readable; an unreadable record is `unprovable`, never `stopped`.
190
+ */
191
+ export function classifySessionProcess(sessionId, probes) {
192
+ const records = probes.leadRecordsBySession();
193
+ if (records === null)
194
+ return 'unprovable';
195
+ const record = records.get(sessionId);
196
+ if (record === undefined)
197
+ return 'no_process_recorded';
198
+ switch (classifyRecordedProcess(record, probes)) {
199
+ case 'alive': return 'alive';
200
+ case 'gone': return 'stopped';
201
+ default: return 'unprovable';
202
+ }
203
+ }
204
+ /**
205
+ * Split the scoped candidates into the ones recovery may close and the ones it
206
+ * must leave alone, with the reason it spared each group.
207
+ *
208
+ * TWO proofs are required, and a row is closed only when both agree.
209
+ *
210
+ * 1. Its OWNER is gone. The host scope answers "whose machine", but a machine
211
+ * is not an owner: two daemon instances share a host. Our own live session,
212
+ * a peer instance still running, an unattributable or remote id -- all spared.
213
+ *
214
+ * 2. Its own PROCESS is gone. A dead owner does NOT imply a stopped team: team
215
+ * leads are spawned detached and survive a hard-killed daemon, and the boot
216
+ * reap that ends them is best-effort by design (a missing or malformed
217
+ * pidfile yields no entries; a failed kill is swallowed). So a live team
218
+ * whose daemon was `kill -9`ed reaches this pass, and closing its row is
219
+ * exactly the falsification the criterion forbids.
220
+ *
221
+ * The asymmetry is deliberate. A stale `running` row is visible and the next
222
+ * sweep re-examines it. A LIVE session marked `failed` is invisible and wrong:
223
+ * it tells every reader -- the dashboard, and `isWorktreeBeingWritten` -- that a
224
+ * running team is not there, which is how a worktree under active write comes to
225
+ * read as quiescent. This pass never kills anything to make its own proof true;
226
+ * ending a surviving orphan belongs to the identity-verified reap paths
227
+ * (`reapAndClearWorkerPidfile`, the untracked-lead sweep), and once they do, the
228
+ * next sweep reads the process as stopped and closes the row.
229
+ */
230
+ export function partitionReapableSessions(sessions, probes, processProbes = defaultSessionProcessProbes()) {
231
+ const reapable = [];
232
+ const spared = new Map();
233
+ const spare = (reason, id) => {
234
+ const ids = spared.get(reason) ?? [];
235
+ ids.push(id);
236
+ spared.set(reason, ids);
237
+ };
238
+ for (const session of sessions) {
239
+ const owner = classifyOwningInstance(session.daemon_instance_id, probes);
240
+ if (owner === 'self') {
241
+ spare('owned by THIS running daemon', session.id);
242
+ continue;
243
+ }
244
+ if (owner === 'peer_alive') {
245
+ spare('owned by another daemon instance still running on this host', session.id);
246
+ continue;
247
+ }
248
+ if (owner === 'unknown') {
249
+ spare('owning daemon instance not identifiable -- cannot prove it stopped', session.id);
250
+ continue;
251
+ }
252
+ // owner === 'gone'. Now the second proof: did its team stop with it?
253
+ switch (classifySessionProcess(session.id, processProbes)) {
254
+ case 'stopped':
255
+ case 'no_process_recorded':
256
+ reapable.push(session);
257
+ break;
258
+ case 'alive':
259
+ spare("the session's own lead is still running -- a detached team outlived its daemon", session.id);
260
+ break;
261
+ case 'unprovable':
262
+ spare('the host lead record is unreadable -- cannot prove the team stopped', session.id);
263
+ break;
264
+ }
265
+ }
266
+ return { reapable, spared };
267
+ }
268
+ /**
269
+ * The subset of `sessions` crash recovery may close, logging what it spared and
270
+ * why. The one call recovery makes: everything it returns has a provably
271
+ * stopped owner, and everything it withheld is named in the log rather than
272
+ * silently dropped.
273
+ */
274
+ export function reconcileOwnedSessions(sessions, probeSource = {}) {
275
+ const { reapable, spared } = partitionReapableSessions(sessions, (probeSource.instanceProbes ?? defaultInstanceProbes)(), (probeSource.sessionProcessProbes ?? defaultSessionProcessProbes)());
276
+ for (const [why, ids] of spared) {
277
+ log.info(`[crash-recovery] Leaving ${ids.length} session(s) alone (${why}): ${ids.join(', ')}`);
278
+ }
279
+ return reapable;
280
+ }
281
+ //# sourceMappingURL=daemon-instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-instance.js","sourceRoot":"","sources":["../../src/shell/daemon-instance.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,18 @@ export interface GracefulShutdownDeps {
19
19
  /** Floor for the reap budget when the engine drain overran the stop budget. */
20
20
  reapMinBudgetMs: number;
21
21
  reapWorkerFleet: (budgetMs: number) => Promise<unknown>;
22
- clearWorkerPidfile: () => void;
22
+ /**
23
+ * Finalize this host's worker-ownership records: drop what is CONFIRMED GONE,
24
+ * keep the rest. The reap above is bounded and best-effort, so a survivor must
25
+ * keep its record for the next boot reap rather than being erased here.
26
+ *
27
+ * OPTIONAL, and that is the point: the shell used to pass this, which made
28
+ * "which function the shutdown clears with" a fact about the shell's source
29
+ * rather than about any run. The default is the production boundary
30
+ * (shell/shutdown-ownership.ts), so no caller chooses it and a test that omits
31
+ * it drives exactly what a real SIGTERM drives.
32
+ */
33
+ finalizeWorkerOwnership?: () => unknown;
23
34
  stopCircuitWedgeWatcher: () => void;
24
35
  stopOtlpReceiver: () => Promise<void>;
25
36
  stopTelemetryWriter: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"graceful-shutdown.d.ts","sourceRoot":"","sources":["../../src/shell/graceful-shutdown.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAuD,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAK1G,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,oFAAoF;AACpF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IACvC,QAAQ,EAAE;QAAE,QAAQ,IAAI,IAAI,CAAA;KAAE,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,wGAAwG;IACxG,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,sFAAsF;IACtF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,uBAAuB,EAAE,MAAM,IAAI,CAAC;IACpC,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IACnC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,kDAAkD;IAClD,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAMD,oEAAoE;AACpE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAoFpG;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,mFAAmF;IACnF,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,eAAe,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IAC3C,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CACxE;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CA0B9D"}
1
+ {"version":3,"file":"graceful-shutdown.d.ts","sourceRoot":"","sources":["../../src/shell/graceful-shutdown.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAuD,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAM1G,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,oFAAoF;AACpF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IACvC,QAAQ,EAAE;QAAE,QAAQ,IAAI,IAAI,CAAA;KAAE,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,wGAAwG;IACxG,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,sFAAsF;IACtF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD;;;;;;;;;;OAUG;IACH,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC;IACxC,uBAAuB,EAAE,MAAM,IAAI,CAAC;IACpC,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IACnC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,kDAAkD;IAClD,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAMD,oEAAoE;AACpE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAqFpG;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,mFAAmF;IACnF,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,eAAe,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IAC3C,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CACxE;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CA0B9D"}
@@ -1,5 +1,6 @@
1
1
  // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
- // Pinned by: packages/daemon/src/shell/graceful-shutdown.test.ts
2
+ // Pinned by: packages/daemon/src/shell/graceful-shutdown.test.ts,
3
+ // packages/daemon/src/shell/shutdown-ownership-wiring.test.ts
3
4
  // PINNED-BY-END
4
5
  /**
5
6
  * The unified daemon's graceful shutdown and the stop signals that run it.
@@ -33,6 +34,7 @@ import { daemonExitCode, isDaemonFailStopRequested } from './fail-stop.js';
33
34
  import { setDaemonShuttingDown } from './shutdown-state.js';
34
35
  import { clearStopRequest, decideStopAction, readStopRequest } from './stop-request.js';
35
36
  import { createLogger } from './log.js';
37
+ import { finalizeWorkerOwnership } from './shutdown-ownership.js';
36
38
  const log = createLogger({ module: 'telemetry' });
37
39
  function message(err) {
38
40
  return err instanceof Error ? err.message : String(err);
@@ -93,9 +95,10 @@ export function createGracefulShutdown(deps) {
93
95
  catch (err) {
94
96
  log.warn(`[reap] Worker-fleet reap failed: ${message(err)}`);
95
97
  }
96
- // The fleet is reaped -- clear the persisted lead pidfile so neither the CLI
97
- // nor the next boot acts on now-dead pids.
98
- deps.clearWorkerPidfile();
98
+ // The fleet is reaped -- drop the record of every worker CONFIRMED gone, so
99
+ // neither the CLI nor the next boot acts on a dead pid, and KEEP the record
100
+ // of anything that outlived the bounded reap so the next boot still can.
101
+ (deps.finalizeWorkerOwnership ?? (() => finalizeWorkerOwnership('graceful shutdown')))();
99
102
  // Phase 2.5: Stop telemetry capture (flush remaining buffered rows, close
100
103
  // the OTLP receiver socket) and tear down the Lacuna sidecar (SIGTERM).
101
104
  deps.stopCircuitWedgeWatcher();
@@ -1 +1 @@
1
- {"version":3,"file":"graceful-shutdown.js","sourceRoot":"","sources":["../../src/shell/graceful-shutdown.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graceful-shutdown.js","sourceRoot":"","sources":["../../src/shell/graceful-shutdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The ONE place a shutdown finalizes this host's worker-ownership records.
3
+ *
4
+ * The daemon has two shutdowns. The graceful one (shell/graceful-shutdown.ts)
5
+ * runs on SIGINT/SIGTERM; the auto-update one (`shutdownForUpdate` in
6
+ * shell/unified-shell.ts) runs before the re-exec. Both must leave the pidfile in
7
+ * the same state, and for the same reason: `reapWorkerFleet` is BOUNDED and
8
+ * best-effort, so a worker that outlived SIGKILL, one that could not be
9
+ * signalled, and a reservation whose spawn is in flight all survive it. Erasing
10
+ * the file here erases the record of exactly those -- right before the next boot
11
+ * reap, or the re-exec'd daemon, would have acted on them.
12
+ *
13
+ * Both paths used to reach for that themselves, and each could reach for the
14
+ * WRONG function: they called the unconditional `clearWorkerPidfile`, and the
15
+ * fix -- passing `clearWorkerPidfileIfAllGone` instead -- left the choice in
16
+ * place at two call sites, testable only by reading the source to see which
17
+ * name was written there. So the choice is gone: this is the only ownership
18
+ * finalization either path can perform, the graceful sequence takes it by
19
+ * default rather than as an injected dep, and worker-pidfile.ts no longer
20
+ * exports an unconditional clear for a call site to pick up.
21
+ *
22
+ * @module shell/shutdown-ownership
23
+ */
24
+ import { type WorkerPidEntry } from './worker-pidfile.js';
25
+ /**
26
+ * Drop the ownership records of every worker CONFIRMED gone, keep the rest.
27
+ *
28
+ * `context` names the shutdown for the log. Returns the survivors kept on disk,
29
+ * so a caller (and a test) can see what this host is still claiming. Never
30
+ * throws: a shutdown step that cannot finalize ownership must not prevent the
31
+ * rest of the teardown, and an unreadable record is kept, not declared empty.
32
+ */
33
+ export declare function finalizeWorkerOwnership(context: string): WorkerPidEntry[];
34
+ //# sourceMappingURL=shutdown-ownership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown-ownership.d.ts","sourceRoot":"","sources":["../../src/shell/shutdown-ownership.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKvF;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAWzE"}
@@ -0,0 +1,51 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/shell/shutdown-ownership-wiring.test.ts
3
+ // PINNED-BY-END
4
+ /**
5
+ * The ONE place a shutdown finalizes this host's worker-ownership records.
6
+ *
7
+ * The daemon has two shutdowns. The graceful one (shell/graceful-shutdown.ts)
8
+ * runs on SIGINT/SIGTERM; the auto-update one (`shutdownForUpdate` in
9
+ * shell/unified-shell.ts) runs before the re-exec. Both must leave the pidfile in
10
+ * the same state, and for the same reason: `reapWorkerFleet` is BOUNDED and
11
+ * best-effort, so a worker that outlived SIGKILL, one that could not be
12
+ * signalled, and a reservation whose spawn is in flight all survive it. Erasing
13
+ * the file here erases the record of exactly those -- right before the next boot
14
+ * reap, or the re-exec'd daemon, would have acted on them.
15
+ *
16
+ * Both paths used to reach for that themselves, and each could reach for the
17
+ * WRONG function: they called the unconditional `clearWorkerPidfile`, and the
18
+ * fix -- passing `clearWorkerPidfileIfAllGone` instead -- left the choice in
19
+ * place at two call sites, testable only by reading the source to see which
20
+ * name was written there. So the choice is gone: this is the only ownership
21
+ * finalization either path can perform, the graceful sequence takes it by
22
+ * default rather than as an injected dep, and worker-pidfile.ts no longer
23
+ * exports an unconditional clear for a call site to pick up.
24
+ *
25
+ * @module shell/shutdown-ownership
26
+ */
27
+ import { clearWorkerPidfileIfAllGone } from './worker-pidfile.js';
28
+ import { createLogger } from './log.js';
29
+ const log = createLogger({ module: 'shutdown-ownership' });
30
+ /**
31
+ * Drop the ownership records of every worker CONFIRMED gone, keep the rest.
32
+ *
33
+ * `context` names the shutdown for the log. Returns the survivors kept on disk,
34
+ * so a caller (and a test) can see what this host is still claiming. Never
35
+ * throws: a shutdown step that cannot finalize ownership must not prevent the
36
+ * rest of the teardown, and an unreadable record is kept, not declared empty.
37
+ */
38
+ export function finalizeWorkerOwnership(context) {
39
+ try {
40
+ const survivors = clearWorkerPidfileIfAllGone();
41
+ if (survivors.length > 0) {
42
+ log.info(`[${context}] ${survivors.length} worker record(s) kept for the next boot reap`);
43
+ }
44
+ return survivors;
45
+ }
46
+ catch (err) {
47
+ log.warn(`[${context}] could not finalize worker ownership: ${err?.message ?? String(err)}`);
48
+ return [];
49
+ }
50
+ }
51
+ //# sourceMappingURL=shutdown-ownership.js.map