@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,81 +1,81 @@
1
+ // The inbox controller against a real daemon: every row, page body, progress
2
+ // autosave and settlement crosses the human/inbox routes, so this fixture binds
3
+ // the daemon's own route tables rather than re-implementing their projections.
1
4
  import assert from 'node:assert/strict';
2
- import { createServer } from 'node:http';
3
- import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import { ticketDir } from '#core/human/root';
7
- import { submitPage, takeoverAndCompletePage, writePageProgress } from '#core/human/tickets';
8
- import { InboxController } from '../../controller.js';
5
+ import { startHumanDaemon } from '../../../__tests__/helpers/human-daemon.js';
6
+ const daemon = await startHumanDaemon('crtr-inbox-controller');
7
+ // Imported after the isolated home is in place, so nothing resolves the
8
+ // developer's own canvas at module load.
9
+ const { cliClient } = await import('#commands/api-client');
10
+ const { InboxController } = await import('../../controller.js');
9
11
  const key = (part = {}) => ({ ctrl: false, meta: false, upArrow: false, downArrow: false, leftArrow: false, rightArrow: false, wordLeft: false, wordRight: false, home: false, end: false, pageUp: false, pageDown: false, del: false, return: false, newline: false, escape: false, tab: false, backspace: false, ...part });
10
12
  function fakeHost() {
11
13
  const handle = { close() { }, invalidate() { }, onClose() { } };
12
14
  return { capabilities: { remote: false, canFocusNode: false, selfPane: undefined }, mount: () => handle, notice() { }, suspendForChild: () => () => { }, focusNode: async () => false, pickReviewFile: async () => null };
13
15
  }
14
- const controller = (options = {}) => new InboxController({ host: fakeHost(), onDirty() { }, onClosed() { }, ...options });
15
- async function waitFor(predicate, message) { const end = Date.now() + 1000; while (!predicate()) {
16
+ const controller = (cols, rows) => new InboxController({ host: fakeHost(), onDirty() { }, onClosed() { }, cols, rows });
17
+ async function waitFor(predicate, message) { const end = Date.now() + 5000; for (;;) {
18
+ if (await predicate())
19
+ return;
16
20
  if (Date.now() > end)
17
21
  throw new Error(message);
18
22
  await new Promise((r) => setTimeout(r, 10));
19
23
  } }
20
- const temp = mkdtempSync(join(tmpdir(), 'crtr-inbox-controller-'));
21
- const sources = mkdtempSync(join(tmpdir(), 'crtr-page-sources-'));
22
- process.env['CRTR_HOME'] = temp;
23
- let received;
24
- async function startDaemon() {
25
- const server = createServer(async (req, res) => {
26
- if (!req.url?.endsWith('/resolve')) {
27
- res.statusCode = 404;
28
- res.end();
29
- return;
30
- }
31
- const body = await new Promise((resolve) => { let text = ''; req.on('data', (c) => { text += c; }); req.on('end', () => resolve(text)); });
32
- const nodeId = req.url.split('/').at(-2);
33
- received = JSON.parse(body).responses;
34
- takeoverAndCompletePage(ticketDir(nodeId), received, { host: 'daemon', pid: 1 });
35
- res.end(JSON.stringify({ delivered: true, kind: 'page' }));
36
- });
37
- await new Promise((resolve) => server.listen(join(temp, 'crtrd.sock'), resolve));
38
- return server;
39
- }
40
- const source = (name, body) => { const path = join(sources, name); writeFileSync(path, body); return path; };
41
- const ask = source('ask.tsx', 'export default () => (\n <Page title="Restored page" subtitle="Pick one">\n <UserQuestion id="choice" label="Restore?" body="Pick one to continue." mode="single" options={[{ id: "yes", label: "Yes" }, { id: "no", label: "No" }]} />\n </Page>\n);\n');
42
- const rich = source('rich.tsx', 'export default () => (\n <Page title="Rich page" subtitle="Browser content">\n <Chart kind="bar" x="day" y={[{ field: "count" }]} />\n </Page>\n);\n');
24
+ const client = cliClient();
43
25
  // A ticket reaches the inbox and settles only with a response lifecycle; a page
44
26
  // carrying neither is a passive display that never opens here.
45
- const inboxDelivery = { placement: 'inline', inbox: true, reply: true };
46
- const server = await startDaemon();
27
+ const delivery = { placement: 'inline', inbox: true, reply: false };
28
+ const createPage = (source) => client.createHumanRequest({ page: { dialect: 'jsx', source }, delivery, creator_cwd: daemon.home });
29
+ const ticketIdOf = async (requestId) => {
30
+ const { tickets } = await client.listHumanInbox();
31
+ const row = tickets.find((ticket) => ticket.request_id === requestId);
32
+ if (row === undefined)
33
+ throw new Error(`request is not listed in the inbox: ${requestId}`);
34
+ return row.ticket_id;
35
+ };
36
+ const ask = 'export default () => (\n <Page title="Restored page" subtitle="Pick one">\n <UserQuestion id="choice" label="Restore?" body="Pick one to continue." mode="single" options={[{ id: "yes", label: "Yes" }, { id: "no", label: "No" }]} />\n </Page>\n);\n';
37
+ const rich = 'export default () => (\n <Page title="Rich page" subtitle="Browser content">\n <Chart kind="bar" x="day" y={[{ field: "count" }]} />\n </Page>\n);\n';
47
38
  try {
48
- const ticket = submitPage({ dir: ticketDir('page-one'), sourceFile: ask, delivery: inboxDelivery });
49
- writePageProgress(ticket.dir, { choice: { selectedOptionIds: ['yes'], comments: [] } });
50
- const active = controller({ cols: 100, rows: 24 });
51
- active.activate();
52
- assert.equal(active.snapshot().screen, 'detail', 'page ticket opens in the page panel');
53
- active.handleKey('n', key());
54
- active.handleKey('', key({ return: true }));
55
- await waitFor(() => existsSync(join(ticket.dir, 'response.json')), 'restored page responses cross the daemon seam');
56
- assert.deepEqual(received, { choice: { selectedOptionIds: ['yes'], comments: [] } }, 'progress.json is restored into the response map');
57
- active.close();
58
- submitPage({ dir: ticketDir('rich-page'), sourceFile: rich, delivery: inboxDelivery });
59
- const preview = controller({ cols: 100, rows: 24 });
39
+ // A page opens with the manifest-backed terminal projection of its content.
40
+ const richRequest = await createPage(rich);
41
+ const preview = controller(100, 24);
42
+ await waitFor(() => preview.snapshot().items.length === 1, 'the rich page reaches the inbox list');
43
+ preview.selectDir(richRequest.request_dir);
60
44
  assert.match(preview.render().join('\n'), /Rich page/, 'a pending page is listed before it is opened');
61
- preview.activate();
45
+ await preview.activate();
62
46
  assert.match(preview.render().join('\n'), /chart — bar: day vs count/, 'a page uses the manifest-backed terminal projection');
63
47
  preview.close();
64
48
  // The list preview shows what the page says, because `s` settles the selected
65
- // ticket from this screen without ever opening it.
66
- submitPage({ dir: ticketDir('preview-page'), sourceFile: ask, delivery: inboxDelivery });
67
- const listing = controller({ cols: 100, rows: 40 });
49
+ // ticket from this screen without ever opening it. The body is a round trip,
50
+ // so the first frame schedules it and a later frame carries it.
51
+ const previewRequest = await createPage(ask);
52
+ const listing = controller(100, 40);
53
+ await waitFor(() => listing.snapshot().items.length === 2, 'both pending pages reach the inbox list');
54
+ listing.selectDir(previewRequest.request_dir);
55
+ await waitFor(() => listing.render().join('\n').includes('Pick one to continue'), 'the preview carries the question body');
68
56
  const shown = listing.render().join('\n');
69
- assert.match(shown, /Pick one to continue/, 'the preview carries the question body');
70
57
  assert.match(shown, /Yes/, 'the preview carries the options the reader would pick from');
71
58
  assert.doesNotMatch(shown, /\d+ components?\b/, 'the component count no longer stands in for the content');
72
59
  listing.close();
60
+ // Autosaved progress is restored into the page the reader opens, and the
61
+ // answer it submits crosses the daemon's settlement route.
62
+ const request = await createPage(ask);
63
+ await client.postInboxProgress(await ticketIdOf(request.request_id), { choice: { selectedOptionIds: ['yes'], comments: [] } });
64
+ const active = controller(100, 24);
65
+ await waitFor(() => active.snapshot().items.length === 3, 'the created page reaches the inbox list');
66
+ active.selectDir(request.request_dir);
67
+ await active.activate();
68
+ assert.equal(active.snapshot().screen, 'detail', 'page ticket opens in the page panel');
69
+ assert.match(active.render().join('\n'), /✓|\[✓\]/, 'the autosaved answer is restored into the open page');
70
+ active.handleKey('n', key());
71
+ active.handleKey('', key({ return: true }));
72
+ await waitFor(async () => (await client.getHumanRequest(request.request_id)).state !== 'pending', 'restored page responses cross the daemon seam');
73
+ const settled = await client.getHumanRequest(request.request_id);
74
+ assert.equal(settled.page?.result?.kind, 'page', 'the page settled with an answer');
75
+ assert.deepEqual(settled.page?.result?.kind === 'page' ? settled.page.result.responses : undefined, { choice: { selectedOptionIds: ['yes'], comments: [] } }, 'saved progress is restored into the response map');
76
+ active.close();
73
77
  }
74
78
  finally {
75
- server.closeAllConnections();
76
- await new Promise((resolve) => server.close(() => resolve()));
77
- rmSync(temp, { recursive: true, force: true });
78
- rmSync(sources, { recursive: true, force: true });
79
- delete process.env['CRTR_HOME'];
79
+ await daemon.dispose();
80
80
  }
81
81
  console.log('inbox controller tests passed');
@@ -1,53 +1,66 @@
1
- // A page ticket that settles out-of-band (answered from another surface, or a
1
+ // A page request that settles out-of-band (answered from another surface, or a
2
2
  // row this inbox failed to drop) must not read as "being edited by another
3
- // inbox" when the reader presses Enter: its response.json already exists, so the
4
- // row is stale and this activation must report the settlement and drop it.
3
+ // inbox" when the reader presses Enter: its request row is already settled, so
4
+ // the row is stale and this activation must report the settlement and drop it.
5
5
  import assert from 'node:assert/strict';
6
- import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
7
- import { tmpdir } from 'node:os';
8
- import { join } from 'node:path';
9
- import { ticketDir } from '#core/human/root';
10
- import { submitPage, takeoverAndCompletePage } from '#core/human/tickets';
11
- import { claimPath } from '#core/human/convention';
12
- import { InboxController } from '../../controller.js';
6
+ import { existsSync } from 'node:fs';
7
+ import { startHumanDaemon } from '../../../__tests__/helpers/human-daemon.js';
8
+ const daemon = await startHumanDaemon('crtr-stale-row');
9
+ // Imported after the isolated home is in place, so nothing resolves the
10
+ // developer's own canvas at module load.
11
+ const { cliClient } = await import('#commands/api-client');
12
+ const { withCanvasWrite } = await import('#core/canvas/db');
13
+ const { settleRequestLocked } = await import('#core/human/requests');
14
+ const { claimPath } = await import('#core/human/convention');
15
+ const { InboxController } = await import('../../controller.js');
13
16
  const key = (part = {}) => ({ ctrl: false, meta: false, upArrow: false, downArrow: false, leftArrow: false, rightArrow: false, wordLeft: false, wordRight: false, home: false, end: false, pageUp: false, pageDown: false, del: false, return: false, newline: false, escape: false, tab: false, backspace: false, ...part });
14
17
  function fakeHost() {
15
18
  const handle = { close() { }, invalidate() { }, onClose() { } };
16
19
  return { capabilities: { remote: false, canFocusNode: false, selfPane: undefined }, mount: () => handle, notice() { }, suspendForChild: () => () => { }, focusNode: async () => false, pickReviewFile: async () => null };
17
20
  }
18
21
  const status = (c) => c.status;
19
- const temp = mkdtempSync(join(tmpdir(), 'crtr-stale-row-'));
20
- const sources = mkdtempSync(join(tmpdir(), 'crtr-stale-src-'));
21
- process.env['CRTR_HOME'] = temp;
22
- const sourceFile = join(sources, 'ask.tsx');
23
- writeFileSync(sourceFile, 'export default () => (\n <Page title="Stale row page" subtitle="Pick one">\n <UserQuestion id="choice" label="Restore?" body="Pick one." mode="single" options={[{ id: "yes", label: "Yes" }, { id: "no", label: "No" }]} />\n </Page>\n);\n');
24
- const inboxDelivery = { placement: 'inline', inbox: true, reply: true };
22
+ async function waitFor(predicate, message) { const end = Date.now() + 5000; while (!predicate()) {
23
+ if (Date.now() > end)
24
+ throw new Error(message);
25
+ await new Promise((r) => setTimeout(r, 10));
26
+ } }
25
27
  try {
26
- const dir = ticketDir('stale-page');
27
- submitPage({ dir, sourceFile, delivery: inboxDelivery });
28
- // Seed the controller's in-memory list from disk while the ticket is pending.
29
- const options = { cols: 100, rows: 24 };
30
- const controller = new InboxController({ host: fakeHost(), onDirty() { }, onClosed() { }, ...options });
31
- assert.equal(controller.snapshot().items.length, 1, 'pending page is listed');
32
- // Settle it out of band, without letting the controller rescan: this is the
33
- // stale-row state the bug leaves behind.
34
- takeoverAndCompletePage(dir, { choice: { selectedOptionIds: ['yes'], comments: [] } }, { host: 'daemon', pid: 1 });
35
- assert.equal(existsSync(join(dir, 'response.json')), true, 'response is published on disk');
36
- assert.equal(existsSync(claimPath(dir)), false, 'completion leaves no claim behind');
28
+ const client = cliClient();
29
+ const request = await client.createHumanRequest({
30
+ page: { dialect: 'jsx', source: 'export default () => (\n <Page title="Stale row page" subtitle="Pick one">\n <UserQuestion id="choice" label="Restore?" body="Pick one." mode="single" options={[{ id: "yes", label: "Yes" }, { id: "no", label: "No" }]} />\n </Page>\n);\n' },
31
+ delivery: { placement: 'inline', inbox: true, reply: false },
32
+ creator_cwd: daemon.home,
33
+ });
34
+ // Seed the controller's in-memory list while the request is open.
35
+ const controller = new InboxController({ host: fakeHost(), onDirty() { }, onClosed() { }, cols: 100, rows: 24 });
36
+ await waitFor(() => controller.snapshot().items.length === 1, 'pending page is listed');
37
+ // Settle it in the canvas without letting the controller rescan: this is the
38
+ // stale-row state the bug leaves behind, and it stands for a settlement some
39
+ // other surface performed.
40
+ const settledAt = new Date().toISOString();
41
+ const settled = withCanvasWrite((db) => settleRequestLocked(db, {
42
+ request_id: request.request_id,
43
+ outcome: 'answered',
44
+ result_json: JSON.stringify({ schema: 'crtr.page-response/v1', kind: 'page', responses: { choice: { selectedOptionIds: ['yes'], comments: [] } }, summary: 'page submitted', completedAt: settledAt }),
45
+ settled_at: settledAt,
46
+ }));
47
+ assert.notEqual(settled, null, 'the out-of-band settlement wins the terminal transition');
48
+ assert.equal((await client.getHumanRequest(request.request_id)).state, 'answered', 'the request row is terminal');
49
+ assert.equal(existsSync(claimPath(request.request_dir)), false, 'settlement leaves no claim behind');
37
50
  assert.equal(controller.snapshot().items.length, 1, 'the in-memory row is still stale');
38
51
  // Pressing Enter on the stale row must report the settlement and drop it,
39
- // never claim it is edited by another inbox.
52
+ // never claim it is edited by another inbox. A claim would still succeed on
53
+ // the directory, so the settled row is recognized from its request record.
40
54
  controller.handleKey('', key({ return: true }));
55
+ await waitFor(() => controller.snapshot().items.length === 0, 'the stale row drops on activation');
41
56
  const reported = status(controller) ?? '';
42
57
  assert.match(reported, /already resolved/, `stale-row activation reports the settlement, got: ${JSON.stringify(reported)}`);
43
58
  assert.doesNotMatch(reported, /being edited/, 'a settled row is never "edited by another inbox"');
44
- assert.equal(controller.snapshot().items.length, 0, 'the stale row drops on activation');
45
59
  assert.equal(controller.snapshot().screen, 'list', 'a settled row never opens the detail pane');
60
+ assert.equal(existsSync(claimPath(request.request_dir)), false, 'a stale row is never claimed');
46
61
  controller.close();
47
62
  console.log('stale-row regression passed');
48
63
  }
49
64
  finally {
50
- rmSync(temp, { recursive: true, force: true });
51
- rmSync(sources, { recursive: true, force: true });
52
- delete process.env['CRTR_HOME'];
65
+ await daemon.dispose();
53
66
  }
@@ -1,6 +1,6 @@
1
- import type { TicketSummary } from '../../core/human/types.js';
2
1
  import type { Key } from './tui/keys.js';
3
2
  import type { SurfaceHost } from '../surfaces/host.js';
3
+ import type { InboxRow } from './dto-adapt.js';
4
4
  export interface InboxControllerOptions {
5
5
  host: SurfaceHost;
6
6
  /** Called whenever the rendered frame may have changed. */
@@ -9,8 +9,8 @@ export interface InboxControllerOptions {
9
9
  onClosed: () => void;
10
10
  cols?: number;
11
11
  rows?: number;
12
- scan?: () => TicketSummary[];
13
- /** Replaces the default synchronous rescan after inbox activity. */
12
+ scan?: () => Promise<InboxRow[]>;
13
+ /** Replaces the default rescan after inbox activity. */
14
14
  onInboxActivity?: () => void;
15
15
  /** Ticket dir to seat the selection on at construction, when present. */
16
16
  initialSelection?: string;
@@ -39,19 +39,31 @@ export declare class InboxController {
39
39
  private heartbeat;
40
40
  private started;
41
41
  private closed;
42
- /** The selected page's projected body, kept across frames because building it
43
- * reads and evaluates the page document. Keyed by what changes it. */
42
+ /** The selected page's projection, kept across frames because fetching it is
43
+ * a round trip. `view` is held so a width change re-wraps without one. */
44
44
  private preview;
45
+ /** A preview read that failed, so the panel says so instead of staying blank. */
46
+ private previewFailed;
47
+ /** In-flight preview fetch, keyed by dir: one request per selection at a time. */
48
+ private previewFetching;
49
+ /** Set by `invalidate`: the held preview may no longer be the current revision. */
50
+ private previewStale;
51
+ /** The revision the open detail was loaded from, so a replacement published
52
+ * while the reader has the page open reloads it. */
53
+ private openRevision;
45
54
  constructor(options: InboxControllerOptions);
46
55
  snapshot(): {
47
- items: TicketSummary[];
56
+ items: InboxRow[];
48
57
  selectedDir?: string;
49
58
  screen: Screen;
50
59
  inputBuffer?: string;
51
60
  };
52
- rescan(): void;
61
+ rescan(): Promise<void>;
53
62
  start(): void;
54
- invalidate(): void;
63
+ /** Inbox activity is the one signal a replacement always emits, and it is
64
+ * emitted after the new revision is selected — so an open page reloads here
65
+ * rather than from a file watch that cannot see a directory swap. */
66
+ invalidate(): Promise<void>;
55
67
  resize(cols: number, rows: number): void;
56
68
  render(): string[];
57
69
  handleKey(input: string, key: Key): void;
@@ -60,12 +72,15 @@ export declare class InboxController {
60
72
  * row without a fast action says so rather than opening the page: the key
61
73
  * means one thing, and the page is what Enter is for. */
62
74
  private sendFastAction;
75
+ /** A review and a page are two different durable records; the inbox dismisses
76
+ * whichever one the selected row actually is. */
63
77
  private cancelFromInbox;
64
- activate(): void;
78
+ activate(): Promise<void>;
65
79
  private activateReview;
66
80
  /** Seat the selection on a ticket dir, when present in the current list. */
67
81
  selectDir(dir: string): void;
68
- reloadSelectedPage(): void;
82
+ /** Re-read the open page and reload it only when its revision actually moved. */
83
+ reloadSelectedPage(): Promise<void>;
69
84
  close(): void;
70
85
  private complete;
71
86
  /** Edit the active buffer while the host releases the terminal to $EDITOR. */
@@ -75,12 +90,22 @@ export declare class InboxController {
75
90
  private detailSize;
76
91
  private detailLines;
77
92
  private focusAvailable;
78
- /** Ask the host to bring the selected ticket's node viewer into view. */
93
+ /** Ask the host to bring the requesting node's viewer into view — the
94
+ * conversation that raised this ask. */
79
95
  private focusSelectedSource;
80
- /** What the selected page actually says, projected once per document and
81
- * kept until the document, the selection, or the pane width changes — the
82
- * projection reads the ticket from disk and evaluates it. */
96
+ /** What the selected page actually says. Rendering is synchronous and the
97
+ * page is a round trip, so the projection is held and a miss schedules the
98
+ * fetch, marking the frame dirty when it lands. A width change re-wraps the
99
+ * held projection without another read. */
83
100
  private pagePreviewBody;
101
+ /** One in-flight preview read at a time. A revision that did not move keeps
102
+ * the held projection, so activity on an unrelated ticket costs no re-wrap. */
103
+ private schedulePreview;
104
+ /** The request record is the settlement authority: a settled page publishes
105
+ * no file, so a claim would still succeed on its directory. An unreachable
106
+ * daemon reads as "not known to be settled" — the settle attempt that
107
+ * follows is the one that fails loudly. */
108
+ private requestOrNull;
84
109
  /** The read-only preview beside the list. It shows the page's own content,
85
110
  * because `s` settles the selected ticket from here without ever opening
86
111
  * it — a reader must not answer something they were never shown. */
@@ -88,6 +113,9 @@ export declare class InboxController {
88
113
  private withStatus;
89
114
  private markDirty;
90
115
  private watchRoots;
116
+ /** The request directory still carries the claim and progress files other
117
+ * inboxes write. Its page content lives in the daemon-selected revision,
118
+ * which `invalidate` re-reads over the API. */
91
119
  private watchSelected;
92
120
  }
93
121
  export {};