@north-light/crouter 0.3.312 → 0.3.313

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (495) hide show
  1. package/dist/api/client.d.ts +27 -30
  2. package/dist/api/client.js +80 -67
  3. package/dist/api/dto/broker-ops.d.ts +10 -20
  4. package/dist/api/dto/canvas.d.ts +1 -10
  5. package/dist/api/dto/health.d.ts +113 -0
  6. package/dist/api/dto/human-requests.d.ts +14 -3
  7. package/dist/api/dto/human.d.ts +2 -48
  8. package/dist/api/dto/human.js +3 -2
  9. package/dist/api/dto/inbox.d.ts +14 -0
  10. package/dist/api/dto/mail.d.ts +43 -0
  11. package/dist/api/dto/nodes.d.ts +11 -8
  12. package/dist/api/dto/recovery.d.ts +96 -0
  13. package/dist/api/dto/reviews.d.ts +2 -2
  14. package/dist/api/routes.d.ts +9 -6
  15. package/dist/api/routes.js +11 -11
  16. package/dist/build-root.d.ts +4 -0
  17. package/dist/build-root.js +11 -1
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
  20. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  22. package/dist/cli.js +10 -1
  23. package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
  24. package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
  25. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  26. package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
  27. package/dist/clients/attach/chrome/inbox-strip.js +17 -9
  28. package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
  29. package/dist/clients/attach/chrome/ticket-panel.js +77 -46
  30. package/dist/clients/attach/overlays/graph.js +6 -9
  31. package/dist/clients/attach/render/chat-view.d.ts +5 -4
  32. package/dist/clients/attach/render/chat-view.js +22 -13
  33. package/dist/clients/attach/render/page-block.d.ts +11 -7
  34. package/dist/clients/attach/render/page-block.js +64 -55
  35. package/dist/clients/attach/viewer.js +746 -735
  36. package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
  37. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
  38. package/dist/clients/inbox/controller.d.ts +42 -14
  39. package/dist/clients/inbox/controller.js +240 -124
  40. package/dist/clients/inbox/dto-adapt.d.ts +28 -0
  41. package/dist/clients/inbox/dto-adapt.js +93 -0
  42. package/dist/clients/inbox/page-adapter.d.ts +5 -4
  43. package/dist/clients/inbox/page-adapter.js +12 -12
  44. package/dist/clients/inbox/resolve.d.ts +10 -5
  45. package/dist/clients/inbox/resolve.js +13 -12
  46. package/dist/clients/inbox/review/review-client.d.ts +0 -1
  47. package/dist/clients/inbox/review/review-client.js +0 -4
  48. package/dist/clients/inbox/review-adapter.d.ts +2 -1
  49. package/dist/clients/inbox/review-adapter.js +3 -5
  50. package/dist/clients/inbox/surface.d.ts +2 -2
  51. package/dist/clients/inbox/surface.js +19 -8
  52. package/dist/clients/inbox/tui.d.ts +3 -3
  53. package/dist/commands/__tests__/human.test.js +31 -15
  54. package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
  55. package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
  56. package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
  57. package/dist/commands/api-client.d.ts +5 -4
  58. package/dist/commands/api-client.js +13 -7
  59. package/dist/commands/canvas-history/shared.js +1 -1
  60. package/dist/commands/canvas-history.js +1 -1
  61. package/dist/commands/canvas-roster.js +1 -1
  62. package/dist/commands/canvas.js +1 -2
  63. package/dist/commands/human/feedback.js +20 -17
  64. package/dist/commands/human/prompts.js +45 -36
  65. package/dist/commands/human/queue.js +73 -123
  66. package/dist/commands/human/review.js +6 -16
  67. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
  68. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
  69. package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
  70. package/dist/commands/node-inspect-artifacts.js +1 -1
  71. package/dist/commands/node-lifecycle-revive.d.ts +2 -2
  72. package/dist/commands/node-lifecycle-revive.js +9 -19
  73. package/dist/commands/pkg/plugin-manage.js +0 -1
  74. package/dist/commands/profile/show.js +4 -12
  75. package/dist/commands/surface-tmux-spread.js +1 -5
  76. package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
  77. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
  78. package/dist/commands/sys/branch.d.ts +2 -0
  79. package/dist/commands/sys/branch.js +19 -0
  80. package/dist/commands/sys/config.js +106 -33
  81. package/dist/commands/sys/daemon.d.ts +2 -0
  82. package/dist/commands/sys/daemon.js +130 -7
  83. package/dist/commands/sys/doctor.js +5 -15
  84. package/dist/commands/sys/migrate.js +21 -92
  85. package/dist/commands/sys/setup-settings-runner.js +4 -2
  86. package/dist/commands/sys.js +2 -15
  87. package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
  88. package/dist/core/__tests__/boot.test.js +32 -25
  89. package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
  90. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
  91. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
  92. package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
  93. package/dist/core/__tests__/canvas.test.js +18 -47
  94. package/dist/core/__tests__/child-death-wake.test.js +29 -7
  95. package/dist/core/__tests__/cli-help-contract.test.js +2 -0
  96. package/dist/core/__tests__/close.test.js +29 -10
  97. package/dist/core/__tests__/collectible.test.js +2 -1
  98. package/dist/core/__tests__/config-change-delta.test.js +2 -0
  99. package/dist/core/__tests__/context-intro.test.js +13 -1
  100. package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
  101. package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
  102. package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
  103. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
  104. package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
  105. package/dist/core/__tests__/daemon-boot.test.js +51 -14
  106. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  107. package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
  108. package/dist/core/__tests__/focuses.test.js +10 -5
  109. package/dist/core/__tests__/helpers/harness.js +50 -18
  110. package/dist/core/__tests__/history-inbox.test.js +12 -16
  111. package/dist/core/__tests__/human-action-delivery.test.js +48 -28
  112. package/dist/core/__tests__/human-actions.test.js +7 -3
  113. package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
  114. package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
  115. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
  116. package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
  117. package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
  118. package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
  119. package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
  120. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  121. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
  122. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  123. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  124. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  125. package/dist/core/__tests__/integration/revive.test.js +67 -21
  126. package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
  127. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  128. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  129. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  130. package/dist/core/__tests__/kickoff.test.js +36 -39
  131. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  132. package/dist/core/__tests__/lifecycle.test.js +2 -1
  133. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  134. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  135. package/dist/core/__tests__/migration.test.js +145 -31
  136. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  137. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  138. package/dist/core/__tests__/node-outcome.test.js +93 -26
  139. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
  141. package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
  142. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  143. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  144. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  145. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  146. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  147. package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
  148. package/dist/core/__tests__/prune.test.js +11 -5
  149. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  150. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  151. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  152. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  153. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  154. package/dist/core/__tests__/revive-all.test.js +13 -3
  155. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  156. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  157. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  158. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  159. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  160. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  161. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  162. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  163. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  164. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  165. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
  166. package/dist/core/__tests__/stop-guard.test.js +2 -1
  167. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  168. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
  169. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  170. package/dist/core/bootstrap.js +2 -0
  171. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  172. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  173. package/dist/core/broker-client/transport-socket.js +13 -5
  174. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  175. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  176. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  177. package/dist/core/canvas/attention.d.ts +2 -5
  178. package/dist/core/canvas/attention.js +15 -64
  179. package/dist/core/canvas/boot.d.ts +6 -7
  180. package/dist/core/canvas/boot.js +5 -9
  181. package/dist/core/canvas/canvas.d.ts +13 -109
  182. package/dist/core/canvas/canvas.js +140 -401
  183. package/dist/core/canvas/crons.d.ts +2 -0
  184. package/dist/core/canvas/crons.js +16 -15
  185. package/dist/core/canvas/daemon-owner.js +10 -0
  186. package/dist/core/canvas/db.d.ts +2 -2
  187. package/dist/core/canvas/db.js +2 -18
  188. package/dist/core/canvas/deliveries.d.ts +104 -0
  189. package/dist/core/canvas/deliveries.js +354 -0
  190. package/dist/core/canvas/executions.d.ts +29 -0
  191. package/dist/core/canvas/executions.js +68 -0
  192. package/dist/core/canvas/history.js +35 -35
  193. package/dist/core/canvas/index.d.ts +3 -1
  194. package/dist/core/canvas/index.js +3 -1
  195. package/dist/core/canvas/migrations.js +20 -12
  196. package/dist/core/canvas/nav-model.js +1 -3
  197. package/dist/core/canvas/nav-render.d.ts +4 -4
  198. package/dist/core/canvas/nav-render.js +11 -21
  199. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  200. package/dist/core/canvas/node-sql-migration.js +261 -0
  201. package/dist/core/canvas/paths.d.ts +2 -16
  202. package/dist/core/canvas/paths.js +2 -31
  203. package/dist/core/canvas/recovery.d.ts +76 -0
  204. package/dist/core/canvas/recovery.js +588 -0
  205. package/dist/core/canvas/remote-canvas-source.js +4 -0
  206. package/dist/core/canvas/render-source.d.ts +16 -27
  207. package/dist/core/canvas/render-source.js +16 -45
  208. package/dist/core/canvas/sessions.d.ts +33 -0
  209. package/dist/core/canvas/sessions.js +78 -0
  210. package/dist/core/canvas/types.d.ts +33 -33
  211. package/dist/core/canvas/types.js +2 -5
  212. package/dist/core/canvas/waits.d.ts +18 -0
  213. package/dist/core/canvas/waits.js +70 -0
  214. package/dist/core/canvas/worktrees.d.ts +40 -0
  215. package/dist/core/canvas/worktrees.js +93 -0
  216. package/dist/core/config.d.ts +17 -12
  217. package/dist/core/config.js +106 -155
  218. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  219. package/dist/core/feed/feed.d.ts +0 -6
  220. package/dist/core/feed/feed.js +68 -56
  221. package/dist/core/feed/inbox.d.ts +7 -81
  222. package/dist/core/feed/inbox.js +46 -378
  223. package/dist/core/feed/messages.d.ts +58 -0
  224. package/dist/core/feed/messages.js +217 -0
  225. package/dist/core/fs-utils.d.ts +4 -0
  226. package/dist/core/fs-utils.js +21 -1
  227. package/dist/core/human/answer.d.ts +1 -1
  228. package/dist/core/human/answer.js +1 -1
  229. package/dist/core/human/claim.d.ts +1 -1
  230. package/dist/core/human/claim.js +2 -4
  231. package/dist/core/human/component-docs.js +1 -1
  232. package/dist/core/human/feedback-companion.js +3 -2
  233. package/dist/core/human/feedback.d.ts +5 -8
  234. package/dist/core/human/feedback.js +57 -85
  235. package/dist/core/human/page-authoring.d.ts +30 -0
  236. package/dist/core/human/page-authoring.js +19 -0
  237. package/dist/core/human/requests.d.ts +136 -0
  238. package/dist/core/human/requests.js +515 -0
  239. package/dist/core/human/root.d.ts +0 -7
  240. package/dist/core/human/root.js +0 -17
  241. package/dist/core/human/scan.d.ts +2 -7
  242. package/dist/core/human/scan.js +48 -132
  243. package/dist/core/human/summary.d.ts +1 -1
  244. package/dist/core/human/summary.js +1 -1
  245. package/dist/core/human/tickets.d.ts +23 -83
  246. package/dist/core/human/tickets.js +92 -243
  247. package/dist/core/human/types.d.ts +5 -1
  248. package/dist/core/installed-plugins.js +8 -3
  249. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  250. package/dist/core/locked-file.d.ts +6 -3
  251. package/dist/core/locked-file.js +36 -9
  252. package/dist/core/memory/extensions.js +2 -0
  253. package/dist/core/preview-registry.js +0 -1
  254. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  255. package/dist/core/profiles/default-binding.d.ts +0 -4
  256. package/dist/core/profiles/default-binding.js +12 -58
  257. package/dist/core/profiles/env-store.js +8 -3
  258. package/dist/core/profiles/manifest.d.ts +2 -37
  259. package/dist/core/profiles/manifest.js +35 -161
  260. package/dist/core/profiles/select.js +3 -0
  261. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  262. package/dist/core/review/__tests__/comments.test.js +28 -10
  263. package/dist/core/review/__tests__/remap.test.js +28 -10
  264. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  265. package/dist/core/review/birth.d.ts +1 -1
  266. package/dist/core/review/birth.js +34 -5
  267. package/dist/core/review/comments.js +1 -1
  268. package/dist/core/review/document.js +1 -1
  269. package/dist/core/review/project.d.ts +5 -1
  270. package/dist/core/review/project.js +14 -36
  271. package/dist/core/review/realize.d.ts +3 -6
  272. package/dist/core/review/realize.js +51 -168
  273. package/dist/core/review/remap.js +1 -1
  274. package/dist/core/review/stage.d.ts +3 -2
  275. package/dist/core/review/stage.js +2 -3
  276. package/dist/core/review/store.d.ts +3 -21
  277. package/dist/core/review/store.js +140 -186
  278. package/dist/core/review/types.d.ts +0 -1
  279. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  280. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  281. package/dist/core/runtime/broker/auth-reload.js +9 -10
  282. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  283. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  284. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  285. package/dist/core/runtime/broker/extension-abort.js +11 -0
  286. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  287. package/dist/core/runtime/broker/fault-retry.js +142 -95
  288. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  289. package/dist/core/runtime/broker/inbox.js +4 -92
  290. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  291. package/dist/core/runtime/broker/rebind.js +21 -12
  292. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  293. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  294. package/dist/core/runtime/broker.js +28 -22
  295. package/dist/core/runtime/close.d.ts +3 -9
  296. package/dist/core/runtime/close.js +16 -27
  297. package/dist/core/runtime/fault.d.ts +3 -93
  298. package/dist/core/runtime/fault.js +9 -419
  299. package/dist/core/runtime/kickoff.d.ts +3 -6
  300. package/dist/core/runtime/kickoff.js +12 -18
  301. package/dist/core/runtime/launch.js +1 -1
  302. package/dist/core/runtime/lifecycle.js +40 -7
  303. package/dist/core/runtime/placement.js +2 -2
  304. package/dist/core/runtime/recycle.js +8 -2
  305. package/dist/core/runtime/reset.js +22 -6
  306. package/dist/core/runtime/revive.js +226 -218
  307. package/dist/core/runtime/spawn.js +14 -3
  308. package/dist/core/scoped-state/db.d.ts +7 -0
  309. package/dist/core/scoped-state/db.js +92 -0
  310. package/dist/core/scoped-state/migrate.d.ts +11 -0
  311. package/dist/core/scoped-state/migrate.js +198 -0
  312. package/dist/core/scoped-state/paths.d.ts +9 -0
  313. package/dist/core/scoped-state/paths.js +27 -0
  314. package/dist/core/scoped-state/profiles.d.ts +12 -0
  315. package/dist/core/scoped-state/profiles.js +62 -0
  316. package/dist/core/scoped-state/providers.d.ts +16 -0
  317. package/dist/core/scoped-state/providers.js +8 -0
  318. package/dist/core/scoped-state/schema.d.ts +3 -0
  319. package/dist/core/scoped-state/schema.js +26 -0
  320. package/dist/core/scoped-state/settings.d.ts +28 -0
  321. package/dist/core/scoped-state/settings.js +82 -0
  322. package/dist/core/subscription-state.d.ts +29 -48
  323. package/dist/core/subscription-state.js +176 -712
  324. package/dist/core/worktree-close.js +4 -4
  325. package/dist/core/worktree-mutation-async.js +2 -2
  326. package/dist/core/worktree-sweep.js +11 -16
  327. package/dist/core/worktree.js +6 -12
  328. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  329. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  330. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  331. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  332. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  333. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
  334. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  335. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  336. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  337. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  338. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  339. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  340. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  341. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  342. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  343. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
  344. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  345. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  346. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  347. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  348. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  349. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
  350. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  351. package/dist/daemon/api/bridge.js +1 -1
  352. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  353. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  354. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  355. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  356. package/dist/daemon/api/handlers/canvas.js +2 -44
  357. package/dist/daemon/api/handlers/daemon.js +41 -0
  358. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  359. package/dist/daemon/api/handlers/health.js +10 -5
  360. package/dist/daemon/api/handlers/human-requests.js +168 -188
  361. package/dist/daemon/api/handlers/human.js +3 -151
  362. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  363. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  364. package/dist/daemon/api/handlers/inbox.js +21 -120
  365. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  366. package/dist/daemon/api/handlers/messages.js +73 -15
  367. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  368. package/dist/daemon/api/handlers/nodes.js +7 -13
  369. package/dist/daemon/api/handlers/reviews.js +4 -29
  370. package/dist/daemon/api/map.d.ts +25 -3
  371. package/dist/daemon/api/map.js +84 -14
  372. package/dist/daemon/api/server.js +68 -9
  373. package/dist/daemon/control.d.ts +18 -0
  374. package/dist/daemon/cron/disposition.d.ts +2 -0
  375. package/dist/daemon/cron/disposition.js +8 -5
  376. package/dist/daemon/cron/settle.js +37 -13
  377. package/dist/daemon/cron/sinks.d.ts +18 -1
  378. package/dist/daemon/cron/sinks.js +47 -93
  379. package/dist/daemon/crtrd-cli.js +4 -19
  380. package/dist/daemon/crtrd.d.ts +2 -0
  381. package/dist/daemon/crtrd.js +206 -81
  382. package/dist/daemon/fleet.js +5 -6
  383. package/dist/daemon/human/finish.d.ts +12 -24
  384. package/dist/daemon/human/finish.js +88 -285
  385. package/dist/daemon/human/settle.d.ts +40 -0
  386. package/dist/daemon/human/settle.js +89 -0
  387. package/dist/daemon/human/sweep.d.ts +20 -5
  388. package/dist/daemon/human/sweep.js +56 -60
  389. package/dist/daemon/manage.d.ts +21 -16
  390. package/dist/daemon/manage.js +85 -51
  391. package/dist/daemon/messaging/node-message.d.ts +1 -1
  392. package/dist/daemon/messaging/node-message.js +9 -12
  393. package/dist/daemon/park-activity.js +3 -3
  394. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  395. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  396. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  397. package/dist/daemon/reconcilers/controller-death.js +6 -18
  398. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  399. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  400. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  401. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  402. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  403. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  404. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  405. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  406. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  407. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  408. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  409. package/dist/daemon/review/companion.js +5 -11
  410. package/dist/daemon/review/deliver.d.ts +1 -5
  411. package/dist/daemon/review/deliver.js +3 -52
  412. package/dist/daemon/review/finish.d.ts +2 -27
  413. package/dist/daemon/review/finish.js +79 -263
  414. package/dist/daemon/review/sweep.d.ts +3 -10
  415. package/dist/daemon/review/sweep.js +13 -131
  416. package/dist/daemon/startup-policy.d.ts +0 -5
  417. package/dist/daemon/startup-policy.js +0 -5
  418. package/dist/index.d.ts +2 -1
  419. package/dist/index.js +1 -1
  420. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  421. package/dist/migrations/__tests__/activation.test.js +9 -6
  422. package/dist/migrations/activation.d.ts +3 -2
  423. package/dist/migrations/activation.js +2 -4
  424. package/dist/migrations/corpus.d.ts +2 -5
  425. package/dist/migrations/corpus.js +9 -20
  426. package/dist/migrations/runner.d.ts +3 -1
  427. package/dist/migrations/runner.js +6 -2
  428. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  429. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  430. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  431. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  432. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  433. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  434. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  435. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  436. package/dist/pi-extensions/canvas-stophook.js +47 -31
  437. package/dist/shared/env.d.ts +1 -0
  438. package/dist/shared/env.js +3 -0
  439. package/package.json +1 -1
  440. package/runtime.lock.json +5 -5
  441. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  442. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  443. package/dist/clients/inbox/review-visibility.js +0 -19
  444. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  445. package/dist/commands/canvas-rebuild-index.js +0 -46
  446. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  447. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  448. package/dist/core/__tests__/human-deliver.test.js +0 -256
  449. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  450. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  451. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  452. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  453. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  454. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  455. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  456. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  457. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  458. package/dist/core/canvas/human-deliveries.js +0 -75
  459. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  460. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  461. package/dist/core/feed/passive.d.ts +0 -3
  462. package/dist/core/feed/passive.js +0 -53
  463. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  464. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  465. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  466. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  467. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  468. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  469. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  470. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  471. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  472. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  473. package/dist/core/review/ticket-filter.d.ts +0 -17
  474. package/dist/core/review/ticket-filter.js +0 -21
  475. package/dist/core/runtime/broker/passive.d.ts +0 -17
  476. package/dist/core/runtime/broker/passive.js +0 -44
  477. package/dist/core/runtime/busy.d.ts +0 -46
  478. package/dist/core/runtime/busy.js +0 -150
  479. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  480. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  481. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  482. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  483. package/dist/daemon/human/deliver-action.d.ts +0 -3
  484. package/dist/daemon/human/deliver-action.js +0 -9
  485. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  486. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  487. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  488. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  489. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  490. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  491. package/dist/daemon/startup-block-marker.d.ts +0 -25
  492. package/dist/daemon/startup-block-marker.js +0 -99
  493. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  494. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  495. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -1,15 +1,11 @@
1
- import { existsSync, mkdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs';
1
+ import { mkdirSync, readFileSync, realpathSync } from 'node:fs';
2
2
  import { basename, isAbsolute, resolve } from 'node:path';
3
- import { randomUUID } from 'node:crypto';
4
3
  import { z } from 'zod';
5
- import { buildPageSummary } from './summary.js';
6
- import { atomicWriteJson, atomicWriteText, clearProgress, claimPath, pageManifestPath, pageModulePath, pagePath, progressPath, publishJsonExclusive, requireTicket, responsePath, reviewPath } from './convention.js';
7
- import { validateReviewDescriptor, validateReviewProjection } from './review-schema.js';
8
- import { compilePage, deriveHtmlPageManifest, derivePageManifestFromDocument, parsePage, parsePageDelivery, PageAuthoringError } from './page.js';
9
- import { isResponseBearingSlot, validatePageResponses, validatePartialPageResponses } from './page-schema.js';
4
+ import { pageModulePath, pagePath } from './convention.js';
5
+ import { parsePage } from './page.js';
6
+ import { validatePageResponses, validatePartialPageResponses } from './page-schema.js';
10
7
  import { signalInboxActivity } from './root.js';
11
- import { readTicketClaim, releaseClaimLocked, withTicketLock } from './claim.js';
12
- import { checkReviewableFile } from './visible-paths.js';
8
+ import { createPageRequest, getRequestByDirectory, presentationDir, publishPageRevision, revisePageRequest, writeProgress as writeRequestProgress } from './requests.js';
13
9
  const feedbackCommentSchema = z.object({ id: z.string().min(1), line: z.number().int().positive(), endLine: z.number().int().positive(), quote: z.string().optional(), colStart: z.number().int().nonnegative().optional(), colEnd: z.number().int().nonnegative().optional(), lineText: z.string(), comment: z.string().min(1), createdAt: z.string().min(1) }).strict();
14
10
  const iso = z.string().datetime({ offset: true });
15
11
  const feedbackSchema = z.object({ file: z.string().min(1).refine(isAbsolute, 'file must be absolute').refine((file) => resolve(file) === file, 'file must be canonical'), submitted: z.literal(true), approved: z.boolean(), comments: z.array(feedbackCommentSchema), submittedAt: iso, savedAt: iso, reviewId: z.string().optional(), changed: z.boolean().optional(), baselineSha256: z.string().optional(), approvedSha256: z.string().optional(), companionNodeId: z.string().optional(), commentsTotal: z.number().int().nonnegative().optional(), commentsUnresolved: z.number().int().nonnegative().optional() }).strict().superRefine((result, ctx) => {
@@ -27,20 +23,21 @@ const pageResultSchema = z.object({ schema: z.literal('crtr.page-response/v1'),
27
23
  const pageProgressSchema = z.object({ kind: z.literal('page'), responses: z.record(z.string(), slotResponseSchema), savedAt: iso }).strict();
28
24
  const reviewResultSchema = z.object({ schema: z.literal('humanloop.review-response/v1'), kind: z.literal('review'), result: feedbackSchema, completedAt: iso }).strict();
29
25
  const canceledResultSchema = z.object({ schema: z.literal('humanloop.cancel/v1'), kind: z.literal('canceled'), canceledAt: iso, reason: z.string().optional(), actor: z.string().optional(), disposition: z.enum(['canceled', 'dismissed']).optional() }).strict();
30
- /** Strict decoder for the only canonical final marker. Only a missing file reads
31
- * as absent — a corrupt (unparseable) response is a real anomaly and throws,
32
- * since silently treating it as "no result yet" would mask data loss instead
33
- * of surfacing it to whichever caller owns the failure boundary. */
26
+ /** Strict decoder for the canonical SQL terminal result. A corrupt result throws
27
+ * rather than being treated as absent, because that would hide data loss from
28
+ * the caller that owns the failure boundary. */
34
29
  export function readTicketResult(dir) {
35
- const path = responsePath(dir);
36
- if (!existsSync(path))
30
+ const request = getRequestByDirectory(dir);
31
+ if (request === null)
32
+ throw new Error(`unknown human request directory: ${dir}`);
33
+ if (request.state !== 'settled' || request.result_json === null)
37
34
  return null;
38
35
  let raw;
39
36
  try {
40
- raw = JSON.parse(readFileSync(path, 'utf8'));
37
+ raw = JSON.parse(request.result_json);
41
38
  }
42
39
  catch (error) {
43
- throw new Error(`corrupt ticket result: ${path}`, { cause: error });
40
+ throw new Error(`corrupt human request result: ${request.request_id}`, { cause: error });
44
41
  }
45
42
  const page = pageResultSchema.safeParse(raw);
46
43
  if (page.success)
@@ -49,107 +46,43 @@ export function readTicketResult(dir) {
49
46
  if (review.success)
50
47
  return review.data;
51
48
  const canceled = canceledResultSchema.safeParse(raw);
52
- return canceled.success ? canceled.data : null;
49
+ if (canceled.success)
50
+ return canceled.data;
51
+ throw new Error(`human request has an invalid terminal result: ${request.request_id}`);
53
52
  }
54
- function hasTicketProtocolState(dir) {
55
- return [pageManifestPath(dir), pagePath(dir), pageModulePath(dir), reviewPath(dir), responsePath(dir), progressPath(dir), claimPath(dir)].some(existsSync);
56
- }
57
- function publishRequest(path, value) {
58
- if (!publishJsonExclusive(path, value))
59
- throw new Error(`ticket request already exists: ${path}`);
60
- }
61
- /** Validate one authored page document into publishable form. The single
62
- * validation path: `preparePage` reads a file and lands here, and a
63
- * programmatic request supplies the same text inline. */
64
- export function preparePageSource(opts) {
65
- const stamped = { ...(opts.source ?? {}), emittedAt: opts.source?.emittedAt ?? new Date().toISOString() };
66
- const delivery = opts.delivery ?? { placement: 'inline', inbox: false, reply: false };
67
- if (opts.dialect === 'html')
68
- return { document: opts.document, manifest: deriveHtmlPageManifest(opts.document, stamped, delivery) };
69
- return { document: opts.document, module: compilePage(opts.document), manifest: derivePageManifestFromDocument(opts.document, opts.productKinds ?? [], stamped, delivery) };
70
- }
71
- /** Validate one authored page source file into publishable form. Reads only: a
72
- * page that fails authoring leaves nothing behind, so a caller that must create
73
- * a bridge node prepares first and creates it only once the page is known good.
74
- * The source is read exactly once, so the published document is the same bytes
75
- * the manifest was derived from even if the file changes underneath. */
76
- export function preparePage(opts) {
77
- const html = /\.html?$/i.test(opts.sourceFile);
78
- if (!html && !opts.sourceFile.endsWith('.tsx'))
79
- throw new PageAuthoringError(`page source file must be .tsx, .html, or .htm: ${opts.sourceFile}`);
80
- if (!existsSync(opts.sourceFile))
81
- throw new PageAuthoringError(`page source file does not exist: ${opts.sourceFile}`);
82
- const { sourceFile: _sourceFile, ...rest } = opts;
83
- return preparePageSource({ ...rest, dialect: html ? 'html' : 'jsx', document: readFileSync(opts.sourceFile, 'utf8') });
84
- }
85
- /** Remove a stale generation file. The manifest always switches first, so a
86
- * failed cleanup leaves one valid current record plus harmless old source. */
87
- function unlinkIfPresent(path) {
88
- try {
89
- unlinkSync(path);
90
- }
91
- catch (error) {
92
- if (error.code !== 'ENOENT')
93
- throw error;
94
- }
95
- }
96
- /** Publish one prepared page ticket: the authored source plus its manifest.
97
- * These writes are overwrites. A ticket's REQUEST is whatever page the agent
98
- * currently wants on screen, and `crtr human send --replace` revises it in
99
- * place while the human watches; only the ANSWER is write-once, which
100
- * `publishJsonExclusive` still guarantees for response.json alone. The manifest
101
- * switches after all new generation files exist, then stale media is removed. */
53
+ /** Publish an immutable revision, then atomically select it with its matching specification. */
102
54
  export function publishPage(dir, prepared) {
103
55
  mkdirSync(dir, { recursive: true, mode: 0o700 });
104
56
  const ticket = realpathSync(dir);
105
- atomicWriteText(pagePath(ticket, prepared.manifest.dialect), prepared.document);
106
- if (prepared.manifest.dialect === 'jsx')
107
- atomicWriteText(pageModulePath(ticket), prepared.module);
108
- atomicWriteJson(pageManifestPath(ticket), prepared.manifest);
109
- if (prepared.manifest.dialect === 'jsx')
110
- unlinkIfPresent(pagePath(ticket, 'html'));
111
- else {
112
- unlinkIfPresent(pagePath(ticket, 'jsx'));
113
- unlinkIfPresent(pageModulePath(ticket));
57
+ const request = getRequestByDirectory(ticket);
58
+ if (request === null) {
59
+ const created = createPageRequest({ request_id: basename(ticket), prepared });
60
+ signalInboxActivity();
61
+ return { id: created.request_id, dir: created.request_dir, kind: 'page', manifest: prepared.manifest };
114
62
  }
115
- // Unconditional: a no-inbox page still lives in an attached viewer's transcript, and
116
- // --replace promises every open surface reloads it.
63
+ const revision = publishPageRevision(ticket, prepared);
64
+ if (!revisePageRequest(request.request_id, {
65
+ presentation_dir: revision,
66
+ specification_json: JSON.stringify(prepared.manifest),
67
+ title: prepared.manifest.title,
68
+ subtitle: prepared.manifest.subtitle ?? null,
69
+ inbox_listed: prepared.manifest.delivery.inbox,
70
+ settleable: prepared.manifest.delivery.inbox || prepared.manifest.delivery.reply,
71
+ }))
72
+ throw new Error(`page request is not open: ${request.request_id}`);
117
73
  signalInboxActivity();
118
- return { id: basename(ticket), dir: ticket, kind: 'page', manifest: prepared.manifest };
74
+ return { id: request.request_id, dir: ticket, kind: 'page', manifest: prepared.manifest };
119
75
  }
120
- /** Validate and publish one page ticket in a single step, for callers that own
121
- * the ticket directory before they have a page. */
122
- export function submitPage(opts) {
123
- if (existsSync(opts.dir) && hasTicketProtocolState(opts.dir) && !existsSync(pageManifestPath(opts.dir)))
124
- throw new Error(`ticket protocol state already exists: ${opts.dir}`);
125
- return publishPage(opts.dir, preparePage(opts));
76
+ function pageRequest(dir) {
77
+ const request = getRequestByDirectory(dir);
78
+ if (request === null || request.kind !== 'page')
79
+ throw new Error(`unknown page request directory: ${dir}`);
80
+ return request;
126
81
  }
127
- export function submitReview(opts) {
128
- // A review takes any reviewable text file: a .md artifact renders as a document,
129
- // and source code renders as a syntax-highlighted code panel.
130
- if (!isAbsolute(opts.review.file) || !existsSync(opts.review.file))
131
- throw new Error('review file must be an existing absolute path');
132
- const reviewable = checkReviewableFile(opts.review.file);
133
- if (!reviewable.ok)
134
- throw new Error(`review file is not reviewable: ${reviewable.reason}`);
135
- const source = realpathSync(opts.review.file);
136
- mkdirSync(opts.dir, { recursive: true, mode: 0o700 });
137
- const dir = realpathSync(opts.dir);
138
- if (hasTicketProtocolState(dir))
139
- throw new Error(`ticket protocol state already exists: ${dir}`);
140
- const descriptor = validateReviewProjection(dir, { schema: 'humanloop.review/v1', file: source, output: resolve(opts.review.output ?? `${dir}/feedback.json`), title: opts.review.title, subtitle: opts.review.subtitle, source: opts.review.source, blockedSince: opts.review.blockedSince ?? new Date().toISOString() });
141
- publishRequest(reviewPath(dir), descriptor);
142
- signalInboxActivity();
143
- return { id: basename(dir), dir, kind: 'review' };
144
- }
145
- function exclusiveResult(dir, result) {
146
- return publishJsonExclusive(responsePath(dir), result);
147
- }
148
- function requireReview(dir) { return validateReviewDescriptor(JSON.parse(readFileSync(reviewPath(dir), 'utf8'))); }
82
+ /** Resolve a stable request directory to the SQL-selected immutable page revision. */
83
+ export function selectedPageDir(dir) { return presentationDir(pageRequest(dir)); }
149
84
  function requirePage(dir) {
150
- if (!existsSync(pageManifestPath(dir)))
151
- throw new Error(`not a page ticket: ${dir}`);
152
- return parsePage(dir);
85
+ return parsePage(selectedPageDir(dir));
153
86
  }
154
87
  function requireSettleablePage(dir) {
155
88
  const manifest = requirePage(dir);
@@ -157,166 +90,82 @@ function requireSettleablePage(dir) {
157
90
  throw new Error('page_has_no_response_lifecycle');
158
91
  return manifest;
159
92
  }
160
- function requireCancelablePage(dir) {
161
- // Cancellation never interprets answers. Reading only this stable envelope
162
- // keeps an older resident daemon able to retract a page authored by a newer CLI.
163
- const delivery = parsePageDelivery(dir);
164
- if (!delivery.inbox && !delivery.reply)
165
- throw new Error('page_has_no_response_lifecycle');
166
- }
167
93
  function isEmptyResponseMap(responses) {
168
94
  return typeof responses === 'object' && responses !== null && !Array.isArray(responses) && Object.keys(responses).length === 0;
169
95
  }
170
96
  /** Validate a final page response against its ticket's reply contract. */
171
- function validateFinalPageTicketResponses(dir, responses) {
172
- const manifest = requireSettleablePage(dir);
173
- // An inbox-only page accepts an empty acknowledgement without requiring an answer
174
- // from every response-bearing component.
175
- if (!manifest.delivery.reply && isEmptyResponseMap(responses)) {
97
+ export function validateFinalPageResponses(manifest, responses) {
98
+ if (!manifest.delivery.reply && isEmptyResponseMap(responses))
176
99
  return validatePartialPageResponses(manifest, responses);
177
- }
178
100
  return validatePageResponses(manifest, responses);
179
101
  }
180
- /** Validate a page resolution against its canonical ticket before claim takeover. */
102
+ function validateFinalPageTicketResponses(dir, responses) {
103
+ return validateFinalPageResponses(requireSettleablePage(dir), responses);
104
+ }
105
+ /** Validate a page resolution against its SQL-selected immutable revision. */
181
106
  export function validatePageTicketResponses(dir, responses) {
182
- const ticket = requireTicket(dir);
183
- return validateFinalPageTicketResponses(ticket, responses);
107
+ return validateFinalPageTicketResponses(dir, responses);
184
108
  }
185
- /** The three terminal states of a page ticket, derived from its published result. */
109
+ /** Page state is derived from the SQL request row. */
186
110
  export function pageTicketState(dir) {
187
- const manifest = requirePage(dir);
188
- if (!manifest.delivery.inbox && !manifest.delivery.reply)
111
+ const request = pageRequest(dir);
112
+ if (!request.settleable)
189
113
  return 'passive';
190
- const result = readTicketResult(dir);
191
- if (result === null)
114
+ if (request.state !== 'settled')
192
115
  return 'pending';
193
- return result.kind === 'canceled' ? 'canceled' : 'resolved';
194
- }
195
- function readPageProgressFor(dir, manifest) {
196
- let raw;
197
- try {
198
- raw = JSON.parse(readFileSync(progressPath(dir), 'utf8'));
199
- }
200
- catch (error) {
201
- if (error.code === 'ENOENT')
202
- return null;
203
- throw new Error(`page progress is not valid JSON: ${progressPath(dir)}`);
204
- }
205
- const parsed = pageProgressSchema.safeParse(raw);
206
- if (!parsed.success)
207
- throw new Error(`page progress is malformed: ${progressPath(dir)}`);
208
- // A pending page's request can be replaced under saved work, so progress may
209
- // name a component the current page no longer declares. Those entries are dropped
210
- // and everything still declared hydrates; what remains is validated, so a
211
- // saved answer that no longer fits its own component is still an error.
212
- const declared = new Set(manifest.slots.filter(isResponseBearingSlot).map((slot) => slot.id));
213
- const current = Object.fromEntries(Object.entries(parsed.data.responses).filter(([id]) => declared.has(id)));
214
- return validatePartialPageResponses(manifest, current);
116
+ return request.outcome === 'canceled' || request.outcome === 'dismissed' ? 'canceled' : 'resolved';
215
117
  }
216
118
  /** Autosave partial page work. Every supplied response is validated; omitted components are permitted. */
217
119
  export function writePageProgress(dir, responses) {
218
- const ticket = requireTicket(dir);
219
- const validated = validatePartialPageResponses(requireSettleablePage(ticket), responses);
220
- atomicWriteJson(progressPath(ticket), { kind: 'page', responses: validated, savedAt: new Date().toISOString() });
120
+ const request = pageRequest(dir);
121
+ const validated = validatePartialPageResponses(requireSettleablePage(dir), responses);
122
+ if (!writeRequestProgress(request.inbox_ticket_id, validated))
123
+ throw new Error(`page request is not open and settleable: ${request.request_id}`);
221
124
  return validated;
222
125
  }
223
- /** Saved partial work, or null when none exists. Malformed progress is an error, never silently discarded. */
126
+ /** Saved partial work from the request row, or null when none exists. */
224
127
  export function readPageProgress(dir) {
225
- const ticket = requireTicket(dir);
226
- return readPageProgressFor(ticket, requireSettleablePage(ticket));
128
+ const request = pageRequest(dir);
129
+ if (request.progress_json === null)
130
+ return null;
131
+ try {
132
+ const progress = JSON.parse(request.progress_json);
133
+ if (progress.kind !== 'page')
134
+ throw new Error('wrong kind');
135
+ return progress.responses;
136
+ }
137
+ catch (error) {
138
+ throw new Error(`page progress is malformed for ${request.request_id}`, { cause: error });
139
+ }
227
140
  }
228
141
  /** Read one addressable page ticket — pending, resolved or canceled alike. */
229
142
  export function readPageTicket(dir) {
230
- const ticket = requireTicket(dir);
231
- const manifest = requirePage(ticket);
143
+ const request = pageRequest(dir);
144
+ const presentation = presentationDir(request);
145
+ const manifest = parsePage(presentation);
232
146
  return {
233
- dir: ticket,
234
- id: basename(ticket),
147
+ dir: request.request_dir,
148
+ id: request.request_id,
235
149
  manifest,
236
- document: readFileSync(pagePath(ticket, manifest.dialect), 'utf8'),
237
- ...(manifest.dialect === 'jsx' ? { module: readFileSync(pageModulePath(ticket), 'utf8') } : { module: undefined }),
238
- state: pageTicketState(ticket),
239
- progress: manifest.delivery.inbox || manifest.delivery.reply ? readPageProgressFor(ticket, manifest) : null,
150
+ document: readFileSync(pagePath(presentation, manifest.dialect), 'utf8'),
151
+ ...(manifest.dialect === 'jsx' ? { module: readFileSync(pageModulePath(presentation), 'utf8') } : { module: undefined }),
152
+ state: pageTicketState(request.request_dir),
153
+ progress: manifest.delivery.inbox || manifest.delivery.reply ? readPageProgress(request.request_dir) : null,
240
154
  };
241
155
  }
242
- function requireClaimOwnership(dir, token) {
243
- if (readTicketClaim(dir)?.token !== token)
244
- throw new Error('only the current claim holder may submit a result');
245
- }
246
- function clearOwnedWork(dir, claimToken) { clearProgress(dir); releaseClaimLocked(dir, claimToken); }
247
- /** Caller holds `.ticket-lock`. */
248
- function completePageLocked(dir, responses, claimToken, opts) {
249
- requireClaimOwnership(dir, claimToken);
250
- const manifest = requirePage(dir);
251
- const parsedResponses = validateFinalPageTicketResponses(dir, responses);
252
- const result = { schema: 'crtr.page-response/v1', kind: 'page', responses: parsedResponses, summary: buildPageSummary(manifest, parsedResponses), completedAt: opts.completedAt ?? new Date().toISOString(), ...(opts.actor === undefined ? {} : { actor: opts.actor }) };
253
- const won = exclusiveResult(dir, result);
254
- clearOwnedWork(dir, claimToken);
255
- // Lost the race: the winner's result must already be on disk, so a null read
256
- // here is an anomaly worth surfacing rather than silently falling back to
257
- // this loser's own uncommitted result.
258
- return { won, result: won ? result : (readTicketResult(dir) ?? (() => { throw new Error(`lost the completion race but no winning result is on disk: ${dir}`); })()) };
259
- }
260
- export function completePage(dir, responses, claimToken, opts = {}) {
261
- const ticket = requireTicket(dir);
262
- const completed = withTicketLock(ticket, () => completePageLocked(ticket, responses, claimToken, opts));
263
- if (completed.won)
264
- signalInboxActivity();
265
- return completed;
266
- }
267
- function takeoverClaim(dir, owner) {
268
- const existing = readTicketClaim(dir);
269
- if (existing !== null)
270
- unlinkSync(claimPath(dir));
271
- const token = randomUUID();
272
- const now = new Date().toISOString();
273
- writeFileSync(claimPath(dir), `${JSON.stringify({ token, host: owner.host, pid: owner.pid, claimedAt: now, heartbeatAt: now })}\n`, { flag: 'wx', mode: 0o600 });
274
- return token;
156
+ /** Page settlement must go through daemon/human/finish, which records its SQL result and direct obligations together. */
157
+ export function completePage(_dir, _responses, _claimToken, _opts = {}) {
158
+ throw new Error('page completion must use settleHumanRequest');
275
159
  }
276
- /** Atomically take a page ticket's claim over, complete it, and release the takeover claim. */
277
- export function takeoverAndCompletePage(dir, responses, owner, opts = {}) {
278
- const ticket = requireTicket(dir);
279
- const completed = withTicketLock(ticket, () => {
280
- const token = takeoverClaim(ticket, owner);
281
- try {
282
- return completePageLocked(ticket, responses, token, opts);
283
- }
284
- finally {
285
- releaseClaimLocked(ticket, token);
286
- }
287
- });
288
- if (completed.won)
289
- signalInboxActivity();
290
- return completed;
160
+ /** Page settlement must go through daemon/human/finish, which records its SQL result and direct obligations together. */
161
+ export function takeoverAndCompletePage(_dir, _responses, _owner, _opts = {}) {
162
+ throw new Error('page completion must use settleHumanRequest');
291
163
  }
292
- /** Cancellation races finalization but never requires or removes another claim. */
293
- export function cancelTicket(dir, opts = {}) {
294
- const ticket = requireTicket(dir);
295
- const canceled = withTicketLock(ticket, () => {
296
- if (existsSync(pageManifestPath(ticket)))
297
- requireCancelablePage(ticket);
298
- const result = { schema: 'humanloop.cancel/v1', kind: 'canceled', canceledAt: new Date().toISOString(), ...(opts.reason === undefined ? {} : { reason: opts.reason }), ...(opts.actor === undefined ? {} : { actor: opts.actor }), ...(opts.disposition === undefined ? {} : { disposition: opts.disposition }) };
299
- const won = exclusiveResult(ticket, result);
300
- // Same anomaly as completePageLocked: losing the race with no winning
301
- // result on disk means the ticket state is broken, not just contended.
302
- return {
303
- status: won ? 'canceled' : 'already_resolved',
304
- result: won ? result : (readTicketResult(ticket) ?? (() => { throw new Error(`lost the cancellation race but no winning result is on disk: ${ticket}`); })()),
305
- };
306
- });
307
- if (canceled.status === 'canceled')
308
- signalInboxActivity();
309
- return canceled;
164
+ /** Page settlement must go through daemon/human/finish, which records its SQL result and direct obligations together. */
165
+ export function cancelTicket(_dir, _opts = {}) {
166
+ throw new Error('page cancellation must use settleHumanRequest');
310
167
  }
311
- /** Write the validated review output once its review result is published. */
312
- export function projectReviewOutput(dir) {
313
- const result = readTicketResult(dir);
314
- if (result === null)
315
- return 'no_result';
316
- if (result.kind !== 'review')
317
- return 'not_a_review';
318
- const ticket = requireTicket(dir);
319
- const descriptor = validateReviewProjection(ticket, requireReview(ticket));
320
- atomicWriteJson(descriptor.output, result.result);
321
- return 'projected';
168
+ /** Review projection is performed by daemon/review/finish after its SQL terminal transition. */
169
+ export function projectReviewOutput(_dir) {
170
+ throw new Error('review projection must use projectReviewResult');
322
171
  }
@@ -36,6 +36,8 @@ export interface ClaimSummary {
36
36
  interface TicketSummaryBase {
37
37
  dir: string;
38
38
  id: string;
39
+ /** Stored opaque inbox identity. It may differ from `id` after legacy migration. */
40
+ inboxTicketId: string;
39
41
  title: string;
40
42
  subtitle: string;
41
43
  blockedSince: string;
@@ -51,6 +53,8 @@ export interface ReviewTicketSummary extends TicketSummaryBase {
51
53
  export interface PageTicketSummary {
52
54
  dir: string;
53
55
  id: string;
56
+ /** Stored opaque inbox identity. It may differ from `id` after legacy migration. */
57
+ inboxTicketId: string;
54
58
  kind: 'page';
55
59
  title: string;
56
60
  subtitle: string;
@@ -109,6 +113,6 @@ export interface CanceledTicketResult {
109
113
  * requester (`canceled`). Absent on records written before the field. */
110
114
  disposition?: 'canceled' | 'dismissed';
111
115
  }
112
- /** The sole canonical response.json union. */
116
+ /** The canonical terminal-result union stored in the human request row. */
113
117
  export type TicketResult = PageTicketResult | ReviewTicketResult | CanceledTicketResult;
114
118
  export {};
@@ -12,8 +12,13 @@ import { CONFIG_FILE } from '../types.js';
12
12
  import { listDirs, pathExists, readJsonIfExists } from './fs-utils.js';
13
13
  import { readPluginManifest } from './manifest.js';
14
14
  import { awaitBundleSwap, bundleSwapInFlightElsewhere } from './plugin-swap-lock.js';
15
- function pluginConfigForRoot(root) {
16
- const cfg = readJsonIfExists(join(root, CONFIG_FILE));
15
+ import { userStore } from './scoped-state/paths.js';
16
+ import { readRawSettings } from './scoped-state/settings.js';
17
+ function pluginConfigForRoot(scope, root) {
18
+ const store = scope === 'user' ? userStore(root) : undefined;
19
+ const cfg = store
20
+ ? readRawSettings({ store, key: 'user', kind: 'user', root: store.root })
21
+ : readJsonIfExists(join(root, CONFIG_FILE));
17
22
  return cfg && cfg.plugins && typeof cfg.plugins === 'object' ? cfg.plugins : {};
18
23
  }
19
24
  /**
@@ -38,7 +43,7 @@ export function listInstalledPluginsInRoot(scope, scopeRootPath) {
38
43
  const dir = join(scopeRootPath, 'plugins');
39
44
  if (!pathExists(dir))
40
45
  return [];
41
- const cfg = pluginConfigForRoot(scopeRootPath);
46
+ const cfg = pluginConfigForRoot(scope, scopeRootPath);
42
47
  const first = collectInstalledPlugins(scope, dir, cfg);
43
48
  const waiting = swapsToWaitOut(scopeRootPath, cfg, new Set(first.map((plugin) => plugin.name)));
44
49
  if (waiting.length === 0)
@@ -1,10 +1,12 @@
1
1
  import { after, before, beforeEach, test } from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
6
  import { KeybindingConcurrentEditError, KeybindingValidationError, canonicalTerminalStroke, matchesTerminalInput, normalizeSparseOverrides, persistUserKeybindings, resolveKeybindings, resolveUserKeybindings, } from '../index.js';
7
- import { readConfig } from '../../config.js';
7
+ import { ensureScopeInitialized, readConfig, readRawScopeConfig, updateRawConfigAtomically, writeRawConfig } from '../../config.js';
8
+ import { closeScopedStores } from '../../scoped-state/db.js';
9
+ import { resetScopeCache } from '../../scope.js';
8
10
  const typeCheckedBindingId = 'crtr.graph.down';
9
11
  // @ts-expect-error The catalog type rejects IDs that are not built in.
10
12
  const rejectedBindingId = 'crtr.graph.not-real';
@@ -24,15 +26,21 @@ beforeEach(() => {
24
26
  home = mkdtempSync(join(tmpdir(), 'crtr-keybindings-'));
25
27
  process.env['HOME'] = home;
26
28
  process.chdir(realCwd);
29
+ resetScopeCache();
30
+ ensureScopeInitialized('user', join(home, '.crouter'));
27
31
  });
28
32
  after(() => {
33
+ closeScopedStores();
29
34
  process.chdir(realCwd);
30
35
  if (realHome === undefined)
31
36
  delete process.env['HOME'];
32
37
  else
33
38
  process.env['HOME'] = realHome;
34
39
  });
35
- test.afterEach(() => rmSync(home, { recursive: true, force: true }));
40
+ test.afterEach(() => {
41
+ closeScopedStores();
42
+ rmSync(home, { recursive: true, force: true });
43
+ });
36
44
  test('terminal literal space matches named space bindings', () => {
37
45
  const raw = { input: ' ' };
38
46
  assert.equal(canonicalTerminalStroke(raw), 'space');
@@ -106,8 +114,7 @@ test('sparsification omits defaults, preserves explicit unbinds, and reset remov
106
114
  assert.throws(() => normalizeSparseOverrides({ 'crtr.graph.down': ['k'] }), KeybindingValidationError);
107
115
  });
108
116
  test('user resolution ignores project keybindings', () => {
109
- mkdirSync(join(home, '.crouter'), { recursive: true });
110
- writeFileSync(join(home, '.crouter', 'config.json'), JSON.stringify({ keybindings: { 'crtr.graph.down': ['down'] } }));
117
+ writeRawConfig('user', { keybindings: { 'crtr.graph.down': ['down'] } });
111
118
  const project = mkdtempSync(join(tmpdir(), 'crtr-keybindings-project-'));
112
119
  mkdirSync(join(project, '.crouter'), { recursive: true });
113
120
  writeFileSync(join(project, '.crouter', 'config.json'), JSON.stringify({ keybindings: { 'crtr.graph.down': ['up'] } }));
@@ -121,8 +128,7 @@ test('user resolution ignores project keybindings', () => {
121
128
  }
122
129
  });
123
130
  test('config hard cut exposes only sparse keybindings and ignores unsupported raw fields', () => {
124
- mkdirSync(join(home, '.crouter'), { recursive: true });
125
- writeFileSync(join(home, '.crouter', 'config.json'), JSON.stringify({
131
+ updateRawConfigAtomically('user', () => ({
126
132
  canvasNav: { prefixKey: 'alt+x' },
127
133
  keybindings: { 'crtr.graph.down': ['down'], bad: 3 },
128
134
  }));
@@ -131,9 +137,7 @@ test('config hard cut exposes only sparse keybindings and ignores unsupported ra
131
137
  assert.equal(Object.hasOwn(config, 'canvasNav'), false);
132
138
  });
133
139
  test('atomic persistence preserves latest unrelated fields and writes only sparse normalized overrides', () => {
134
- mkdirSync(join(home, '.crouter'), { recursive: true });
135
- const path = join(home, '.crouter', 'config.json');
136
- writeFileSync(path, JSON.stringify({
140
+ updateRawConfigAtomically('user', () => ({
137
141
  customFutureField: { retained: true },
138
142
  max_panes_per_window: 4,
139
143
  canvasNav: { prefixKey: 'alt+z' },
@@ -143,7 +147,7 @@ test('atomic persistence preserves latest unrelated fields and writes only spars
143
147
  'crtr.graph.close-subtree': [],
144
148
  'crtr.graph.down': ['j'],
145
149
  });
146
- const raw = JSON.parse(readFileSync(path, 'utf8'));
150
+ const raw = readRawScopeConfig('user');
147
151
  assert.deepEqual(raw.customFutureField, { retained: true });
148
152
  assert.equal(raw.max_panes_per_window, 4);
149
153
  assert.deepEqual(raw.keybindings, {
@@ -153,17 +157,15 @@ test('atomic persistence preserves latest unrelated fields and writes only spars
153
157
  assert.equal(raw.canvasNav, undefined);
154
158
  });
155
159
  test('reset-all removes the top-level member and concurrent keybinding edits are refused', () => {
156
- mkdirSync(join(home, '.crouter'), { recursive: true });
157
- const path = join(home, '.crouter', 'config.json');
158
160
  const original = { 'crtr.graph.down': ['down'] };
159
- writeFileSync(path, JSON.stringify({ keybindings: original, retained: 1 }));
161
+ updateRawConfigAtomically('user', () => ({ keybindings: original, retained: 1 }));
160
162
  persistUserKeybindings({}, { expectedKeybindings: original });
161
- let raw = JSON.parse(readFileSync(path, 'utf8'));
163
+ let raw = readRawScopeConfig('user');
162
164
  assert.equal(raw.keybindings, undefined);
163
165
  assert.equal(raw.retained, 1);
164
- writeFileSync(path, JSON.stringify({ keybindings: { 'crtr.graph.up': ['up'] }, retained: 2 }));
166
+ updateRawConfigAtomically('user', () => ({ keybindings: { 'crtr.graph.up': ['up'] }, retained: 2 }));
165
167
  assert.throws(() => persistUserKeybindings({}, { expectedKeybindings: original }), KeybindingConcurrentEditError);
166
- raw = JSON.parse(readFileSync(path, 'utf8'));
168
+ raw = readRawScopeConfig('user');
167
169
  assert.deepEqual(raw.keybindings, { 'crtr.graph.up': ['up'] });
168
170
  assert.equal(raw.retained, 2);
169
171
  });
@@ -6,8 +6,9 @@ export type LockResult<T> = {
6
6
  * from INSIDE the critical section must call it immediately before that write, because the
7
7
  * post-`fn` check cannot un-write a file the callback already replaced. */
8
8
  export type LockHeldGuard = () => void;
9
+ export type LockedFilePublish = (next: string) => void;
9
10
  export interface LockedFileBackend {
10
- withLockAsync<T>(fn: (current: string | undefined, assertLockHeld: LockHeldGuard) => Promise<LockResult<T>>): Promise<T>;
11
+ withLockAsync<T>(fn: (current: string | undefined, assertLockHeld: LockHeldGuard, publish: LockedFilePublish) => Promise<LockResult<T>>): Promise<T>;
11
12
  /** The current whole-file contents, or undefined when the file does not exist yet.
12
13
  * Lock-free: every writer replaces the file by atomic rename. */
13
14
  readUnlocked(): string | undefined;
@@ -15,10 +16,12 @@ export interface LockedFileBackend {
15
16
  /** A 0600 JSON file guarded by a cross-process `proper-lockfile` lock on its own path. */
16
17
  export declare class LockedJsonFile implements LockedFileBackend {
17
18
  private readonly path;
18
- constructor(path: string);
19
+ private readonly durable;
20
+ private readonly initialContent;
21
+ constructor(path: string, durable?: boolean, initialContent?: string);
19
22
  readUnlocked(): string | undefined;
20
23
  private ensureFile;
21
24
  /** Acquire the lock without blocking the event loop, keeping every read-modify-write
22
25
  * callback on the same asynchronous path. */
23
- withLockAsync<T>(fn: (current: string | undefined, assertLockHeld: LockHeldGuard) => Promise<LockResult<T>>): Promise<T>;
26
+ withLockAsync<T>(fn: (current: string | undefined, assertLockHeld: LockHeldGuard, publish: LockedFilePublish) => Promise<LockResult<T>>): Promise<T>;
24
27
  }