@north-light/crouter 0.3.311 → 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 (520) 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 +17 -5
  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 +312 -1
  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 +62 -19
  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 +4 -3
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +8 -7
  141. package/dist/core/__tests__/on-read-nested-store.test.js +8 -7
  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 +35 -32
  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 +5 -4
  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 +19 -5
  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/command-hooks/artifact.d.ts +6 -0
  217. package/dist/core/command-hooks/artifact.js +22 -6
  218. package/dist/core/command-hooks/discovery.d.ts +2 -0
  219. package/dist/core/command-hooks/discovery.js +2 -0
  220. package/dist/core/command-hooks/lifecycle-catalog.d.ts +20 -2
  221. package/dist/core/command-hooks/lifecycle-catalog.js +7 -2
  222. package/dist/core/command-hooks/lifecycle-create.d.ts +4 -0
  223. package/dist/core/command-hooks/lifecycle-create.js +51 -0
  224. package/dist/core/command-hooks/schema.d.ts +2 -2
  225. package/dist/core/command-hooks/schema.js +5 -4
  226. package/dist/core/command-hooks/transport/exec-invoke.js +3 -1
  227. package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +18 -8
  228. package/dist/core/command-hooks/transport/exec-lifecycle.js +89 -30
  229. package/dist/core/config.d.ts +17 -12
  230. package/dist/core/config.js +106 -155
  231. package/dist/core/errors.d.ts +1 -0
  232. package/dist/core/errors.js +4 -0
  233. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  234. package/dist/core/feed/feed.d.ts +0 -6
  235. package/dist/core/feed/feed.js +68 -56
  236. package/dist/core/feed/inbox.d.ts +7 -81
  237. package/dist/core/feed/inbox.js +46 -378
  238. package/dist/core/feed/messages.d.ts +58 -0
  239. package/dist/core/feed/messages.js +217 -0
  240. package/dist/core/fs-utils.d.ts +4 -0
  241. package/dist/core/fs-utils.js +21 -1
  242. package/dist/core/human/answer.d.ts +1 -1
  243. package/dist/core/human/answer.js +1 -1
  244. package/dist/core/human/claim.d.ts +1 -1
  245. package/dist/core/human/claim.js +2 -4
  246. package/dist/core/human/component-docs.js +1 -1
  247. package/dist/core/human/feedback-companion.js +4 -3
  248. package/dist/core/human/feedback.d.ts +5 -8
  249. package/dist/core/human/feedback.js +57 -85
  250. package/dist/core/human/page-authoring.d.ts +30 -0
  251. package/dist/core/human/page-authoring.js +19 -0
  252. package/dist/core/human/requests.d.ts +136 -0
  253. package/dist/core/human/requests.js +515 -0
  254. package/dist/core/human/root.d.ts +0 -7
  255. package/dist/core/human/root.js +0 -17
  256. package/dist/core/human/scan.d.ts +2 -7
  257. package/dist/core/human/scan.js +48 -132
  258. package/dist/core/human/summary.d.ts +1 -1
  259. package/dist/core/human/summary.js +1 -1
  260. package/dist/core/human/tickets.d.ts +23 -83
  261. package/dist/core/human/tickets.js +92 -243
  262. package/dist/core/human/types.d.ts +5 -1
  263. package/dist/core/installed-plugins.js +8 -3
  264. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  265. package/dist/core/locked-file.d.ts +6 -3
  266. package/dist/core/locked-file.js +36 -9
  267. package/dist/core/memory/extensions.js +2 -0
  268. package/dist/core/preview-registry.js +0 -1
  269. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  270. package/dist/core/profiles/default-binding.d.ts +0 -4
  271. package/dist/core/profiles/default-binding.js +12 -58
  272. package/dist/core/profiles/env-store.js +8 -3
  273. package/dist/core/profiles/manifest.d.ts +2 -37
  274. package/dist/core/profiles/manifest.js +35 -161
  275. package/dist/core/profiles/select.js +3 -0
  276. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  277. package/dist/core/review/__tests__/comments.test.js +28 -10
  278. package/dist/core/review/__tests__/remap.test.js +28 -10
  279. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  280. package/dist/core/review/birth.d.ts +1 -1
  281. package/dist/core/review/birth.js +34 -5
  282. package/dist/core/review/comments.js +1 -1
  283. package/dist/core/review/companion.d.ts +1 -1
  284. package/dist/core/review/companion.js +2 -2
  285. package/dist/core/review/document.js +1 -1
  286. package/dist/core/review/project.d.ts +5 -1
  287. package/dist/core/review/project.js +14 -36
  288. package/dist/core/review/realize.d.ts +3 -6
  289. package/dist/core/review/realize.js +51 -168
  290. package/dist/core/review/remap.js +1 -1
  291. package/dist/core/review/stage.d.ts +3 -2
  292. package/dist/core/review/stage.js +2 -3
  293. package/dist/core/review/store.d.ts +3 -21
  294. package/dist/core/review/store.js +140 -186
  295. package/dist/core/review/types.d.ts +0 -1
  296. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  297. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  298. package/dist/core/runtime/broker/auth-reload.js +9 -10
  299. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  300. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  301. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  302. package/dist/core/runtime/broker/extension-abort.js +11 -0
  303. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  304. package/dist/core/runtime/broker/fault-retry.js +142 -95
  305. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  306. package/dist/core/runtime/broker/inbox.js +4 -92
  307. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  308. package/dist/core/runtime/broker/rebind.js +21 -12
  309. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  310. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  311. package/dist/core/runtime/broker.js +28 -22
  312. package/dist/core/runtime/close.d.ts +3 -9
  313. package/dist/core/runtime/close.js +16 -27
  314. package/dist/core/runtime/fault.d.ts +3 -93
  315. package/dist/core/runtime/fault.js +9 -419
  316. package/dist/core/runtime/kickoff.d.ts +3 -6
  317. package/dist/core/runtime/kickoff.js +12 -18
  318. package/dist/core/runtime/launch.js +1 -1
  319. package/dist/core/runtime/lifecycle.js +40 -7
  320. package/dist/core/runtime/nodes.d.ts +5 -1
  321. package/dist/core/runtime/nodes.js +26 -11
  322. package/dist/core/runtime/placement.js +2 -2
  323. package/dist/core/runtime/recycle.js +24 -14
  324. package/dist/core/runtime/reset.js +26 -7
  325. package/dist/core/runtime/revive.js +226 -217
  326. package/dist/core/runtime/spawn.d.ts +4 -1
  327. package/dist/core/runtime/spawn.js +43 -5
  328. package/dist/core/runtime/warm-pool.d.ts +2 -2
  329. package/dist/core/runtime/warm-pool.js +23 -3
  330. package/dist/core/scoped-state/db.d.ts +7 -0
  331. package/dist/core/scoped-state/db.js +92 -0
  332. package/dist/core/scoped-state/migrate.d.ts +11 -0
  333. package/dist/core/scoped-state/migrate.js +198 -0
  334. package/dist/core/scoped-state/paths.d.ts +9 -0
  335. package/dist/core/scoped-state/paths.js +27 -0
  336. package/dist/core/scoped-state/profiles.d.ts +12 -0
  337. package/dist/core/scoped-state/profiles.js +62 -0
  338. package/dist/core/scoped-state/providers.d.ts +16 -0
  339. package/dist/core/scoped-state/providers.js +8 -0
  340. package/dist/core/scoped-state/schema.d.ts +3 -0
  341. package/dist/core/scoped-state/schema.js +26 -0
  342. package/dist/core/scoped-state/settings.d.ts +28 -0
  343. package/dist/core/scoped-state/settings.js +82 -0
  344. package/dist/core/subscription-state.d.ts +29 -48
  345. package/dist/core/subscription-state.js +176 -712
  346. package/dist/core/worktree-close.js +4 -4
  347. package/dist/core/worktree-mutation-async.js +2 -2
  348. package/dist/core/worktree-sweep.js +11 -16
  349. package/dist/core/worktree.js +6 -12
  350. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  351. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  352. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  353. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  354. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  355. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +41 -17
  356. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  357. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  358. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  359. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  360. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  361. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  362. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  363. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  364. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  365. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +6 -3
  366. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  367. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  368. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  369. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  370. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  371. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +29 -43
  372. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  373. package/dist/daemon/api/bridge.js +1 -1
  374. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  375. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  376. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  377. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  378. package/dist/daemon/api/handlers/canvas.js +2 -44
  379. package/dist/daemon/api/handlers/daemon.js +41 -0
  380. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  381. package/dist/daemon/api/handlers/health.js +10 -5
  382. package/dist/daemon/api/handlers/human-requests.js +168 -188
  383. package/dist/daemon/api/handlers/human.js +3 -148
  384. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  385. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  386. package/dist/daemon/api/handlers/inbox.js +21 -120
  387. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  388. package/dist/daemon/api/handlers/messages.js +73 -15
  389. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  390. package/dist/daemon/api/handlers/nodes.d.ts +1 -0
  391. package/dist/daemon/api/handlers/nodes.js +41 -42
  392. package/dist/daemon/api/handlers/reviews.js +4 -29
  393. package/dist/daemon/api/map.d.ts +25 -3
  394. package/dist/daemon/api/map.js +84 -14
  395. package/dist/daemon/api/server.js +68 -9
  396. package/dist/daemon/control.d.ts +18 -0
  397. package/dist/daemon/cron/disposition.d.ts +2 -0
  398. package/dist/daemon/cron/disposition.js +8 -5
  399. package/dist/daemon/cron/settle.js +37 -13
  400. package/dist/daemon/cron/sinks.d.ts +18 -1
  401. package/dist/daemon/cron/sinks.js +47 -93
  402. package/dist/daemon/crtrd-cli.js +4 -19
  403. package/dist/daemon/crtrd.d.ts +2 -0
  404. package/dist/daemon/crtrd.js +206 -81
  405. package/dist/daemon/fleet.js +5 -6
  406. package/dist/daemon/human/finish.d.ts +12 -24
  407. package/dist/daemon/human/finish.js +88 -285
  408. package/dist/daemon/human/settle.d.ts +40 -0
  409. package/dist/daemon/human/settle.js +89 -0
  410. package/dist/daemon/human/sweep.d.ts +20 -5
  411. package/dist/daemon/human/sweep.js +56 -60
  412. package/dist/daemon/manage.d.ts +21 -16
  413. package/dist/daemon/manage.js +85 -51
  414. package/dist/daemon/messaging/node-message.d.ts +1 -1
  415. package/dist/daemon/messaging/node-message.js +9 -12
  416. package/dist/daemon/park-activity.js +3 -3
  417. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  418. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  419. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  420. package/dist/daemon/reconcilers/controller-death.js +6 -18
  421. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  422. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  423. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  424. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  425. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  426. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  427. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  428. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  429. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  430. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  431. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  432. package/dist/daemon/review/companion.js +5 -11
  433. package/dist/daemon/review/deliver.d.ts +1 -5
  434. package/dist/daemon/review/deliver.js +3 -52
  435. package/dist/daemon/review/finish.d.ts +2 -27
  436. package/dist/daemon/review/finish.js +79 -263
  437. package/dist/daemon/review/sweep.d.ts +3 -10
  438. package/dist/daemon/review/sweep.js +13 -131
  439. package/dist/daemon/startup-policy.d.ts +0 -5
  440. package/dist/daemon/startup-policy.js +0 -5
  441. package/dist/hook-authoring.d.ts +28 -2
  442. package/dist/hook-authoring.js +53 -1
  443. package/dist/index.d.ts +3 -2
  444. package/dist/index.js +1 -1
  445. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  446. package/dist/migrations/__tests__/activation.test.js +9 -6
  447. package/dist/migrations/activation.d.ts +3 -2
  448. package/dist/migrations/activation.js +2 -4
  449. package/dist/migrations/corpus.d.ts +2 -5
  450. package/dist/migrations/corpus.js +9 -20
  451. package/dist/migrations/runner.d.ts +3 -1
  452. package/dist/migrations/runner.js +6 -2
  453. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  454. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  455. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  456. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  457. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  458. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  459. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  460. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  461. package/dist/pi-extensions/canvas-stophook.js +47 -31
  462. package/dist/shared/env.d.ts +1 -0
  463. package/dist/shared/env.js +3 -0
  464. package/package.json +1 -1
  465. package/runtime.lock.json +5 -5
  466. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  467. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  468. package/dist/clients/inbox/review-visibility.js +0 -19
  469. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  470. package/dist/commands/canvas-rebuild-index.js +0 -46
  471. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  472. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  473. package/dist/core/__tests__/human-deliver.test.js +0 -256
  474. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  475. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  476. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  477. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  478. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  479. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  480. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  481. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  482. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  483. package/dist/core/canvas/human-deliveries.js +0 -75
  484. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  485. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  486. package/dist/core/feed/passive.d.ts +0 -3
  487. package/dist/core/feed/passive.js +0 -53
  488. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  489. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  490. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  491. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  492. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  493. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  494. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  495. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  496. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  497. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  498. package/dist/core/review/ticket-filter.d.ts +0 -17
  499. package/dist/core/review/ticket-filter.js +0 -21
  500. package/dist/core/runtime/broker/passive.d.ts +0 -17
  501. package/dist/core/runtime/broker/passive.js +0 -44
  502. package/dist/core/runtime/busy.d.ts +0 -46
  503. package/dist/core/runtime/busy.js +0 -150
  504. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  505. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  506. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  507. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  508. package/dist/daemon/human/deliver-action.d.ts +0 -3
  509. package/dist/daemon/human/deliver-action.js +0 -9
  510. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  511. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  512. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  513. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  514. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  515. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  516. package/dist/daemon/startup-block-marker.d.ts +0 -25
  517. package/dist/daemon/startup-block-marker.js +0 -99
  518. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  519. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  520. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -1,14 +1,17 @@
1
1
  import { envProfileId } from '../shared/env.js';
2
- import { dirname, join } from 'node:path';
2
+ import { basename, dirname, join } from 'node:path';
3
3
  import { isDeepStrictEqual } from 'node:util';
4
4
  import { CONFIG_FILE, STATE_FILE, CONDENSED_HISTORY_MODES, MOUSE_MODE_DEFAULTS, WHIP_MESSAGE_MODES, DEFAULT_WHIP_MESSAGES, defaultScopeConfig, defaultScopeState, defaultModelLaddersConfig, defaultKindsConfig, defaultRemoteCanvasConfig } from '../types.js';
5
5
  import { BINDING_CATALOG, BINDING_IDS, isAttachPaneBinding } from './keybindings/catalog.js';
6
- import { usage } from './errors.js';
6
+ import { notFound, usage } from './errors.js';
7
7
  import { emitEvent } from './events/emit.js';
8
- import { atomicWriteJson, readJsonIfExists, writeJson, ensureDir } from './fs-utils.js';
9
- import { scopeRoot, requireScopeRoot, findProjectScopeRoots } from './scope.js';
8
+ import { atomicWriteJson, readJsonIfExists, ensureDir } from './fs-utils.js';
9
+ import { scopeRoot, requireScopeRoot, findProjectScopeRoots, userScopeRoot } from './scope.js';
10
10
  import { listInstalledPluginsInRoot } from './installed-plugins.js';
11
- import { profileRoot } from './profiles/manifest.js';
11
+ import { profileRoot, listProfiles } from './profiles/manifest.js';
12
+ import { canonicalRoot, projectStore, userStore } from './scoped-state/paths.js';
13
+ import { convertLegacyScopedStore, initializeFreshScopedStore } from './scoped-state/migrate.js';
14
+ import { mutateRawSettings, readBookkeeping, readRawSettings, readRawSettingsWithRevision, replaceRawSettings, writeBookkeeping, writeRawSettings } from './scoped-state/settings.js';
12
15
  import { withExclusiveDirectoryLock } from './exclusive-lock.js';
13
16
  import { SCOPE_CONFIG_KEYS } from './user-settings.js';
14
17
  import { normalizeWorkingGerunds } from '../shared/working-activity.js';
@@ -16,6 +19,10 @@ import { mergePageComponentLayers, normalizePageComponents } from './human/page-
16
19
  function configPathFor(root) {
17
20
  return join(root, CONFIG_FILE);
18
21
  }
22
+ function withConfigPathLock(path, run) {
23
+ ensureDir(dirname(path));
24
+ return withExclusiveDirectoryLock(`${path}.lock`, () => run(path), { timeoutError: () => new Error(`timed out waiting for config lock: ${path}`) });
25
+ }
19
26
  function statePathFor(root) {
20
27
  return join(root, STATE_FILE);
21
28
  }
@@ -27,100 +34,83 @@ export function statePath(scope) {
27
34
  const root = scopeRoot(scope);
28
35
  return root ? statePathFor(root) : null;
29
36
  }
30
- export function readConfig(scope) {
31
- const root = scopeRoot(scope);
32
- if (!root)
33
- return defaultScopeConfig();
34
- return readConfigAtRoot(root);
35
- }
36
- /** Read the config that lives at an EXACT scope-root dir, rather than the
37
- * resolved nearest root for a scope. Lets a caller iterate each visible
38
- * project root's own config when several are in scope (nested `.crouter` dirs
39
- * or a profile-widened stack), instead of collapsing them onto the nearest. */
40
- export function readConfigAtRoot(root) {
41
- const existing = readJsonIfExists(configPathFor(root));
42
- if (!existing)
43
- return defaultScopeConfig();
44
- return mergeConfig(existing);
45
- }
46
- export function readState(scope) {
47
- const root = scopeRoot(scope);
48
- if (!root)
49
- return defaultScopeState();
50
- const existing = readJsonIfExists(statePathFor(root));
51
- if (!existing)
52
- return defaultScopeState();
53
- return {
54
- marketplaces: existing.marketplaces ?? {},
55
- plugins: existing.plugins ?? {},
56
- last_self_check: existing.last_self_check,
57
- activeCanvas: existing.activeCanvas ?? null,
58
- };
59
- }
60
- function withConfigPathLock(path, run) {
61
- ensureDir(dirname(path));
62
- return withExclusiveDirectoryLock(`${path}.lock`, () => run(path), {
63
- timeoutError: () => new Error(`timed out waiting for config lock: ${path}`),
64
- });
37
+ function settingsOwnerForRoot(root) {
38
+ const canonical = canonicalRoot(root);
39
+ const userRoot = canonicalRoot(userScopeRoot());
40
+ if (canonical === userRoot)
41
+ return { store: userStore(userRoot), key: 'user', kind: 'user', root: userRoot };
42
+ // Project roots can be classified without opening the user store. This keeps a
43
+ // fresh project usable when an unrelated legacy user root still needs conversion.
44
+ if (basename(canonical) === '.crouter') {
45
+ convertLegacyScopedStore({ kind: 'project', root: canonical });
46
+ return { store: projectStore(canonical), key: 'project', kind: 'project', root: canonical };
47
+ }
48
+ for (const profile of listProfiles()) {
49
+ const profilePath = canonicalRoot(profileRoot(profile.profileId));
50
+ if (canonical === profilePath)
51
+ return { store: userStore(userRoot), key: `profile:${profile.profileId}`, kind: 'profile', root: profilePath, profileId: profile.profileId };
52
+ }
53
+ throw new Error(`settings root is not a recognized owner: ${root}`);
54
+ }
55
+ function readRawAtRoot(root) {
56
+ const owner = settingsOwnerForRoot(root);
57
+ if (owner.kind === 'project')
58
+ return readJsonIfExists(configPathFor(owner.root));
59
+ return readRawSettings(owner);
60
+ }
61
+ function writeRawAtRoot(root, raw) {
62
+ const owner = settingsOwnerForRoot(root);
63
+ if (owner.kind === 'project') {
64
+ withConfigPathLock(configPathFor(owner.root), (path) => atomicWriteJson(path, raw));
65
+ return;
66
+ }
67
+ writeRawSettings(owner, raw);
68
+ }
69
+ function mutateRawAtRoot(root, mutate) {
70
+ const owner = settingsOwnerForRoot(root);
71
+ if (owner.kind === 'project')
72
+ return withConfigPathLock(configPathFor(owner.root), (path) => { const result = mutate(readJsonIfExists(path) ?? {}); atomicWriteJson(path, result.next); return result.value; });
73
+ return mutateRawSettings(owner, (raw) => mutate(raw));
74
+ }
75
+ export function readConfig(scope) { const root = scopeRoot(scope); return root ? readConfigAtRoot(root) : defaultScopeConfig(); }
76
+ export function readConfigAtRoot(root) { const existing = readRawAtRoot(root); return existing ? mergeConfig(existing) : defaultScopeConfig(); }
77
+ export function readState(scope) { const root = scopeRoot(scope); if (!root)
78
+ return defaultScopeState(); return readBookkeeping(settingsOwnerForRoot(root)); }
79
+ export class InvalidRawSettingsError extends Error {
80
+ constructor(cause) {
81
+ super(`settings cannot be merged: ${cause instanceof Error ? cause.message : String(cause)}`);
82
+ this.name = 'InvalidRawSettingsError';
83
+ }
65
84
  }
66
- function withConfigLock(scope, run) {
67
- const root = requireScopeRoot(scope);
68
- return withConfigPathLock(configPathFor(root), run);
85
+ function rawSettingsOwnerForTarget(target) {
86
+ if (target.scope === 'user')
87
+ return settingsOwnerForRoot(userScopeRoot());
88
+ if (!listProfiles().some((profile) => profile.profileId === target.profileId)) {
89
+ throw notFound(`profile not found: ${target.profileId}`, { received: target.profileId, next: 'Run `crtr profile list` to see available profiles.' });
90
+ }
91
+ return settingsOwnerForRoot(profileRoot(target.profileId));
69
92
  }
70
- export function updateRawConfigAtomically(scope, mutate) {
71
- return withConfigLock(scope, (path) => {
72
- const latest = readJsonIfExists(path) ?? {};
73
- const next = mutate(latest);
74
- atomicWriteJson(path, next);
75
- return next;
76
- });
93
+ export function readRawSettingsForTarget(target) {
94
+ return readRawSettingsWithRevision(rawSettingsOwnerForTarget(target));
77
95
  }
78
- export function writeConfig(scope, config) {
79
- withConfigLock(scope, (path) => atomicWriteJson(path, config));
80
- }
81
- /** Persist a RAW PARTIAL config to a scope's config.json exactly as given.
82
- * Unlike `writeConfig`, this makes no promise that every field is present --
83
- * callers pass the sparse partial that should live on disk, so an untouched
84
- * key stays absent (and `readConfig`/`mergeConfig` re-derive it live from
85
- * whichever build is running). This is the single central primitive every
86
- * mutation path funnels through so no writer ever freezes a fully
87
- * materialized `modelLadders` (concrete model ids that go stale across
88
- * builds -- the live-Hearth freeze bug) onto disk. */
89
- export function writeRawConfig(scope, config) {
90
- withConfigLock(scope, (path) => atomicWriteJson(path, config));
91
- }
92
- export function writeState(scope, state) {
93
- const root = requireScopeRoot(scope);
94
- ensureDir(root);
95
- writeJson(statePathFor(root), state);
96
- }
97
- export function ensureScopeInitialized(scope, root) {
98
- ensureDir(root);
99
- withConfigPathLock(configPathFor(root), (cfgPath) => {
100
- if (readJsonIfExists(cfgPath))
101
- return;
102
- // Persist no materialized model ladder or keybinding defaults. `modelLadders` holds concrete model ids (e.g.
103
- // "openai-codex/gpt-5.6-sol:high") that are only valid as long as they match whatever
104
- // pi model registry this crouter build ships. Baking a materialized snapshot here would
105
- // freeze this scope's ladder to whatever ids happened to be current the moment it was
106
- // first initialized -- a later crouter/pi upgrade (or a rollback) that changes the
107
- // registry can then never reach it: the stale persisted override still wins over the
108
- // new build's compiled defaults in `mergeConfig`/`mergeModelLadders`, producing a
109
- // fallback rung `resolveViableFallback` rejects as unregistered while the terminal
110
- // error misleadingly claims the fallback provider needs authenticating (see live
111
- // incident: a home's config.json was seeded with `gpt-5.6-*` ladder entries from a
112
- // build newer than the one actually installed, so its own model registry never had
113
- // them). Omitting the key here is safe: `readConfig`/`mergeConfig` already default-fill
114
- // a missing `modelLadders` from whatever build is CURRENTLY running, and an explicit
115
- // user override (`sys config set modelLadders...`) still persists and still wins.
116
- // Keybindings are sparse user overrides, so an untouched scope omits that member too.
117
- const { modelLadders: _omittedModelLadders, keybindings: _omittedKeybindings, tmux_passthrough: _omittedTmuxPassthrough, brokerThresholds, lifecycle, ...rest } = defaultScopeConfig();
118
- // Broker thresholds and lifecycle timing are host-wide daemon policy, so
119
- // only user scope persists them. Project config must never acquire a
120
- // machine setting.
121
- atomicWriteJson(cfgPath, scope === 'user' ? { ...rest, brokerThresholds, lifecycle } : rest);
96
+ export function replaceRawSettingsForTarget(target, next, ifRevision) {
97
+ return replaceRawSettings(rawSettingsOwnerForTarget(target), next, ifRevision, (settings) => {
98
+ try {
99
+ mergeConfig(settings);
100
+ }
101
+ catch (error) {
102
+ throw new InvalidRawSettingsError(error);
103
+ }
122
104
  });
123
105
  }
106
+ export function updateRawConfigAtomically(scope, mutate) { const root = requireScopeRoot(scope); return mutateRawAtRoot(root, (latest) => { const next = mutate(latest); return { value: next, next }; }); }
107
+ export function writeConfig(scope, config) { writeRawAtRoot(requireScopeRoot(scope), config); }
108
+ export function writeRawConfig(scope, config) { writeRawAtRoot(requireScopeRoot(scope), config); }
109
+ export function writeState(scope, state) { const root = requireScopeRoot(scope); writeBookkeeping(settingsOwnerForRoot(root), state); }
110
+ export function ensureScopeInitialized(scope, root) { ensureDir(root); const owner = scope === 'user' ? settingsOwnerForRoot(userScopeRoot()) : settingsOwnerForRoot(root); initializeFreshScopedStore({ kind: owner.kind === 'user' ? 'user' : 'project', root: owner.store.root }); if (owner.kind === 'project' && readJsonIfExists(configPathFor(owner.root)) === null) {
111
+ const { modelLadders: _a, keybindings: _b, tmux_passthrough: _c, brokerThresholds, lifecycle, ...rest } = defaultScopeConfig();
112
+ writeRawAtRoot(owner.root, rest);
113
+ } }
124
114
  function normalizeKeybindings(raw) {
125
115
  if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
126
116
  return {};
@@ -798,7 +788,7 @@ function mergeConfig(partial) {
798
788
  * for why `readConfig(scope)`, which already defaults-fills, is unusable
799
789
  * for cross-scope layering). */
800
790
  export function readRawConfigAtRoot(root) {
801
- return readJsonIfExists(configPathFor(root));
791
+ return readRawAtRoot(root);
802
792
  }
803
793
  export function readRawScopeConfig(scope) {
804
794
  const root = scopeRoot(scope);
@@ -814,7 +804,7 @@ function readRawProfileConfig(profileId) {
814
804
  if (profileId === null || profileId === '')
815
805
  return null;
816
806
  try {
817
- return readJsonIfExists(configPathFor(profileRoot(profileId)));
807
+ return readRawAtRoot(profileRoot(profileId));
818
808
  }
819
809
  catch {
820
810
  return null;
@@ -832,7 +822,7 @@ function readRawProjectScopeConfigs(targetCwd, targetProfileId) {
832
822
  const farthestFirst = [...nearestFirst].reverse();
833
823
  // A root with no config.json still contributes: its installed plugins may
834
824
  // declare kinds, so the ROOT rides along and `raw` stays null.
835
- return farthestFirst.map((root) => ({ root, raw: readJsonIfExists(configPathFor(root)) }));
825
+ return farthestFirst.map((root) => ({ root, raw: readRawAtRoot(root) }));
836
826
  }
837
827
  /** Merge launch knobs (`kinds`, `modelLadders`) across scopes in
838
828
  * project stack > profile > user > builtin precedence — the same precedence
@@ -994,27 +984,21 @@ function sparseModelLaddersToPersist(rawLadders, baseline, mutated) {
994
984
  * `sys config set` never freezes a full concrete `modelLadders` to disk,
995
985
  * and a direct ladder-rung set still persists only the touched rung -- all
996
986
  * through one lock-protected atomic update path. */
997
- function mutateConfigAtPath(path, mutate) {
998
- return withConfigPathLock(path, (lockedPath) => {
999
- const raw = readJsonIfExists(lockedPath) ?? {};
987
+ function mutateConfigAtRoot(root, mutate) {
988
+ return mutateRawAtRoot(root, (raw) => {
1000
989
  const baseline = mergeConfig(raw);
1001
990
  const cfg = mergeConfig(raw);
1002
991
  mutate(cfg);
1003
992
  const partial = { ...raw };
1004
- for (const key of SCOPE_CONFIG_KEYS) {
1005
- if (key === 'modelLadders')
1006
- continue;
1007
- if (!isDeepStrictEqual(cfg[key], baseline[key])) {
993
+ for (const key of SCOPE_CONFIG_KEYS)
994
+ if (key !== 'modelLadders' && !isDeepStrictEqual(cfg[key], baseline[key]))
1008
995
  partial[key] = cfg[key];
1009
- }
1010
- }
1011
996
  const ladders = sparseModelLaddersToPersist(raw.modelLadders, baseline.modelLadders, cfg.modelLadders);
1012
997
  if (ladders !== undefined)
1013
998
  partial.modelLadders = ladders;
1014
999
  else
1015
1000
  delete partial.modelLadders;
1016
- atomicWriteJson(lockedPath, partial);
1017
- return cfg;
1001
+ return { value: cfg, next: partial };
1018
1002
  });
1019
1003
  }
1020
1004
  /** Persist an EXPLICIT sparse `modelLadders` intent at one config root: the
@@ -1029,21 +1013,10 @@ function mutateConfigAtPath(path, mutate) {
1029
1013
  * evaporates as soon as the default moves. Explicit intent is recorded as
1030
1014
  * written. Mirrors `persistDefaultKindModel`. */
1031
1015
  export function persistModelLadders(root, patch) {
1032
- const path = configPathFor(root);
1033
- ensureDir(root);
1034
- withConfigPathLock(path, (lockedPath) => {
1035
- const raw = readJsonIfExists(lockedPath) ?? {};
1036
- const current = (raw.modelLadders ?? {});
1037
- const next = { ...current };
1038
- if (patch.defaultProvider !== undefined)
1039
- next.defaultProvider = patch.defaultProvider;
1040
- for (const provider of ['anthropic', 'openai']) {
1041
- const rungs = patch[provider];
1042
- if (rungs !== undefined)
1043
- next[provider] = { ...(current[provider] ?? {}), ...rungs };
1044
- }
1045
- atomicWriteJson(lockedPath, { ...raw, modelLadders: next });
1046
- });
1016
+ mutateRawAtRoot(root, (raw) => { const current = (raw.modelLadders ?? {}); const next = { ...current }; if (patch.defaultProvider !== undefined)
1017
+ next.defaultProvider = patch.defaultProvider; for (const provider of ['anthropic', 'openai'])
1018
+ if (patch[provider] !== undefined)
1019
+ next[provider] = { ...(current[provider] ?? {}), ...patch[provider] }; return { value: undefined, next: { ...raw, modelLadders: next } }; });
1047
1020
  }
1048
1021
  /** Persist one complete route at one exact config root. The route is the only
1049
1022
  * raw route entry touched; unrelated routes and local ladder defaults remain
@@ -1054,33 +1027,18 @@ export function persistModelRoute(root, routeId, route) {
1054
1027
  assertCompleteModelRoute(routeId, route);
1055
1028
  else if (routeId.trim() === '')
1056
1029
  throw new Error('model route id must be a non-empty string');
1057
- const path = configPathFor(root);
1058
- ensureDir(root);
1059
- withConfigPathLock(path, (lockedPath) => {
1060
- const raw = readJsonIfExists(lockedPath) ?? {};
1061
- const routes = copyRouteRecord((raw.modelRoutes ?? {}));
1062
- if (route === undefined)
1063
- delete routes[routeId];
1064
- else {
1065
- const current = routes[routeId];
1066
- setRouteRecord(routes, routeId, {
1067
- ...(current !== null && typeof current === 'object' && !Array.isArray(current) ? current : {}),
1068
- ...route,
1069
- models: { ...route.models },
1070
- });
1071
- }
1072
- const next = { ...raw };
1073
- if (Object.keys(routes).length === 0)
1074
- delete next.modelRoutes;
1075
- else
1076
- next.modelRoutes = routes;
1077
- atomicWriteJson(lockedPath, next);
1078
- });
1030
+ mutateRawAtRoot(root, (raw) => { const routes = copyRouteRecord((raw.modelRoutes ?? {})); if (route === undefined)
1031
+ delete routes[routeId];
1032
+ else
1033
+ setRouteRecord(routes, routeId, { ...route, models: { ...route.models } }); const next = { ...raw }; if (Object.keys(routes).length === 0)
1034
+ delete next.modelRoutes;
1035
+ else
1036
+ next.modelRoutes = routes; return { value: undefined, next }; });
1079
1037
  }
1080
1038
  /** Mutate one exact config root. Setup uses this for profile and explicit
1081
1039
  * project targets that cannot be represented by the user/project Scope enum. */
1082
1040
  export function updateConfigAtRoot(root, mutate) {
1083
- return mutateConfigAtPath(configPathFor(root), mutate);
1041
+ return mutateConfigAtRoot(root, mutate);
1084
1042
  }
1085
1043
  export function updateConfig(scope, mutate) {
1086
1044
  return updateConfigAtRoot(requireScopeRoot(scope), mutate);
@@ -1090,15 +1048,8 @@ export function updateConfig(scope, mutate) {
1090
1048
  * scopes instead of freezing a materialized registry snapshot. */
1091
1049
  export function persistDefaultKindModel(opts) {
1092
1050
  const field = opts.orchestrator === true ? 'orchestratorModel' : 'model';
1093
- const path = configPathFor(opts.root);
1094
- ensureDir(opts.root);
1095
- withConfigPathLock(path, (lockedPath) => {
1096
- const raw = readJsonIfExists(lockedPath) ?? {};
1097
- const kinds = { ...(raw.kinds ?? {}) };
1098
- kinds[opts.kind] = { ...(kinds[opts.kind] ?? {}), [field]: opts.model };
1099
- atomicWriteJson(lockedPath, { ...raw, kinds });
1100
- });
1101
- return { path, field };
1051
+ mutateRawAtRoot(opts.root, (raw) => { const kinds = { ...(raw.kinds ?? {}), [opts.kind]: { ...(raw.kinds ?? {})[opts.kind], [field]: opts.model } }; return { value: undefined, next: { ...raw, kinds: kinds } }; });
1052
+ return { path: configPathFor(opts.root), field };
1102
1053
  }
1103
1054
  export function updateState(scope, mutate) {
1104
1055
  const s = readState(scope);
@@ -22,6 +22,7 @@ export declare function general(message: string, details?: Record<string, unknow
22
22
  * `apiErrorToCliError` (tier 2) reconstructs the exact code, message, and
23
23
  * actionable `next` instead of degrading to a generic `invalid_request`. */
24
24
  export declare function brokerLaunchFailed(message: string, next: string): CrtrError;
25
+ export declare function nodeCreateRefused(message: string, next: string): CrtrError;
25
26
  /** Thrown by stub handlers for leaves not yet wired in P3+.
26
27
  * code='not_implemented', exitCode=GENERAL, next names the node. */
27
28
  export declare function notImplemented(node: string): CrtrError;
@@ -41,6 +41,10 @@ export function brokerLaunchFailed(message, next) {
41
41
  const code = 'broker_launch_failed';
42
42
  return new CrtrError(code, message, ExitCode.GENERAL, { error: code, message, next });
43
43
  }
44
+ export function nodeCreateRefused(message, next) {
45
+ const code = 'node_create_refused';
46
+ return new CrtrError(code, message, ExitCode.GENERAL, { error: code, message, next });
47
+ }
44
48
  /** Thrown by stub handlers for leaves not yet wired in P3+.
45
49
  * code='not_implemented', exitCode=GENERAL, next names the node. */
46
50
  export function notImplemented(node) {
@@ -1,49 +1,45 @@
1
- // Regression (2026-07-22 revive-loop flap): removing legacy inbox compat
2
- // (e2904a09) with no on-host cutover left every pre-cutover node fatally
3
- // unreadable — the broker crashed at inbox seed and the daemon revived it
4
- // forever, flashing "node refreshing — reconnecting…" in every attached
5
- // viewer. The boot migration must rewrite identity-less rows and map the
6
- // ISO-timestamp cursor boundary onto an exact entry identity the strict
7
- // reader accepts.
1
+ // Retained legacy mailbox import: the SQL cutover preserves historical inbox
2
+ // bytes by importing them into messages and deliveries without leaving a
3
+ // runtime filesystem reader.
8
4
  import { test } from 'node:test';
9
5
  import assert from 'node:assert/strict';
10
- import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
6
+ import { DatabaseSync } from 'node:sqlite';
7
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
11
8
  import { join } from 'node:path';
12
9
  import { tmpdir } from 'node:os';
13
- import { migrateLegacyInboxIdentities } from '../inbox-migrate.js';
14
- import { readCursor, readInboxSince } from '../inbox.js';
15
- import { nodeDir, inboxPath } from '../../canvas/paths.js';
16
- test('legacy inbox rows and ISO cursor migrate to strict identities once', () => {
10
+ import { migrateMessagingState } from '../../canvas/deliveries.js';
11
+ import { inboxPath, nodeDir } from '../../canvas/paths.js';
12
+ test('legacy inbox rows and cursor import once into acknowledged and pending SQL deliveries', () => {
17
13
  const home = mkdtempSync(join(tmpdir(), 'crtr-inboxmigrate-'));
18
14
  const orig = process.env['CRTR_HOME'];
19
15
  process.env['CRTR_HOME'] = home;
20
16
  const nodeId = 'mrtest01-aabbccdd';
17
+ const db = new DatabaseSync(join(home, 'canvas.db'));
21
18
  try {
19
+ db.exec('CREATE TABLE nodes (node_id TEXT PRIMARY KEY, waiting_for TEXT, waiting_for_generation INTEGER)');
20
+ db.prepare('INSERT INTO nodes (node_id, waiting_for, waiting_for_generation) VALUES (?, NULL, 0)').run(nodeId);
21
+ db.exec('CREATE TABLE human_action_deliveries (request_id TEXT, state TEXT, attempt INTEGER, argv_json TEXT, cwd TEXT, completion_json TEXT, next_attempt_at INTEGER, claimed_at INTEGER, claim_owner TEXT, accepted_at INTEGER, permanent_failed_at INTEGER, last_failure_json TEXT, created_at INTEGER, updated_at INTEGER)');
22
+ db.exec('CREATE TABLE node_outcome_deliveries (node_id TEXT, state TEXT, attempt INTEGER, action_name TEXT, argv_json TEXT, cwd TEXT, config_origin TEXT, payload_json TEXT, document_json TEXT, next_attempt_at INTEGER, claimed_at INTEGER, claim_owner TEXT, accepted_at INTEGER, permanent_failed_at INTEGER, last_failure_json TEXT, created_at INTEGER, updated_at INTEGER)');
22
23
  mkdirSync(nodeDir(nodeId), { recursive: true });
23
24
  const lines = [
24
- '{"ts":"2026-07-09T22:54:06.294Z","from":"mrpeer1-00000001","tier":"normal","kind":"final","label":"one"}',
25
- '{"ts":"2026-07-10T01:00:00.000Z","from":"mrpeer1-00000001","tier":"normal","kind":"update","label":"two"}',
25
+ '{"entry_id":"11111111111111111111111111111111","operation_id":"22222222222222222222222222222222","ts":"2026-07-09T22:54:06.294Z","from":"mrpeer1-00000001","tier":"normal","kind":"final","label":"one"}',
26
+ '{"entry_id":"33333333333333333333333333333333","operation_id":"44444444444444444444444444444444","ts":"2026-07-10T01:00:00.000Z","from":"mrpeer1-00000001","tier":"normal","kind":"update","label":"two"}',
26
27
  ];
27
28
  writeFileSync(inboxPath(nodeId), `${lines.join('\n')}\n`, 'utf8');
28
- writeFileSync(`${inboxPath(nodeId)}.cursor`, '2026-07-09T22:54:06.294Z', 'utf8');
29
- const first = migrateLegacyInboxIdentities();
30
- assert.equal(first.nodes, 1);
31
- assert.equal(first.entries, 2);
32
- assert.equal(first.cursors, 1);
33
- assert.equal(first.failures, 0);
34
- // The strict reader consumes the migrated state: cursor covers entry one,
35
- // exactly entry two is unread.
36
- const cursor = readCursor(nodeId);
37
- assert.notEqual(cursor, undefined);
38
- const unread = readInboxSince(nodeId, cursor);
39
- assert.equal(unread.length, 1);
40
- assert.equal(unread[0].ts, '2026-07-10T01:00:00.000Z');
41
- const migratedRaw = readFileSync(inboxPath(nodeId), 'utf8');
42
- const second = migrateLegacyInboxIdentities();
43
- assert.deepEqual(second, { nodes: 0, entries: 0, cursors: 0, failures: 0 });
44
- assert.equal(readFileSync(inboxPath(nodeId), 'utf8'), migratedRaw, 'second run is a no-op');
29
+ writeFileSync(`${inboxPath(nodeId)}.cursor`, '11111111111111111111111111111111', 'utf8');
30
+ migrateMessagingState(db);
31
+ const deliveries = db.prepare(`SELECT m.message_id, m.created_at, d.state, d.acknowledged_at
32
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
33
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? ORDER BY m.seq`).all(nodeId);
34
+ assert.deepEqual(deliveries.map(({ message_id, state }) => ({ message_id, state })), [
35
+ { message_id: '11111111111111111111111111111111', state: 'acknowledged' },
36
+ { message_id: '33333333333333333333333333333333', state: 'pending' },
37
+ ], 'the retained file rows become ordered SQL message/delivery facts and the legacy cursor becomes acknowledgement');
38
+ assert.equal(deliveries[0].acknowledged_at, Date.parse('2026-07-09T22:54:06.294Z'));
39
+ assert.equal(deliveries[1].created_at, '2026-07-10T01:00:00.000Z');
45
40
  }
46
41
  finally {
42
+ db.close();
47
43
  if (orig === undefined)
48
44
  delete process.env['CRTR_HOME'];
49
45
  else
@@ -27,12 +27,6 @@ export declare class FinalizationError extends Error {
27
27
  readonly reportPath?: string | undefined;
28
28
  constructor(code: 'already_finalized' | 'finalization_forbidden' | 'unknown_node', reportPath?: string | undefined);
29
29
  }
30
- export declare class FinalDeliveryError extends Error {
31
- readonly reportPath: string;
32
- readonly deliveredTo: string[];
33
- readonly failedTo: string[];
34
- constructor(reportPath: string, deliveredTo: string[], failedTo: string[]);
35
- }
36
30
  export declare function push(nodeId: string, opts: PushOpts): Promise<PushResult>;
37
31
  export declare function pushUpdate(nodeId: string, body: string, opts?: {
38
32
  from?: string;