@north-light/crouter 0.3.312 → 0.3.313

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 (495) hide show
  1. package/dist/api/client.d.ts +27 -30
  2. package/dist/api/client.js +80 -67
  3. package/dist/api/dto/broker-ops.d.ts +10 -20
  4. package/dist/api/dto/canvas.d.ts +1 -10
  5. package/dist/api/dto/health.d.ts +113 -0
  6. package/dist/api/dto/human-requests.d.ts +14 -3
  7. package/dist/api/dto/human.d.ts +2 -48
  8. package/dist/api/dto/human.js +3 -2
  9. package/dist/api/dto/inbox.d.ts +14 -0
  10. package/dist/api/dto/mail.d.ts +43 -0
  11. package/dist/api/dto/nodes.d.ts +11 -8
  12. package/dist/api/dto/recovery.d.ts +96 -0
  13. package/dist/api/dto/reviews.d.ts +2 -2
  14. package/dist/api/routes.d.ts +9 -6
  15. package/dist/api/routes.js +11 -11
  16. package/dist/build-root.d.ts +4 -0
  17. package/dist/build-root.js +11 -1
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
  20. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  22. package/dist/cli.js +10 -1
  23. package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
  24. package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
  25. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  26. package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
  27. package/dist/clients/attach/chrome/inbox-strip.js +17 -9
  28. package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
  29. package/dist/clients/attach/chrome/ticket-panel.js +77 -46
  30. package/dist/clients/attach/overlays/graph.js +6 -9
  31. package/dist/clients/attach/render/chat-view.d.ts +5 -4
  32. package/dist/clients/attach/render/chat-view.js +22 -13
  33. package/dist/clients/attach/render/page-block.d.ts +11 -7
  34. package/dist/clients/attach/render/page-block.js +64 -55
  35. package/dist/clients/attach/viewer.js +746 -735
  36. package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
  37. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
  38. package/dist/clients/inbox/controller.d.ts +42 -14
  39. package/dist/clients/inbox/controller.js +240 -124
  40. package/dist/clients/inbox/dto-adapt.d.ts +28 -0
  41. package/dist/clients/inbox/dto-adapt.js +93 -0
  42. package/dist/clients/inbox/page-adapter.d.ts +5 -4
  43. package/dist/clients/inbox/page-adapter.js +12 -12
  44. package/dist/clients/inbox/resolve.d.ts +10 -5
  45. package/dist/clients/inbox/resolve.js +13 -12
  46. package/dist/clients/inbox/review/review-client.d.ts +0 -1
  47. package/dist/clients/inbox/review/review-client.js +0 -4
  48. package/dist/clients/inbox/review-adapter.d.ts +2 -1
  49. package/dist/clients/inbox/review-adapter.js +3 -5
  50. package/dist/clients/inbox/surface.d.ts +2 -2
  51. package/dist/clients/inbox/surface.js +19 -8
  52. package/dist/clients/inbox/tui.d.ts +3 -3
  53. package/dist/commands/__tests__/human.test.js +31 -15
  54. package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
  55. package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
  56. package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
  57. package/dist/commands/api-client.d.ts +5 -4
  58. package/dist/commands/api-client.js +13 -7
  59. package/dist/commands/canvas-history/shared.js +1 -1
  60. package/dist/commands/canvas-history.js +1 -1
  61. package/dist/commands/canvas-roster.js +1 -1
  62. package/dist/commands/canvas.js +1 -2
  63. package/dist/commands/human/feedback.js +20 -17
  64. package/dist/commands/human/prompts.js +45 -36
  65. package/dist/commands/human/queue.js +73 -123
  66. package/dist/commands/human/review.js +6 -16
  67. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
  68. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
  69. package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
  70. package/dist/commands/node-inspect-artifacts.js +1 -1
  71. package/dist/commands/node-lifecycle-revive.d.ts +2 -2
  72. package/dist/commands/node-lifecycle-revive.js +9 -19
  73. package/dist/commands/pkg/plugin-manage.js +0 -1
  74. package/dist/commands/profile/show.js +4 -12
  75. package/dist/commands/surface-tmux-spread.js +1 -5
  76. package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
  77. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
  78. package/dist/commands/sys/branch.d.ts +2 -0
  79. package/dist/commands/sys/branch.js +19 -0
  80. package/dist/commands/sys/config.js +106 -33
  81. package/dist/commands/sys/daemon.d.ts +2 -0
  82. package/dist/commands/sys/daemon.js +130 -7
  83. package/dist/commands/sys/doctor.js +5 -15
  84. package/dist/commands/sys/migrate.js +21 -92
  85. package/dist/commands/sys/setup-settings-runner.js +4 -2
  86. package/dist/commands/sys.js +2 -15
  87. package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
  88. package/dist/core/__tests__/boot.test.js +32 -25
  89. package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
  90. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
  91. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
  92. package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
  93. package/dist/core/__tests__/canvas.test.js +18 -47
  94. package/dist/core/__tests__/child-death-wake.test.js +29 -7
  95. package/dist/core/__tests__/cli-help-contract.test.js +2 -0
  96. package/dist/core/__tests__/close.test.js +29 -10
  97. package/dist/core/__tests__/collectible.test.js +2 -1
  98. package/dist/core/__tests__/config-change-delta.test.js +2 -0
  99. package/dist/core/__tests__/context-intro.test.js +13 -1
  100. package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
  101. package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
  102. package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
  103. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
  104. package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
  105. package/dist/core/__tests__/daemon-boot.test.js +51 -14
  106. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  107. package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
  108. package/dist/core/__tests__/focuses.test.js +10 -5
  109. package/dist/core/__tests__/helpers/harness.js +50 -18
  110. package/dist/core/__tests__/history-inbox.test.js +12 -16
  111. package/dist/core/__tests__/human-action-delivery.test.js +48 -28
  112. package/dist/core/__tests__/human-actions.test.js +7 -3
  113. package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
  114. package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
  115. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
  116. package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
  117. package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
  118. package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
  119. package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
  120. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  121. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
  122. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  123. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  124. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  125. package/dist/core/__tests__/integration/revive.test.js +67 -21
  126. package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
  127. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  128. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  129. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  130. package/dist/core/__tests__/kickoff.test.js +36 -39
  131. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  132. package/dist/core/__tests__/lifecycle.test.js +2 -1
  133. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  134. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  135. package/dist/core/__tests__/migration.test.js +145 -31
  136. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  137. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  138. package/dist/core/__tests__/node-outcome.test.js +93 -26
  139. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
  141. package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
  142. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  143. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  144. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  145. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  146. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  147. package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
  148. package/dist/core/__tests__/prune.test.js +11 -5
  149. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  150. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  151. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  152. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  153. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  154. package/dist/core/__tests__/revive-all.test.js +13 -3
  155. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  156. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  157. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  158. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  159. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  160. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  161. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  162. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  163. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  164. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  165. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
  166. package/dist/core/__tests__/stop-guard.test.js +2 -1
  167. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  168. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
  169. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  170. package/dist/core/bootstrap.js +2 -0
  171. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  172. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  173. package/dist/core/broker-client/transport-socket.js +13 -5
  174. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  175. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  176. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  177. package/dist/core/canvas/attention.d.ts +2 -5
  178. package/dist/core/canvas/attention.js +15 -64
  179. package/dist/core/canvas/boot.d.ts +6 -7
  180. package/dist/core/canvas/boot.js +5 -9
  181. package/dist/core/canvas/canvas.d.ts +13 -109
  182. package/dist/core/canvas/canvas.js +140 -401
  183. package/dist/core/canvas/crons.d.ts +2 -0
  184. package/dist/core/canvas/crons.js +16 -15
  185. package/dist/core/canvas/daemon-owner.js +10 -0
  186. package/dist/core/canvas/db.d.ts +2 -2
  187. package/dist/core/canvas/db.js +2 -18
  188. package/dist/core/canvas/deliveries.d.ts +104 -0
  189. package/dist/core/canvas/deliveries.js +354 -0
  190. package/dist/core/canvas/executions.d.ts +29 -0
  191. package/dist/core/canvas/executions.js +68 -0
  192. package/dist/core/canvas/history.js +35 -35
  193. package/dist/core/canvas/index.d.ts +3 -1
  194. package/dist/core/canvas/index.js +3 -1
  195. package/dist/core/canvas/migrations.js +20 -12
  196. package/dist/core/canvas/nav-model.js +1 -3
  197. package/dist/core/canvas/nav-render.d.ts +4 -4
  198. package/dist/core/canvas/nav-render.js +11 -21
  199. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  200. package/dist/core/canvas/node-sql-migration.js +261 -0
  201. package/dist/core/canvas/paths.d.ts +2 -16
  202. package/dist/core/canvas/paths.js +2 -31
  203. package/dist/core/canvas/recovery.d.ts +76 -0
  204. package/dist/core/canvas/recovery.js +588 -0
  205. package/dist/core/canvas/remote-canvas-source.js +4 -0
  206. package/dist/core/canvas/render-source.d.ts +16 -27
  207. package/dist/core/canvas/render-source.js +16 -45
  208. package/dist/core/canvas/sessions.d.ts +33 -0
  209. package/dist/core/canvas/sessions.js +78 -0
  210. package/dist/core/canvas/types.d.ts +33 -33
  211. package/dist/core/canvas/types.js +2 -5
  212. package/dist/core/canvas/waits.d.ts +18 -0
  213. package/dist/core/canvas/waits.js +70 -0
  214. package/dist/core/canvas/worktrees.d.ts +40 -0
  215. package/dist/core/canvas/worktrees.js +93 -0
  216. package/dist/core/config.d.ts +17 -12
  217. package/dist/core/config.js +106 -155
  218. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  219. package/dist/core/feed/feed.d.ts +0 -6
  220. package/dist/core/feed/feed.js +68 -56
  221. package/dist/core/feed/inbox.d.ts +7 -81
  222. package/dist/core/feed/inbox.js +46 -378
  223. package/dist/core/feed/messages.d.ts +58 -0
  224. package/dist/core/feed/messages.js +217 -0
  225. package/dist/core/fs-utils.d.ts +4 -0
  226. package/dist/core/fs-utils.js +21 -1
  227. package/dist/core/human/answer.d.ts +1 -1
  228. package/dist/core/human/answer.js +1 -1
  229. package/dist/core/human/claim.d.ts +1 -1
  230. package/dist/core/human/claim.js +2 -4
  231. package/dist/core/human/component-docs.js +1 -1
  232. package/dist/core/human/feedback-companion.js +3 -2
  233. package/dist/core/human/feedback.d.ts +5 -8
  234. package/dist/core/human/feedback.js +57 -85
  235. package/dist/core/human/page-authoring.d.ts +30 -0
  236. package/dist/core/human/page-authoring.js +19 -0
  237. package/dist/core/human/requests.d.ts +136 -0
  238. package/dist/core/human/requests.js +515 -0
  239. package/dist/core/human/root.d.ts +0 -7
  240. package/dist/core/human/root.js +0 -17
  241. package/dist/core/human/scan.d.ts +2 -7
  242. package/dist/core/human/scan.js +48 -132
  243. package/dist/core/human/summary.d.ts +1 -1
  244. package/dist/core/human/summary.js +1 -1
  245. package/dist/core/human/tickets.d.ts +23 -83
  246. package/dist/core/human/tickets.js +92 -243
  247. package/dist/core/human/types.d.ts +5 -1
  248. package/dist/core/installed-plugins.js +8 -3
  249. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  250. package/dist/core/locked-file.d.ts +6 -3
  251. package/dist/core/locked-file.js +36 -9
  252. package/dist/core/memory/extensions.js +2 -0
  253. package/dist/core/preview-registry.js +0 -1
  254. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  255. package/dist/core/profiles/default-binding.d.ts +0 -4
  256. package/dist/core/profiles/default-binding.js +12 -58
  257. package/dist/core/profiles/env-store.js +8 -3
  258. package/dist/core/profiles/manifest.d.ts +2 -37
  259. package/dist/core/profiles/manifest.js +35 -161
  260. package/dist/core/profiles/select.js +3 -0
  261. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  262. package/dist/core/review/__tests__/comments.test.js +28 -10
  263. package/dist/core/review/__tests__/remap.test.js +28 -10
  264. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  265. package/dist/core/review/birth.d.ts +1 -1
  266. package/dist/core/review/birth.js +34 -5
  267. package/dist/core/review/comments.js +1 -1
  268. package/dist/core/review/document.js +1 -1
  269. package/dist/core/review/project.d.ts +5 -1
  270. package/dist/core/review/project.js +14 -36
  271. package/dist/core/review/realize.d.ts +3 -6
  272. package/dist/core/review/realize.js +51 -168
  273. package/dist/core/review/remap.js +1 -1
  274. package/dist/core/review/stage.d.ts +3 -2
  275. package/dist/core/review/stage.js +2 -3
  276. package/dist/core/review/store.d.ts +3 -21
  277. package/dist/core/review/store.js +140 -186
  278. package/dist/core/review/types.d.ts +0 -1
  279. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  280. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  281. package/dist/core/runtime/broker/auth-reload.js +9 -10
  282. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  283. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  284. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  285. package/dist/core/runtime/broker/extension-abort.js +11 -0
  286. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  287. package/dist/core/runtime/broker/fault-retry.js +142 -95
  288. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  289. package/dist/core/runtime/broker/inbox.js +4 -92
  290. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  291. package/dist/core/runtime/broker/rebind.js +21 -12
  292. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  293. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  294. package/dist/core/runtime/broker.js +28 -22
  295. package/dist/core/runtime/close.d.ts +3 -9
  296. package/dist/core/runtime/close.js +16 -27
  297. package/dist/core/runtime/fault.d.ts +3 -93
  298. package/dist/core/runtime/fault.js +9 -419
  299. package/dist/core/runtime/kickoff.d.ts +3 -6
  300. package/dist/core/runtime/kickoff.js +12 -18
  301. package/dist/core/runtime/launch.js +1 -1
  302. package/dist/core/runtime/lifecycle.js +40 -7
  303. package/dist/core/runtime/placement.js +2 -2
  304. package/dist/core/runtime/recycle.js +8 -2
  305. package/dist/core/runtime/reset.js +22 -6
  306. package/dist/core/runtime/revive.js +226 -218
  307. package/dist/core/runtime/spawn.js +14 -3
  308. package/dist/core/scoped-state/db.d.ts +7 -0
  309. package/dist/core/scoped-state/db.js +92 -0
  310. package/dist/core/scoped-state/migrate.d.ts +11 -0
  311. package/dist/core/scoped-state/migrate.js +198 -0
  312. package/dist/core/scoped-state/paths.d.ts +9 -0
  313. package/dist/core/scoped-state/paths.js +27 -0
  314. package/dist/core/scoped-state/profiles.d.ts +12 -0
  315. package/dist/core/scoped-state/profiles.js +62 -0
  316. package/dist/core/scoped-state/providers.d.ts +16 -0
  317. package/dist/core/scoped-state/providers.js +8 -0
  318. package/dist/core/scoped-state/schema.d.ts +3 -0
  319. package/dist/core/scoped-state/schema.js +26 -0
  320. package/dist/core/scoped-state/settings.d.ts +28 -0
  321. package/dist/core/scoped-state/settings.js +82 -0
  322. package/dist/core/subscription-state.d.ts +29 -48
  323. package/dist/core/subscription-state.js +176 -712
  324. package/dist/core/worktree-close.js +4 -4
  325. package/dist/core/worktree-mutation-async.js +2 -2
  326. package/dist/core/worktree-sweep.js +11 -16
  327. package/dist/core/worktree.js +6 -12
  328. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  329. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  330. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  331. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  332. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  333. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
  334. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  335. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  336. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  337. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  338. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  339. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  340. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  341. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  342. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  343. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
  344. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  345. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  346. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  347. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  348. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  349. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
  350. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  351. package/dist/daemon/api/bridge.js +1 -1
  352. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  353. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  354. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  355. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  356. package/dist/daemon/api/handlers/canvas.js +2 -44
  357. package/dist/daemon/api/handlers/daemon.js +41 -0
  358. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  359. package/dist/daemon/api/handlers/health.js +10 -5
  360. package/dist/daemon/api/handlers/human-requests.js +168 -188
  361. package/dist/daemon/api/handlers/human.js +3 -151
  362. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  363. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  364. package/dist/daemon/api/handlers/inbox.js +21 -120
  365. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  366. package/dist/daemon/api/handlers/messages.js +73 -15
  367. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  368. package/dist/daemon/api/handlers/nodes.js +7 -13
  369. package/dist/daemon/api/handlers/reviews.js +4 -29
  370. package/dist/daemon/api/map.d.ts +25 -3
  371. package/dist/daemon/api/map.js +84 -14
  372. package/dist/daemon/api/server.js +68 -9
  373. package/dist/daemon/control.d.ts +18 -0
  374. package/dist/daemon/cron/disposition.d.ts +2 -0
  375. package/dist/daemon/cron/disposition.js +8 -5
  376. package/dist/daemon/cron/settle.js +37 -13
  377. package/dist/daemon/cron/sinks.d.ts +18 -1
  378. package/dist/daemon/cron/sinks.js +47 -93
  379. package/dist/daemon/crtrd-cli.js +4 -19
  380. package/dist/daemon/crtrd.d.ts +2 -0
  381. package/dist/daemon/crtrd.js +206 -81
  382. package/dist/daemon/fleet.js +5 -6
  383. package/dist/daemon/human/finish.d.ts +12 -24
  384. package/dist/daemon/human/finish.js +88 -285
  385. package/dist/daemon/human/settle.d.ts +40 -0
  386. package/dist/daemon/human/settle.js +89 -0
  387. package/dist/daemon/human/sweep.d.ts +20 -5
  388. package/dist/daemon/human/sweep.js +56 -60
  389. package/dist/daemon/manage.d.ts +21 -16
  390. package/dist/daemon/manage.js +85 -51
  391. package/dist/daemon/messaging/node-message.d.ts +1 -1
  392. package/dist/daemon/messaging/node-message.js +9 -12
  393. package/dist/daemon/park-activity.js +3 -3
  394. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  395. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  396. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  397. package/dist/daemon/reconcilers/controller-death.js +6 -18
  398. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  399. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  400. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  401. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  402. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  403. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  404. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  405. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  406. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  407. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  408. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  409. package/dist/daemon/review/companion.js +5 -11
  410. package/dist/daemon/review/deliver.d.ts +1 -5
  411. package/dist/daemon/review/deliver.js +3 -52
  412. package/dist/daemon/review/finish.d.ts +2 -27
  413. package/dist/daemon/review/finish.js +79 -263
  414. package/dist/daemon/review/sweep.d.ts +3 -10
  415. package/dist/daemon/review/sweep.js +13 -131
  416. package/dist/daemon/startup-policy.d.ts +0 -5
  417. package/dist/daemon/startup-policy.js +0 -5
  418. package/dist/index.d.ts +2 -1
  419. package/dist/index.js +1 -1
  420. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  421. package/dist/migrations/__tests__/activation.test.js +9 -6
  422. package/dist/migrations/activation.d.ts +3 -2
  423. package/dist/migrations/activation.js +2 -4
  424. package/dist/migrations/corpus.d.ts +2 -5
  425. package/dist/migrations/corpus.js +9 -20
  426. package/dist/migrations/runner.d.ts +3 -1
  427. package/dist/migrations/runner.js +6 -2
  428. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  429. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  430. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  431. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  432. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  433. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  434. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  435. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  436. package/dist/pi-extensions/canvas-stophook.js +47 -31
  437. package/dist/shared/env.d.ts +1 -0
  438. package/dist/shared/env.js +3 -0
  439. package/package.json +1 -1
  440. package/runtime.lock.json +5 -5
  441. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  442. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  443. package/dist/clients/inbox/review-visibility.js +0 -19
  444. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  445. package/dist/commands/canvas-rebuild-index.js +0 -46
  446. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  447. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  448. package/dist/core/__tests__/human-deliver.test.js +0 -256
  449. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  450. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  451. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  452. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  453. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  454. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  455. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  456. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  457. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  458. package/dist/core/canvas/human-deliveries.js +0 -75
  459. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  460. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  461. package/dist/core/feed/passive.d.ts +0 -3
  462. package/dist/core/feed/passive.js +0 -53
  463. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  464. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  465. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  466. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  467. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  468. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  469. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  470. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  471. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  472. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  473. package/dist/core/review/ticket-filter.d.ts +0 -17
  474. package/dist/core/review/ticket-filter.js +0 -21
  475. package/dist/core/runtime/broker/passive.d.ts +0 -17
  476. package/dist/core/runtime/broker/passive.js +0 -44
  477. package/dist/core/runtime/busy.d.ts +0 -46
  478. package/dist/core/runtime/busy.js +0 -150
  479. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  480. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  481. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  482. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  483. package/dist/daemon/human/deliver-action.d.ts +0 -3
  484. package/dist/daemon/human/deliver-action.js +0 -9
  485. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  486. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  487. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  488. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  489. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  490. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  491. package/dist/daemon/startup-block-marker.d.ts +0 -25
  492. package/dist/daemon/startup-block-marker.js +0 -99
  493. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  494. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  495. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -1,3 +1,4 @@
1
+ import { openDb } from './db.js';
1
2
  export type CronScope = 'profile' | 'global';
2
3
  export type CronOverlap = 'skip' | 'queue' | 'replace';
3
4
  export type CronOnOutput = 'silent' | 'on-failure' | 'always' | 'on-change';
@@ -297,4 +298,5 @@ export declare function runningCrons(): Cron[];
297
298
  * written: a cron canceled while its run was in flight is already gone (and
298
299
  * its history cascaded), so a settling run no-ops with `false` instead of
299
300
  * violating the FK. */
301
+ export declare function recordCronRunLocked(db: ReturnType<typeof openDb>, run: CronRunRecord): boolean;
300
302
  export declare function recordCronRun(run: CronRunRecord): boolean;
@@ -484,20 +484,21 @@ export function runningCrons() {
484
484
  * written: a cron canceled while its run was in flight is already gone (and
485
485
  * its history cascaded), so a settling run no-ops with `false` instead of
486
486
  * violating the FK. */
487
+ export function recordCronRunLocked(db, run) {
488
+ if (db.prepare('SELECT 1 FROM crons WHERE cron_id = ?').get(run.cron_id) === undefined) {
489
+ return false;
490
+ }
491
+ db.prepare(`INSERT INTO cron_runs (
492
+ run_id, cron_id, started, finished, duration_ms, exit_code,
493
+ stdout_head, stderr_head, delivered
494
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(run.run_id, run.cron_id, run.started, run.finished, run.duration_ms, run.exit_code, run.stdout_head, run.stderr_head, run.delivered);
495
+ db.prepare(`DELETE FROM cron_runs
496
+ WHERE cron_id = ? AND run_id NOT IN (
497
+ SELECT run_id FROM cron_runs WHERE cron_id = ?
498
+ ORDER BY started DESC, run_id DESC LIMIT ?
499
+ )`).run(run.cron_id, run.cron_id, CRON_RUN_RING);
500
+ return true;
501
+ }
487
502
  export function recordCronRun(run) {
488
- return withCanvasWrite((db) => {
489
- if (db.prepare('SELECT 1 FROM crons WHERE cron_id = ?').get(run.cron_id) === undefined) {
490
- return false;
491
- }
492
- db.prepare(`INSERT INTO cron_runs (
493
- run_id, cron_id, started, finished, duration_ms, exit_code,
494
- stdout_head, stderr_head, delivered
495
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(run.run_id, run.cron_id, run.started, run.finished, run.duration_ms, run.exit_code, run.stdout_head, run.stderr_head, run.delivered);
496
- db.prepare(`DELETE FROM cron_runs
497
- WHERE cron_id = ? AND run_id NOT IN (
498
- SELECT run_id FROM cron_runs WHERE cron_id = ?
499
- ORDER BY started DESC, run_id DESC LIMIT ?
500
- )`).run(run.cron_id, run.cron_id, CRON_RUN_RING);
501
- return true;
502
- });
503
+ return withCanvasWrite((db) => recordCronRunLocked(db, run));
503
504
  }
@@ -62,6 +62,15 @@ function parseClaim(raw) {
62
62
  return null;
63
63
  return { pid: pid, pid_identity, token };
64
64
  }
65
+ /** Bootstrap only `canvas_meta`; daemon startup owns explicit schema migration. */
66
+ function ensureOwnerTable() {
67
+ openDb().exec(`
68
+ CREATE TABLE IF NOT EXISTS canvas_meta (
69
+ key TEXT PRIMARY KEY,
70
+ value TEXT NOT NULL
71
+ )
72
+ `);
73
+ }
65
74
  /** Read the incumbent's exact serialized `value` for `DAEMON_OWNER_META_KEY`
66
75
  * as a plain snapshot — no transaction. `undefined` means the key is
67
76
  * currently absent. */
@@ -95,6 +104,7 @@ export function claimDaemonOwnership(token) {
95
104
  }
96
105
  const claim = { pid: process.pid, pid_identity: ownIdentity, token };
97
106
  const serializedClaim = serializeClaim(claim);
107
+ ensureOwnerTable();
98
108
  for (;;) {
99
109
  const snapshot = readSnapshot();
100
110
  if (snapshot === undefined) {
@@ -1,7 +1,7 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
2
  export { migrate, MIGRATIONS } from './migrations.js';
3
- /** Open (or reuse) the canvas db at the current `CRTR_HOME`, initializing the
4
- * schema and WAL on first open. Keyed by path so tests with distinct homes get
3
+ /** Open (or reuse) the canvas db at the current `CRTR_HOME`, configuring WAL
4
+ * but never changing its schema. Keyed by path so tests with distinct homes get
5
5
  * independent handles.
6
6
  *
7
7
  * A cache hit trusts the cached handle outright: `crtrd` is this process's
@@ -1,21 +1,6 @@
1
- // canvas.db — connection management only. Opens (or reuses) the WAL-mode
2
- // handle, applies pending schema migrations on first open, and serializes
3
- // writes under one transaction boundary. sqlite in WAL mode so the many
4
- // concurrent writers a sisyphus-grade swarm produces don't contend on a
5
- // single locked JSON.
6
- //
7
- // Node rows are a rebuildable index over each node's meta.json (the source of
8
- // truth). The `subscribes_to` edges are the one genuinely-mutable part no meta
9
- // owns, so the db is authoritative for them.
10
- //
11
- // The versioned schema chain itself (`MIGRATIONS`, `migrate()`) lives in
12
- // `./migrations.js` — see that file's header for how this repo's three
13
- // migration homes divide. Re-exported here so every existing import of
14
- // `migrate`/`MIGRATIONS` from this module keeps working.
15
1
  import { DatabaseSync } from 'node:sqlite';
16
2
  import { appendFileSync } from 'node:fs';
17
3
  import { canvasDbPath, ensureHome } from './paths.js';
18
- import { migrate } from './migrations.js';
19
4
  export { migrate, MIGRATIONS } from './migrations.js';
20
5
  const handles = new Map();
21
6
  // V-2 open-ledger instrumentation
@@ -45,8 +30,8 @@ function recordOpenLedger() {
45
30
  /* instrumentation only — never let a ledger write break a db open */
46
31
  }
47
32
  }
48
- /** Open (or reuse) the canvas db at the current `CRTR_HOME`, initializing the
49
- * schema and WAL on first open. Keyed by path so tests with distinct homes get
33
+ /** Open (or reuse) the canvas db at the current `CRTR_HOME`, configuring WAL
34
+ * but never changing its schema. Keyed by path so tests with distinct homes get
50
35
  * independent handles.
51
36
  *
52
37
  * A cache hit trusts the cached handle outright: `crtrd` is this process's
@@ -78,7 +63,6 @@ export function openDb() {
78
63
  // Load-bearing as of migration v4: the edges→nodes FK (ON DELETE CASCADE)
79
64
  // needs this ON at the deleting connection so a node delete reaps its edges.
80
65
  db.exec('PRAGMA foreign_keys = ON;');
81
- migrate(db);
82
66
  handles.set(path, db);
83
67
  return db;
84
68
  }
@@ -0,0 +1,104 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import type { DeliveryFailure, DeliverySettlement as ProcessDeliverySettlement } from './delivery-contract.js';
3
+ export type DeliveryState = 'armed' | 'pending' | 'handed_off' | 'acknowledged' | 'permanent_failed';
4
+ export type DeliveryDestinationKind = 'node_inbox' | 'external_action';
5
+ export type DeliveryUrgency = 'critical' | 'urgent' | 'normal' | 'deferred';
6
+ export interface ArmedExternalActionInput {
7
+ destinationId: string;
8
+ adapter: Record<string, unknown>;
9
+ idempotencyKey: string;
10
+ }
11
+ export interface DeliveryRecord {
12
+ deliveryId: string;
13
+ messageId: string | null;
14
+ destinationKind: DeliveryDestinationKind;
15
+ destinationId: string;
16
+ urgency: DeliveryUrgency;
17
+ wake: 0 | 1;
18
+ state: DeliveryState;
19
+ attempts: number;
20
+ nextAttemptAt: number | null;
21
+ claimedAt: number | null;
22
+ claimOwner: string | null;
23
+ acknowledgedAt: number | null;
24
+ failedAt: number | null;
25
+ lastFailure: DeliveryFailure | null;
26
+ adapter: Record<string, unknown> | null;
27
+ effect: Record<string, unknown> | null;
28
+ idempotencyKey: string | null;
29
+ canceledAt: number | null;
30
+ createdAt: number;
31
+ updatedAt: number;
32
+ }
33
+ export interface ClaimedNodeMail extends DeliveryRecord {
34
+ message: {
35
+ messageId: string;
36
+ seq: number;
37
+ operationId: string;
38
+ createdAt: string;
39
+ senderNodeId: string | null;
40
+ senderName: string | null;
41
+ kind: 'update' | 'urgent' | 'final' | 'message' | 'completed';
42
+ label: string;
43
+ bodyRef: string | null;
44
+ bodyJson: unknown | null;
45
+ disposition: 'human-answer' | 'human-canceled' | null;
46
+ };
47
+ handoff: 'fresh' | 'inherited';
48
+ }
49
+ /** Claim one broker channel. A refreshing or stale broker receives hold and changes nothing. */
50
+ export declare function claimNodeMail(nodeId: string, executionId: string, channel: 'wake' | 'passive', now?: number): {
51
+ action: 'claimed' | 'hold';
52
+ deliveries: ClaimedNodeMail[];
53
+ };
54
+ /** Pre-launch wake claim. It uses the same state transition but deliberately does not reject refresh intent. */
55
+ export declare function claimKickoffMail(nodeId: string, executionId: string, now?: number): ClaimedNodeMail[];
56
+ export type DeliverySettlement = {
57
+ state: 'acknowledged';
58
+ } | {
59
+ state: 'released';
60
+ } | {
61
+ state: 'permanent_failed';
62
+ failure: DeliveryFailure;
63
+ } | {
64
+ state: 'pending';
65
+ nextAttemptAt: number;
66
+ failure: DeliveryFailure;
67
+ };
68
+ /** The one execution/attempt/owner fence for node mail and external actions. */
69
+ export declare function settleDelivery(deliveryId: string, attempts: number, claimOwner: string, settlement: DeliverySettlement | ProcessDeliverySettlement, now?: number): boolean;
70
+ /** Broker acknowledgement additionally checks the live execution/refresh guards before the per-row fence. */
71
+ export declare function acknowledgeNodeMail(nodeId: string, executionId: string, deliveries: Array<{
72
+ deliveryId: string;
73
+ attempts: number;
74
+ disposition: 'acknowledged' | 'released';
75
+ }>, now?: number): {
76
+ action: 'acknowledged' | 'hold';
77
+ results: Array<{
78
+ deliveryId: string;
79
+ outcome: 'acknowledged' | 'released' | 'stale';
80
+ }>;
81
+ };
82
+ /** The immutable action document belonging to one external-action obligation. */
83
+ export declare function messageBodyForDelivery(deliveryId: string): unknown | null;
84
+ /** Read the one external-action obligation with this caller-owned identity. */
85
+ export declare function readExternalActionDelivery(idempotencyKey: string): DeliveryRecord | null;
86
+ export declare function dueExternalActionDeliveries(now: number): DeliveryRecord[];
87
+ export declare function claimExternalActionDelivery(deliveryId: string, now: number, claimOwner: string): DeliveryRecord | null;
88
+ export declare function resetHandedOffDeliveries(now: number): number;
89
+ /** Reset mail held by an execution that is no longer this node's one live execution. */
90
+ export declare function resetStaleExecutionMail(now: number): number;
91
+ export declare function hasUnseenWakeMail(nodeId: string): boolean;
92
+ export declare function hasWakeMailSince(nodeId: string, since: number): boolean;
93
+ export declare function acknowledgedNodeMailCount(nodeId: string): number;
94
+ /** Register an outcome's frozen external destination before an outcome message exists. */
95
+ export declare function armExternalActionDelivery(db: DatabaseSync, input: ArmedExternalActionInput, now?: number): void;
96
+ /** Attach a newly written immutable outcome message and make its registered action due. */
97
+ export declare function activateArmedExternalActionDelivery(db: DatabaseSync, input: {
98
+ idempotencyKey: string;
99
+ label: string;
100
+ bodyJson: string;
101
+ supersede: boolean;
102
+ }, now?: number): boolean;
103
+ /** Create the messaging schema and import every legacy mailbox/action/wait row. The caller owns the encompassing migration transaction. */
104
+ export declare function migrateMessagingState(db: DatabaseSync): void;
@@ -0,0 +1,354 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { openDb, withCanvasWrite } from './db.js';
4
+ import { inboxPath, passivePath } from './paths.js';
5
+ function deliveryFrom(row) {
6
+ return {
7
+ deliveryId: row.delivery_id,
8
+ messageId: row.message_id,
9
+ destinationKind: row.destination_kind,
10
+ destinationId: row.destination_id,
11
+ urgency: row.urgency,
12
+ wake: row.wake === 1 ? 1 : 0,
13
+ state: row.state,
14
+ attempts: row.attempts,
15
+ nextAttemptAt: row.next_attempt_at,
16
+ claimedAt: row.claimed_at,
17
+ claimOwner: row.claim_owner,
18
+ acknowledgedAt: row.acknowledged_at,
19
+ failedAt: row.failed_at,
20
+ lastFailure: row.last_failure_json === null ? null : JSON.parse(row.last_failure_json),
21
+ adapter: row.adapter_json === null ? null : JSON.parse(row.adapter_json),
22
+ effect: row.effect_json === null ? null : JSON.parse(row.effect_json),
23
+ idempotencyKey: row.idempotency_key,
24
+ canceledAt: row.canceled_at,
25
+ createdAt: row.created_at,
26
+ updatedAt: row.updated_at,
27
+ };
28
+ }
29
+ function executionIsLive(db, nodeId, executionId, rejectRefresh) {
30
+ const row = db.prepare(`SELECT 1 FROM nodes n JOIN executions e ON e.node_id = n.node_id
31
+ WHERE n.node_id = ? AND e.execution_id = ? AND e.state != 'exited'${rejectRefresh ? " AND n.intent IS NOT 'refresh'" : ''}`).get(nodeId, executionId);
32
+ return row !== undefined;
33
+ }
34
+ function nodeMailRows(db, nodeId, executionId, wake, freshIds) {
35
+ const rows = db.prepare(`SELECT d.*, m.message_id AS m_message_id, m.seq, m.operation_id, m.created_at AS message_created_at,
36
+ m.sender_node_id, m.sender_name, m.kind, m.label, m.body_ref, m.body_json, m.disposition,
37
+ CASE WHEN d.state = 'handed_off' THEN 'inherited' ELSE 'fresh' END AS handoff
38
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
39
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.wake = ?
40
+ AND d.canceled_at IS NULL
41
+ AND (d.state = 'pending' OR (d.state = 'handed_off' AND d.claim_owner = ?))
42
+ ORDER BY m.seq`).all(nodeId, wake, executionId);
43
+ return rows.map((row) => ({
44
+ ...deliveryFrom(row),
45
+ message: {
46
+ messageId: row['m_message_id'],
47
+ seq: Number(row['seq']),
48
+ operationId: row['operation_id'],
49
+ createdAt: row['message_created_at'],
50
+ senderNodeId: row['sender_node_id'],
51
+ senderName: row['sender_name'],
52
+ kind: row['kind'],
53
+ label: row['label'],
54
+ bodyRef: row['body_ref'],
55
+ bodyJson: row['body_json'] === null ? null : JSON.parse(row['body_json']),
56
+ disposition: row['disposition'],
57
+ },
58
+ handoff: freshIds?.has(row.delivery_id) === true ? 'fresh' : row['handoff'],
59
+ }));
60
+ }
61
+ /** Claim one broker channel. A refreshing or stale broker receives hold and changes nothing. */
62
+ export function claimNodeMail(nodeId, executionId, channel, now = Date.now()) {
63
+ return withCanvasWrite((db) => {
64
+ if (!executionIsLive(db, nodeId, executionId, true))
65
+ return { action: 'hold', deliveries: [] };
66
+ const wake = channel === 'wake' ? 1 : 0;
67
+ const freshIds = new Set(db.prepare(`SELECT delivery_id FROM deliveries
68
+ WHERE destination_kind = 'node_inbox' AND destination_id = ? AND wake = ? AND state = 'pending' AND canceled_at IS NULL`).all(nodeId, wake).map((row) => row.delivery_id));
69
+ db.prepare(`UPDATE deliveries SET state = 'handed_off', attempts = attempts + 1, claim_owner = ?, claimed_at = ?, updated_at = ?
70
+ WHERE destination_kind = 'node_inbox' AND destination_id = ? AND wake = ? AND state = 'pending' AND canceled_at IS NULL`).run(executionId, now, now, nodeId, wake);
71
+ return { action: 'claimed', deliveries: nodeMailRows(db, nodeId, executionId, wake, freshIds) };
72
+ });
73
+ }
74
+ /** Pre-launch wake claim. It uses the same state transition but deliberately does not reject refresh intent. */
75
+ export function claimKickoffMail(nodeId, executionId, now = Date.now()) {
76
+ return withCanvasWrite((db) => {
77
+ if (!executionIsLive(db, nodeId, executionId, false))
78
+ throw new Error(`execution ${executionId} is not live for node ${nodeId}`);
79
+ const freshIds = new Set(db.prepare(`SELECT delivery_id FROM deliveries
80
+ WHERE destination_kind = 'node_inbox' AND destination_id = ? AND wake = 1 AND state = 'pending' AND canceled_at IS NULL`).all(nodeId).map((row) => row.delivery_id));
81
+ db.prepare(`UPDATE deliveries SET state = 'handed_off', attempts = attempts + 1, claim_owner = ?, claimed_at = ?, updated_at = ?
82
+ WHERE destination_kind = 'node_inbox' AND destination_id = ? AND wake = 1 AND state = 'pending' AND canceled_at IS NULL`).run(executionId, now, now, nodeId);
83
+ return nodeMailRows(db, nodeId, executionId, 1, freshIds);
84
+ });
85
+ }
86
+ // Convert the existing subprocess contract at the one shared fence. The old names do not escape this module.
87
+ function normalizedSettlement(settlement) {
88
+ if (settlement.state === 'accepted')
89
+ return { state: 'acknowledged' };
90
+ return settlement;
91
+ }
92
+ /** The one execution/attempt/owner fence for node mail and external actions. */
93
+ export function settleDelivery(deliveryId, attempts, claimOwner, settlement, now = Date.now()) {
94
+ const next = normalizedSettlement(settlement);
95
+ const failure = next.state === 'pending' || next.state === 'permanent_failed' ? JSON.stringify(next.failure) : null;
96
+ const state = next.state === 'released' ? 'pending' : next.state;
97
+ const nextAttemptAt = next.state === 'pending' ? next.nextAttemptAt : state === 'pending' ? now : null;
98
+ return withCanvasWrite((db) => Number(db.prepare(`UPDATE deliveries
99
+ SET state = ?, next_attempt_at = ?, claimed_at = NULL, claim_owner = NULL,
100
+ acknowledged_at = ?, failed_at = ?, last_failure_json = ?, updated_at = ?
101
+ WHERE delivery_id = ? AND state = 'handed_off' AND attempts = ? AND claim_owner = ?`).run(state, nextAttemptAt, state === 'acknowledged' ? now : null, state === 'permanent_failed' ? now : null, failure, now, deliveryId, attempts, claimOwner).changes) === 1);
102
+ }
103
+ /** Broker acknowledgement additionally checks the live execution/refresh guards before the per-row fence. */
104
+ export function acknowledgeNodeMail(nodeId, executionId, deliveries, now = Date.now()) {
105
+ return withCanvasWrite((db) => {
106
+ if (!executionIsLive(db, nodeId, executionId, true))
107
+ return { action: 'hold', results: [] };
108
+ const results = deliveries.map((delivery) => {
109
+ const owned = db.prepare("SELECT 1 FROM deliveries WHERE delivery_id = ? AND destination_kind = 'node_inbox' AND destination_id = ?").get(delivery.deliveryId, nodeId) !== undefined;
110
+ return {
111
+ deliveryId: delivery.deliveryId,
112
+ outcome: owned && settleDelivery(delivery.deliveryId, delivery.attempts, executionId, { state: delivery.disposition }, now)
113
+ ? delivery.disposition
114
+ : 'stale',
115
+ };
116
+ });
117
+ return { action: 'acknowledged', results };
118
+ });
119
+ }
120
+ /** The immutable action document belonging to one external-action obligation. */
121
+ export function messageBodyForDelivery(deliveryId) {
122
+ const row = openDb().prepare(`SELECT m.body_json FROM deliveries d JOIN messages m ON m.message_id = d.message_id
123
+ WHERE d.delivery_id = ? AND d.destination_kind = 'external_action'`).get(deliveryId);
124
+ return row === undefined || row.body_json === null ? null : JSON.parse(row.body_json);
125
+ }
126
+ /** Read the one external-action obligation with this caller-owned identity. */
127
+ export function readExternalActionDelivery(idempotencyKey) {
128
+ const row = openDb().prepare("SELECT * FROM deliveries WHERE destination_kind = 'external_action' AND idempotency_key = ?").get(idempotencyKey);
129
+ return row === undefined ? null : deliveryFrom(row);
130
+ }
131
+ export function dueExternalActionDeliveries(now) {
132
+ return openDb().prepare(`SELECT * FROM deliveries
133
+ WHERE destination_kind = 'external_action' AND state = 'pending' AND canceled_at IS NULL AND next_attempt_at <= ?
134
+ ORDER BY next_attempt_at, created_at, delivery_id`).all(now).map(deliveryFrom);
135
+ }
136
+ export function claimExternalActionDelivery(deliveryId, now, claimOwner) {
137
+ return withCanvasWrite((db) => {
138
+ const result = db.prepare(`UPDATE deliveries SET state = 'handed_off', attempts = attempts + 1, claimed_at = ?, claim_owner = ?, updated_at = ?
139
+ WHERE delivery_id = ? AND destination_kind = 'external_action' AND state = 'pending' AND canceled_at IS NULL AND next_attempt_at <= ?`).run(now, claimOwner, now, deliveryId, now);
140
+ if (Number(result.changes) === 0)
141
+ return null;
142
+ return deliveryFrom(db.prepare('SELECT * FROM deliveries WHERE delivery_id = ?').get(deliveryId));
143
+ });
144
+ }
145
+ export function resetHandedOffDeliveries(now) {
146
+ return withCanvasWrite((db) => Number(db.prepare(`UPDATE deliveries SET state = 'pending', next_attempt_at = CASE WHEN destination_kind = 'external_action' THEN ? ELSE NULL END,
147
+ claimed_at = NULL, claim_owner = NULL, updated_at = ? WHERE state = 'handed_off'`).run(now, now).changes));
148
+ }
149
+ /** Reset mail held by an execution that is no longer this node's one live execution. */
150
+ export function resetStaleExecutionMail(now) {
151
+ return withCanvasWrite((db) => Number(db.prepare(`UPDATE deliveries AS d SET state = 'pending', claimed_at = NULL, claim_owner = NULL, updated_at = ?
152
+ WHERE d.destination_kind = 'node_inbox' AND d.state = 'handed_off'
153
+ AND NOT EXISTS (
154
+ SELECT 1 FROM executions e WHERE e.node_id = d.destination_id AND e.execution_id = d.claim_owner AND e.state != 'exited'
155
+ )`).run(now).changes));
156
+ }
157
+ export function hasUnseenWakeMail(nodeId) {
158
+ return openDb().prepare(`SELECT 1 FROM deliveries WHERE destination_kind = 'node_inbox' AND destination_id = ? AND wake = 1
159
+ AND canceled_at IS NULL AND urgency != 'deferred' AND state IN ('pending', 'handed_off') LIMIT 1`).get(nodeId) !== undefined;
160
+ }
161
+ export function hasWakeMailSince(nodeId, since) {
162
+ return openDb().prepare(`SELECT 1 FROM deliveries WHERE destination_kind = 'node_inbox' AND destination_id = ? AND wake = 1
163
+ AND urgency != 'deferred' AND created_at >= ? LIMIT 1`).get(nodeId, since) !== undefined;
164
+ }
165
+ export function acknowledgedNodeMailCount(nodeId) {
166
+ return Number(openDb().prepare("SELECT COUNT(*) AS count FROM deliveries WHERE destination_kind = 'node_inbox' AND destination_id = ? AND acknowledged_at IS NOT NULL").get(nodeId).count);
167
+ }
168
+ /** Register an outcome's frozen external destination before an outcome message exists. */
169
+ export function armExternalActionDelivery(db, input, now = Date.now()) {
170
+ const deliveryId = newId();
171
+ const result = db.prepare(`INSERT INTO deliveries
172
+ (delivery_id, message_id, destination_kind, destination_id, urgency, wake, state, adapter_json, idempotency_key, created_at, updated_at)
173
+ VALUES (?, NULL, 'external_action', ?, 'normal', 0, 'armed', ?, ?, ?, ?)
174
+ ON CONFLICT(idempotency_key) WHERE idempotency_key IS NOT NULL DO UPDATE SET
175
+ destination_id = excluded.destination_id, adapter_json = excluded.adapter_json, updated_at = excluded.updated_at
176
+ WHERE deliveries.state = 'armed'`).run(deliveryId, input.destinationId, JSON.stringify(input.adapter), input.idempotencyKey, now, now);
177
+ if (Number(result.changes) === 0)
178
+ throw new Error(`external action ${input.idempotencyKey} is already active`);
179
+ }
180
+ /** Attach a newly written immutable outcome message and make its registered action due. */
181
+ export function activateArmedExternalActionDelivery(db, input, now = Date.now()) {
182
+ const row = db.prepare("SELECT state FROM deliveries WHERE destination_kind = 'external_action' AND idempotency_key = ?").get(input.idempotencyKey);
183
+ if (row === undefined || (!input.supersede && row.state !== 'armed'))
184
+ return false;
185
+ let body;
186
+ try {
187
+ body = JSON.parse(input.bodyJson);
188
+ }
189
+ catch (error) {
190
+ throw new Error(`external action ${input.idempotencyKey} has invalid outcome document: ${error instanceof Error ? error.message : String(error)}`);
191
+ }
192
+ const messageId = newId();
193
+ const seq = Number(db.prepare('SELECT COALESCE(MAX(seq), 0) AS seq FROM messages').get().seq) + 1;
194
+ db.prepare(`INSERT INTO messages
195
+ (message_id, seq, operation_id, created_at, sender_node_id, sender_name, kind, label, body_ref, body_json, disposition)
196
+ VALUES (?, ?, ?, ?, NULL, NULL, 'completed', ?, NULL, ?, NULL)`).run(messageId, seq, newId(), new Date(now).toISOString(), input.label, JSON.stringify(body));
197
+ if (input.supersede) {
198
+ db.prepare(`UPDATE deliveries SET message_id = ?, state = 'pending', attempts = 0, next_attempt_at = ?, claimed_at = NULL, claim_owner = NULL,
199
+ acknowledged_at = NULL, failed_at = NULL, last_failure_json = NULL, updated_at = ?
200
+ WHERE destination_kind = 'external_action' AND idempotency_key = ?`).run(messageId, now, now, input.idempotencyKey);
201
+ }
202
+ else {
203
+ db.prepare(`UPDATE deliveries SET message_id = ?, state = 'pending', next_attempt_at = ?, updated_at = ?
204
+ WHERE destination_kind = 'external_action' AND idempotency_key = ? AND state = 'armed'`).run(messageId, now, now, input.idempotencyKey);
205
+ }
206
+ return true;
207
+ }
208
+ function newId() {
209
+ let id;
210
+ do
211
+ id = randomBytes(16).toString('hex');
212
+ while (id === '00000000000000000000000000000000');
213
+ return id;
214
+ }
215
+ function readJsonLines(path) {
216
+ if (!existsSync(path))
217
+ return [];
218
+ const raw = readFileSync(path, 'utf8');
219
+ if (raw.trim() === '')
220
+ return [];
221
+ return raw.split('\n').filter((line) => line.trim() !== '').map((line, index) => {
222
+ let entry;
223
+ try {
224
+ entry = JSON.parse(line);
225
+ }
226
+ catch (error) {
227
+ throw new Error(`invalid mailbox JSON at ${path}:${index + 1}: ${error.message}`);
228
+ }
229
+ if (!/^(?!0{32}$)[0-9a-f]{32}$/.test(entry.entry_id) || !/^(?!0{32}$)[0-9a-f]{32}$/.test(entry.operation_id)) {
230
+ throw new Error(`invalid mailbox identity at ${path}:${index + 1}`);
231
+ }
232
+ if (typeof entry.ts !== 'string' || typeof entry.label !== 'string')
233
+ throw new Error(`incomplete mailbox entry at ${path}:${index + 1}`);
234
+ return entry;
235
+ });
236
+ }
237
+ function readCanceled(path) {
238
+ if (!existsSync(path))
239
+ return new Set();
240
+ return new Set(readFileSync(path, 'utf8').split('\n').map((line) => line.trim()).filter(Boolean));
241
+ }
242
+ /** Create the messaging schema and import every legacy mailbox/action/wait row. The caller owns the encompassing migration transaction. */
243
+ export function migrateMessagingState(db) {
244
+ db.exec(`
245
+ CREATE TABLE IF NOT EXISTS messages (
246
+ message_id TEXT PRIMARY KEY,
247
+ seq INTEGER NOT NULL UNIQUE,
248
+ operation_id TEXT NOT NULL,
249
+ created_at TEXT NOT NULL,
250
+ sender_node_id TEXT,
251
+ sender_name TEXT,
252
+ kind TEXT NOT NULL CHECK (kind IN ('update', 'urgent', 'final', 'message', 'completed')),
253
+ label TEXT NOT NULL,
254
+ body_ref TEXT,
255
+ body_json TEXT,
256
+ disposition TEXT CHECK (disposition IS NULL OR disposition IN ('human-answer', 'human-canceled'))
257
+ );
258
+ CREATE TABLE IF NOT EXISTS deliveries (
259
+ delivery_id TEXT PRIMARY KEY,
260
+ message_id TEXT REFERENCES messages(message_id),
261
+ destination_kind TEXT NOT NULL CHECK (destination_kind IN ('node_inbox', 'external_action')),
262
+ destination_id TEXT NOT NULL,
263
+ urgency TEXT NOT NULL CHECK (urgency IN ('critical', 'urgent', 'normal', 'deferred')),
264
+ wake INTEGER NOT NULL CHECK (wake IN (0, 1)),
265
+ state TEXT NOT NULL CHECK (state IN ('armed', 'pending', 'handed_off', 'acknowledged', 'permanent_failed')),
266
+ attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0),
267
+ next_attempt_at INTEGER,
268
+ claimed_at INTEGER,
269
+ claim_owner TEXT,
270
+ acknowledged_at INTEGER,
271
+ failed_at INTEGER,
272
+ last_failure_json TEXT,
273
+ adapter_json TEXT,
274
+ effect_json TEXT,
275
+ idempotency_key TEXT,
276
+ canceled_at INTEGER,
277
+ created_at INTEGER NOT NULL,
278
+ updated_at INTEGER NOT NULL,
279
+ CHECK (state = 'armed' OR message_id IS NOT NULL)
280
+ );
281
+ CREATE UNIQUE INDEX IF NOT EXISTS messages_seq ON messages(seq);
282
+ CREATE INDEX IF NOT EXISTS deliveries_node_inbox ON deliveries(destination_id, wake, state, created_at) WHERE destination_kind = 'node_inbox';
283
+ CREATE INDEX IF NOT EXISTS deliveries_external_due ON deliveries(state, next_attempt_at) WHERE destination_kind = 'external_action';
284
+ CREATE INDEX IF NOT EXISTS deliveries_handed_off ON deliveries(claim_owner) WHERE state = 'handed_off';
285
+ CREATE INDEX IF NOT EXISTS deliveries_message ON deliveries(message_id);
286
+ CREATE UNIQUE INDEX IF NOT EXISTS deliveries_idempotency ON deliveries(idempotency_key) WHERE idempotency_key IS NOT NULL;
287
+ CREATE TABLE IF NOT EXISTS waits (
288
+ wait_id TEXT PRIMARY KEY,
289
+ node_id TEXT NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
290
+ kind TEXT NOT NULL CHECK (kind = 'controller'),
291
+ target_id TEXT NOT NULL,
292
+ generation INTEGER NOT NULL,
293
+ created_at INTEGER NOT NULL,
294
+ satisfied_at INTEGER
295
+ );
296
+ CREATE UNIQUE INDEX IF NOT EXISTS waits_live ON waits(node_id) WHERE satisfied_at IS NULL;
297
+ CREATE INDEX IF NOT EXISTS waits_node ON waits(node_id);
298
+ `);
299
+ const mail = [];
300
+ for (const row of db.prepare('SELECT node_id FROM nodes').all()) {
301
+ const nodeId = row.node_id;
302
+ const inbox = readJsonLines(inboxPath(nodeId));
303
+ const cursor = existsSync(`${inboxPath(nodeId)}.cursor`) ? readFileSync(`${inboxPath(nodeId)}.cursor`, 'utf8').trim() : '';
304
+ const cursorIndex = inbox.findIndex((entry) => entry.entry_id === cursor);
305
+ const canceled = readCanceled(`${inboxPath(nodeId)}.canceled`);
306
+ inbox.forEach((entry, physicalIndex) => mail.push({ nodeId, wake: 1, source: 'inbox', fileRank: 0, physicalIndex, entry, acknowledged: cursorIndex >= 0 && physicalIndex <= cursorIndex, canceled: canceled.has(entry.entry_id), seq: 0 }));
307
+ for (const [fileRank, path] of [[0, `${passivePath(nodeId)}.draining`], [1, passivePath(nodeId)]]) {
308
+ readJsonLines(path).forEach((entry, physicalIndex) => mail.push({ nodeId, wake: 0, source: 'passive', fileRank, physicalIndex, entry, acknowledged: false, canceled: false, seq: 0 }));
309
+ }
310
+ }
311
+ mail.sort((a, b) => a.entry.ts.localeCompare(b.entry.ts) || a.nodeId.localeCompare(b.nodeId) || a.fileRank - b.fileRank || a.physicalIndex - b.physicalIndex);
312
+ mail.forEach((entry, index) => { entry.seq = index + 1; });
313
+ const groups = new Map();
314
+ for (const entry of mail) {
315
+ const key = `${entry.nodeId}\0${entry.source}`;
316
+ const group = groups.get(key) ?? [];
317
+ group.push(entry);
318
+ groups.set(key, group);
319
+ }
320
+ for (const group of groups.values()) {
321
+ const available = group.map((entry) => entry.seq).sort((a, b) => a - b);
322
+ group.sort((a, b) => a.fileRank - b.fileRank || a.physicalIndex - b.physicalIndex).forEach((entry, index) => { entry.seq = available[index]; });
323
+ }
324
+ const insertMessage = db.prepare(`INSERT INTO messages (message_id, seq, operation_id, created_at, sender_node_id, sender_name, kind, label, body_ref, body_json, disposition) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
325
+ const insertDelivery = db.prepare(`INSERT INTO deliveries (delivery_id, message_id, destination_kind, destination_id, urgency, wake, state, attempts, acknowledged_at, canceled_at, created_at, updated_at) VALUES (?, ?, 'node_inbox', ?, ?, ?, ?, 0, ?, ?, ?, ?)`);
326
+ for (const imported of mail) {
327
+ const entry = imported.entry;
328
+ insertMessage.run(entry.entry_id, imported.seq, entry.operation_id, entry.ts, entry.from, entry.from_name ?? null, entry.kind, entry.label, entry.ref ?? null, entry.data === undefined ? null : JSON.stringify(entry.data), entry.disposition ?? null);
329
+ const timestamp = Date.parse(entry.ts);
330
+ const now = Number.isFinite(timestamp) ? timestamp : 0;
331
+ insertDelivery.run(newId(), entry.entry_id, imported.nodeId, entry.tier, imported.wake, imported.acknowledged ? 'acknowledged' : 'pending', imported.acknowledged ? now : null, imported.canceled ? now : null, now, now);
332
+ }
333
+ const actionRows = db.prepare('SELECT * FROM human_action_deliveries').all();
334
+ for (const row of actionRows) {
335
+ const messageId = newId();
336
+ insertMessage.run(messageId, nextSeq(db), newId(), new Date(row.created_at).toISOString(), null, null, 'completed', row.request_id, null, row.completion_json, null);
337
+ const state = row.state === 'running' ? 'handed_off' : row.state === 'accepted' ? 'acknowledged' : row.state;
338
+ db.prepare(`INSERT INTO deliveries (delivery_id, message_id, destination_kind, destination_id, urgency, wake, state, attempts, next_attempt_at, claimed_at, claim_owner, acknowledged_at, failed_at, last_failure_json, adapter_json, idempotency_key, created_at, updated_at) VALUES (?, ?, 'external_action', 'human_action', 'normal', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(newId(), messageId, state, row.attempt, row.next_attempt_at, row.claimed_at, row.claim_owner, row.accepted_at, row.permanent_failed_at, row.last_failure_json, JSON.stringify({ adapter: 'subprocess', argv: JSON.parse(row.argv_json), cwd: row.cwd }), `human-action:${row.request_id}`, row.created_at, row.updated_at);
339
+ }
340
+ const outcomeRows = db.prepare('SELECT * FROM node_outcome_deliveries').all();
341
+ for (const row of outcomeRows) {
342
+ const messageId = row.document_json === null ? null : newId();
343
+ if (messageId !== null)
344
+ insertMessage.run(messageId, nextSeq(db), newId(), new Date(row.created_at).toISOString(), null, null, 'completed', row.node_id, null, row.document_json, null);
345
+ const state = row.document_json === null ? 'armed' : row.state === 'running' ? 'handed_off' : row.state === 'accepted' ? 'acknowledged' : row.state;
346
+ db.prepare(`INSERT INTO deliveries (delivery_id, message_id, destination_kind, destination_id, urgency, wake, state, attempts, next_attempt_at, claimed_at, claim_owner, acknowledged_at, failed_at, last_failure_json, adapter_json, idempotency_key, created_at, updated_at) VALUES (?, ?, 'external_action', ?, 'normal', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(newId(), messageId, row.action_name, state, row.attempt, row.next_attempt_at, row.claimed_at, row.claim_owner, row.accepted_at, row.permanent_failed_at, row.last_failure_json, JSON.stringify({ adapter: 'subprocess', argv: JSON.parse(row.argv_json), cwd: row.cwd, config_origin: row.config_origin, payload: row.payload_json === null ? null : JSON.parse(row.payload_json) }), `node-outcome:${row.node_id}`, row.created_at, row.updated_at);
347
+ }
348
+ const waiting = db.prepare('SELECT node_id, waiting_for, waiting_for_generation FROM nodes WHERE waiting_for IS NOT NULL').all();
349
+ for (const row of waiting)
350
+ db.prepare("INSERT INTO waits (wait_id, node_id, kind, target_id, generation, created_at, satisfied_at) VALUES (?, ?, 'controller', ?, ?, ?, NULL)").run(newId(), row.node_id, row.waiting_for, row.waiting_for_generation, Date.now());
351
+ }
352
+ function nextSeq(db) {
353
+ return Number(db.prepare('SELECT COALESCE(MAX(seq), 0) AS seq FROM messages').get().seq) + 1;
354
+ }
@@ -0,0 +1,29 @@
1
+ export interface Execution {
2
+ executionId: string;
3
+ nodeId: string;
4
+ daemonEpoch: string | null;
5
+ pid: number | null;
6
+ processIdentity: string | null;
7
+ state: 'launching' | 'running' | 'exited';
8
+ startedAt: string;
9
+ endedAt: string | null;
10
+ exitReason: string | null;
11
+ }
12
+ export declare function liveExecutionForNode(nodeId: string): Execution | null;
13
+ export declare function beginExecution(nodeId: string, input: {
14
+ daemonEpoch: string | null;
15
+ }): string;
16
+ /** Returns shutdown when a late broker references an execution no longer live. */
17
+ export declare function recordExecutionPid(executionId: string, nodeId: string, pid: number): 'recorded' | 'shutdown';
18
+ export declare function endExecution(executionId: string, input: {
19
+ reason: string;
20
+ }): boolean;
21
+ export declare function abortExecution(executionId: string, input: {
22
+ reason: string;
23
+ }): boolean;
24
+ export declare function endLiveExecution(nodeId: string, input: {
25
+ reason: string;
26
+ }): boolean;
27
+ export declare function migrateLegacyExecutionIdentities(captureIdentity?: (pid: number) => string | undefined): {
28
+ migrated: string[];
29
+ };