@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
@@ -12,7 +12,7 @@
12
12
  // matters: pi asks its CredentialStore for every registered provider (~75) whenever it
13
13
  // builds a model runtime, and routing those through the lock made each boot serialize
14
14
  // behind seconds of lock acquisition backoff.
15
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
15
+ import { chmodSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, writeFileSync, closeSync } from 'node:fs';
16
16
  import { dirname } from 'node:path';
17
17
  import lockfile from 'proper-lockfile';
18
18
  const WRITE_OPTIONS = { encoding: 'utf-8', mode: 0o600 };
@@ -24,8 +24,12 @@ const LOCK_UPDATE_MS = 5_000;
24
24
  /** A 0600 JSON file guarded by a cross-process `proper-lockfile` lock on its own path. */
25
25
  export class LockedJsonFile {
26
26
  path;
27
- constructor(path) {
27
+ durable;
28
+ initialContent;
29
+ constructor(path, durable = false, initialContent = '{}') {
28
30
  this.path = path;
31
+ this.durable = durable;
32
+ this.initialContent = initialContent;
29
33
  }
30
34
  readUnlocked() {
31
35
  return existsSync(this.path) ? readFileSync(this.path, 'utf-8') : undefined;
@@ -35,7 +39,7 @@ export class LockedJsonFile {
35
39
  if (!existsSync(dir))
36
40
  mkdirSync(dir, { recursive: true, mode: 0o700 });
37
41
  if (!existsSync(this.path)) {
38
- writeFileSync(this.path, '{}', WRITE_OPTIONS);
42
+ writeFileSync(this.path, this.initialContent, WRITE_OPTIONS);
39
43
  chmodSync(this.path, 0o600);
40
44
  }
41
45
  }
@@ -64,16 +68,39 @@ export class LockedJsonFile {
64
68
  });
65
69
  throwIfCompromised();
66
70
  const current = existsSync(this.path) ? readFileSync(this.path, 'utf-8') : undefined;
67
- const { result, next } = await fn(current, throwIfCompromised);
68
- throwIfCompromised();
69
- if (next !== undefined) {
70
- // Atomic replace, so a concurrent lock-free reader can never observe a half-written
71
- // file (the reason `readUnlocked` is safe).
71
+ let published = false;
72
+ const publish = (next) => {
73
+ if (published)
74
+ throw new Error(`locked file was published twice: ${this.path}`);
75
+ throwIfCompromised();
72
76
  const tmp = `${this.path}.${process.pid}.${Date.now()}.tmp`;
73
77
  writeFileSync(tmp, next, WRITE_OPTIONS);
74
78
  chmodSync(tmp, 0o600);
79
+ if (this.durable) {
80
+ const fd = openSync(tmp, 'r');
81
+ try {
82
+ fsyncSync(fd);
83
+ }
84
+ finally {
85
+ closeSync(fd);
86
+ }
87
+ }
75
88
  renameSync(tmp, this.path);
76
- }
89
+ if (this.durable) {
90
+ const fd = openSync(dirname(this.path), 'r');
91
+ try {
92
+ fsyncSync(fd);
93
+ }
94
+ finally {
95
+ closeSync(fd);
96
+ }
97
+ }
98
+ published = true;
99
+ };
100
+ const { result, next } = await fn(current, throwIfCompromised, publish);
101
+ throwIfCompromised();
102
+ if (next !== undefined)
103
+ publish(next);
77
104
  throwIfCompromised();
78
105
  return result;
79
106
  }
@@ -134,6 +134,8 @@ function catalogForPlugins(groups, enabledOnly, candidate) {
134
134
  /** Declarations for a native document. Project documents use their owning
135
135
  * project scope only, then user scope; profile/node/user docs use user scope. */
136
136
  function pluginGroupsForDocument(doc) {
137
+ if (doc.scope === 'builtin')
138
+ return [];
137
139
  if (doc.scope === 'project') {
138
140
  const marker = `${sep}.crouter${sep}`;
139
141
  const markerIndex = doc.path.lastIndexOf(marker);
@@ -101,7 +101,6 @@ export const LEAF_ICON_CP = {
101
101
  'surface tmux-spread': 0xf009,
102
102
  'canvas issue new': 0xf188,
103
103
  'canvas prune': 0xf0c4,
104
- 'canvas rebuild-index': 0xf0ad,
105
104
  'human send': 0xf059,
106
105
  'human review': 0xf044,
107
106
  'human show': 0xf108,
@@ -10,6 +10,7 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
10
10
  import { tmpdir } from 'node:os';
11
11
  import { join } from 'node:path';
12
12
  import { createProfile } from '../manifest.js';
13
+ import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
13
14
  import { resetScopeCache } from '../../scope.js';
14
15
  import { isReservedEnvName, listProfileEnvNames, readProfileEnvVars, removeProfileEnvVar, setProfileEnvVar, } from '../env-store.js';
15
16
  import { envBranch } from '../../../commands/profile/env.js';
@@ -32,6 +33,7 @@ beforeEach(() => {
32
33
  home = mkdtempSync(join(tmpdir(), 'crtr-env-store-home-'));
33
34
  process.env.HOME = home;
34
35
  resetScopeCache();
36
+ initializeFreshScopedStore({ kind: 'user', root: join(home, '.crouter') });
35
37
  });
36
38
  afterEach(() => {
37
39
  rmSync(home, { recursive: true, force: true });
@@ -1,10 +1,6 @@
1
- /** Resolve cwd to the same absolute, realpath'd form the profile selector and
2
- * manifest use. Falls back to the plain resolved path when it cannot be stat'd. */
3
1
  export declare function resolveBindingCwd(cwd: string): string;
4
2
  export declare function getDefaultProfileId(cwd: string): string | null;
5
3
  export declare function setDefaultProfileId(cwd: string, profileId: string): void;
6
4
  export declare function clearDefaultProfile(cwd: string): void;
7
- /** Every directory currently pinned to `profileId`, in deterministic order. */
8
5
  export declare function defaultProfileDirs(profileId: string): string[];
9
- /** Clear every per-directory default that names `profileId`. */
10
6
  export declare function clearDefaultProfileId(profileId: string): string[];
@@ -1,13 +1,9 @@
1
- // A per-cwd default-profile pin. Pins are independent of profile purview: the
2
- // profile manifest says what a profile can cover, while this user-level map says
3
- // which covering profile is preferred for one realpath'd directory.
1
+ // Per-directory default-profile pins are user-root SQL state. Profile identity
2
+ // remains stable across renames, so the pin stores only the generated id.
4
3
  import { existsSync, realpathSync } from 'node:fs';
5
- import { join, resolve as resolvePath } from 'node:path';
6
- import { homedir } from 'node:os';
7
- import { CRTR_DIR_NAME } from '../../types.js';
8
- import { readJsonIfExists, writeJson, removePath } from '../fs-utils.js';
9
- /** Resolve cwd to the same absolute, realpath'd form the profile selector and
10
- * manifest use. Falls back to the plain resolved path when it cannot be stat'd. */
4
+ import { resolve as resolvePath } from 'node:path';
5
+ import { userScopeRoot } from '../scope.js';
6
+ import { mutateStoredDefaults, readStoredDefaults } from '../scoped-state/profiles.js';
11
7
  export function resolveBindingCwd(cwd) {
12
8
  const abs = resolvePath(cwd);
13
9
  if (!existsSync(abs))
@@ -19,52 +15,10 @@ export function resolveBindingCwd(cwd) {
19
15
  return abs;
20
16
  }
21
17
  }
22
- function bindingsPath() {
23
- return join(homedir(), CRTR_DIR_NAME, 'profile-defaults.json');
24
- }
25
- function bindings() {
26
- const value = readJsonIfExists(bindingsPath());
27
- if (value === null || typeof value !== 'object' || Array.isArray(value))
28
- return {};
29
- return Object.fromEntries(Object.entries(value).filter(([, profileId]) => typeof profileId === 'string' && profileId !== ''));
30
- }
31
- export function getDefaultProfileId(cwd) {
32
- return bindings()[resolveBindingCwd(cwd)] ?? null;
33
- }
34
- export function setDefaultProfileId(cwd, profileId) {
35
- const value = bindings();
36
- value[resolveBindingCwd(cwd)] = profileId;
37
- writeJson(bindingsPath(), value);
38
- }
39
- export function clearDefaultProfile(cwd) {
40
- const value = bindings();
41
- delete value[resolveBindingCwd(cwd)];
42
- if (Object.keys(value).length === 0)
43
- removePath(bindingsPath());
44
- else
45
- writeJson(bindingsPath(), value);
46
- }
47
- /** Every directory currently pinned to `profileId`, in deterministic order. */
48
- export function defaultProfileDirs(profileId) {
49
- return Object.entries(bindings())
50
- .filter(([, pinnedProfileId]) => pinnedProfileId === profileId)
51
- .map(([dir]) => dir)
52
- .sort();
53
- }
54
- /** Clear every per-directory default that names `profileId`. */
55
- export function clearDefaultProfileId(profileId) {
56
- const value = bindings();
57
- const cleared = Object.entries(value)
58
- .filter(([, pinnedProfileId]) => pinnedProfileId === profileId)
59
- .map(([dir]) => dir)
60
- .sort();
61
- if (cleared.length === 0)
62
- return [];
63
- for (const dir of cleared)
64
- delete value[dir];
65
- if (Object.keys(value).length === 0)
66
- removePath(bindingsPath());
67
- else
68
- writeJson(bindingsPath(), value);
69
- return cleared;
70
- }
18
+ function bindings() { return readStoredDefaults(userScopeRoot()); }
19
+ export function getDefaultProfileId(cwd) { return bindings()[resolveBindingCwd(cwd)] ?? null; }
20
+ export function setDefaultProfileId(cwd, profileId) { const directory = resolveBindingCwd(cwd); mutateStoredDefaults(userScopeRoot(), (current) => ({ ...current, [directory]: profileId })); }
21
+ export function clearDefaultProfile(cwd) { const directory = resolveBindingCwd(cwd); mutateStoredDefaults(userScopeRoot(), (current) => { const next = { ...current }; delete next[directory]; return next; }); }
22
+ export function defaultProfileDirs(profileId) { return Object.entries(bindings()).filter(([, id]) => id === profileId).map(([directory]) => directory).sort(); }
23
+ export function clearDefaultProfileId(profileId) { let cleared = []; mutateStoredDefaults(userScopeRoot(), (current) => { cleared = Object.entries(current).filter(([, id]) => id === profileId).map(([directory]) => directory).sort(); const next = { ...current }; for (const directory of cleared)
24
+ delete next[directory]; return next; }); return cleared; }
@@ -11,7 +11,7 @@
11
11
  // and no caller should log or echo what `readProfileEnvVars` returns.
12
12
  import { existsSync, readFileSync, renameSync, writeFileSync, chmodSync } from 'node:fs';
13
13
  import { join } from 'node:path';
14
- import { profileRoot, profileManifestPath, withProfileManifestLock } from './manifest.js';
14
+ import { loadExactProfileManifest, profileRoot, withProfileManifestLock } from './manifest.js';
15
15
  import { ensureDir } from '../fs-utils.js';
16
16
  import { notFound, usage } from '../errors.js';
17
17
  const ENV_FILE = 'env.json';
@@ -87,8 +87,13 @@ function writeStoreAtomic(profileId, store) {
87
87
  renameSync(tmp, p);
88
88
  }
89
89
  function requireProfileExists(profileId) {
90
- if (!existsSync(profileManifestPath(profileId))) {
91
- throw notFound(`profile not found: ${profileId}`, { received: profileId });
90
+ try {
91
+ loadExactProfileManifest(profileId);
92
+ }
93
+ catch (error) {
94
+ if (error instanceof Error && error.message.startsWith('profile not found:'))
95
+ throw notFound(`profile not found: ${profileId}`, { received: profileId });
96
+ throw error;
92
97
  }
93
98
  }
94
99
  /** Every stored variable NAME for a profile, sorted. Never returns values. */
@@ -5,62 +5,27 @@ export declare function profilesRoot(): string;
5
5
  export declare function profileRoot(profileId: string): string;
6
6
  export declare function profileManifestPath(profileId: string): string;
7
7
  export declare function profileMemoryDir(profileId: string): string;
8
- /** Reject anything a valid project array may not carry. Shape only: takes
9
- * `unknown` because an API body and a hand-edited manifest both prove nothing
10
- * at runtime, and deliberately says nothing about whether a path exists —
11
- * existence and realpath resolution belong to the create/add paths alone, so
12
- * a request naming a since-deleted directory still validates. */
13
8
  export declare function assertProfileProjects(entries: unknown): asserts entries is ProfileProject[];
14
- /** The directory a node running under `profileId` is pinned to — the profile's
15
- * home, but only while it still exists on disk (a node pinned to a deleted dir
16
- * cannot boot). Null for the root profile, a profile with no projects, a home
17
- * that has since been deleted, and an unresolvable id.
18
- *
19
- * This is the whole of the profile→cwd direction: cwd SELECTS a profile at the
20
- * front door, and the selected profile then SUPPLIES the node's cwd. Never
21
- * throws — it sits on the node-create path. */
22
9
  export declare function profileHome(profileId: string | null): string | null;
23
10
  export interface ProfileEntry {
24
11
  profileId: string;
25
12
  manifest: ProfileManifest;
26
13
  }
27
14
  export interface ProfileEnumeration {
28
- /** Every profile with a readable manifest, in directory-listing order. */
29
15
  entries: ProfileEntry[];
30
- /** Ids whose directory is present under the profiles root but whose manifest
31
- * did not read — corrupt JSON, a pre-object project shape, an unreadable or
32
- * missing `profile.json`. These are NOT deleted profiles, and a caller that
33
- * cannot tell the two apart silently substitutes another identity. */
34
16
  unreadableIds: string[];
35
17
  }
36
- /** Every profile with a readable manifest, in directory-listing order. A
37
- * profile whose manifest is missing or corrupt is skipped rather than
38
- * crashing the whole listing — `lookupProfile` is how a caller that must not
39
- * confuse "skipped" with "gone" asks. */
40
18
  export declare function listProfiles(): ProfileEntry[];
41
- /** What a stored profile id resolves to — including the case every
42
- * null-returning resolver here erases: the profile is THERE and could not be
43
- * read. A caller that treats that as "deleted" substitutes another identity
44
- * (root) for the one its work was pinned to. */
45
19
  export type ProfileLookup = {
46
20
  kind: 'resolved';
47
21
  entry: ProfileEntry;
48
- }
49
- /** Nothing on this host answers to the id or name — a deleted profile. */
50
- | {
22
+ } | {
51
23
  kind: 'absent';
52
- }
53
- /** The id's directory is present; its manifest did not read. */
54
- | {
24
+ } | {
55
25
  kind: 'unreadable';
56
26
  profileId: string;
57
27
  manifestPath: string;
58
28
  };
59
- /** Exact resolution (generated id, else a unique manifest `name`) that reports
60
- * WHY it failed. Only an ID can come back `unreadable`: a name lives inside
61
- * the manifest that could not be read, so an unreadable profile has no name to
62
- * match on. Throws on an ambiguous name, exactly as `loadProfileManifest`
63
- * does. */
64
29
  export declare function lookupProfile(profileIdOrName: string): ProfileLookup;
65
30
  /** Read one exact profile id without falling back to manifest-name matching.
66
31
  * Used at persistence gates that already hold a resolved durable identity. */
@@ -6,12 +6,13 @@
6
6
  // here, so no caller ever passes a raw CLI operand or `CRTR_PROFILE_ID`
7
7
  // straight into `join(profilesRoot(), value)`.
8
8
  import { randomUUID } from 'node:crypto';
9
- import { existsSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
10
- import { dirname, join, relative, resolve as resolvePath, sep } from 'node:path';
9
+ import { existsSync, realpathSync, rmSync, statSync, } from 'node:fs';
10
+ import { join, relative, resolve as resolvePath, sep } from 'node:path';
11
11
  import { SCHEMA_VERSION, PROFILE_DIR } from '../../types.js';
12
12
  import { PROFILE_PROJECT_MEMORY_VALUES } from '../../api/dto/profiles.js';
13
13
  import { userScopeRoot, resetScopeCache } from '../scope.js';
14
14
  import { ensureDir, nowIso } from '../fs-utils.js';
15
+ import { deleteStoredProfile, getStoredProfile, insertStoredProfile, listStoredProfiles, updateStoredProfile } from '../scoped-state/profiles.js';
15
16
  import { usage, notFound, ambiguous, general } from '../errors.js';
16
17
  import { withExclusiveDirectoryLock } from '../exclusive-lock.js';
17
18
  import { fuzzyMatchProfile } from './fuzzy-match.js';
@@ -24,11 +25,10 @@ import { fuzzyMatchProfile } from './fuzzy-match.js';
24
25
  const ID_SHAPE = /^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/;
25
26
  export const ROOT_PROFILE_ID = 'root-00000000';
26
27
  const ROOT_PROFILE_NAME = 'root';
27
- function scaffoldProfile(profileId, manifest) {
28
+ function scaffoldProfile(profileId, _manifest) {
28
29
  const root = profileRoot(profileId);
29
30
  ensureDir(root);
30
31
  ensureDir(join(root, 'memory'));
31
- writeManifestAtomic(profileManifestPath(profileId), manifest);
32
32
  }
33
33
  export function profilesRoot() {
34
34
  return join(userScopeRoot(), PROFILE_DIR);
@@ -82,144 +82,36 @@ export function profileMemoryDir(profileId) {
82
82
  * the user started them, not in whatever dir was last added to its purview. */
83
83
  function normalizeHome(profileId, manifest) {
84
84
  const stored = typeof manifest.home === 'string' && manifest.home !== '' ? manifest.home : null;
85
- const home = profileId === ROOT_PROFILE_ID ? stored : stored ?? manifest.projects[0]?.path ?? null;
86
- const pausedAt = typeof manifest.paused_at === 'string' && manifest.paused_at !== '' ? manifest.paused_at : null;
87
- return { ...manifest, home, paused_at: pausedAt };
88
- }
89
- /** Reject anything a valid project array may not carry. Shape only: takes
90
- * `unknown` because an API body and a hand-edited manifest both prove nothing
91
- * at runtime, and deliberately says nothing about whether a path exists —
92
- * existence and realpath resolution belong to the create/add paths alone, so
93
- * a request naming a since-deleted directory still validates. */
85
+ return { ...manifest, home: profileId === ROOT_PROFILE_ID ? stored : stored ?? manifest.projects[0]?.path ?? null, paused_at: typeof manifest.paused_at === 'string' && manifest.paused_at !== '' ? manifest.paused_at : null };
86
+ }
94
87
  export function assertProfileProjects(entries) {
95
88
  const memories = PROFILE_PROJECT_MEMORY_VALUES.join(', ');
96
- if (!Array.isArray(entries)) {
97
- throw usage('projects must be an array of {path, memory} objects', {
98
- received: typeof entries,
99
- field: 'projects',
100
- next: `Pass entries like {"path": "/abs/dir", "memory": "content"} — memory is one of: ${memories}.`,
101
- });
102
- }
89
+ if (!Array.isArray(entries))
90
+ throw usage('projects must be an array of {path, memory} objects', { received: typeof entries, field: 'projects', next: `Pass entries like {"path": "/abs/dir", "memory": "content"} — memory is one of: ${memories}.` });
103
91
  for (const entry of entries) {
104
- if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
105
- throw usage(`invalid project entry: ${JSON.stringify(entry)}`, {
106
- field: 'projects',
107
- next: 'Every project is an object like {"path": "/abs/dir", "memory": "content"}.',
108
- });
109
- }
92
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
93
+ throw usage(`invalid project entry: ${JSON.stringify(entry)}`, { field: 'projects', next: 'Every project is an object like {"path": "/abs/dir", "memory": "content"}.' });
110
94
  const { path, memory } = entry;
111
- if (typeof path !== 'string' || path.trim() === '') {
112
- throw usage(`project entry has no path: ${JSON.stringify(entry)}`, {
113
- field: 'projects',
114
- next: 'Give every project entry a non-empty directory path.',
115
- });
116
- }
117
- if (typeof memory !== 'string' || !PROFILE_PROJECT_MEMORY_VALUES.includes(memory)) {
118
- throw usage(`invalid project memory value: ${JSON.stringify(memory)}`, {
119
- received: String(memory),
120
- field: 'projects',
121
- next: `Set memory on ${path} to one of: ${memories}.`,
122
- });
123
- }
95
+ if (typeof path !== 'string' || path.trim() === '')
96
+ throw usage(`project entry has no path: ${JSON.stringify(entry)}`, { field: 'projects', next: 'Give every project entry a non-empty directory path.' });
97
+ if (typeof memory !== 'string' || !PROFILE_PROJECT_MEMORY_VALUES.includes(memory))
98
+ throw usage(`invalid project memory value: ${JSON.stringify(memory)}`, { received: String(memory), field: 'projects', next: `Set memory on ${path} to one of: ${memories}.` });
124
99
  }
125
100
  }
126
- function hasCurrentProjectShape(projects) {
127
- try {
128
- assertProfileProjects(projects);
129
- return true;
130
- }
131
- catch {
132
- return false;
133
- }
134
- }
135
- /** A manifest whose projects are not the current object shape is unreadable —
136
- * the same treatment as corrupt JSON, so nothing downstream ever guesses a
137
- * memory value. `crtr sys migrate` reads raw bytes and is the only converter
138
- * of pre-object manifests. */
139
- function readManifestFile(path, profileId) {
140
- if (!existsSync(path))
141
- return null;
142
- try {
143
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
144
- if (!hasCurrentProjectShape(parsed.projects))
145
- return null;
146
- return normalizeHome(profileId, parsed);
147
- }
148
- catch {
149
- return null;
150
- }
151
- }
152
- /** The directory a node running under `profileId` is pinned to — the profile's
153
- * home, but only while it still exists on disk (a node pinned to a deleted dir
154
- * cannot boot). Null for the root profile, a profile with no projects, a home
155
- * that has since been deleted, and an unresolvable id.
156
- *
157
- * This is the whole of the profile→cwd direction: cwd SELECTS a profile at the
158
- * front door, and the selected profile then SUPPLIES the node's cwd. Never
159
- * throws — it sits on the node-create path. */
160
101
  export function profileHome(profileId) {
161
102
  if (profileId === null || profileId === '')
162
103
  return null;
163
104
  try {
164
- const { manifest } = loadProfileManifest(profileId);
165
- const home = manifest.home;
166
- if (home === null)
167
- return null;
168
- return existsSync(home) && statSync(home).isDirectory() ? home : null;
105
+ const home = loadProfileManifest(profileId).manifest.home;
106
+ return home !== null && existsSync(home) && statSync(home).isDirectory() ? home : null;
169
107
  }
170
108
  catch {
171
109
  return null;
172
110
  }
173
111
  }
174
- /** The one walk of the profiles root: immediate children only — never
175
- * recurses, never trusts a caller-supplied id. A dir whose name doesn't match
176
- * the generated id shape is not a profile at all and is ignored. */
177
- function enumerateProfiles() {
178
- const root = profilesRoot();
179
- if (!existsSync(root))
180
- return { entries: [], unreadableIds: [] };
181
- const entries = [];
182
- const unreadableIds = [];
183
- for (const dirent of readdirSync(root, { withFileTypes: true })) {
184
- if (!dirent.isDirectory())
185
- continue;
186
- const profileId = dirent.name;
187
- if (!ID_SHAPE.test(profileId))
188
- continue;
189
- const manifest = readManifestFile(join(root, profileId, 'profile.json'), profileId);
190
- if (manifest === null)
191
- unreadableIds.push(profileId);
192
- else
193
- entries.push({ profileId, manifest });
194
- }
195
- return { entries, unreadableIds };
196
- }
197
- /** Every profile with a readable manifest, in directory-listing order. A
198
- * profile whose manifest is missing or corrupt is skipped rather than
199
- * crashing the whole listing — `lookupProfile` is how a caller that must not
200
- * confuse "skipped" with "gone" asks. */
201
- export function listProfiles() {
202
- return enumerateProfiles().entries;
203
- }
204
- /** Exact resolution (generated id, else a unique manifest `name`) that reports
205
- * WHY it failed. Only an ID can come back `unreadable`: a name lives inside
206
- * the manifest that could not be read, so an unreadable profile has no name to
207
- * match on. Throws on an ambiguous name, exactly as `loadProfileManifest`
208
- * does. */
209
- export function lookupProfile(profileIdOrName) {
210
- const { entries, unreadableIds } = enumerateProfiles();
211
- const exact = findExactProfile(entries, profileIdOrName);
212
- if (exact !== null)
213
- return { kind: 'resolved', entry: exact };
214
- if (unreadableIds.includes(profileIdOrName)) {
215
- return {
216
- kind: 'unreadable',
217
- profileId: profileIdOrName,
218
- manifestPath: join(profilesRoot(), profileIdOrName, 'profile.json'),
219
- };
220
- }
221
- return { kind: 'absent' };
222
- }
112
+ function enumerateProfiles() { return { entries: listStoredProfiles(userScopeRoot()).map(({ profileId, manifest }) => ({ profileId, manifest: normalizeHome(profileId, manifest) })), unreadableIds: [] }; }
113
+ export function listProfiles() { return enumerateProfiles().entries; }
114
+ export function lookupProfile(profileIdOrName) { const entries = listProfiles(); const exact = findExactProfile(entries, profileIdOrName); return exact ? { kind: 'resolved', entry: exact } : { kind: 'absent' }; }
223
115
  // Three resolvers, in descending tolerance. `resolveProfileOperand` is what a
224
116
  // command leaf calls for a value the USER TYPED: exact id, exact name, then a
225
117
  // fuzzy fallback. `loadProfileManifest` is the strict middle — exact id or
@@ -233,13 +125,10 @@ export function lookupProfile(profileIdOrName) {
233
125
  * Used at persistence gates that already hold a resolved durable identity. */
234
126
  export function loadExactProfileManifest(profileId) {
235
127
  assertSafeProfileId(profileId);
236
- const manifest = readManifestFile(profileManifestPath(profileId), profileId);
237
- if (manifest !== null)
238
- return { profileId, manifest };
239
- throw notFound(`profile not found: ${profileId}`, {
240
- received: profileId,
241
- next: 'Run `crtr profile list` to see available profiles.',
242
- });
128
+ const stored = getStoredProfile(userScopeRoot(), profileId);
129
+ if (stored !== null)
130
+ return { profileId, manifest: normalizeHome(profileId, stored.manifest) };
131
+ throw notFound(`profile not found: ${profileId}`, { received: profileId, next: 'Run `crtr profile list` to see available profiles.' });
243
132
  }
244
133
  /** Exact resolution only: the generated id, else a unique manifest `name`.
245
134
  * Returns `null` when nothing matches exactly; still THROWS when a name is
@@ -334,12 +223,6 @@ export function withProfileManifestLock(profileId, fn) {
334
223
  }),
335
224
  });
336
225
  }
337
- function writeManifestAtomic(path, manifest) {
338
- ensureDir(dirname(path));
339
- const tmp = `${path}.tmp.${process.pid}.${Math.random().toString(36).slice(2)}`;
340
- writeFileSync(tmp, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
341
- renameSync(tmp, path);
342
- }
343
226
  // Project dirs — resolved to absolute real paths, must exist + be a directory
344
227
  // on add/create, deduped by real path, stored in manifest order.
345
228
  function resolveExistingProjectDir(dir) {
@@ -391,16 +274,11 @@ function generateProfileId(name) {
391
274
  // (never for a bare last-used-at touch).
392
275
  export function ensureRootProfile() {
393
276
  return withProfileManifestLock(ROOT_PROFILE_ID, () => {
394
- const manifestPath = profileManifestPath(ROOT_PROFILE_ID);
395
- const manifestExists = existsSync(manifestPath);
396
- const manifest = manifestExists ? readManifestFile(manifestPath, ROOT_PROFILE_ID) : null;
397
- if (manifestExists && manifest === null) {
398
- throw general(`root profile manifest is unreadable: ${manifestPath}`);
399
- }
400
- if (manifest !== null) {
277
+ const existing = getStoredProfile(userScopeRoot(), ROOT_PROFILE_ID);
278
+ if (existing !== null) {
401
279
  ensureDir(profileRoot(ROOT_PROFILE_ID));
402
280
  ensureDir(profileMemoryDir(ROOT_PROFILE_ID));
403
- return { profileId: ROOT_PROFILE_ID, manifest };
281
+ return { profileId: ROOT_PROFILE_ID, manifest: normalizeHome(ROOT_PROFILE_ID, existing.manifest) };
404
282
  }
405
283
  const next = {
406
284
  schema_version: SCHEMA_VERSION,
@@ -412,6 +290,7 @@ export function ensureRootProfile() {
412
290
  last_used_at: nowIso(),
413
291
  };
414
292
  scaffoldProfile(ROOT_PROFILE_ID, next);
293
+ insertStoredProfile(userScopeRoot(), { profileId: ROOT_PROFILE_ID, manifest: next }, profileRoot(ROOT_PROFILE_ID));
415
294
  resetScopeCache();
416
295
  return { profileId: ROOT_PROFILE_ID, manifest: next };
417
296
  });
@@ -515,30 +394,23 @@ export function createProfile(name, projects = [], opts = {}) {
515
394
  last_used_at: null,
516
395
  };
517
396
  scaffoldProfile(profileId, manifest);
397
+ insertStoredProfile(userScopeRoot(), { profileId, manifest }, profileRoot(profileId));
518
398
  resetScopeCache();
519
399
  return { profileId, manifest };
520
400
  });
521
401
  }
522
402
  function requireExistingProfile(profileId) {
523
403
  assertSafeProfileId(profileId);
524
- if (!existsSync(profileManifestPath(profileId))) {
525
- throw notFound(`profile not found: ${profileId}`, {
526
- received: profileId,
527
- next: 'Run `crtr profile list` to see available profiles.',
528
- });
529
- }
404
+ if (getStoredProfile(userScopeRoot(), profileId) === null)
405
+ throw notFound(`profile not found: ${profileId}`, { received: profileId, next: 'Run `crtr profile list` to see available profiles.' });
530
406
  }
531
407
  function mutateManifest(profileId, mutate) {
532
408
  requireExistingProfile(profileId);
533
409
  return withProfileManifestLock(profileId, () => {
534
- const path = profileManifestPath(profileId);
535
- const current = readManifestFile(path, profileId);
536
- if (current === null) {
410
+ const result = updateStoredProfile(userScopeRoot(), profileId, mutate);
411
+ if (result === null)
537
412
  throw notFound(`profile not found: ${profileId}`, { received: profileId });
538
- }
539
- const next = mutate(current);
540
- writeManifestAtomic(path, next);
541
- return { profileId, manifest: next };
413
+ return { profileId, manifest: normalizeHome(profileId, result.manifest) };
542
414
  });
543
415
  }
544
416
  export function updateProfileLastUsed(profileId) {
@@ -694,6 +566,8 @@ export function removeProfileProject(profileId, dir) {
694
566
  export function deleteProfile(profileId) {
695
567
  requireExistingProfile(profileId);
696
568
  withProfileManifestLock(profileId, () => {
569
+ if (!deleteStoredProfile(userScopeRoot(), profileId))
570
+ throw notFound(`profile not found: ${profileId}`, { received: profileId });
697
571
  rmSync(profileRoot(profileId), { recursive: true, force: true });
698
572
  });
699
573
  resetScopeCache();
@@ -12,6 +12,8 @@ import { existsSync, realpathSync } from 'node:fs';
12
12
  import { basename, resolve as resolvePath, sep } from 'node:path';
13
13
  import { createInterface } from 'node:readline/promises';
14
14
  import { emitKeypressEvents } from 'node:readline';
15
+ import { initializeFreshScopedStore } from '../scoped-state/migrate.js';
16
+ import { userScopeRoot } from '../scope.js';
15
17
  import { listProfiles, loadProfileManifest, resolveProfileOperand, updateProfileLastUsed, createProfile, addProfileProject, ensureRootProfile, ROOT_PROFILE_ID, } from './manifest.js';
16
18
  import { getDefaultProfileId, setDefaultProfileId, clearDefaultProfile, } from './default-binding.js';
17
19
  import { tildify } from '../fs-utils.js';
@@ -668,6 +670,7 @@ async function promptPickProfileOrCreate(candidates, cwd, exact, pinnedId, bindi
668
670
  * default to the root profile and print the recovery instruction to STDERR —
669
671
  * never stdout, which the caller may be piping. */
670
672
  export async function selectProfileForCwd(cwd, explicitProfile, forcePicker = false) {
673
+ initializeFreshScopedStore({ kind: 'user', root: userScopeRoot() });
671
674
  const resolvedCwd = resolveCwd(cwd);
672
675
  const bindings = resolveUserKeybindings();
673
676
  if (explicitProfile !== undefined && explicitProfile !== null && explicitProfile !== '') {