@north-light/crouter 0.3.312 → 0.3.314

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/dist/api/client.d.ts +27 -30
  2. package/dist/api/client.js +80 -67
  3. package/dist/api/dto/broker-ops.d.ts +10 -20
  4. package/dist/api/dto/canvas.d.ts +1 -10
  5. package/dist/api/dto/health.d.ts +113 -0
  6. package/dist/api/dto/human-requests.d.ts +14 -3
  7. package/dist/api/dto/human.d.ts +2 -48
  8. package/dist/api/dto/human.js +3 -2
  9. package/dist/api/dto/inbox.d.ts +14 -0
  10. package/dist/api/dto/mail.d.ts +43 -0
  11. package/dist/api/dto/nodes.d.ts +11 -8
  12. package/dist/api/dto/recovery.d.ts +96 -0
  13. package/dist/api/dto/reviews.d.ts +2 -2
  14. package/dist/api/routes.d.ts +9 -6
  15. package/dist/api/routes.js +11 -11
  16. package/dist/build-root.d.ts +4 -0
  17. package/dist/build-root.js +11 -1
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
  20. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  22. package/dist/cli.js +10 -1
  23. package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
  24. package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
  25. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  26. package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
  27. package/dist/clients/attach/chrome/inbox-strip.js +17 -9
  28. package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
  29. package/dist/clients/attach/chrome/ticket-panel.js +77 -46
  30. package/dist/clients/attach/overlays/graph.js +6 -9
  31. package/dist/clients/attach/render/chat-view.d.ts +5 -4
  32. package/dist/clients/attach/render/chat-view.js +22 -13
  33. package/dist/clients/attach/render/page-block.d.ts +11 -7
  34. package/dist/clients/attach/render/page-block.js +64 -55
  35. package/dist/clients/attach/viewer.js +757 -746
  36. package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
  37. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
  38. package/dist/clients/inbox/controller.d.ts +42 -14
  39. package/dist/clients/inbox/controller.js +240 -124
  40. package/dist/clients/inbox/dto-adapt.d.ts +28 -0
  41. package/dist/clients/inbox/dto-adapt.js +93 -0
  42. package/dist/clients/inbox/page-adapter.d.ts +5 -4
  43. package/dist/clients/inbox/page-adapter.js +12 -12
  44. package/dist/clients/inbox/resolve.d.ts +10 -5
  45. package/dist/clients/inbox/resolve.js +13 -12
  46. package/dist/clients/inbox/review/review-client.d.ts +0 -1
  47. package/dist/clients/inbox/review/review-client.js +0 -4
  48. package/dist/clients/inbox/review-adapter.d.ts +2 -1
  49. package/dist/clients/inbox/review-adapter.js +3 -5
  50. package/dist/clients/inbox/surface.d.ts +2 -2
  51. package/dist/clients/inbox/surface.js +19 -8
  52. package/dist/clients/inbox/tui.d.ts +3 -3
  53. package/dist/commands/__tests__/human.test.js +31 -15
  54. package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
  55. package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
  56. package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
  57. package/dist/commands/api-client.d.ts +5 -4
  58. package/dist/commands/api-client.js +13 -7
  59. package/dist/commands/canvas-history/shared.js +1 -1
  60. package/dist/commands/canvas-history.js +1 -1
  61. package/dist/commands/canvas-roster.js +1 -1
  62. package/dist/commands/canvas.js +1 -2
  63. package/dist/commands/human/feedback.js +20 -17
  64. package/dist/commands/human/prompts.js +45 -36
  65. package/dist/commands/human/queue.js +73 -123
  66. package/dist/commands/human/review.js +6 -16
  67. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
  68. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
  69. package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
  70. package/dist/commands/node-inspect-artifacts.js +1 -1
  71. package/dist/commands/node-lifecycle-revive.d.ts +2 -2
  72. package/dist/commands/node-lifecycle-revive.js +9 -19
  73. package/dist/commands/pkg/plugin-manage.js +0 -1
  74. package/dist/commands/profile/show.js +4 -12
  75. package/dist/commands/surface-tmux-spread.js +1 -5
  76. package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
  77. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
  78. package/dist/commands/sys/branch.d.ts +2 -0
  79. package/dist/commands/sys/branch.js +19 -0
  80. package/dist/commands/sys/config.js +106 -33
  81. package/dist/commands/sys/daemon.d.ts +2 -0
  82. package/dist/commands/sys/daemon.js +130 -7
  83. package/dist/commands/sys/doctor.js +5 -15
  84. package/dist/commands/sys/migrate.js +21 -92
  85. package/dist/commands/sys/setup-settings-runner.js +4 -2
  86. package/dist/commands/sys.js +2 -15
  87. package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
  88. package/dist/core/__tests__/boot.test.js +32 -25
  89. package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
  90. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
  91. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
  92. package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
  93. package/dist/core/__tests__/canvas.test.js +18 -47
  94. package/dist/core/__tests__/child-death-wake.test.js +29 -7
  95. package/dist/core/__tests__/cli-help-contract.test.js +2 -0
  96. package/dist/core/__tests__/close.test.js +29 -10
  97. package/dist/core/__tests__/collectible.test.js +2 -1
  98. package/dist/core/__tests__/config-change-delta.test.js +2 -0
  99. package/dist/core/__tests__/context-intro.test.js +13 -1
  100. package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
  101. package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
  102. package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
  103. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
  104. package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
  105. package/dist/core/__tests__/daemon-boot.test.js +51 -14
  106. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  107. package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
  108. package/dist/core/__tests__/focuses.test.js +10 -5
  109. package/dist/core/__tests__/helpers/harness.js +50 -18
  110. package/dist/core/__tests__/history-inbox.test.js +12 -16
  111. package/dist/core/__tests__/human-action-delivery.test.js +48 -28
  112. package/dist/core/__tests__/human-actions.test.js +7 -3
  113. package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
  114. package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
  115. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
  116. package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
  117. package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
  118. package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
  119. package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
  120. package/dist/core/__tests__/integration/fresh-home-commands.test.js +49 -0
  121. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  122. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
  123. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  124. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  125. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  126. package/dist/core/__tests__/integration/revive.test.js +67 -21
  127. package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
  128. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  129. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  130. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  131. package/dist/core/__tests__/kickoff.test.js +36 -39
  132. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  133. package/dist/core/__tests__/lifecycle.test.js +2 -1
  134. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  135. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  136. package/dist/core/__tests__/migration.test.js +145 -31
  137. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  138. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  139. package/dist/core/__tests__/node-outcome.test.js +93 -26
  140. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  141. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
  142. package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
  143. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  144. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  145. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  146. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  147. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  148. package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
  149. package/dist/core/__tests__/prune.test.js +11 -5
  150. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  151. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  152. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  153. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  154. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  155. package/dist/core/__tests__/revive-all.test.js +13 -3
  156. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  157. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  158. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  159. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  160. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  161. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  162. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  163. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  164. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  165. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  166. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
  167. package/dist/core/__tests__/stop-guard.test.js +2 -1
  168. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  169. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
  170. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  171. package/dist/core/bootstrap.js +2 -0
  172. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  173. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  174. package/dist/core/broker-client/transport-socket.js +13 -5
  175. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  176. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  177. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  178. package/dist/core/canvas/attention.d.ts +2 -5
  179. package/dist/core/canvas/attention.js +15 -64
  180. package/dist/core/canvas/boot.d.ts +6 -7
  181. package/dist/core/canvas/boot.js +5 -9
  182. package/dist/core/canvas/canvas.d.ts +13 -109
  183. package/dist/core/canvas/canvas.js +140 -401
  184. package/dist/core/canvas/crons.d.ts +2 -0
  185. package/dist/core/canvas/crons.js +16 -15
  186. package/dist/core/canvas/daemon-owner.js +10 -0
  187. package/dist/core/canvas/db.d.ts +2 -2
  188. package/dist/core/canvas/db.js +2 -18
  189. package/dist/core/canvas/deliveries.d.ts +104 -0
  190. package/dist/core/canvas/deliveries.js +354 -0
  191. package/dist/core/canvas/executions.d.ts +29 -0
  192. package/dist/core/canvas/executions.js +68 -0
  193. package/dist/core/canvas/history.js +35 -35
  194. package/dist/core/canvas/index.d.ts +3 -1
  195. package/dist/core/canvas/index.js +3 -1
  196. package/dist/core/canvas/migrations.js +20 -12
  197. package/dist/core/canvas/nav-model.js +1 -3
  198. package/dist/core/canvas/nav-render.d.ts +4 -4
  199. package/dist/core/canvas/nav-render.js +11 -21
  200. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  201. package/dist/core/canvas/node-sql-migration.js +261 -0
  202. package/dist/core/canvas/paths.d.ts +2 -16
  203. package/dist/core/canvas/paths.js +2 -31
  204. package/dist/core/canvas/recovery.d.ts +76 -0
  205. package/dist/core/canvas/recovery.js +588 -0
  206. package/dist/core/canvas/remote-canvas-source.js +4 -0
  207. package/dist/core/canvas/render-source.d.ts +16 -27
  208. package/dist/core/canvas/render-source.js +16 -45
  209. package/dist/core/canvas/sessions.d.ts +33 -0
  210. package/dist/core/canvas/sessions.js +78 -0
  211. package/dist/core/canvas/types.d.ts +33 -33
  212. package/dist/core/canvas/types.js +2 -5
  213. package/dist/core/canvas/waits.d.ts +18 -0
  214. package/dist/core/canvas/waits.js +70 -0
  215. package/dist/core/canvas/worktrees.d.ts +40 -0
  216. package/dist/core/canvas/worktrees.js +93 -0
  217. package/dist/core/command.js +11 -0
  218. package/dist/core/config.d.ts +17 -12
  219. package/dist/core/config.js +106 -155
  220. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  221. package/dist/core/feed/feed.d.ts +0 -6
  222. package/dist/core/feed/feed.js +68 -56
  223. package/dist/core/feed/inbox.d.ts +7 -81
  224. package/dist/core/feed/inbox.js +46 -378
  225. package/dist/core/feed/messages.d.ts +58 -0
  226. package/dist/core/feed/messages.js +217 -0
  227. package/dist/core/fs-utils.d.ts +4 -0
  228. package/dist/core/fs-utils.js +21 -1
  229. package/dist/core/human/answer.d.ts +1 -1
  230. package/dist/core/human/answer.js +1 -1
  231. package/dist/core/human/claim.d.ts +1 -1
  232. package/dist/core/human/claim.js +2 -4
  233. package/dist/core/human/component-docs.js +1 -1
  234. package/dist/core/human/feedback-companion.js +3 -2
  235. package/dist/core/human/feedback.d.ts +5 -8
  236. package/dist/core/human/feedback.js +57 -85
  237. package/dist/core/human/page-authoring.d.ts +30 -0
  238. package/dist/core/human/page-authoring.js +19 -0
  239. package/dist/core/human/requests.d.ts +136 -0
  240. package/dist/core/human/requests.js +515 -0
  241. package/dist/core/human/root.d.ts +0 -7
  242. package/dist/core/human/root.js +0 -17
  243. package/dist/core/human/scan.d.ts +2 -7
  244. package/dist/core/human/scan.js +48 -132
  245. package/dist/core/human/summary.d.ts +1 -1
  246. package/dist/core/human/summary.js +1 -1
  247. package/dist/core/human/tickets.d.ts +23 -83
  248. package/dist/core/human/tickets.js +92 -243
  249. package/dist/core/human/types.d.ts +5 -1
  250. package/dist/core/installed-plugins.js +8 -3
  251. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  252. package/dist/core/locked-file.d.ts +6 -3
  253. package/dist/core/locked-file.js +36 -9
  254. package/dist/core/memory/extensions.js +2 -0
  255. package/dist/core/preview-registry.js +0 -1
  256. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  257. package/dist/core/profiles/default-binding.d.ts +0 -4
  258. package/dist/core/profiles/default-binding.js +12 -58
  259. package/dist/core/profiles/env-store.js +8 -3
  260. package/dist/core/profiles/manifest.d.ts +2 -37
  261. package/dist/core/profiles/manifest.js +35 -161
  262. package/dist/core/profiles/select.js +3 -0
  263. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  264. package/dist/core/review/__tests__/comments.test.js +28 -10
  265. package/dist/core/review/__tests__/remap.test.js +28 -10
  266. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  267. package/dist/core/review/birth.d.ts +1 -1
  268. package/dist/core/review/birth.js +34 -5
  269. package/dist/core/review/comments.js +1 -1
  270. package/dist/core/review/document.js +1 -1
  271. package/dist/core/review/project.d.ts +5 -1
  272. package/dist/core/review/project.js +14 -36
  273. package/dist/core/review/realize.d.ts +3 -6
  274. package/dist/core/review/realize.js +51 -168
  275. package/dist/core/review/remap.js +1 -1
  276. package/dist/core/review/stage.d.ts +3 -2
  277. package/dist/core/review/stage.js +2 -3
  278. package/dist/core/review/store.d.ts +3 -21
  279. package/dist/core/review/store.js +140 -186
  280. package/dist/core/review/types.d.ts +0 -1
  281. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  282. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  283. package/dist/core/runtime/broker/auth-reload.js +9 -10
  284. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  285. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  286. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  287. package/dist/core/runtime/broker/extension-abort.js +11 -0
  288. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  289. package/dist/core/runtime/broker/fault-retry.js +142 -95
  290. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  291. package/dist/core/runtime/broker/inbox.js +4 -92
  292. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  293. package/dist/core/runtime/broker/rebind.js +21 -12
  294. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  295. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  296. package/dist/core/runtime/broker.js +28 -22
  297. package/dist/core/runtime/close.d.ts +3 -9
  298. package/dist/core/runtime/close.js +16 -27
  299. package/dist/core/runtime/fault.d.ts +3 -93
  300. package/dist/core/runtime/fault.js +9 -419
  301. package/dist/core/runtime/kickoff.d.ts +3 -6
  302. package/dist/core/runtime/kickoff.js +12 -18
  303. package/dist/core/runtime/launch.js +1 -1
  304. package/dist/core/runtime/lifecycle.js +40 -7
  305. package/dist/core/runtime/placement.js +2 -2
  306. package/dist/core/runtime/recycle.js +8 -2
  307. package/dist/core/runtime/reset.js +22 -6
  308. package/dist/core/runtime/revive.js +226 -218
  309. package/dist/core/runtime/spawn.js +14 -3
  310. package/dist/core/scoped-state/db.d.ts +18 -0
  311. package/dist/core/scoped-state/db.js +135 -0
  312. package/dist/core/scoped-state/migrate.d.ts +8 -0
  313. package/dist/core/scoped-state/migrate.js +181 -0
  314. package/dist/core/scoped-state/paths.d.ts +9 -0
  315. package/dist/core/scoped-state/paths.js +27 -0
  316. package/dist/core/scoped-state/profiles.d.ts +12 -0
  317. package/dist/core/scoped-state/profiles.js +62 -0
  318. package/dist/core/scoped-state/providers.d.ts +16 -0
  319. package/dist/core/scoped-state/providers.js +8 -0
  320. package/dist/core/scoped-state/schema.d.ts +3 -0
  321. package/dist/core/scoped-state/schema.js +26 -0
  322. package/dist/core/scoped-state/settings.d.ts +28 -0
  323. package/dist/core/scoped-state/settings.js +82 -0
  324. package/dist/core/subscription-state.d.ts +29 -48
  325. package/dist/core/subscription-state.js +176 -712
  326. package/dist/core/worktree-close.js +4 -4
  327. package/dist/core/worktree-mutation-async.js +2 -2
  328. package/dist/core/worktree-sweep.js +11 -16
  329. package/dist/core/worktree.js +6 -12
  330. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  331. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  332. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  333. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  334. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  335. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
  336. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  337. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  338. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  339. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  340. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  341. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  342. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  343. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  344. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  345. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
  346. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  347. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  348. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  349. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  350. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  351. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
  352. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  353. package/dist/daemon/api/bridge.js +1 -1
  354. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  355. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  356. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  357. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  358. package/dist/daemon/api/handlers/canvas.js +2 -44
  359. package/dist/daemon/api/handlers/daemon.js +41 -0
  360. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  361. package/dist/daemon/api/handlers/health.js +10 -5
  362. package/dist/daemon/api/handlers/human-requests.js +168 -188
  363. package/dist/daemon/api/handlers/human.js +3 -151
  364. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  365. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  366. package/dist/daemon/api/handlers/inbox.js +21 -120
  367. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  368. package/dist/daemon/api/handlers/messages.js +73 -15
  369. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  370. package/dist/daemon/api/handlers/nodes.js +7 -13
  371. package/dist/daemon/api/handlers/reviews.js +4 -29
  372. package/dist/daemon/api/map.d.ts +25 -3
  373. package/dist/daemon/api/map.js +84 -14
  374. package/dist/daemon/api/server.js +68 -9
  375. package/dist/daemon/control.d.ts +18 -0
  376. package/dist/daemon/cron/disposition.d.ts +2 -0
  377. package/dist/daemon/cron/disposition.js +8 -5
  378. package/dist/daemon/cron/settle.js +37 -13
  379. package/dist/daemon/cron/sinks.d.ts +18 -1
  380. package/dist/daemon/cron/sinks.js +47 -93
  381. package/dist/daemon/crtrd-cli.js +4 -19
  382. package/dist/daemon/crtrd.d.ts +2 -0
  383. package/dist/daemon/crtrd.js +206 -81
  384. package/dist/daemon/fleet.js +5 -6
  385. package/dist/daemon/human/finish.d.ts +12 -24
  386. package/dist/daemon/human/finish.js +88 -285
  387. package/dist/daemon/human/settle.d.ts +40 -0
  388. package/dist/daemon/human/settle.js +89 -0
  389. package/dist/daemon/human/sweep.d.ts +20 -5
  390. package/dist/daemon/human/sweep.js +56 -60
  391. package/dist/daemon/manage.d.ts +21 -16
  392. package/dist/daemon/manage.js +85 -51
  393. package/dist/daemon/messaging/node-message.d.ts +1 -1
  394. package/dist/daemon/messaging/node-message.js +9 -12
  395. package/dist/daemon/park-activity.js +3 -3
  396. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  397. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  398. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  399. package/dist/daemon/reconcilers/controller-death.js +6 -18
  400. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  401. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  402. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  403. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  404. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  405. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  406. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  407. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  408. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  409. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  410. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  411. package/dist/daemon/review/companion.js +5 -11
  412. package/dist/daemon/review/deliver.d.ts +1 -5
  413. package/dist/daemon/review/deliver.js +3 -52
  414. package/dist/daemon/review/finish.d.ts +2 -27
  415. package/dist/daemon/review/finish.js +79 -263
  416. package/dist/daemon/review/sweep.d.ts +3 -10
  417. package/dist/daemon/review/sweep.js +13 -131
  418. package/dist/daemon/startup-policy.d.ts +0 -5
  419. package/dist/daemon/startup-policy.js +0 -5
  420. package/dist/index.d.ts +2 -1
  421. package/dist/index.js +1 -1
  422. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  423. package/dist/migrations/__tests__/activation.test.js +9 -6
  424. package/dist/migrations/activation.d.ts +3 -2
  425. package/dist/migrations/activation.js +2 -4
  426. package/dist/migrations/corpus.d.ts +2 -5
  427. package/dist/migrations/corpus.js +9 -20
  428. package/dist/migrations/runner.d.ts +3 -1
  429. package/dist/migrations/runner.js +6 -2
  430. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  431. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  432. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  433. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  434. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  435. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  436. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  437. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  438. package/dist/pi-extensions/canvas-stophook.js +47 -31
  439. package/dist/shared/env.d.ts +1 -0
  440. package/dist/shared/env.js +3 -0
  441. package/package.json +1 -1
  442. package/runtime.lock.json +5 -5
  443. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  444. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  445. package/dist/clients/inbox/review-visibility.js +0 -19
  446. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  447. package/dist/commands/canvas-rebuild-index.js +0 -46
  448. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  449. package/dist/core/__tests__/human-deliver.test.js +0 -256
  450. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  451. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  452. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  453. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  454. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  455. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  456. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  457. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  458. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  459. package/dist/core/canvas/human-deliveries.js +0 -75
  460. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  461. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  462. package/dist/core/feed/passive.d.ts +0 -3
  463. package/dist/core/feed/passive.js +0 -53
  464. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  465. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  466. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  467. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  468. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  469. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  470. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  471. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  472. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  473. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  474. package/dist/core/review/ticket-filter.d.ts +0 -17
  475. package/dist/core/review/ticket-filter.js +0 -21
  476. package/dist/core/runtime/broker/passive.d.ts +0 -17
  477. package/dist/core/runtime/broker/passive.js +0 -44
  478. package/dist/core/runtime/busy.d.ts +0 -46
  479. package/dist/core/runtime/busy.js +0 -150
  480. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  481. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  482. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  483. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  484. package/dist/daemon/human/deliver-action.d.ts +0 -3
  485. package/dist/daemon/human/deliver-action.js +0 -9
  486. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  487. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  488. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  489. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  490. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  491. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  492. package/dist/daemon/startup-block-marker.d.ts +0 -25
  493. package/dist/daemon/startup-block-marker.js +0 -99
  494. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  495. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  496. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
  497. /package/dist/core/__tests__/{human-deliver.test.d.ts → integration/fresh-home-commands.test.d.ts} +0 -0
@@ -1,304 +1,107 @@
1
- import { ApiError } from '../../api/index.js';
2
- import { getRow, subscribersOf } from '../../core/canvas/canvas.js';
3
- import { cancelCronsOnWake } from '../../core/canvas/crons.js';
4
- import { opaqueInboxTicketId, ticketDir } from '../../core/human/root.js';
5
- import { readTicketActionBinding } from '../../core/human/action-binding.js';
6
- import { completionEventFor, composeHumanCompletion } from '../../core/human/completion.js';
7
- import { enqueueActionDelivery } from '../../core/canvas/human-deliveries.js';
8
- import { parsePage } from '../../core/human/page.js';
9
- import { cancelTicket, projectReviewOutput, readTicketResult, takeoverAndCompletePage, } from '../../core/human/tickets.js';
10
- import { describePageAnswer } from '../../core/human/answer.js';
11
- import { renderAnswerText } from '../../core/human/answer-text.js';
12
- import { pagePath, readTicketReplyRoute, responsePath } from '../../core/human/convention.js';
13
- import { readPageFeedback } from '../../core/human/feedback.js';
14
- import { pageDocumentSha256 } from '../../core/human/feedback-companion.js';
15
- import { appendInbox } from '../../core/feed/inbox.js';
16
- import { FinalDeliveryError, pushFinal, pushUpdate } from '../../core/feed/feed.js';
17
- import { transition } from '../../core/runtime/lifecycle.js';
18
- import { emitEvent } from '../../core/events/emit.js';
1
+ import { getRequest, getRequestByInboxTicket } from '../../core/human/requests.js';
2
+ import { buildPageSummary } from '../../core/human/summary.js';
3
+ import { validateFinalPageResponses } from '../../core/human/tickets.js';
4
+ import { composeHumanCompletion } from '../../core/human/completion.js';
19
5
  import { retireCompanion } from '../companion-retire.js';
20
- /** Every ticket-ending action retires the ticket's feedback companion — done
21
- * with a canonical final when approved, never left resident. Idempotent and replay-safe; a
22
- * corrupt feedback file must not block result publication. Exported for the
23
- * daemon-start sweep, which must settle terminal tickets whether or not a
24
- * reply bridge exists. */
25
- export async function settleFeedbackCompanion(ticketId, outcome) {
26
- try {
27
- const companionNodeId = readPageFeedback(ticketDir(ticketId)).companionNodeId;
28
- if (companionNodeId !== undefined)
29
- await retireCompanion(companionNodeId, outcome);
30
- }
31
- catch (error) {
32
- emitEvent({ level: 'warn', event: 'human.feedback_companion.retire_failed', error });
33
- }
6
+ import { settleHumanRequest } from './settle.js';
7
+ function requestForTicket(ticketId) {
8
+ const request = getRequest(ticketId) ?? getRequestByInboxTicket(ticketId);
9
+ if (request === null || request.kind !== 'page')
10
+ throw new Error(`unknown page request: ${ticketId}`);
11
+ return request;
34
12
  }
35
- /** The settle-time feedback addendum to a page completion — answer and
36
- * cancellation alike: a change notice when the final page differs from the
37
- * version first commented on, and each still-open item's quote and note.
38
- * Silent when the page is unchanged and no feedback remains open; never a
39
- * manufactured prose diff. */
40
- function feedbackAddendum(dir) {
41
- let sections;
13
+ function manifest(request) {
42
14
  try {
43
- const feedback = readPageFeedback(dir);
44
- if (feedback.companionNodeId === undefined)
45
- return '';
46
- sections = [];
47
- if (feedback.firstCommentedSha256 !== undefined && pageDocumentSha256(dir) !== feedback.firstCommentedSha256) {
48
- sections.push(`The page changed during feedback: the person commented on it and its companion conversation (node ${feedback.companionNodeId}) revised it in place. ` +
49
- `Reread the final page at ${pagePath(dir, 'jsx')} before interpreting this outcome.`);
50
- }
51
- const open = feedback.comments.filter((comment) => comment.status === 'open');
52
- if (open.length > 0) {
53
- const items = open.map((comment) => [
54
- ...comment.quote.split('\n').map((line) => `> ${line}`),
55
- comment.note,
56
- ].join('\n'));
57
- sections.push([
58
- `Feedback left unresolved at settlement — the companion conversation (node ${feedback.companionNodeId}) had not dealt with these items:`,
59
- ...items,
60
- ].join('\n\n'));
61
- }
15
+ const value = JSON.parse(request.specification_json);
16
+ if (typeof value !== 'object' || value === null)
17
+ throw new Error('not an object');
18
+ return value;
62
19
  }
63
- catch {
64
- // The answer itself is the deliverable; a broken feedback record adds nothing.
65
- return '';
20
+ catch (error) {
21
+ throw new Error(`corrupt page specification: ${request.request_id}`, { cause: error });
66
22
  }
67
- return sections.length === 0 ? '' : `\n\n${sections.join('\n\n')}`;
68
23
  }
69
- /** A corrupt manifest must not strand the asking node: the raw response remains its escape hatch. */
70
- function renderCorruptPageAnswer(result, resultPath) {
71
- return [
72
- 'The page manifest could not be read, so the raw response is provided.',
73
- '',
74
- JSON.stringify(result.responses),
75
- '',
76
- `Response record: ${resultPath}`,
77
- ].join('\n');
78
- }
79
- function pageTitle(dir) {
80
- try {
81
- return parsePage(dir).title;
82
- }
83
- catch {
24
+ function completionAction(request, result) {
25
+ if (request.action_json === null)
84
26
  return undefined;
85
- }
86
- }
87
- /** Person-authored response values, derived from the structured answer rather
88
- * than display prose. Empty values have no words to attribute. */
89
- function personWords(answer) {
90
- if (answer.kind === 'acknowledgement')
91
- return '';
92
- const words = [];
93
- for (const slot of answer.slots) {
94
- if (slot.state !== 'answered')
95
- continue;
96
- if (slot.raw !== undefined) {
97
- words.push(JSON.stringify(slot.raw));
98
- continue;
99
- }
100
- if (slot.text !== undefined) {
101
- if (slot.text.edited || slot.text.singleLine === true)
102
- words.push(slot.text.value);
103
- continue;
104
- }
105
- for (const group of slot.groups)
106
- words.push(...group.picked.map((item) => item.label));
107
- if (slot.freetext !== undefined)
108
- words.push(slot.freetext);
109
- words.push(...slot.comments.map((comment) => comment.text));
110
- }
111
- return words.filter((word) => word !== '').join('\n\n');
112
- }
113
- /** The answer push: the person's words alone when the page captured any, tagged
114
- * so every reader attributes them to the person without re-deriving it from
115
- * prose. A page that captured nothing stays an ordinary report carrying the
116
- * full record, because there is nothing of the person's to attribute. */
117
- function renderPageAnswer(dir, result, resultPath) {
118
- let manifest;
119
27
  try {
120
- manifest = parsePage(dir);
28
+ const binding = JSON.parse(request.action_json);
29
+ if (binding.schema !== 'crtr.human-action-binding/v1' || !Array.isArray(binding.argv) || typeof binding.cwd !== 'string')
30
+ throw new Error('invalid binding');
31
+ return {
32
+ destinationId: binding.name,
33
+ adapter: { adapter: 'subprocess', argv: binding.argv, cwd: binding.cwd },
34
+ bodyJson: composeHumanCompletion({ requestId: request.request_id, result: result, binding, source: manifest(request).source ?? {} }),
35
+ };
121
36
  }
122
- catch {
123
- return { body: renderCorruptPageAnswer(result, resultPath) };
37
+ catch (error) {
38
+ throw new Error(`corrupt frozen completion action: ${request.request_id}`, { cause: error });
124
39
  }
125
- const answer = describePageAnswer(manifest, result.responses, { completedAt: result.completedAt });
126
- const rendered = renderAnswerText(answer, resultPath);
127
- const addendum = feedbackAddendum(dir);
128
- const words = personWords(answer);
129
- return words === ''
130
- ? { body: rendered + addendum }
131
- : { body: words, disposition: 'human-answer', ...(addendum === '' ? {} : { feedback: addendum.trim() }) };
132
40
  }
133
- function deliveryFailed(error) {
134
- // A canonical final is committed inside its own write boundary and can never
135
- // be replayed (`already_finalized`), so a subscriber that missed the fanout
136
- // stays missed. Say that rather than promising a retry that cannot happen.
137
- if (error instanceof FinalDeliveryError) {
138
- return new ApiError(500, 'delivery_failed', `The answer is recorded and the bridge node is finalized; its report is at ${error.reportPath}. Notifying ${error.failedTo.join(', ')} failed and will not be retried.`);
139
- }
140
- const detail = error instanceof Error ? error.message : String(error);
141
- return new ApiError(500, 'delivery_failed', `The answer is recorded and delivery will be retried: ${detail}`);
41
+ function notification(request, disposition, bodyJson) {
42
+ if (request.requester_node_id === null)
43
+ return undefined;
44
+ return { label: disposition === 'human-answer' ? 'human request answered' : 'human request canceled', disposition, body_json: bodyJson };
142
45
  }
143
- async function deliverOrConflict(ticketId) {
144
- try {
145
- await deliverTerminalResult(ticketId);
146
- }
147
- catch (error) {
148
- throw deliveryFailed(error);
149
- }
150
- throw new ApiError(409, 'ticket_already_resolved', 'ticket already resolved');
46
+ function retire(request) {
47
+ return request.companion_node_id === null ? undefined : { retire: async (nodeId, outcome) => { await retireCompanion(nodeId, outcome); } };
151
48
  }
152
- /**
153
- * The one completion path for a page ticket: claim takeover, exclusive result
154
- * publication, then optional reply delivery. Every addressing surface routes
155
- * through this function, so the reply-route descriptor is the sole authority
156
- * on whether completion reaches a bridge.
157
- */
49
+ /** Settle a page request directly in human_requests; result files and bridge nodes are not part of this path. */
158
50
  export async function resolvePageTicket(ticketId, responses, opts = {}) {
159
- const completed = takeoverAndCompletePage(ticketDir(ticketId), responses, { host: 'crtrd', pid: process.pid }, opts);
160
- if (!completed.won)
161
- return deliverOrConflict(ticketId);
162
- releaseActionDelivery(ticketId);
163
- try {
164
- await deliverTerminalResult(ticketId);
165
- }
166
- catch (error) {
167
- throw deliveryFailed(error);
168
- }
169
- return completed.result;
51
+ const request = requestForTicket(ticketId);
52
+ const page = manifest(request);
53
+ const parsedResponses = validateFinalPageResponses(page, responses);
54
+ const result = {
55
+ schema: 'crtr.page-response/v1', kind: 'page', responses: parsedResponses,
56
+ summary: buildPageSummary(page, parsedResponses), completedAt: opts.completedAt ?? new Date().toISOString(),
57
+ ...(opts.actor === undefined ? {} : { actor: opts.actor }),
58
+ };
59
+ const settled = await settleHumanRequest({
60
+ request_id: request.request_id,
61
+ outcome: 'answered',
62
+ result_json: JSON.stringify(result),
63
+ ...(notification(request, 'human-answer', result) === undefined ? {} : { notification: notification(request, 'human-answer', result) }),
64
+ ...(completionAction(request, result) === undefined ? {} : { completion_action: completionAction(request, result) }),
65
+ ...(retire(request) === undefined ? {} : { post_commit: retire(request) }),
66
+ });
67
+ if (settled.outcome === 'already_settled') {
68
+ if (settled.request.result_json === null)
69
+ throw new Error(`settled page request lacks a result: ${request.request_id}`);
70
+ return JSON.parse(settled.request.result_json);
71
+ }
72
+ return result;
170
73
  }
171
- /** Cancel a ticket with the store's first-writer-wins publication. */
172
74
  export async function cancelHumanTicket(ticketId, opts) {
173
- const canceled = cancelTicket(ticketDir(ticketId), opts);
174
- if (canceled.status === 'already_resolved')
175
- return deliverOrConflict(ticketId);
176
- releaseActionDelivery(ticketId);
177
- try {
178
- await deliverTerminalResult(ticketId);
179
- }
180
- catch (error) {
181
- throw deliveryFailed(error);
182
- }
183
- return canceled.result;
75
+ const request = requestForTicket(ticketId);
76
+ const at = new Date().toISOString();
77
+ const result = {
78
+ schema: 'humanloop.cancel/v1', kind: 'canceled', canceledAt: at,
79
+ ...(opts.reason === undefined ? {} : { reason: opts.reason }),
80
+ ...(opts.actor === undefined ? {} : { actor: opts.actor }),
81
+ ...(opts.disposition === undefined ? {} : { disposition: opts.disposition }),
82
+ };
83
+ const settled = await settleHumanRequest({
84
+ request_id: request.request_id,
85
+ outcome: opts.disposition === 'dismissed' ? 'dismissed' : 'canceled',
86
+ result_json: JSON.stringify(result),
87
+ ...(notification(request, 'human-canceled', result) === undefined ? {} : { notification: notification(request, 'human-canceled', result) }),
88
+ ...(completionAction(request, result) === undefined ? {} : { completion_action: completionAction(request, result) }),
89
+ ...(retire(request) === undefined ? {} : { post_commit: retire(request) }),
90
+ });
91
+ if (settled.outcome === 'already_settled') {
92
+ if (settled.request.result_json === null)
93
+ throw new Error(`settled page request lacks a result: ${request.request_id}`);
94
+ return JSON.parse(settled.request.result_json);
95
+ }
96
+ return result;
184
97
  }
185
- /** Schedule the settled request's completion action, if it bound one. Only the
186
- * settlement winner calls this; the daemon-start sweep replays it for a
187
- * settlement that died before enqueueing, and the insert is idempotent on the
188
- * request id so neither path can create a second delivery. */
189
- export function releaseActionDelivery(ticketId) {
190
- try {
191
- const dir = ticketDir(ticketId);
192
- const binding = readTicketActionBinding(dir);
193
- if (binding === null)
194
- return;
195
- const result = readTicketResult(dir);
196
- if (result === null || result.kind === 'review')
197
- return;
198
- const requestId = opaqueInboxTicketId(ticketId);
199
- enqueueActionDelivery({
200
- requestId,
201
- argv: binding.argv,
202
- cwd: binding.cwd,
203
- completion: composeHumanCompletion({ requestId, result, binding, source: pageSource(dir) }),
204
- });
205
- }
206
- catch (error) {
207
- // The canonical outcome is already durable; the sweep replays this.
208
- emitEvent({ level: 'error', event: 'human.action_delivery.enqueue_failed', node_id: ticketId, error });
209
- }
210
- }
211
- function pageSource(dir) {
212
- try {
213
- return parsePage(dir).source ?? {};
214
- }
215
- catch {
216
- return {};
217
- }
218
- }
219
- /**
220
- * Deliver a terminal ticket result to its bridge. The canonical response is the
221
- * receipt: every action below is replay-safe, so lost delivery can be retried
222
- * by a later resolve/cancel attempt or the daemon-start sweep.
223
- */
98
+ /** Legacy callers use this as a replay hook. Settlement effects are repaired by sweepHumanRequests. */
224
99
  export async function deliverTerminalResult(ticketId) {
225
- const dir = ticketDir(ticketId);
226
- const result = readTicketResult(dir);
227
- if (result === null)
228
- return false;
229
- // Companion retirement rides the committed result, BEFORE the reply-route
230
- // gate: every ticket-ending path (resolve, cancel, conflict replay) funnels
231
- // through here, and an inbox-only page with no reply bridge still settles
232
- // its companion. The daemon-start sweep settles the tickets that die before
233
- // reaching this call.
234
- await settleFeedbackCompanion(ticketId, result.kind === 'canceled' ? 'canceled' : 'approved');
235
- const replyRoute = readTicketReplyRoute(dir);
236
- if (replyRoute === null)
237
- return false;
238
- const bridgeNodeId = replyRoute.bridge_node_id;
239
- projectReviewOutput(dir);
240
- // The ROW, not node metadata: a bridge whose row has been pruned may retain
241
- // its ticket files, but no lifecycle target remains. No row means nothing is
242
- // left to deliver to.
243
- const node = getRow(bridgeNodeId);
244
- if (node === null || node.status === 'dead' || node.status === 'canceled' || (node.status === 'done' && node.final_report !== null)) {
245
- return true;
246
- }
247
- if (result.kind === 'canceled') {
248
- const title = pageTitle(dir);
249
- const reason = result.reason !== undefined && result.reason !== '' ? result.reason : 'no reason was given';
250
- // A dismissal is a rejection, not a routing event: say so plainly so the
251
- // agent acts on the "no" instead of re-raising the same ask.
252
- const dismissed = completionEventFor(result) === 'dismissed';
253
- const subject = dismissed
254
- ? (title === undefined ? `human interaction ${ticketId} dismissed without answering` : `human interaction ${ticketId} — “${title}” dismissed without answering`)
255
- : (title === undefined ? `human interaction ${ticketId} canceled — no answer is coming` : `human interaction ${ticketId} — “${title}” canceled, no answer is coming`);
256
- // Dismissal and withdrawal are settlement paths too: the sender still
257
- // needs any unresolved feedback and the exceptional page-change notice.
258
- const body = (dismissed
259
- ? `The user dismissed ${title === undefined ? `the human interaction ${ticketId}` : `“${title}”`} without answering — a deliberate “no, I'm not answering this.” Treat it as a rejection: no response will arrive, no partial answer exists, and the same ask must not be raised again.`
260
- : (title === undefined ? `The human interaction ${ticketId} was canceled — ${reason}. No response will arrive, and any partial answers the user had entered were discarded unsent.` : `The page “${title}” was canceled — ${reason}. No response will arrive, and any partial answers the user had entered were discarded unsent.`)) + feedbackAddendum(dir);
261
- // One failing subscriber must not silence the rest, and the bridge stays
262
- // live until every notice lands: a partial fanout leaves the ticket for the
263
- // next resolve/cancel attempt or the daemon-start sweep to replay. A
264
- // subscriber notified twice reads the same idempotent "no answer is coming";
265
- // a subscriber never notified waits forever.
266
- const failedTo = [];
267
- for (const subscriber of subscribersOf(bridgeNodeId)) {
268
- if (result.actor !== undefined && subscriber.node_id === result.actor)
269
- continue;
270
- try {
271
- appendInbox(subscriber.node_id, {
272
- from: bridgeNodeId,
273
- tier: 'normal',
274
- kind: 'message',
275
- label: subject,
276
- data: { body },
277
- disposition: 'human-canceled',
278
- });
279
- cancelCronsOnWake(subscriber.node_id);
280
- }
281
- catch {
282
- failedTo.push(subscriber.node_id);
283
- }
284
- }
285
- if (failedTo.length > 0)
286
- throw new Error(`cancellation notice failed for ${failedTo.join(', ')}`);
287
- // Parking the bridge done is the cancellation's END STATE, not a step it
288
- // owes: a bridge already done — parked by an earlier cancel replay, or by a
289
- // close that finished it while the page was still pending — is already
290
- // there, and `finish` is illegal from done. Re-running it would turn a
291
- // recorded, delivered cancellation into delivery_failed and make every
292
- // retry repeat that error.
293
- if (node.status !== 'done')
294
- transition(bridgeNodeId, 'finish');
295
- return true;
296
- }
297
- if (result.kind === 'page') {
298
- const answer = renderPageAnswer(dir, result, responsePath(dir));
299
- if (answer.feedback !== undefined)
300
- await pushUpdate(bridgeNodeId, answer.feedback);
301
- await pushFinal(bridgeNodeId, answer.body, answer.disposition === undefined ? undefined : { disposition: answer.disposition });
302
- }
303
- return true;
100
+ const request = requestForTicket(ticketId);
101
+ return request.state === 'settled';
102
+ }
103
+ export async function settleFeedbackCompanion(ticketId, outcome) {
104
+ const request = requestForTicket(ticketId);
105
+ if (request.companion_node_id !== null)
106
+ await retireCompanion(request.companion_node_id, outcome);
304
107
  }
@@ -0,0 +1,40 @@
1
+ import { type ExternalActionAdapter } from '../../core/feed/messages.js';
2
+ import { type HumanRequestOutcome, type HumanRequestRecord } from '../../core/human/requests.js';
3
+ export interface SettleHumanRequestInput {
4
+ request_id: string;
5
+ outcome: HumanRequestOutcome;
6
+ result_json: string;
7
+ settled_at?: string;
8
+ notification?: {
9
+ label: string;
10
+ disposition: string;
11
+ body_ref?: string;
12
+ body_json?: unknown;
13
+ };
14
+ completion_action?: {
15
+ destinationId: string;
16
+ adapter: ExternalActionAdapter;
17
+ bodyJson: unknown;
18
+ };
19
+ post_commit?: {
20
+ project?: (request: HumanRequestRecord) => Promise<void> | void;
21
+ retire?: (companionNodeId: string, outcome: 'approved' | 'canceled') => Promise<void>;
22
+ };
23
+ }
24
+ export type SettleHumanRequestOutcome = {
25
+ outcome: 'settled';
26
+ request: HumanRequestRecord;
27
+ } | {
28
+ outcome: 'already_settled';
29
+ request: HumanRequestRecord;
30
+ };
31
+ /**
32
+ * The only terminal transition. The conditional update is first-writer-wins;
33
+ * the winner's result and every obligation are committed in the same canvas write.
34
+ */
35
+ export declare function settleHumanRequest(input: SettleHumanRequestInput): Promise<SettleHumanRequestOutcome>;
36
+ /** Projection and companion retirement happen after settlement because filesystem and node lifecycle cannot join SQLite. */
37
+ export declare function projectAndRetire(request: HumanRequestRecord, effects: {
38
+ project?: (request: HumanRequestRecord) => Promise<void> | void;
39
+ retire?: (companionNodeId: string, outcome: 'approved' | 'canceled') => Promise<void>;
40
+ }): Promise<void>;
@@ -0,0 +1,89 @@
1
+ import { withCanvasWrite } from '../../core/canvas/db.js';
2
+ import { cancelDelivery, enqueueExternalAction, enqueueNodeMail, } from '../../core/feed/messages.js';
3
+ import { getRequest, markCompanionRetired, markProjected, markProjectionError, setSettlementDeliveryMarksLocked, settleRequestLocked, } from '../../core/human/requests.js';
4
+ /**
5
+ * The only terminal transition. The conditional update is first-writer-wins;
6
+ * the winner's result and every obligation are committed in the same canvas write.
7
+ */
8
+ export async function settleHumanRequest(input) {
9
+ const committed = withCanvasWrite((db) => {
10
+ const settledAt = input.settled_at ?? new Date().toISOString();
11
+ const won = settleRequestLocked(db, {
12
+ request_id: input.request_id,
13
+ outcome: input.outcome,
14
+ result_json: input.result_json,
15
+ settled_at: settledAt,
16
+ });
17
+ if (won === null) {
18
+ const winner = getRequest(input.request_id);
19
+ if (winner === null)
20
+ throw new Error(`human request disappeared while settling: ${input.request_id}`);
21
+ return { outcome: 'already_settled', request: winner };
22
+ }
23
+ if (won.requester_node_id !== null && input.notification === undefined) {
24
+ throw new Error(`request ${won.request_id} needs a requester notification`);
25
+ }
26
+ if (won.action_json !== null && won.kind === 'page' && input.completion_action === undefined) {
27
+ throw new Error(`request ${won.request_id} needs its frozen completion action`);
28
+ }
29
+ if (won.kind === 'review' && won.projected_at === null && input.post_commit?.project === undefined) {
30
+ throw new Error(`review ${won.request_id} needs a projection effect`);
31
+ }
32
+ if (won.companion_node_id !== null && won.companion_retired_at === null && input.post_commit?.retire === undefined) {
33
+ throw new Error(`request ${won.request_id} needs a companion retirement effect`);
34
+ }
35
+ let settlementDeliveryId = null;
36
+ let actionDeliveryId = null;
37
+ if (won.requester_node_id !== null) {
38
+ const notification = input.notification;
39
+ if (notification === undefined)
40
+ throw new Error(`request ${won.request_id} needs a requester notification`);
41
+ settlementDeliveryId = enqueueNodeMail(won.requester_node_id, {
42
+ kind: 'completed', label: notification.label, senderName: 'crtrd',
43
+ ...(notification.body_ref === undefined ? {} : { bodyRef: notification.body_ref }),
44
+ ...(notification.body_json === undefined ? {} : { bodyJson: notification.body_json }),
45
+ disposition: notification.disposition === 'human-answer' ? 'human-answer' : 'human-canceled', urgency: 'normal', wake: 1,
46
+ }).deliveryId;
47
+ }
48
+ if (won.action_json !== null && input.completion_action !== undefined && won.kind === 'page') {
49
+ actionDeliveryId = enqueueExternalAction({
50
+ ...input.completion_action,
51
+ idempotencyKey: `human-action:${won.inbox_ticket_id}`,
52
+ }).deliveryId;
53
+ }
54
+ if (won.submit_note_delivery_id !== null)
55
+ cancelDelivery(won.submit_note_delivery_id);
56
+ setSettlementDeliveryMarksLocked(db, won.request_id, {
57
+ settlement_delivery_id: settlementDeliveryId,
58
+ action_delivery_id: actionDeliveryId,
59
+ });
60
+ const request = getRequest(won.request_id);
61
+ if (request === null)
62
+ throw new Error(`settled human request disappeared: ${won.request_id}`);
63
+ return { outcome: 'settled', request };
64
+ });
65
+ if (committed.outcome === 'settled' && input.post_commit !== undefined)
66
+ await projectAndRetire(committed.request, input.post_commit);
67
+ return committed;
68
+ }
69
+ /** Projection and companion retirement happen after settlement because filesystem and node lifecycle cannot join SQLite. */
70
+ export async function projectAndRetire(request, effects) {
71
+ if (request.kind === 'review' && request.projected_at === null && effects.project !== undefined) {
72
+ try {
73
+ await effects.project(request);
74
+ markProjected(request.request_id, new Date().toISOString());
75
+ }
76
+ catch (error) {
77
+ markProjectionError(request.request_id, error instanceof Error ? error.message : String(error));
78
+ }
79
+ }
80
+ if (request.companion_node_id !== null && request.companion_retired_at === null && effects.retire !== undefined) {
81
+ try {
82
+ await effects.retire(request.companion_node_id, request.outcome === 'canceled' || request.outcome === 'dismissed' ? 'canceled' : 'approved');
83
+ markCompanionRetired(request.request_id, new Date().toISOString());
84
+ }
85
+ catch {
86
+ // The durable NULL mark is the authoritative retry obligation for the sweep.
87
+ }
88
+ }
89
+ }
@@ -1,6 +1,21 @@
1
- /** Deliver terminal ticket results left behind before their bridge could finish. */
2
- export declare function reconcileUndeliveredTickets(): Promise<{
3
- scanned: number;
4
- delivered: number;
1
+ import { type HumanRequestRecord } from '../../core/human/requests.js';
2
+ export interface HumanRequestSweepEffects {
3
+ realizeReview(requestId: string): Promise<HumanRequestRecord>;
4
+ projectReview(request: HumanRequestRecord): Promise<void> | void;
5
+ retireCompanion(nodeId: string, outcome: 'approved' | 'canceled'): Promise<void>;
6
+ }
7
+ export interface HumanRequestSweepResult {
8
+ realized: number;
9
+ collected: number;
10
+ reprojected: number;
11
+ retired: number;
5
12
  failed: number;
6
- }>;
13
+ }
14
+ /**
15
+ * The one startup repair pass for SQL-authoritative pages and reviews. It does
16
+ * not infer a missing notification or action delivery: settlement created both
17
+ * atomically. The two remaining filesystem effects retain their durable marks.
18
+ */
19
+ /** Direct outbox rows are created in the settlement transaction; there is no separate ticket-delivery repair pass. */
20
+ export declare function reconcileUndeliveredTickets(): Promise<void>;
21
+ export declare function sweepHumanRequests(effects: HumanRequestSweepEffects): Promise<HumanRequestSweepResult>;