@north-light/crouter 0.3.312 → 0.3.314

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 (497) 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 +757 -746
  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/fresh-home-commands.test.js +49 -0
  121. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  122. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
  123. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  124. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  125. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  126. package/dist/core/__tests__/integration/revive.test.js +67 -21
  127. package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
  128. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  129. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  130. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  131. package/dist/core/__tests__/kickoff.test.js +36 -39
  132. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  133. package/dist/core/__tests__/lifecycle.test.js +2 -1
  134. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  135. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  136. package/dist/core/__tests__/migration.test.js +145 -31
  137. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  138. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  139. package/dist/core/__tests__/node-outcome.test.js +93 -26
  140. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  141. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
  142. package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
  143. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  144. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  145. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  146. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  147. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  148. package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
  149. package/dist/core/__tests__/prune.test.js +11 -5
  150. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  151. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  152. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  153. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  154. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  155. package/dist/core/__tests__/revive-all.test.js +13 -3
  156. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  157. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  158. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  159. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  160. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  161. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  162. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  163. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  164. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  165. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  166. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
  167. package/dist/core/__tests__/stop-guard.test.js +2 -1
  168. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  169. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
  170. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  171. package/dist/core/bootstrap.js +2 -0
  172. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  173. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  174. package/dist/core/broker-client/transport-socket.js +13 -5
  175. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  176. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  177. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  178. package/dist/core/canvas/attention.d.ts +2 -5
  179. package/dist/core/canvas/attention.js +15 -64
  180. package/dist/core/canvas/boot.d.ts +6 -7
  181. package/dist/core/canvas/boot.js +5 -9
  182. package/dist/core/canvas/canvas.d.ts +13 -109
  183. package/dist/core/canvas/canvas.js +140 -401
  184. package/dist/core/canvas/crons.d.ts +2 -0
  185. package/dist/core/canvas/crons.js +16 -15
  186. package/dist/core/canvas/daemon-owner.js +10 -0
  187. package/dist/core/canvas/db.d.ts +2 -2
  188. package/dist/core/canvas/db.js +2 -18
  189. package/dist/core/canvas/deliveries.d.ts +104 -0
  190. package/dist/core/canvas/deliveries.js +354 -0
  191. package/dist/core/canvas/executions.d.ts +29 -0
  192. package/dist/core/canvas/executions.js +68 -0
  193. package/dist/core/canvas/history.js +35 -35
  194. package/dist/core/canvas/index.d.ts +3 -1
  195. package/dist/core/canvas/index.js +3 -1
  196. package/dist/core/canvas/migrations.js +20 -12
  197. package/dist/core/canvas/nav-model.js +1 -3
  198. package/dist/core/canvas/nav-render.d.ts +4 -4
  199. package/dist/core/canvas/nav-render.js +11 -21
  200. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  201. package/dist/core/canvas/node-sql-migration.js +261 -0
  202. package/dist/core/canvas/paths.d.ts +2 -16
  203. package/dist/core/canvas/paths.js +2 -31
  204. package/dist/core/canvas/recovery.d.ts +76 -0
  205. package/dist/core/canvas/recovery.js +588 -0
  206. package/dist/core/canvas/remote-canvas-source.js +4 -0
  207. package/dist/core/canvas/render-source.d.ts +16 -27
  208. package/dist/core/canvas/render-source.js +16 -45
  209. package/dist/core/canvas/sessions.d.ts +33 -0
  210. package/dist/core/canvas/sessions.js +78 -0
  211. package/dist/core/canvas/types.d.ts +33 -33
  212. package/dist/core/canvas/types.js +2 -5
  213. package/dist/core/canvas/waits.d.ts +18 -0
  214. package/dist/core/canvas/waits.js +70 -0
  215. package/dist/core/canvas/worktrees.d.ts +40 -0
  216. package/dist/core/canvas/worktrees.js +93 -0
  217. package/dist/core/command.js +11 -0
  218. package/dist/core/config.d.ts +17 -12
  219. package/dist/core/config.js +106 -155
  220. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  221. package/dist/core/feed/feed.d.ts +0 -6
  222. package/dist/core/feed/feed.js +68 -56
  223. package/dist/core/feed/inbox.d.ts +7 -81
  224. package/dist/core/feed/inbox.js +46 -378
  225. package/dist/core/feed/messages.d.ts +58 -0
  226. package/dist/core/feed/messages.js +217 -0
  227. package/dist/core/fs-utils.d.ts +4 -0
  228. package/dist/core/fs-utils.js +21 -1
  229. package/dist/core/human/answer.d.ts +1 -1
  230. package/dist/core/human/answer.js +1 -1
  231. package/dist/core/human/claim.d.ts +1 -1
  232. package/dist/core/human/claim.js +2 -4
  233. package/dist/core/human/component-docs.js +1 -1
  234. package/dist/core/human/feedback-companion.js +3 -2
  235. package/dist/core/human/feedback.d.ts +5 -8
  236. package/dist/core/human/feedback.js +57 -85
  237. package/dist/core/human/page-authoring.d.ts +30 -0
  238. package/dist/core/human/page-authoring.js +19 -0
  239. package/dist/core/human/requests.d.ts +136 -0
  240. package/dist/core/human/requests.js +515 -0
  241. package/dist/core/human/root.d.ts +0 -7
  242. package/dist/core/human/root.js +0 -17
  243. package/dist/core/human/scan.d.ts +2 -7
  244. package/dist/core/human/scan.js +48 -132
  245. package/dist/core/human/summary.d.ts +1 -1
  246. package/dist/core/human/summary.js +1 -1
  247. package/dist/core/human/tickets.d.ts +23 -83
  248. package/dist/core/human/tickets.js +92 -243
  249. package/dist/core/human/types.d.ts +5 -1
  250. package/dist/core/installed-plugins.js +8 -3
  251. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  252. package/dist/core/locked-file.d.ts +6 -3
  253. package/dist/core/locked-file.js +36 -9
  254. package/dist/core/memory/extensions.js +2 -0
  255. package/dist/core/preview-registry.js +0 -1
  256. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  257. package/dist/core/profiles/default-binding.d.ts +0 -4
  258. package/dist/core/profiles/default-binding.js +12 -58
  259. package/dist/core/profiles/env-store.js +8 -3
  260. package/dist/core/profiles/manifest.d.ts +2 -37
  261. package/dist/core/profiles/manifest.js +35 -161
  262. package/dist/core/profiles/select.js +3 -0
  263. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  264. package/dist/core/review/__tests__/comments.test.js +28 -10
  265. package/dist/core/review/__tests__/remap.test.js +28 -10
  266. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  267. package/dist/core/review/birth.d.ts +1 -1
  268. package/dist/core/review/birth.js +34 -5
  269. package/dist/core/review/comments.js +1 -1
  270. package/dist/core/review/document.js +1 -1
  271. package/dist/core/review/project.d.ts +5 -1
  272. package/dist/core/review/project.js +14 -36
  273. package/dist/core/review/realize.d.ts +3 -6
  274. package/dist/core/review/realize.js +51 -168
  275. package/dist/core/review/remap.js +1 -1
  276. package/dist/core/review/stage.d.ts +3 -2
  277. package/dist/core/review/stage.js +2 -3
  278. package/dist/core/review/store.d.ts +3 -21
  279. package/dist/core/review/store.js +140 -186
  280. package/dist/core/review/types.d.ts +0 -1
  281. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  282. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  283. package/dist/core/runtime/broker/auth-reload.js +9 -10
  284. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  285. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  286. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  287. package/dist/core/runtime/broker/extension-abort.js +11 -0
  288. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  289. package/dist/core/runtime/broker/fault-retry.js +142 -95
  290. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  291. package/dist/core/runtime/broker/inbox.js +4 -92
  292. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  293. package/dist/core/runtime/broker/rebind.js +21 -12
  294. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  295. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  296. package/dist/core/runtime/broker.js +28 -22
  297. package/dist/core/runtime/close.d.ts +3 -9
  298. package/dist/core/runtime/close.js +16 -27
  299. package/dist/core/runtime/fault.d.ts +3 -93
  300. package/dist/core/runtime/fault.js +9 -419
  301. package/dist/core/runtime/kickoff.d.ts +3 -6
  302. package/dist/core/runtime/kickoff.js +12 -18
  303. package/dist/core/runtime/launch.js +1 -1
  304. package/dist/core/runtime/lifecycle.js +40 -7
  305. package/dist/core/runtime/placement.js +2 -2
  306. package/dist/core/runtime/recycle.js +8 -2
  307. package/dist/core/runtime/reset.js +22 -6
  308. package/dist/core/runtime/revive.js +226 -218
  309. package/dist/core/runtime/spawn.js +14 -3
  310. package/dist/core/scoped-state/db.d.ts +18 -0
  311. package/dist/core/scoped-state/db.js +135 -0
  312. package/dist/core/scoped-state/migrate.d.ts +8 -0
  313. package/dist/core/scoped-state/migrate.js +181 -0
  314. package/dist/core/scoped-state/paths.d.ts +9 -0
  315. package/dist/core/scoped-state/paths.js +27 -0
  316. package/dist/core/scoped-state/profiles.d.ts +12 -0
  317. package/dist/core/scoped-state/profiles.js +62 -0
  318. package/dist/core/scoped-state/providers.d.ts +16 -0
  319. package/dist/core/scoped-state/providers.js +8 -0
  320. package/dist/core/scoped-state/schema.d.ts +3 -0
  321. package/dist/core/scoped-state/schema.js +26 -0
  322. package/dist/core/scoped-state/settings.d.ts +28 -0
  323. package/dist/core/scoped-state/settings.js +82 -0
  324. package/dist/core/subscription-state.d.ts +29 -48
  325. package/dist/core/subscription-state.js +176 -712
  326. package/dist/core/worktree-close.js +4 -4
  327. package/dist/core/worktree-mutation-async.js +2 -2
  328. package/dist/core/worktree-sweep.js +11 -16
  329. package/dist/core/worktree.js +6 -12
  330. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  331. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  332. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  333. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  334. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  335. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
  336. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  337. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  338. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  339. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  340. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  341. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  342. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  343. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  344. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  345. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
  346. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  347. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  348. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  349. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  350. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  351. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
  352. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  353. package/dist/daemon/api/bridge.js +1 -1
  354. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  355. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  356. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  357. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  358. package/dist/daemon/api/handlers/canvas.js +2 -44
  359. package/dist/daemon/api/handlers/daemon.js +41 -0
  360. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  361. package/dist/daemon/api/handlers/health.js +10 -5
  362. package/dist/daemon/api/handlers/human-requests.js +168 -188
  363. package/dist/daemon/api/handlers/human.js +3 -151
  364. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  365. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  366. package/dist/daemon/api/handlers/inbox.js +21 -120
  367. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  368. package/dist/daemon/api/handlers/messages.js +73 -15
  369. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  370. package/dist/daemon/api/handlers/nodes.js +7 -13
  371. package/dist/daemon/api/handlers/reviews.js +4 -29
  372. package/dist/daemon/api/map.d.ts +25 -3
  373. package/dist/daemon/api/map.js +84 -14
  374. package/dist/daemon/api/server.js +68 -9
  375. package/dist/daemon/control.d.ts +18 -0
  376. package/dist/daemon/cron/disposition.d.ts +2 -0
  377. package/dist/daemon/cron/disposition.js +8 -5
  378. package/dist/daemon/cron/settle.js +37 -13
  379. package/dist/daemon/cron/sinks.d.ts +18 -1
  380. package/dist/daemon/cron/sinks.js +47 -93
  381. package/dist/daemon/crtrd-cli.js +4 -19
  382. package/dist/daemon/crtrd.d.ts +2 -0
  383. package/dist/daemon/crtrd.js +206 -81
  384. package/dist/daemon/fleet.js +5 -6
  385. package/dist/daemon/human/finish.d.ts +12 -24
  386. package/dist/daemon/human/finish.js +88 -285
  387. package/dist/daemon/human/settle.d.ts +40 -0
  388. package/dist/daemon/human/settle.js +89 -0
  389. package/dist/daemon/human/sweep.d.ts +20 -5
  390. package/dist/daemon/human/sweep.js +56 -60
  391. package/dist/daemon/manage.d.ts +21 -16
  392. package/dist/daemon/manage.js +85 -51
  393. package/dist/daemon/messaging/node-message.d.ts +1 -1
  394. package/dist/daemon/messaging/node-message.js +9 -12
  395. package/dist/daemon/park-activity.js +3 -3
  396. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  397. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  398. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  399. package/dist/daemon/reconcilers/controller-death.js +6 -18
  400. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  401. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  402. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  403. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  404. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  405. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  406. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  407. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  408. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  409. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  410. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  411. package/dist/daemon/review/companion.js +5 -11
  412. package/dist/daemon/review/deliver.d.ts +1 -5
  413. package/dist/daemon/review/deliver.js +3 -52
  414. package/dist/daemon/review/finish.d.ts +2 -27
  415. package/dist/daemon/review/finish.js +79 -263
  416. package/dist/daemon/review/sweep.d.ts +3 -10
  417. package/dist/daemon/review/sweep.js +13 -131
  418. package/dist/daemon/startup-policy.d.ts +0 -5
  419. package/dist/daemon/startup-policy.js +0 -5
  420. package/dist/index.d.ts +2 -1
  421. package/dist/index.js +1 -1
  422. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  423. package/dist/migrations/__tests__/activation.test.js +9 -6
  424. package/dist/migrations/activation.d.ts +3 -2
  425. package/dist/migrations/activation.js +2 -4
  426. package/dist/migrations/corpus.d.ts +2 -5
  427. package/dist/migrations/corpus.js +9 -20
  428. package/dist/migrations/runner.d.ts +3 -1
  429. package/dist/migrations/runner.js +6 -2
  430. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  431. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  432. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  433. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  434. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  435. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  436. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  437. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  438. package/dist/pi-extensions/canvas-stophook.js +47 -31
  439. package/dist/shared/env.d.ts +1 -0
  440. package/dist/shared/env.js +3 -0
  441. package/package.json +1 -1
  442. package/runtime.lock.json +5 -5
  443. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  444. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  445. package/dist/clients/inbox/review-visibility.js +0 -19
  446. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  447. package/dist/commands/canvas-rebuild-index.js +0 -46
  448. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  449. package/dist/core/__tests__/human-deliver.test.js +0 -256
  450. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  451. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  452. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  453. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  454. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  455. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  456. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  457. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  458. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  459. package/dist/core/canvas/human-deliveries.js +0 -75
  460. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  461. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  462. package/dist/core/feed/passive.d.ts +0 -3
  463. package/dist/core/feed/passive.js +0 -53
  464. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  465. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  466. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  467. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  468. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  469. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  470. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  471. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  472. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  473. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  474. package/dist/core/review/ticket-filter.d.ts +0 -17
  475. package/dist/core/review/ticket-filter.js +0 -21
  476. package/dist/core/runtime/broker/passive.d.ts +0 -17
  477. package/dist/core/runtime/broker/passive.js +0 -44
  478. package/dist/core/runtime/busy.d.ts +0 -46
  479. package/dist/core/runtime/busy.js +0 -150
  480. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  481. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  482. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  483. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  484. package/dist/daemon/human/deliver-action.d.ts +0 -3
  485. package/dist/daemon/human/deliver-action.js +0 -9
  486. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  487. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  488. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  489. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  490. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  491. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  492. package/dist/daemon/startup-block-marker.d.ts +0 -25
  493. package/dist/daemon/startup-block-marker.js +0 -99
  494. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  495. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  496. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
  497. /package/dist/core/__tests__/{human-deliver.test.d.ts → integration/fresh-home-commands.test.d.ts} +0 -0
@@ -1,70 +1,66 @@
1
- import { existsSync, readdirSync, statSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { getRow } from '../../core/canvas/canvas.js';
4
- import { getReviewByBridge } from '../../core/review/store.js';
5
- import { emitEvent } from '../../core/events/emit.js';
6
- import { reviewPath } from '../../core/human/convention.js';
7
- import { ticketsRoot } from '../../core/human/root.js';
8
- import { readTicketResult } from '../../core/human/tickets.js';
9
- import { cancelHumanTicket, deliverTerminalResult, releaseActionDelivery, settleFeedbackCompanion } from './finish.js';
10
- /** No surface can open a review ticket that has no canonical row, so cancellation is its
11
- * only reachable end. Boot runs this before API traffic, and a row is committed before its
12
- * ticket is published, so an in-flight review is never mistaken for one of these. */
13
- const UNBACKED_REVIEW_REASON = 'this review predates canonical review records and cannot be opened; re-file the review';
14
- function isTerminalBridge(status) {
15
- return status === 'done' || status === 'dead' || status === 'canceled';
16
- }
17
- /** Deliver terminal ticket results left behind before their bridge could finish. */
18
- export async function reconcileUndeliveredTickets() {
19
- let scanned = 0;
20
- let delivered = 0;
1
+ import { existsSync, readdirSync, rmSync } from 'node:fs';
2
+ import { reviewsRoot } from '../../core/canvas/paths.js';
3
+ import { listBindingRequests, listRequests, listUnprojectedReviews, markProjectionError, } from '../../core/human/requests.js';
4
+ import { projectAndRetire } from './settle.js';
5
+ function errorText(error) { return error instanceof Error ? error.message : String(error); }
6
+ /**
7
+ * The one startup repair pass for SQL-authoritative pages and reviews. It does
8
+ * not infer a missing notification or action delivery: settlement created both
9
+ * atomically. The two remaining filesystem effects retain their durable marks.
10
+ */
11
+ /** Direct outbox rows are created in the settlement transaction; there is no separate ticket-delivery repair pass. */
12
+ export async function reconcileUndeliveredTickets() { }
13
+ export async function sweepHumanRequests(effects) {
14
+ let realized = 0;
15
+ let collected = 0;
16
+ let reprojected = 0;
17
+ let retired = 0;
21
18
  let failed = 0;
22
- const root = ticketsRoot();
23
- for (const entry of readdirSync(root)) {
19
+ for (const request of listBindingRequests()) {
24
20
  try {
25
- const dir = join(root, entry);
26
- if (!statSync(dir).isDirectory())
27
- continue;
28
- scanned += 1;
29
- const canonical = getReviewByBridge(entry) !== null;
30
- const ticketResult = readTicketResult(dir);
31
- if (ticketResult === null) {
32
- if (!canonical && existsSync(reviewPath(dir))) {
33
- await cancelHumanTicket(entry, { reason: UNBACKED_REVIEW_REASON, actor: 'system' });
34
- delivered += 1;
21
+ await effects.realizeReview(request.request_id);
22
+ realized += 1;
23
+ }
24
+ catch {
25
+ // A missing branch is a valid binding-recovery outcome. Its terminal row
26
+ // is written by realization; other failures remain visible for next boot.
27
+ failed += 1;
28
+ }
29
+ }
30
+ try {
31
+ const live = new Set(listRequests({ kind: 'review' }).map((request) => request.request_id));
32
+ if (existsSync(reviewsRoot())) {
33
+ for (const entry of readdirSync(reviewsRoot(), { withFileTypes: true })) {
34
+ if (entry.isDirectory() && !live.has(entry.name)) {
35
+ rmSync(`${reviewsRoot()}/${entry.name}`, { recursive: true, force: true });
36
+ collected += 1;
35
37
  }
36
- continue;
37
38
  }
38
- // Canonical reviews own terminal delivery and bridge retirement. Their
39
- // finisher retries only when its winning operation is explicitly repeated.
40
- if (canonical)
41
- continue;
42
- // A terminal page ticket retires its feedback companion whether or not a
43
- // reply bridge exists: inbox-only pages have no bridge row, yet the
44
- // daemon can die between result publication and companion retirement.
45
- await settleFeedbackCompanion(entry, ticketResult.kind === 'canceled' ? 'canceled' : 'approved');
46
- // A settlement that died before enqueueing its completion action still
47
- // owes that delivery; the insert is idempotent on the request id.
48
- releaseActionDelivery(entry);
49
- // The ROW is what carries status; `getNode` would answer from a pruned
50
- // bridge's surviving meta.json with no status at all.
51
- const bridge = getRow(entry);
52
- if (bridge === null || isTerminalBridge(bridge.status))
53
- continue;
54
- if (await deliverTerminalResult(entry))
55
- delivered += 1;
39
+ }
40
+ }
41
+ catch {
42
+ failed += 1;
43
+ }
44
+ for (const request of listUnprojectedReviews()) {
45
+ try {
46
+ await projectAndRetire(request, { project: effects.projectReview, retire: effects.retireCompanion });
47
+ reprojected += 1;
56
48
  }
57
49
  catch (error) {
50
+ markProjectionError(request.request_id, errorText(error));
51
+ failed += 1;
52
+ }
53
+ }
54
+ for (const request of listRequests({ state: 'settled' })) {
55
+ if (request.companion_node_id === null || request.companion_retired_at !== null)
56
+ continue;
57
+ try {
58
+ await projectAndRetire(request, { retire: effects.retireCompanion });
59
+ retired += 1;
60
+ }
61
+ catch {
58
62
  failed += 1;
59
- emitEvent({
60
- level: 'error',
61
- event: 'human.delivery.reconcile_failed',
62
- node_id: entry,
63
- error,
64
- });
65
63
  }
66
64
  }
67
- const result = { scanned, delivered, failed };
68
- emitEvent({ level: 'info', event: 'human.delivery.reconciled', fields: result });
69
- return result;
65
+ return { realized, collected, reprojected, retired, failed };
70
66
  }
@@ -1,3 +1,4 @@
1
+ import { type HealthDTO, type StartupPhaseDTO } from '../api/index.js';
1
2
  /** Env keys that must NEVER reach the daemon process. Restarting crtrd is
2
3
  * overwhelmingly done from inside an agent node's own bash tool (`crtr sys
3
4
  * daemon stop && start`), which inherits that node's FULL env — its identity
@@ -39,6 +40,10 @@ export declare function sweepStrayDaemons(keepPid: number | null): number[];
39
40
  /** Replace PID 1 with the selected daemon generation. Docker keeps a container
40
41
  * alive only while PID 1 lives, so a handover there must preserve its pid. */
41
42
  export declare function execDaemon(): Promise<never>;
43
+ export interface SpawnDaemonOptions {
44
+ /** The phase that proves a daemon launched by this call is available. */
45
+ targetPhase?: 'ready' | 'prepared';
46
+ }
42
47
  export interface SpawnDaemonResult {
43
48
  /** True when a new daemon process was spawned. */
44
49
  started: boolean;
@@ -52,9 +57,12 @@ export interface SpawnDaemonResult {
52
57
  serving?: false;
53
58
  /** The final `/healthz` probe failure when a live owner misses readiness. */
54
59
  probe_error?: string;
60
+ /** The observed phase when the daemon was available. */
61
+ startup_phase?: StartupPhaseDTO;
55
62
  }
56
- /** Make one `/healthz` observation without client-side recovery. The caller
57
- * owns the availability window, so this probe cannot extend it. */
63
+ /** Make one `/healthz` observation without client-side recovery. The caller owns the availability window, so this probe cannot extend it. */
64
+ export declare function probeDaemonHealth(timeoutMs?: number): Promise<HealthDTO>;
65
+ /** True only when one mandatory Unix API socket probe answers the existing ready health contract. */
58
66
  export declare function probeDaemonServing(timeoutMs?: number): Promise<void>;
59
67
  /** True only when one mandatory Unix API socket probe answers `/healthz`.
60
68
  * A pidfile identifies an owner; it is never readiness. */
@@ -72,13 +80,16 @@ export interface DaemonStartupDeps extends DaemonWaitDeps {
72
80
  } | null;
73
81
  isDaemonServing?: () => Promise<boolean>;
74
82
  probeDaemonServing?: (timeoutMs: number) => Promise<void>;
83
+ probeDaemonHealth?: (timeoutMs: number) => Promise<HealthDTO>;
75
84
  }
76
- /** Thrown when the spawned daemon exited on `DAEMON_EXIT_STARTUP_BLOCKED` — a
77
- * standing condition (a blocked on-disk migration) that the very next spawn
78
- * would hit identically. Callers must stop retrying and surface the repair;
79
- * the daemon has already written the detail to `crtrd.err`. */
85
+ /** A live daemon has bound its repair socket but cannot serve normal work
86
+ * until its corpus migration succeeds. */
80
87
  export declare class DaemonStartupBlockedError extends Error {
81
- constructor(pid: number);
88
+ constructor(pid: number, message: string);
89
+ }
90
+ /** A preparation request cannot succeed unless the live daemon explicitly reports prepared. */
91
+ export declare class DaemonNotPreparedError extends Error {
92
+ constructor(pid: number, startupPhase: StartupPhaseDTO | undefined);
82
93
  }
83
94
  /** Tiny bounded post-spawn guard: wait for the daemon pidfile and live pid to
84
95
  * appear before reporting success. This catches the "started:true but not yet
@@ -93,7 +104,7 @@ export declare class DaemonStartupBlockedError extends Error {
93
104
  *
94
105
  * Returns null when the spawned pid owns the pidfile; returns a different live
95
106
  * daemon pid when startup lost the race to an already-running daemon. */
96
- export declare function verifyDaemonStartup(pid: number, timeoutMs?: number, deps?: DaemonStartupDeps): Promise<number | null>;
107
+ export declare function verifyDaemonStartup(pid: number, timeoutMs?: number, deps?: DaemonStartupDeps, targetPhase?: 'ready' | 'prepared'): Promise<number | null>;
97
108
  /** Wait until a signaled daemon has exited and relinquished its pidfile. This
98
109
  * makes a following start safe to claim the singleton rather than racing its
99
110
  * previous owner. */
@@ -119,7 +130,7 @@ export declare function stopDaemonProcess(pid: number, timeoutMs?: number, deps?
119
130
  *
120
131
  * If the daemon is already running, returns {started:false, existing_pid}.
121
132
  * If spawning fails (e.g. missing dist — run `npm run build` first), throws. */
122
- export declare function spawnDaemon(): Promise<SpawnDaemonResult>;
133
+ export declare function spawnDaemon(opts?: SpawnDaemonOptions): Promise<SpawnDaemonResult>;
123
134
  /** Whether autostart is suppressed for this invocation. `--no-autostart` or
124
135
  * `CRTR_NO_DAEMON_AUTOSTART=1` forces fail-loud on a cold socket (spec §7.1) —
125
136
  * for environments that manage crtrd externally and want the diagnostic, and
@@ -134,13 +145,7 @@ export declare function spawnDaemon(): Promise<SpawnDaemonResult>;
134
145
  * explicit `crtr sys daemon start` calls `spawnDaemon` directly and is
135
146
  * deliberately unaffected. */
136
147
  export declare function autostartDisabled(): boolean;
137
- /** Why implicit autostart is currently suppressed, or null when it is free to
138
- * run. Set only by a `DaemonStartupBlockedError`, which no retry can clear —
139
- * an explicit `crtr sys daemon start` still calls `spawnDaemon` directly and
140
- * reports the failure in full. */
141
- export declare function daemonAutostartSuppression(): string | null;
142
- /** Clear the suppression and the backoff — for a caller that has repaired the
143
- * standing condition and wants autostart live again without a new process. */
148
+ /** Reset in-process spawn backoff after a daemon is observed alive. */
144
149
  export declare function resetDaemonAutostart(): void;
145
150
  export interface EnsureDaemonDeps {
146
151
  isDaemonRunning?: () => boolean;
@@ -14,8 +14,7 @@ import { hostExecPath } from '../core/runtime/branded-host.js';
14
14
  // Pulled from the lean db-free pidfile module (NOT crtrd.js, whose module graph
15
15
  // reaches openDb) so the CLI daemon front door stays canvas.db-free (plan B-0).
16
16
  import { isDaemonRunning, readPidfile, isPidAlive } from './pidfile.js';
17
- import { DAEMON_EXIT_STARTUP_BLOCKED, DAEMON_STARTUP_WINDOW_MS } from './startup-policy.js';
18
- import { clearDaemonStartupBlocked, daemonStartupBlocked } from './startup-block-marker.js';
17
+ import { DAEMON_STARTUP_WINDOW_MS } from './startup-policy.js';
19
18
  import { exclusiveLockOwnerPid } from '../core/exclusive-lock.js';
20
19
  import { onDiskMigrationLockPath } from '../core/canvas/paths.js';
21
20
  import { envNoDaemonAutostart } from '../shared/env.js';
@@ -208,11 +207,18 @@ const DAEMON_VERIFY_POLL_MS = 10;
208
207
  /** A status/start readiness probe must not hang behind a daemon that accepts a
209
208
  * Unix connection but never answers it. */
210
209
  const DAEMON_HEALTH_PROBE_TIMEOUT_MS = 1_000;
211
- /** Make one `/healthz` observation without client-side recovery. The caller
212
- * owns the availability window, so this probe cannot extend it. */
213
- export async function probeDaemonServing(timeoutMs = DAEMON_HEALTH_PROBE_TIMEOUT_MS) {
210
+ /** Make one `/healthz` observation without client-side recovery. The caller owns the availability window, so this probe cannot extend it. */
211
+ export async function probeDaemonHealth(timeoutMs = DAEMON_HEALTH_PROBE_TIMEOUT_MS) {
214
212
  const probeTimeoutMs = Math.min(timeoutMs, DAEMON_HEALTH_PROBE_TIMEOUT_MS);
215
- const health = await CrtrClient.forLocalSocket({ autostart: false, timeoutMs: probeTimeoutMs }).probeHealthz(probeTimeoutMs);
213
+ return CrtrClient.forLocalSocket({ autostart: false, timeoutMs: probeTimeoutMs }).probeHealthz(probeTimeoutMs);
214
+ }
215
+ /** True only when one mandatory Unix API socket probe answers the existing ready health contract. */
216
+ export async function probeDaemonServing(timeoutMs = DAEMON_HEALTH_PROBE_TIMEOUT_MS) {
217
+ const health = await probeDaemonHealth(timeoutMs);
218
+ if (health.startup_blocked !== undefined) {
219
+ const pid = readPidfile() ?? process.pid;
220
+ throw new DaemonStartupBlockedError(pid, health.startup_blocked);
221
+ }
216
222
  if (!health.ok)
217
223
  throw new Error('crtrd health check reported not ready');
218
224
  }
@@ -232,16 +238,23 @@ function sleepMs(ms) {
232
238
  setTimeout(resolve, ms);
233
239
  });
234
240
  }
235
- /** Thrown when the spawned daemon exited on `DAEMON_EXIT_STARTUP_BLOCKED` — a
236
- * standing condition (a blocked on-disk migration) that the very next spawn
237
- * would hit identically. Callers must stop retrying and surface the repair;
238
- * the daemon has already written the detail to `crtrd.err`. */
241
+ /** A live daemon has bound its repair socket but cannot serve normal work
242
+ * until its corpus migration succeeds. */
239
243
  export class DaemonStartupBlockedError extends Error {
240
- constructor(pid) {
241
- super(`daemon ${pid} exited: startup is blocked by a standing condition that retrying cannot clear. See crtrd.err, then run \`crtr sys migrate\`.`);
244
+ constructor(pid, message) {
245
+ super(`daemon ${pid} is running but normal startup is blocked: ${message}`);
242
246
  this.name = 'DaemonStartupBlockedError';
243
247
  }
244
248
  }
249
+ /** A preparation request cannot succeed unless the live daemon explicitly reports prepared. */
250
+ export class DaemonNotPreparedError extends Error {
251
+ constructor(pid, startupPhase) {
252
+ super(startupPhase === undefined
253
+ ? `daemon ${pid} is serving but did not report the prepared startup phase`
254
+ : `daemon ${pid} reported startup phase ${startupPhase}, not prepared`);
255
+ this.name = 'DaemonNotPreparedError';
256
+ }
257
+ }
245
258
  /** Tiny bounded post-spawn guard: wait for the daemon pidfile and live pid to
246
259
  * appear before reporting success. This catches the "started:true but not yet
247
260
  * plausibly alive" race without turning startup into a retry loop.
@@ -255,7 +268,7 @@ export class DaemonStartupBlockedError extends Error {
255
268
  *
256
269
  * Returns null when the spawned pid owns the pidfile; returns a different live
257
270
  * daemon pid when startup lost the race to an already-running daemon. */
258
- export async function verifyDaemonStartup(pid, timeoutMs = DAEMON_STARTUP_WINDOW_MS, deps = {}) {
271
+ export async function verifyDaemonStartup(pid, timeoutMs = DAEMON_STARTUP_WINDOW_MS, deps = {}, targetPhase = 'ready') {
259
272
  const read = deps.readPidfile ?? readPidfile;
260
273
  const alive = deps.isPidAlive ?? isPidAlive;
261
274
  const exited = deps.childExited;
@@ -268,6 +281,7 @@ export async function verifyDaemonStartup(pid, timeoutMs = DAEMON_STARTUP_WINDOW
268
281
  }
269
282
  await probeDaemonServing(remainingMs);
270
283
  });
284
+ const probeHealth = deps.probeDaemonHealth ?? probeDaemonHealth;
271
285
  const sleep = deps.sleepMs ?? sleepMs;
272
286
  const now = deps.now ?? Date.now;
273
287
  let owner = null;
@@ -285,18 +299,39 @@ export async function verifyDaemonStartup(pid, timeoutMs = DAEMON_STARTUP_WINDOW
285
299
  if (exitState !== null && exitState !== undefined) {
286
300
  if (exitState.signal !== null)
287
301
  terminalError = new Error(`daemon ${pid} exited before becoming ready (by ${exitState.signal})`);
288
- else if (exitState.code === DAEMON_EXIT_STARTUP_BLOCKED)
289
- terminalError = new DaemonStartupBlockedError(pid);
290
302
  else if (exitState.code !== 0)
291
303
  terminalError = new Error(`daemon ${pid} exited before becoming ready (with exit code ${exitState.code ?? '?'})`);
292
- // A clean exit can have lost the ownership claim, so keep observing.
293
304
  if (terminalError !== null)
294
305
  throw terminalError;
295
306
  }
296
307
  throw new Error(`daemon ${pid} has no live pidfile owner`);
297
308
  }
298
- await probeServing(remainingMs);
299
- owner = candidate;
309
+ if (targetPhase === 'ready') {
310
+ try {
311
+ await probeServing(remainingMs);
312
+ }
313
+ catch (error) {
314
+ if (error instanceof DaemonStartupBlockedError)
315
+ terminalError = error;
316
+ throw error;
317
+ }
318
+ owner = candidate;
319
+ return;
320
+ }
321
+ const health = await probeHealth(remainingMs);
322
+ if (health.startup_phase === 'prepared') {
323
+ owner = candidate;
324
+ return;
325
+ }
326
+ if (health.startup_phase === 'blocked') {
327
+ terminalError = new DaemonStartupBlockedError(candidate, health.startup_blocked ?? 'on-disk migration is blocked');
328
+ throw terminalError;
329
+ }
330
+ if (health.startup_phase === 'ready') {
331
+ terminalError = new DaemonNotPreparedError(candidate, health.startup_phase);
332
+ throw terminalError;
333
+ }
334
+ throw new Error(`daemon ${candidate} is ${health.startup_phase ?? 'not reporting a startup phase'}; waiting for prepared`);
300
335
  },
301
336
  });
302
337
  return owner === pid ? null : owner;
@@ -371,14 +406,37 @@ export async function stopDaemonProcess(pid, timeoutMs = DAEMON_SHUTDOWN_WINDOW_
371
406
  *
372
407
  * If the daemon is already running, returns {started:false, existing_pid}.
373
408
  * If spawning fails (e.g. missing dist — run `npm run build` first), throws. */
374
- export async function spawnDaemon() {
409
+ export async function spawnDaemon(opts = {}) {
410
+ const targetPhase = opts.targetPhase ?? 'ready';
375
411
  const recordedPid = readPidfile();
376
412
  if (recordedPid !== null && isPidAlive(recordedPid)) {
377
413
  try {
378
- await probeDaemonServing();
379
- return { started: false, existing_pid: recordedPid };
414
+ const health = await probeDaemonHealth();
415
+ if (targetPhase === 'prepared') {
416
+ if (health.startup_phase === 'prepared') {
417
+ return { started: false, existing_pid: recordedPid, startup_phase: health.startup_phase };
418
+ }
419
+ throw new DaemonNotPreparedError(recordedPid, health.startup_phase);
420
+ }
421
+ if (health.ok) {
422
+ return {
423
+ started: false,
424
+ existing_pid: recordedPid,
425
+ ...(health.startup_phase === undefined ? {} : { startup_phase: health.startup_phase }),
426
+ };
427
+ }
428
+ return {
429
+ started: false,
430
+ existing_pid: recordedPid,
431
+ running: true,
432
+ serving: false,
433
+ probe_error: health.startup_blocked ?? 'crtrd health check reported not ready',
434
+ ...(health.startup_phase === undefined ? {} : { startup_phase: health.startup_phase }),
435
+ };
380
436
  }
381
437
  catch (error) {
438
+ if (error instanceof DaemonNotPreparedError)
439
+ throw error;
382
440
  return {
383
441
  started: false,
384
442
  existing_pid: recordedPid,
@@ -415,7 +473,7 @@ export async function spawnDaemon() {
415
473
  // exactly instead of scanning the whole machine. crtrd-cli.ts's argv
416
474
  // parsing only looks for `--tcp`, so this extra pair of tokens is inert
417
475
  // there — it exists purely for `ps` visibility.
418
- const child = spawn(hostExecPath(), [...launchArgs, '--canvas-home', crtrHome()], {
476
+ const child = spawn(hostExecPath(), [...launchArgs, '--canvas-home', crtrHome(), ...(targetPhase === 'prepared' ? ['--prepare'] : [])], {
419
477
  detached: true,
420
478
  stdio: ['ignore', errFd, errFd],
421
479
  env: sanitizedDaemonEnv(),
@@ -444,7 +502,7 @@ export async function spawnDaemon() {
444
502
  try {
445
503
  existingPid = await verifyDaemonStartup(pid, DAEMON_STARTUP_WINDOW_MS, {
446
504
  childExited: () => exitState,
447
- });
505
+ }, targetPhase);
448
506
  }
449
507
  catch (error) {
450
508
  // Reap ONLY a child that is not doing the work. A child holding the on-disk
@@ -455,7 +513,7 @@ export async function spawnDaemon() {
455
513
  // corpus can legitimately outlast the window, and that daemon should be
456
514
  // allowed to finish and serve.
457
515
  const childOwnsMigrationLock = exclusiveLockOwnerPid(onDiskMigrationLockPath()) === pid;
458
- if (exitState === null && !childOwnsMigrationLock) {
516
+ if (exitState === null && !childOwnsMigrationLock && !(error instanceof DaemonStartupBlockedError)) {
459
517
  try {
460
518
  await stopDaemonProcess(pid, DAEMON_REAP_WINDOW_MS);
461
519
  }
@@ -464,9 +522,9 @@ export async function spawnDaemon() {
464
522
  throw error;
465
523
  }
466
524
  if (existingPid !== null) {
467
- return { started: false, existing_pid: existingPid };
525
+ return { started: false, existing_pid: existingPid, startup_phase: targetPhase };
468
526
  }
469
- return { started: true, pid };
527
+ return { started: true, pid, startup_phase: targetPhase };
470
528
  }
471
529
  // ensureDaemon — fire-and-forget front-door helper
472
530
  /** Whether autostart is suppressed for this invocation. `--no-autostart` or
@@ -495,24 +553,13 @@ const AUTOSTART_BACKOFF_MAX_MS = 60_000;
495
553
  let autostartInFlight = false;
496
554
  let autostartFailures = 0;
497
555
  let autostartNextAttemptAt = 0;
498
- let autostartBlocked = null;
499
556
  function autostartBackoffMs(failures) {
500
557
  return Math.min(AUTOSTART_BACKOFF_BASE_MS * 2 ** (failures - 1), AUTOSTART_BACKOFF_MAX_MS);
501
558
  }
502
- /** Why implicit autostart is currently suppressed, or null when it is free to
503
- * run. Set only by a `DaemonStartupBlockedError`, which no retry can clear —
504
- * an explicit `crtr sys daemon start` still calls `spawnDaemon` directly and
505
- * reports the failure in full. */
506
- export function daemonAutostartSuppression() {
507
- return autostartBlocked;
508
- }
509
- /** Clear the suppression and the backoff — for a caller that has repaired the
510
- * standing condition and wants autostart live again without a new process. */
559
+ /** Reset in-process spawn backoff after a daemon is observed alive. */
511
560
  export function resetDaemonAutostart() {
512
561
  autostartFailures = 0;
513
562
  autostartNextAttemptAt = 0;
514
- autostartBlocked = null;
515
- clearDaemonStartupBlocked();
516
563
  }
517
564
  /** Start the daemon if it is not already running. No-op if already up, if
518
565
  * autostart is suppressed for this invocation, while an attempt is in flight,
@@ -525,8 +572,6 @@ export function ensureDaemon(deps = {}) {
525
572
  const now = deps.now ?? Date.now;
526
573
  if (autostartDisabled())
527
574
  return;
528
- if (autostartBlocked !== null)
529
- return;
530
575
  if (autostartInFlight)
531
576
  return;
532
577
  if (now() < autostartNextAttemptAt)
@@ -535,15 +580,6 @@ export function ensureDaemon(deps = {}) {
535
580
  resetDaemonAutostart();
536
581
  return;
537
582
  }
538
- // The durable half of the gate. Everything above is module state, which caps
539
- // exactly one process; the storm is made of many. A daemon that already
540
- // proved startup is blocked left this marker in the canvas home, and it
541
- // retires itself as soon as the corpus it named changes.
542
- const blocked = daemonStartupBlocked();
543
- if (blocked !== null) {
544
- autostartBlocked = blocked;
545
- return;
546
- }
547
583
  autostartInFlight = true;
548
584
  void spawn().then(() => {
549
585
  autostartFailures = 0;
@@ -553,8 +589,6 @@ export function ensureDaemon(deps = {}) {
553
589
  // pre-build) must not break the calling command.
554
590
  autostartFailures += 1;
555
591
  autostartNextAttemptAt = now() + autostartBackoffMs(autostartFailures);
556
- if (error instanceof DaemonStartupBlockedError)
557
- autostartBlocked = error.message;
558
592
  }).finally(() => {
559
593
  autostartInFlight = false;
560
594
  });
@@ -14,7 +14,7 @@ export interface RuntimeMessageCard {
14
14
  }
15
15
  /**
16
16
  * Deliver one daemon-originated node message. Interactive messages use the live
17
- * broker when available; every other successful path is a durable inbox entry.
17
+ * broker when available; every other successful path is durable node mail.
18
18
  */
19
19
  export declare function deliverNodeMessage(args: {
20
20
  node_id: string;
@@ -1,7 +1,7 @@
1
1
  import { getNode } from '../../core/canvas/canvas.js';
2
2
  import { cancelCronsOnWake } from '../../core/canvas/crons.js';
3
3
  import { withFreshTerminalGuard } from '../../core/canvas/canvas.js';
4
- import { appendInbox } from '../../core/feed/inbox.js';
4
+ import { enqueueNodeMail } from '../../core/feed/messages.js';
5
5
  import { BrokerUnreachableError } from '../../core/runtime/broker-request.js';
6
6
  import { deliverLive } from '../../core/runtime/interactive-deliver.js';
7
7
  import { isBrokerLive } from '../../core/runtime/model-swap.js';
@@ -24,7 +24,7 @@ function guardRoutable(nodeId, body) {
24
24
  }
25
25
  /**
26
26
  * Deliver one daemon-originated node message. Interactive messages use the live
27
- * broker when available; every other successful path is a durable inbox entry.
27
+ * broker when available; every other successful path is durable node mail.
28
28
  */
29
29
  export async function deliverNodeMessage(args) {
30
30
  // A live interactive delivery has no durable append to guard, so establish
@@ -67,20 +67,17 @@ export async function deliverNodeMessage(args) {
67
67
  throw error;
68
68
  }
69
69
  }
70
- const entry = guardRoutable(args.node_id, () => appendInbox(args.node_id, {
71
- from: args.from ?? null,
72
- tier: args.mode === 'quiet' ? 'deferred' : 'normal',
70
+ const entry = guardRoutable(args.node_id, () => enqueueNodeMail(args.node_id, {
71
+ senderNodeId: args.from ?? null,
73
72
  kind: 'message',
74
73
  label: args.label,
75
- data: { ...args.data, body: args.body },
74
+ bodyJson: { ...args.data, body: args.body },
75
+ urgency: args.mode === 'quiet' ? 'deferred' : 'normal',
76
+ wake: 1,
76
77
  }));
77
- if (entry.tier !== 'deferred' || target.frozen_at !== null)
78
- cancelCronsOnWake(args.node_id);
79
- // The APPENDED entry's tier decides the wake: a quiet send to a terminal
80
- // target was raised out of `deferred` by the append, and must be delivered.
81
78
  let revived = false;
82
79
  let reason;
83
- if (entry.tier !== 'deferred') {
80
+ if (entry.urgency !== 'deferred') {
84
81
  target = getNode(args.node_id);
85
82
  if (target === null)
86
83
  deliveryUnroutable(args.node_id, 'origin_missing');
@@ -98,5 +95,5 @@ export async function deliverNodeMessage(args) {
98
95
  }
99
96
  }
100
97
  }
101
- return { entry_id: entry.entry_id, via: 'inbox', revived, ...(reason === undefined ? {} : { reason }) };
98
+ return { entry_id: entry.messageId, via: 'inbox', revived, ...(reason === undefined ? {} : { reason }) };
102
99
  }
@@ -1,7 +1,7 @@
1
1
  import { statSync } from 'node:fs';
2
2
  import { emitEvent } from '../core/events/emit.js';
3
- import { hasWakeMailSince } from '../core/feed/inbox.js';
4
- import { isBusy } from '../core/runtime/busy.js';
3
+ import { hasWakeMailSince } from '../core/canvas/deliveries.js';
4
+ import { readLiveTurn } from '../core/canvas/recovery.js';
5
5
  import { parkEvent } from './reconcilers/live-obligation.js';
6
6
  /** A main-session write after parking was armed is durable evidence of live
7
7
  * input even when the turn has since settled and the viewer detached. */
@@ -23,7 +23,7 @@ export function mainSessionActivitySince(sessionFile, startedAt) {
23
23
  * the verdict cannot race a viewer, live delivery, or durable wake. */
24
24
  export function shouldParkAfterSummary(nodeId, node, pending) {
25
25
  return !pending.activity
26
- && !isBusy(nodeId)
26
+ && readLiveTurn(nodeId)?.busy !== true
27
27
  && !mainSessionActivitySince(node.pi_session_file, pending.startedAt)
28
28
  && !hasWakeMailSince(nodeId, pending.startedAt)
29
29
  && parkEvent(nodeId, node.lifecycle) === 'park';
@@ -1,8 +1,7 @@
1
1
  import { bashJobPaths, expiredAutoBackgroundBashJobs, formatBashElapsed, stopBackgroundBashJob, } from '../../core/bash-jobs.js';
2
2
  import { isSafeNodeId } from '../../core/canvas/paths.js';
3
3
  import { contextDir } from '../../core/canvas/paths.js';
4
- import { cancelCronsOnWake } from '../../core/canvas/crons.js';
5
- import { appendInbox } from '../../core/feed/inbox.js';
4
+ import { enqueueNodeMail } from '../../core/feed/messages.js';
6
5
  import { emitEvent } from '../../core/events/emit.js';
7
6
  import { operationIdContext } from '../../core/events/operation-id.js';
8
7
  import { isProfilePaused } from '../../core/profiles/manifest.js';
@@ -60,18 +59,18 @@ export class BashDeadlineReconciler {
60
59
  operationIdContext.fresh(() => {
61
60
  try {
62
61
  emitEvent({ level: 'info', event: 'bash_deadline.canceled', ...(isSafeNodeId(nodeId) ? { node_id: nodeId } : {}), fields: { job_id: jobId } });
63
- appendInbox(nodeId, {
64
- from: null,
65
- tier: 'urgent',
62
+ enqueueNodeMail(nodeId, {
63
+ senderNodeId: null,
64
+ urgency: 'urgent',
65
+ wake: 1,
66
66
  kind: 'urgent',
67
67
  label: 'background bash job canceled at its deadline',
68
- data: {
68
+ bodyJson: {
69
69
  body: `Background bash job ${jobId} was canceled after ${formatBashElapsed(elapsedMs)} because its background deadline passed and you did not claim it. ` +
70
70
  `Next time, give a long command more room up front (a larger inline timeout), or claim the job with \`crtr node bash extend ${jobId} --for <duration>\` before the deadline. ` +
71
71
  `Log: ${paths.jobLog} Command: ${paths.cmdSh}`,
72
72
  },
73
73
  });
74
- cancelCronsOnWake(nodeId);
75
74
  }
76
75
  catch (err) {
77
76
  emitEvent({ level: 'error', event: 'bash_deadline.notice_failed', ...(isSafeNodeId(nodeId) ? { node_id: nodeId } : {}), error: err, fields: { job_id: jobId } });
@@ -55,7 +55,7 @@ export declare class BrokerSupervisionReconciler {
55
55
  private readonly unattendedSince;
56
56
  private readonly sampleTree;
57
57
  private readonly signal;
58
- private knownAuthMtimeMs;
58
+ private knownCredentialRevision;
59
59
  private lastOverCapWarnAt;
60
60
  constructor(options?: BrokerSupervisionOptions);
61
61
  /** The fleet-wide preamble, run BEFORE the tick's row-list guard: an auth