@north-light/crouter 0.3.311 → 0.3.313

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (520) hide show
  1. package/dist/api/client.d.ts +27 -30
  2. package/dist/api/client.js +80 -67
  3. package/dist/api/dto/broker-ops.d.ts +10 -20
  4. package/dist/api/dto/canvas.d.ts +1 -10
  5. package/dist/api/dto/health.d.ts +113 -0
  6. package/dist/api/dto/human-requests.d.ts +14 -3
  7. package/dist/api/dto/human.d.ts +2 -48
  8. package/dist/api/dto/human.js +3 -2
  9. package/dist/api/dto/inbox.d.ts +14 -0
  10. package/dist/api/dto/mail.d.ts +43 -0
  11. package/dist/api/dto/nodes.d.ts +11 -8
  12. package/dist/api/dto/recovery.d.ts +96 -0
  13. package/dist/api/dto/reviews.d.ts +2 -2
  14. package/dist/api/routes.d.ts +9 -6
  15. package/dist/api/routes.js +11 -11
  16. package/dist/build-root.d.ts +4 -0
  17. package/dist/build-root.js +11 -1
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
  20. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  22. package/dist/cli.js +10 -1
  23. package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
  24. package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
  25. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  26. package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
  27. package/dist/clients/attach/chrome/inbox-strip.js +17 -9
  28. package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
  29. package/dist/clients/attach/chrome/ticket-panel.js +77 -46
  30. package/dist/clients/attach/overlays/graph.js +6 -9
  31. package/dist/clients/attach/render/chat-view.d.ts +5 -4
  32. package/dist/clients/attach/render/chat-view.js +22 -13
  33. package/dist/clients/attach/render/page-block.d.ts +11 -7
  34. package/dist/clients/attach/render/page-block.js +64 -55
  35. package/dist/clients/attach/viewer.js +746 -735
  36. package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
  37. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
  38. package/dist/clients/inbox/controller.d.ts +42 -14
  39. package/dist/clients/inbox/controller.js +240 -124
  40. package/dist/clients/inbox/dto-adapt.d.ts +28 -0
  41. package/dist/clients/inbox/dto-adapt.js +93 -0
  42. package/dist/clients/inbox/page-adapter.d.ts +5 -4
  43. package/dist/clients/inbox/page-adapter.js +12 -12
  44. package/dist/clients/inbox/resolve.d.ts +10 -5
  45. package/dist/clients/inbox/resolve.js +13 -12
  46. package/dist/clients/inbox/review/review-client.d.ts +0 -1
  47. package/dist/clients/inbox/review/review-client.js +0 -4
  48. package/dist/clients/inbox/review-adapter.d.ts +2 -1
  49. package/dist/clients/inbox/review-adapter.js +3 -5
  50. package/dist/clients/inbox/surface.d.ts +2 -2
  51. package/dist/clients/inbox/surface.js +19 -8
  52. package/dist/clients/inbox/tui.d.ts +3 -3
  53. package/dist/commands/__tests__/human.test.js +31 -15
  54. package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
  55. package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
  56. package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
  57. package/dist/commands/api-client.d.ts +5 -4
  58. package/dist/commands/api-client.js +13 -7
  59. package/dist/commands/canvas-history/shared.js +1 -1
  60. package/dist/commands/canvas-history.js +1 -1
  61. package/dist/commands/canvas-roster.js +1 -1
  62. package/dist/commands/canvas.js +1 -2
  63. package/dist/commands/human/feedback.js +20 -17
  64. package/dist/commands/human/prompts.js +45 -36
  65. package/dist/commands/human/queue.js +73 -123
  66. package/dist/commands/human/review.js +6 -16
  67. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
  68. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
  69. package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
  70. package/dist/commands/node-inspect-artifacts.js +1 -1
  71. package/dist/commands/node-lifecycle-revive.d.ts +2 -2
  72. package/dist/commands/node-lifecycle-revive.js +9 -19
  73. package/dist/commands/pkg/plugin-manage.js +0 -1
  74. package/dist/commands/profile/show.js +4 -12
  75. package/dist/commands/surface-tmux-spread.js +1 -5
  76. package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
  77. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
  78. package/dist/commands/sys/branch.d.ts +2 -0
  79. package/dist/commands/sys/branch.js +19 -0
  80. package/dist/commands/sys/config.js +106 -33
  81. package/dist/commands/sys/daemon.d.ts +2 -0
  82. package/dist/commands/sys/daemon.js +130 -7
  83. package/dist/commands/sys/doctor.js +5 -15
  84. package/dist/commands/sys/migrate.js +21 -92
  85. package/dist/commands/sys/setup-settings-runner.js +4 -2
  86. package/dist/commands/sys.js +2 -15
  87. package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
  88. package/dist/core/__tests__/boot.test.js +32 -25
  89. package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
  90. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
  91. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
  92. package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
  93. package/dist/core/__tests__/canvas.test.js +18 -47
  94. package/dist/core/__tests__/child-death-wake.test.js +29 -7
  95. package/dist/core/__tests__/cli-help-contract.test.js +2 -0
  96. package/dist/core/__tests__/close.test.js +29 -10
  97. package/dist/core/__tests__/collectible.test.js +2 -1
  98. package/dist/core/__tests__/config-change-delta.test.js +2 -0
  99. package/dist/core/__tests__/context-intro.test.js +17 -5
  100. package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
  101. package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
  102. package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
  103. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
  104. package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
  105. package/dist/core/__tests__/daemon-boot.test.js +51 -14
  106. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  107. package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
  108. package/dist/core/__tests__/focuses.test.js +10 -5
  109. package/dist/core/__tests__/helpers/harness.js +50 -18
  110. package/dist/core/__tests__/history-inbox.test.js +12 -16
  111. package/dist/core/__tests__/human-action-delivery.test.js +48 -28
  112. package/dist/core/__tests__/human-actions.test.js +7 -3
  113. package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
  114. package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
  115. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
  116. package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
  117. package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
  118. package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
  119. package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
  120. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  121. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +312 -1
  122. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  123. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  124. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  125. package/dist/core/__tests__/integration/revive.test.js +67 -21
  126. package/dist/core/__tests__/integration/spawn-root.test.js +62 -19
  127. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  128. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  129. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  130. package/dist/core/__tests__/kickoff.test.js +36 -39
  131. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  132. package/dist/core/__tests__/lifecycle.test.js +2 -1
  133. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  134. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  135. package/dist/core/__tests__/migration.test.js +145 -31
  136. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  137. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  138. package/dist/core/__tests__/node-outcome.test.js +93 -26
  139. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +4 -3
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +8 -7
  141. package/dist/core/__tests__/on-read-nested-store.test.js +8 -7
  142. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  143. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  144. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  145. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  146. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  147. package/dist/core/__tests__/profile-project-memory-delivery.test.js +35 -32
  148. package/dist/core/__tests__/prune.test.js +11 -5
  149. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  150. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  151. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  152. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  153. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  154. package/dist/core/__tests__/revive-all.test.js +13 -3
  155. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  156. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  157. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  158. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  159. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  160. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  161. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  162. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  163. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  164. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  165. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +5 -4
  166. package/dist/core/__tests__/stop-guard.test.js +2 -1
  167. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  168. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +19 -5
  169. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  170. package/dist/core/bootstrap.js +2 -0
  171. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  172. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  173. package/dist/core/broker-client/transport-socket.js +13 -5
  174. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  175. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  176. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  177. package/dist/core/canvas/attention.d.ts +2 -5
  178. package/dist/core/canvas/attention.js +15 -64
  179. package/dist/core/canvas/boot.d.ts +6 -7
  180. package/dist/core/canvas/boot.js +5 -9
  181. package/dist/core/canvas/canvas.d.ts +13 -109
  182. package/dist/core/canvas/canvas.js +140 -401
  183. package/dist/core/canvas/crons.d.ts +2 -0
  184. package/dist/core/canvas/crons.js +16 -15
  185. package/dist/core/canvas/daemon-owner.js +10 -0
  186. package/dist/core/canvas/db.d.ts +2 -2
  187. package/dist/core/canvas/db.js +2 -18
  188. package/dist/core/canvas/deliveries.d.ts +104 -0
  189. package/dist/core/canvas/deliveries.js +354 -0
  190. package/dist/core/canvas/executions.d.ts +29 -0
  191. package/dist/core/canvas/executions.js +68 -0
  192. package/dist/core/canvas/history.js +35 -35
  193. package/dist/core/canvas/index.d.ts +3 -1
  194. package/dist/core/canvas/index.js +3 -1
  195. package/dist/core/canvas/migrations.js +20 -12
  196. package/dist/core/canvas/nav-model.js +1 -3
  197. package/dist/core/canvas/nav-render.d.ts +4 -4
  198. package/dist/core/canvas/nav-render.js +11 -21
  199. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  200. package/dist/core/canvas/node-sql-migration.js +261 -0
  201. package/dist/core/canvas/paths.d.ts +2 -16
  202. package/dist/core/canvas/paths.js +2 -31
  203. package/dist/core/canvas/recovery.d.ts +76 -0
  204. package/dist/core/canvas/recovery.js +588 -0
  205. package/dist/core/canvas/remote-canvas-source.js +4 -0
  206. package/dist/core/canvas/render-source.d.ts +16 -27
  207. package/dist/core/canvas/render-source.js +16 -45
  208. package/dist/core/canvas/sessions.d.ts +33 -0
  209. package/dist/core/canvas/sessions.js +78 -0
  210. package/dist/core/canvas/types.d.ts +33 -33
  211. package/dist/core/canvas/types.js +2 -5
  212. package/dist/core/canvas/waits.d.ts +18 -0
  213. package/dist/core/canvas/waits.js +70 -0
  214. package/dist/core/canvas/worktrees.d.ts +40 -0
  215. package/dist/core/canvas/worktrees.js +93 -0
  216. package/dist/core/command-hooks/artifact.d.ts +6 -0
  217. package/dist/core/command-hooks/artifact.js +22 -6
  218. package/dist/core/command-hooks/discovery.d.ts +2 -0
  219. package/dist/core/command-hooks/discovery.js +2 -0
  220. package/dist/core/command-hooks/lifecycle-catalog.d.ts +20 -2
  221. package/dist/core/command-hooks/lifecycle-catalog.js +7 -2
  222. package/dist/core/command-hooks/lifecycle-create.d.ts +4 -0
  223. package/dist/core/command-hooks/lifecycle-create.js +51 -0
  224. package/dist/core/command-hooks/schema.d.ts +2 -2
  225. package/dist/core/command-hooks/schema.js +5 -4
  226. package/dist/core/command-hooks/transport/exec-invoke.js +3 -1
  227. package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +18 -8
  228. package/dist/core/command-hooks/transport/exec-lifecycle.js +89 -30
  229. package/dist/core/config.d.ts +17 -12
  230. package/dist/core/config.js +106 -155
  231. package/dist/core/errors.d.ts +1 -0
  232. package/dist/core/errors.js +4 -0
  233. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  234. package/dist/core/feed/feed.d.ts +0 -6
  235. package/dist/core/feed/feed.js +68 -56
  236. package/dist/core/feed/inbox.d.ts +7 -81
  237. package/dist/core/feed/inbox.js +46 -378
  238. package/dist/core/feed/messages.d.ts +58 -0
  239. package/dist/core/feed/messages.js +217 -0
  240. package/dist/core/fs-utils.d.ts +4 -0
  241. package/dist/core/fs-utils.js +21 -1
  242. package/dist/core/human/answer.d.ts +1 -1
  243. package/dist/core/human/answer.js +1 -1
  244. package/dist/core/human/claim.d.ts +1 -1
  245. package/dist/core/human/claim.js +2 -4
  246. package/dist/core/human/component-docs.js +1 -1
  247. package/dist/core/human/feedback-companion.js +4 -3
  248. package/dist/core/human/feedback.d.ts +5 -8
  249. package/dist/core/human/feedback.js +57 -85
  250. package/dist/core/human/page-authoring.d.ts +30 -0
  251. package/dist/core/human/page-authoring.js +19 -0
  252. package/dist/core/human/requests.d.ts +136 -0
  253. package/dist/core/human/requests.js +515 -0
  254. package/dist/core/human/root.d.ts +0 -7
  255. package/dist/core/human/root.js +0 -17
  256. package/dist/core/human/scan.d.ts +2 -7
  257. package/dist/core/human/scan.js +48 -132
  258. package/dist/core/human/summary.d.ts +1 -1
  259. package/dist/core/human/summary.js +1 -1
  260. package/dist/core/human/tickets.d.ts +23 -83
  261. package/dist/core/human/tickets.js +92 -243
  262. package/dist/core/human/types.d.ts +5 -1
  263. package/dist/core/installed-plugins.js +8 -3
  264. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  265. package/dist/core/locked-file.d.ts +6 -3
  266. package/dist/core/locked-file.js +36 -9
  267. package/dist/core/memory/extensions.js +2 -0
  268. package/dist/core/preview-registry.js +0 -1
  269. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  270. package/dist/core/profiles/default-binding.d.ts +0 -4
  271. package/dist/core/profiles/default-binding.js +12 -58
  272. package/dist/core/profiles/env-store.js +8 -3
  273. package/dist/core/profiles/manifest.d.ts +2 -37
  274. package/dist/core/profiles/manifest.js +35 -161
  275. package/dist/core/profiles/select.js +3 -0
  276. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  277. package/dist/core/review/__tests__/comments.test.js +28 -10
  278. package/dist/core/review/__tests__/remap.test.js +28 -10
  279. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  280. package/dist/core/review/birth.d.ts +1 -1
  281. package/dist/core/review/birth.js +34 -5
  282. package/dist/core/review/comments.js +1 -1
  283. package/dist/core/review/companion.d.ts +1 -1
  284. package/dist/core/review/companion.js +2 -2
  285. package/dist/core/review/document.js +1 -1
  286. package/dist/core/review/project.d.ts +5 -1
  287. package/dist/core/review/project.js +14 -36
  288. package/dist/core/review/realize.d.ts +3 -6
  289. package/dist/core/review/realize.js +51 -168
  290. package/dist/core/review/remap.js +1 -1
  291. package/dist/core/review/stage.d.ts +3 -2
  292. package/dist/core/review/stage.js +2 -3
  293. package/dist/core/review/store.d.ts +3 -21
  294. package/dist/core/review/store.js +140 -186
  295. package/dist/core/review/types.d.ts +0 -1
  296. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  297. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  298. package/dist/core/runtime/broker/auth-reload.js +9 -10
  299. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  300. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  301. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  302. package/dist/core/runtime/broker/extension-abort.js +11 -0
  303. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  304. package/dist/core/runtime/broker/fault-retry.js +142 -95
  305. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  306. package/dist/core/runtime/broker/inbox.js +4 -92
  307. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  308. package/dist/core/runtime/broker/rebind.js +21 -12
  309. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  310. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  311. package/dist/core/runtime/broker.js +28 -22
  312. package/dist/core/runtime/close.d.ts +3 -9
  313. package/dist/core/runtime/close.js +16 -27
  314. package/dist/core/runtime/fault.d.ts +3 -93
  315. package/dist/core/runtime/fault.js +9 -419
  316. package/dist/core/runtime/kickoff.d.ts +3 -6
  317. package/dist/core/runtime/kickoff.js +12 -18
  318. package/dist/core/runtime/launch.js +1 -1
  319. package/dist/core/runtime/lifecycle.js +40 -7
  320. package/dist/core/runtime/nodes.d.ts +5 -1
  321. package/dist/core/runtime/nodes.js +26 -11
  322. package/dist/core/runtime/placement.js +2 -2
  323. package/dist/core/runtime/recycle.js +24 -14
  324. package/dist/core/runtime/reset.js +26 -7
  325. package/dist/core/runtime/revive.js +226 -217
  326. package/dist/core/runtime/spawn.d.ts +4 -1
  327. package/dist/core/runtime/spawn.js +43 -5
  328. package/dist/core/runtime/warm-pool.d.ts +2 -2
  329. package/dist/core/runtime/warm-pool.js +23 -3
  330. package/dist/core/scoped-state/db.d.ts +7 -0
  331. package/dist/core/scoped-state/db.js +92 -0
  332. package/dist/core/scoped-state/migrate.d.ts +11 -0
  333. package/dist/core/scoped-state/migrate.js +198 -0
  334. package/dist/core/scoped-state/paths.d.ts +9 -0
  335. package/dist/core/scoped-state/paths.js +27 -0
  336. package/dist/core/scoped-state/profiles.d.ts +12 -0
  337. package/dist/core/scoped-state/profiles.js +62 -0
  338. package/dist/core/scoped-state/providers.d.ts +16 -0
  339. package/dist/core/scoped-state/providers.js +8 -0
  340. package/dist/core/scoped-state/schema.d.ts +3 -0
  341. package/dist/core/scoped-state/schema.js +26 -0
  342. package/dist/core/scoped-state/settings.d.ts +28 -0
  343. package/dist/core/scoped-state/settings.js +82 -0
  344. package/dist/core/subscription-state.d.ts +29 -48
  345. package/dist/core/subscription-state.js +176 -712
  346. package/dist/core/worktree-close.js +4 -4
  347. package/dist/core/worktree-mutation-async.js +2 -2
  348. package/dist/core/worktree-sweep.js +11 -16
  349. package/dist/core/worktree.js +6 -12
  350. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  351. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  352. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  353. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  354. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  355. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +41 -17
  356. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  357. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  358. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  359. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  360. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  361. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  362. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  363. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  364. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  365. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +6 -3
  366. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  367. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  368. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  369. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  370. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  371. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +29 -43
  372. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  373. package/dist/daemon/api/bridge.js +1 -1
  374. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  375. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  376. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  377. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  378. package/dist/daemon/api/handlers/canvas.js +2 -44
  379. package/dist/daemon/api/handlers/daemon.js +41 -0
  380. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  381. package/dist/daemon/api/handlers/health.js +10 -5
  382. package/dist/daemon/api/handlers/human-requests.js +168 -188
  383. package/dist/daemon/api/handlers/human.js +3 -148
  384. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  385. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  386. package/dist/daemon/api/handlers/inbox.js +21 -120
  387. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  388. package/dist/daemon/api/handlers/messages.js +73 -15
  389. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  390. package/dist/daemon/api/handlers/nodes.d.ts +1 -0
  391. package/dist/daemon/api/handlers/nodes.js +41 -42
  392. package/dist/daemon/api/handlers/reviews.js +4 -29
  393. package/dist/daemon/api/map.d.ts +25 -3
  394. package/dist/daemon/api/map.js +84 -14
  395. package/dist/daemon/api/server.js +68 -9
  396. package/dist/daemon/control.d.ts +18 -0
  397. package/dist/daemon/cron/disposition.d.ts +2 -0
  398. package/dist/daemon/cron/disposition.js +8 -5
  399. package/dist/daemon/cron/settle.js +37 -13
  400. package/dist/daemon/cron/sinks.d.ts +18 -1
  401. package/dist/daemon/cron/sinks.js +47 -93
  402. package/dist/daemon/crtrd-cli.js +4 -19
  403. package/dist/daemon/crtrd.d.ts +2 -0
  404. package/dist/daemon/crtrd.js +206 -81
  405. package/dist/daemon/fleet.js +5 -6
  406. package/dist/daemon/human/finish.d.ts +12 -24
  407. package/dist/daemon/human/finish.js +88 -285
  408. package/dist/daemon/human/settle.d.ts +40 -0
  409. package/dist/daemon/human/settle.js +89 -0
  410. package/dist/daemon/human/sweep.d.ts +20 -5
  411. package/dist/daemon/human/sweep.js +56 -60
  412. package/dist/daemon/manage.d.ts +21 -16
  413. package/dist/daemon/manage.js +85 -51
  414. package/dist/daemon/messaging/node-message.d.ts +1 -1
  415. package/dist/daemon/messaging/node-message.js +9 -12
  416. package/dist/daemon/park-activity.js +3 -3
  417. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  418. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  419. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  420. package/dist/daemon/reconcilers/controller-death.js +6 -18
  421. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  422. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  423. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  424. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  425. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  426. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  427. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  428. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  429. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  430. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  431. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  432. package/dist/daemon/review/companion.js +5 -11
  433. package/dist/daemon/review/deliver.d.ts +1 -5
  434. package/dist/daemon/review/deliver.js +3 -52
  435. package/dist/daemon/review/finish.d.ts +2 -27
  436. package/dist/daemon/review/finish.js +79 -263
  437. package/dist/daemon/review/sweep.d.ts +3 -10
  438. package/dist/daemon/review/sweep.js +13 -131
  439. package/dist/daemon/startup-policy.d.ts +0 -5
  440. package/dist/daemon/startup-policy.js +0 -5
  441. package/dist/hook-authoring.d.ts +28 -2
  442. package/dist/hook-authoring.js +53 -1
  443. package/dist/index.d.ts +3 -2
  444. package/dist/index.js +1 -1
  445. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  446. package/dist/migrations/__tests__/activation.test.js +9 -6
  447. package/dist/migrations/activation.d.ts +3 -2
  448. package/dist/migrations/activation.js +2 -4
  449. package/dist/migrations/corpus.d.ts +2 -5
  450. package/dist/migrations/corpus.js +9 -20
  451. package/dist/migrations/runner.d.ts +3 -1
  452. package/dist/migrations/runner.js +6 -2
  453. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  454. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  455. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  456. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  457. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  458. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  459. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  460. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  461. package/dist/pi-extensions/canvas-stophook.js +47 -31
  462. package/dist/shared/env.d.ts +1 -0
  463. package/dist/shared/env.js +3 -0
  464. package/package.json +1 -1
  465. package/runtime.lock.json +5 -5
  466. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  467. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  468. package/dist/clients/inbox/review-visibility.js +0 -19
  469. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  470. package/dist/commands/canvas-rebuild-index.js +0 -46
  471. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  472. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  473. package/dist/core/__tests__/human-deliver.test.js +0 -256
  474. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  475. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  476. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  477. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  478. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  479. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  480. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  481. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  482. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  483. package/dist/core/canvas/human-deliveries.js +0 -75
  484. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  485. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  486. package/dist/core/feed/passive.d.ts +0 -3
  487. package/dist/core/feed/passive.js +0 -53
  488. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  489. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  490. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  491. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  492. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  493. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  494. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  495. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  496. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  497. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  498. package/dist/core/review/ticket-filter.d.ts +0 -17
  499. package/dist/core/review/ticket-filter.js +0 -21
  500. package/dist/core/runtime/broker/passive.d.ts +0 -17
  501. package/dist/core/runtime/broker/passive.js +0 -44
  502. package/dist/core/runtime/busy.d.ts +0 -46
  503. package/dist/core/runtime/busy.js +0 -150
  504. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  505. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  506. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  507. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  508. package/dist/daemon/human/deliver-action.d.ts +0 -3
  509. package/dist/daemon/human/deliver-action.js +0 -9
  510. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  511. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  512. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  513. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  514. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  515. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  516. package/dist/daemon/startup-block-marker.d.ts +0 -25
  517. package/dist/daemon/startup-block-marker.js +0 -99
  518. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  519. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  520. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -46,7 +46,7 @@ export const filterParams = [
46
46
  { kind: 'flag', name: 'all-cwds', type: 'bool', required: false, constraint: 'Search every cwd on the canvas. Mutually exclusive with --cwd.' },
47
47
  { kind: 'flag', name: 'under', type: 'string', required: false, constraint: 'Restrict to a node and its subscription descendants (one initiative / sub-DAG).' },
48
48
  { kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Restrict to specific node ids. Comma-separated for several.' },
49
- { kind: 'flag', name: 'type', type: 'string', required: false, constraint: `Corpus to search; comma-separated for several. One of: ${TYPES.join(', ')}. report = push history; doc = context artifacts; roadmap = roadmap.md specifically; meta = node name/description/kind; inbox = raw inbox.jsonl history, cursor-independent and including full un-clipped direct-message bodies (diagnostic, not curated work). Default: report, doc, roadmap, meta \u2014 inbox is opt-in only, pass --type inbox explicitly.` },
49
+ { kind: 'flag', name: 'type', type: 'string', required: false, constraint: `Corpus to search; comma-separated for several. One of: ${TYPES.join(', ')}. report = push history; doc = context artifacts; roadmap = roadmap.md specifically; meta = node name/description/kind; inbox = inbox history including full un-clipped direct-message bodies (diagnostic, not curated work). Default: report, doc, roadmap, meta \u2014 inbox is opt-in only, pass --type inbox explicitly.` },
50
50
  { kind: 'flag', name: 'report-kind', type: 'enum', choices: ['final', 'update'], required: false, constraint: 'Narrow reports to outcome summaries (final) or progress (update). Implies --type report.' },
51
51
  { kind: 'flag', name: 'kind', type: 'string', required: false, constraint: `Node kind; comma-separated for several. One of: ${NODE_KINDS.join(', ')}.` },
52
52
  { kind: 'flag', name: 'status', type: 'string', required: false, constraint: `Node lifecycle status; comma-separated for several. One of: ${STATUSES.join(', ')} (e.g. done = completed work only).` },
@@ -25,7 +25,7 @@ export const historyBranch = defineBranch({
25
25
  help: {
26
26
  name: 'canvas history',
27
27
  summary: 'search and recall the cwd-wide content corpus — every node\'s reports and context docs in this cwd',
28
- model: 'The accumulated record of every node that ran in a cwd and the artifacts it left — its reports (final/update outcome summaries), context docs (specs, designs, roadmaps, findings), and the conversations themselves. `search` finds by content (ranked, filtered, sorted; omit the query to browse by recency); `grep` finds by an exact ECMAScript regex over body lines (pattern required, one hit per matching line — use this over `search` when you know the literal string/pattern, not just the topic); `read` pulls one hit\'s full body by its <node-id>:<relpath> ref, `--format raw` for the verbatim bytes; `stats` counts the same filtered corpus by one dimension instead of listing hits. Corpus is a DIMENSION here, not a location: `--type` selects which record you search, and every leaf takes the same scope and filters. This is the episodic record of what was DONE — distinct from `crtr memory`, which is curated semantic knowledge. Already have a node id and want everything IT left behind (not a cwd-wide search)? Use `node inspect artifacts <node-id>` instead. Two corpora are opt-in diagnostics rather than curated work: `--type inbox` is one node\'s full cursor-independent inbox.jsonl including un-clipped message bodies, and `--type transcript` is the conversation itself, message by message, each inbound turn carrying the origin that drove it (`--origin`, `--role`, `--from`, and `stats --group-by origin` for the census). Transcripts require a narrowing flag because there is no index. To reopen a node you found, use `node lifecycle revive`; for its place in the graph, `node inspect show`.',
28
+ model: 'The accumulated record of every node that ran in a cwd and the artifacts it left — its reports (final/update outcome summaries), context docs (specs, designs, roadmaps, findings), and the conversations themselves. `search` finds by content (ranked, filtered, sorted; omit the query to browse by recency); `grep` finds by an exact ECMAScript regex over body lines (pattern required, one hit per matching line — use this over `search` when you know the literal string/pattern, not just the topic); `read` pulls one hit\'s full body by its <node-id>:<relpath> ref, `--format raw` for the verbatim bytes; `stats` counts the same filtered corpus by one dimension instead of listing hits. Corpus is a DIMENSION here, not a location: `--type` selects which record you search, and every leaf takes the same scope and filters. This is the episodic record of what was DONE — distinct from `crtr memory`, which is curated semantic knowledge. Already have a node id and want everything IT left behind (not a cwd-wide search)? Use `node inspect artifacts <node-id>` instead. Two corpora are opt-in diagnostics rather than curated work: `--type inbox` is one node\'s full inbox history including un-clipped message bodies, and `--type transcript` is the conversation itself, message by message, each inbound turn carrying the origin that drove it (`--origin`, `--role`, `--from`, and `stats --group-by origin` for the census). Transcripts require a narrowing flag because there is no index. To reopen a node you found, use `node lifecycle revive`; for its place in the graph, `node inspect show`.',
29
29
  dynamicState: corpusBlock,
30
30
  },
31
31
  children: [searchLeaf, grepLeaf, readLeaf, statsLeaf],
@@ -15,7 +15,7 @@ export const canvasRosterLeaf = defineLeaf({
15
15
  name: 'nodes',
16
16
  type: 'object[]',
17
17
  required: true,
18
- constraint: 'Lean node rows (no per-row meta.json read) plus attention_count for the returned node set.',
18
+ constraint: 'Lean node rows plus attention_count for the returned node set.',
19
19
  },
20
20
  {
21
21
  name: 'subscriptions',
@@ -15,7 +15,6 @@ import { reviveLeaf } from './revive.js';
15
15
  import { attentionBranch } from './attention.js';
16
16
  import { chordLeaf } from './chord.js';
17
17
  import { canvasPruneLeaf } from './canvas-prune.js';
18
- import { canvasRebuildIndexLeaf } from './canvas-rebuild-index.js';
19
18
  import { historyBranch } from './canvas-history.js';
20
19
  import { issueNewLeaf } from './canvas-issue.js';
21
20
  import { canvasConfigBranch } from './canvas-config.js';
@@ -45,6 +44,6 @@ export function registerCanvas() {
45
44
  summary: 'observe and supervise the whole agent graph',
46
45
  model: 'Canvas-wide operations, distinct from per-node work (`node`) and a node\'s own spine write verb (`push`). `dashboard` renders the subscription forest as a tree; `browse` opens an interactive full-screen navigator (tabs/tree/search) over the whole canvas and resumes the chosen node; `attention` aggregates pending replies from the user across the graph; `revive --all` mass-reconnects every disconnected node (bringing back ONE named node is `node lifecycle revive <id>` instead); `history` searches and recalls the content record (reports + context docs) of past work in a cwd; `prune` bounds growth by deleting terminal nodes past a TTL. `config` registers/lists remote canvas targets (preview endpoint + relay token); `use` switches remote-capable read commands such as `node inspect` onto a configured target or back to local.',
47
46
  },
48
- children: [dashboardLeaf, canvasSnapshotLeaf, canvasRosterLeaf, browseLeaf, attentionBranch, reviveLeaf, historyBranch, chordLeaf, issueBranch, canvasPruneLeaf, canvasRebuildIndexLeaf, canvasConfigBranch, canvasUseLeaf],
47
+ children: [dashboardLeaf, canvasSnapshotLeaf, canvasRosterLeaf, browseLeaf, attentionBranch, reviveLeaf, historyBranch, chordLeaf, issueBranch, canvasPruneLeaf, canvasConfigBranch, canvasUseLeaf],
49
48
  });
50
49
  }
@@ -4,21 +4,24 @@
4
4
  // Creating comments is the person's channel (the page surface); no agent
5
5
  // authors, edits, or deletes them.
6
6
  import { envNodeId } from '../../shared/env.js';
7
- import { existsSync } from 'node:fs';
8
7
  import { ApiError } from '../../api/index.js';
9
8
  import { defineBranch, defineLeaf } from '../../core/command.js';
10
- import { pageManifestPath } from '../../core/human/convention.js';
11
- import { opaqueInboxTicketId, ticketDir } from '../../core/human/root.js';
12
9
  import { InputError } from '../../core/io.js';
13
10
  import { cliClient, rethrowAsCliError } from '../api-client.js';
14
- /** Feedback tickets are addressed by the same raw ids as `human resolve`:
15
- * the page_id from human send, which is also the ticket's directory name. */
16
- function requirePageTicketId(raw) {
17
- const ticketId = raw;
18
- if (!existsSync(pageManifestPath(ticketDir(ticketId)))) {
19
- throw new InputError({ error: 'not_found', message: `no page ticket: ${ticketId}`, field: 'ticket_id', next: 'Pass the ticket id from your bearings, or a page ticket_id from `crtr human list`.' });
11
+ /** Feedback tickets are addressed by request identity; crtrd returns their inbox identity. */
12
+ async function requirePageTicketId(raw) {
13
+ const requestId = raw;
14
+ try {
15
+ const request = await cliClient().getHumanRequest(requestId);
16
+ if (request.kind !== 'page')
17
+ throw new InputError({ error: 'not_found', message: `no page ticket: ${requestId}`, field: 'ticket_id', next: 'Pass the ticket id from your bearings, or a page ticket_id from `crtr human list`.' });
18
+ return { requestId, inboxTicketId: request.inbox_ticket_id };
19
+ }
20
+ catch (error) {
21
+ if (error instanceof InputError)
22
+ throw error;
23
+ rethrowAsCliError(error, 'Pass the ticket id from your bearings, or a page ticket_id from `crtr human list`.');
20
24
  }
21
- return ticketId;
22
25
  }
23
26
  const humanFeedbackList = defineLeaf({
24
27
  name: 'list',
@@ -39,10 +42,10 @@ const humanFeedbackList = defineLeaf({
39
42
  effects: ['None. Read-only.'],
40
43
  },
41
44
  run: async (input) => {
42
- const ticketId = requirePageTicketId(input['ticket_id']);
45
+ const ticket = await requirePageTicketId(input['ticket_id']);
43
46
  try {
44
- const page = await cliClient().getHumanInboxPage(opaqueInboxTicketId(ticketId));
45
- return { ticket_id: ticketId, open_count: page.feedback.open_count, comments: page.feedback.comments };
47
+ const page = await cliClient().getHumanInboxPage(ticket.inboxTicketId);
48
+ return { ticket_id: ticket.requestId, open_count: page.feedback.open_count, comments: page.feedback.comments };
46
49
  }
47
50
  catch (e) {
48
51
  rethrowAsCliError(e);
@@ -80,14 +83,14 @@ const humanFeedbackResolve = defineLeaf({
80
83
  if (binding === undefined || binding === null || binding.kind !== 'page_feedback') {
81
84
  throw new InputError({ error: 'not_companion', message: 'this node is not a page-feedback companion — only the ticket\u2019s bound companion resolves its comments', next: 'Resolve from the companion conversation bound to the ticket.' });
82
85
  }
83
- const ticketId = requirePageTicketId(binding.review_id);
86
+ const ticket = await requirePageTicketId(binding.review_id);
84
87
  try {
85
- const result = await cliClient().resolvePageFeedbackComment(opaqueInboxTicketId(ticketId), commentId, callerNodeId);
86
- return { resolved: true, ticket_id: ticketId, comment_id: commentId, open_count: result.feedback.open_count };
88
+ const result = await cliClient().resolvePageFeedbackComment(ticket.inboxTicketId, commentId, callerNodeId);
89
+ return { resolved: true, ticket_id: ticket.requestId, comment_id: commentId, open_count: result.feedback.open_count };
87
90
  }
88
91
  catch (e) {
89
92
  if (e instanceof ApiError && e.status === 409 && e.code === 'comment_already_resolved') {
90
- return { resolved: false, ticket_id: ticketId, comment_id: commentId, reason: 'comment_already_resolved' };
93
+ return { resolved: false, ticket_id: ticket.requestId, comment_id: commentId, reason: 'comment_already_resolved' };
91
94
  }
92
95
  rethrowAsCliError(e);
93
96
  }
@@ -1,18 +1,14 @@
1
1
  import { envNodeId } from '../../shared/env.js';
2
2
  import { defineLeaf } from '../../core/command.js';
3
3
  import { InputError } from '../../core/io.js';
4
- import { cliClient } from '../api-client.js';
4
+ import { cliClient, rethrowAsCliError } from '../api-client.js';
5
5
  import { join } from 'node:path';
6
- import { randomBytes } from 'node:crypto';
7
- import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
6
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
8
7
  import { tmpdir } from 'node:os';
9
8
  import { resolvePageComponents } from '../../core/config.js';
10
9
  import { markStopSignal } from '../../core/runtime/stop-signals.js';
11
- import { bindTicketReplyRoute } from '../../core/human/convention.js';
12
- import { ticketDir } from '../../core/human/root.js';
13
- import { preparePage, publishPage, readTicketResult } from '../../core/human/tickets.js';
14
- import { PageAuthoringError, parsePage } from '../../core/human/page.js';
15
- import { pageManifestPath } from '../../core/human/convention.js';
10
+ import { preparePage } from '../../core/human/page-authoring.js';
11
+ import { PageAuthoringError } from '../../core/human/page.js';
16
12
  import { isResponseBearingSlot, validateAuthoredPageManifest } from '../../core/human/page-schema.js';
17
13
  import { display } from '../../core/termrender/display.js';
18
14
  import { pageHelpText, resolveMaxPanes } from './shared.js';
@@ -100,18 +96,19 @@ function submitAuthoredPage(submit) {
100
96
  });
101
97
  }
102
98
  }
103
- /** The page a `--replace` names, read for the one thing a revision inherits. */
104
- function replacedPage(ticketId) {
105
- const dir = ticketDir(ticketId);
106
- if (!existsSync(pageManifestPath(dir))) {
107
- throw new InputError({
108
- error: 'unknown_page',
109
- message: `no page ${ticketId} on this canvas`,
110
- field: 'replace',
111
- next: 'Pass the page_id a previous send returned, or omit --replace to raise a new page.',
112
- });
99
+ /** The page a `--replace` names, read through crtrd for the routing it retains. */
100
+ async function replacedPage(requestId) {
101
+ try {
102
+ const request = await cliClient().getHumanRequest(requestId);
103
+ if (request.kind !== 'page')
104
+ throw new InputError({ error: 'unknown_page', message: `no page ${requestId} on this canvas`, field: 'replace', next: 'Pass the page_id a previous send returned, or omit --replace to raise a new page.' });
105
+ return (await cliClient().getHumanInboxPage(request.inbox_ticket_id)).page;
106
+ }
107
+ catch (error) {
108
+ if (error instanceof InputError)
109
+ throw error;
110
+ rethrowAsCliError(error, 'Pass the page_id a previous send returned, or omit --replace to raise a new page.');
113
111
  }
114
- return parsePage(dir);
115
112
  }
116
113
  function writePipedPage(document) {
117
114
  const dir = mkdtempSync(join(tmpdir(), 'crtr-human-page-'));
@@ -184,14 +181,14 @@ export function humanSend(pageSurface = false) {
184
181
  { name: 'dir', type: 'string', required: true, constraint: pageHelp.dirConstraint },
185
182
  { name: 'placement', type: 'inline | panel', required: true, constraint: 'Resolved page placement.' },
186
183
  { name: 'inbox', type: 'boolean', required: true, constraint: 'Whether the page is projected into the inbox.' },
187
- { name: 'reply', type: 'boolean', required: true, constraint: 'Whether completion is routed back through a human bridge. Not a flag: true exactly when the page has response-bearing components.' },
184
+ { name: 'reply', type: 'boolean', required: true, constraint: 'Whether completion is delivered to the asking request identity. Not a flag: true exactly when the page has response-bearing components.' },
188
185
  { name: 'follow_up', type: 'string', required: true, constraint: 'What happens next; never instructs the caller to poll.' },
189
186
  ],
190
187
  outputKind: 'object',
191
188
  effects: [
192
- 'Writes one durable page record. Inbox projection follows the delivery flags, except from a terminal node, which always projects; reply routing follows the page\'s own components.',
189
+ 'Writes one durable page request. Inbox projection follows the delivery flags, except from a terminal node, which always projects; completion delivery follows the page\'s own components.',
193
190
  "A display-only page's inbox entry is a notice, not a task: it clears itself once the person has actually seen the page, wherever they see it.",
194
- 'A reply-bearing page creates a kind:"human" bridge under the caller and routes completion back.',
191
+ 'A reply-bearing page records the asking node on the request and delivers completion directly to that node.',
195
192
  '--replace rewrites source in place while retaining delivery and emitted time; open surfaces reload it without creating another page.',
196
193
  ],
197
194
  },
@@ -207,11 +204,12 @@ export function humanSend(pageSurface = false) {
207
204
  }
208
205
  try {
209
206
  if (replacePage !== undefined) {
210
- const prior = replacedPage(replacePage);
207
+ const prior = await replacedPage(replacePage);
211
208
  // The human's submit wins the race with a revision: once the ticket has
212
209
  // any terminal outcome, replacement is refused, and that refusal is the
213
210
  // reviser's signal that the ticket is closed.
214
- if (readTicketResult(ticketDir(replacePage)) !== null) {
211
+ const existing = await cliClient().getHumanRequest(replacePage);
212
+ if (existing.state !== 'pending') {
215
213
  throw new InputError({
216
214
  error: 'page_settled',
217
215
  message: `page ${replacePage} already has a terminal outcome`,
@@ -223,8 +221,8 @@ export function humanSend(pageSurface = false) {
223
221
  // time stay the original sender's, so the prior source is inherited
224
222
  // verbatim — a fork revising the page must not repoint its provenance.
225
223
  const revised = submitAuthoredPage(() => preparePage({ sourceFile, source: prior.source, productKinds: pageProductKinds(), delivery: prior.delivery }));
226
- const { dir } = publishPage(ticketDir(replacePage), revised);
227
- return { page_id: replacePage, dir, ...prior.delivery, follow_up: 'Replaced in place. Every open surface reloads it; delivery and emitted time are unchanged.' };
224
+ const updated = await cliClient().replaceHumanRequest(replacePage, { page: { dialect: revised.manifest.dialect, source: revised.document } });
225
+ return { page_id: replacePage, dir: updated.request_dir, ...prior.delivery, follow_up: 'Replaced in place. Every open surface reloads it; delivery and emitted time are unchanged.' };
228
226
  }
229
227
  const { source, lifecycle } = await sender();
230
228
  // A terminal node finishes and its transcript stops being read, so the
@@ -247,8 +245,14 @@ export function humanSend(pageSurface = false) {
247
245
  const delivery = deliveryFrom(input, responseBearing, lifecycle);
248
246
  prepared.manifest = submitAuthoredPage(() => validateAuthoredPageManifest({ ...prepared.manifest, delivery }, productKinds));
249
247
  if (!delivery.reply) {
250
- const pageId = `page-${randomBytes(4).toString('hex')}`;
251
- const { dir } = publishPage(ticketDir(pageId), prepared);
248
+ const created = await cliClient().createHumanRequest({
249
+ page: { dialect: prepared.manifest.dialect, source: prepared.document },
250
+ delivery,
251
+ source: prepared.manifest.source,
252
+ requester_node_id: null,
253
+ creator_cwd: process.cwd(),
254
+ });
255
+ const dir = created.request_dir;
252
256
  // A page with no reply route carries no response-bearing components, so what the
253
257
  // follow-up must get right is where the person will meet it and how it
254
258
  // settles: an inline page is already in this conversation, and its
@@ -262,16 +266,21 @@ export function humanSend(pageSurface = false) {
262
266
  : delivery.inbox
263
267
  ? 'Queued in the inbox; it opens in the panel and waits until dismissed.'
264
268
  : 'Published as passive page content with no inbox or response lifecycle.';
265
- return { page_id: pageId, dir, ...delivery, follow_up: followUp };
269
+ return { page_id: created.request_id, dir, ...delivery, follow_up: followUp };
266
270
  }
267
271
  const originNodeId = askingNode();
268
- const bridgeNodeId = (await cliClient().createHumanBridge({ kind: 'human', parent: originNodeId, cwd: process.cwd(), name: 'human-page' })).node_id;
269
- const idir = ticketDir(bridgeNodeId);
270
- bindTicketReplyRoute(idir, bridgeNodeId);
271
- const { dir } = publishPage(idir, prepared);
272
- if (originNodeId !== null && originNodeId !== '')
273
- markStopSignal(originNodeId, 'requestedHumanReply');
274
- return { page_id: bridgeNodeId, dir, ...delivery, follow_up: queuedFollowUp() };
272
+ if (originNodeId === null || originNodeId === '') {
273
+ throw new InputError({ error: 'no_requester', message: 'a response-bearing page must be sent from a crouter node', field: 'page', next: 'Run `crtr human send` from the node that should receive the answer.' });
274
+ }
275
+ const created = await cliClient().createHumanRequest({
276
+ page: { dialect: prepared.manifest.dialect, source: prepared.document },
277
+ delivery,
278
+ source: prepared.manifest.source,
279
+ requester_node_id: originNodeId,
280
+ creator_cwd: process.cwd(),
281
+ });
282
+ markStopSignal(originNodeId, 'requestedHumanReply');
283
+ return { page_id: created.request_id, dir: created.request_dir, ...delivery, follow_up: queuedFollowUp() };
275
284
  }
276
285
  finally {
277
286
  if (tempDir !== undefined)
@@ -1,38 +1,26 @@
1
1
  import { envNodeId } from '../../shared/env.js';
2
- import { existsSync } from 'node:fs';
3
2
  import { ApiError } from '../../api/index.js';
4
3
  import { defineLeaf } from '../../core/command.js';
5
- import { ticketDir } from '../../core/human/root.js';
6
- import { scanInbox } from '../../core/human/scan.js';
7
- import { pageManifestPath, isResolved, readTicketReplyRoute, reviewPath } from '../../core/human/convention.js';
8
4
  import { InputError } from '../../core/io.js';
9
5
  import { paginate } from '../../core/pagination.js';
10
6
  import { cliClient, rethrowAsCliError } from '../api-client.js';
11
- import { filterVisibleReviewTickets } from '../../clients/inbox/review-visibility.js';
12
- function toRef(n) {
13
- return { node_id: n.node_id, name: n.name, cwd: n.cwd, parent: n.parent, status: n.status };
14
- }
15
- /** The canvas roster, fetched ONCE per leaf run through the API and threaded
16
- * into the sync provenance helpers below (the API surface is async, so the
17
- * fetch is hoisted to the leaf and the helpers read the pre-fetched
18
- * array/map). */
19
7
  async function fetchNodeRefs() {
20
- return (await cliClient().listNodes()).map(toRef);
8
+ return (await cliClient().listNodes()).map((node) => ({ node_id: node.node_id, name: node.name, cwd: node.cwd, parent: node.parent, status: node.status }));
21
9
  }
22
10
  function resolveConversation(asking, nodes) {
23
11
  if (asking === undefined)
24
12
  return undefined;
25
- const byId = new Map(nodes.map((n) => [n.node_id, n]));
26
- let cur = asking;
13
+ const byId = new Map(nodes.map((node) => [node.node_id, node]));
14
+ let current = asking;
27
15
  const seen = new Set();
28
- while (cur.parent !== null && !seen.has(cur.node_id)) {
29
- seen.add(cur.node_id);
30
- const parent = byId.get(cur.parent);
16
+ while (current.parent !== null && !seen.has(current.node_id)) {
17
+ seen.add(current.node_id);
18
+ const parent = byId.get(current.parent);
31
19
  if (parent === undefined)
32
20
  break;
33
- cur = parent;
21
+ current = parent;
34
22
  }
35
- return cur;
23
+ return current;
36
24
  }
37
25
  function readableSourceLabel(source) {
38
26
  const sessionName = source?.sessionName?.trim();
@@ -41,55 +29,39 @@ function readableSourceLabel(source) {
41
29
  const askedBy = source?.askedBy?.trim();
42
30
  return askedBy === '' ? undefined : askedBy;
43
31
  }
44
- function askingNodeFor(jobId, byId) {
45
- const bridge = byId.get(jobId);
46
- if (bridge?.parent !== undefined && bridge.parent !== null)
47
- return byId.get(bridge.parent) ?? bridge;
48
- return bridge;
49
- }
50
- /** A missing reply route is the complete representation of a standalone page;
51
- * a route whose bridge disappeared is an orphaned reply-bearing ticket. */
52
- function replyBridgeId(dir) {
53
- return readTicketReplyRoute(dir)?.bridge_node_id ?? null;
54
- }
55
32
  function summarizeTicket(summary, nodes, byId) {
56
- const bridgeNodeId = replyBridgeId(summary.dir);
57
- // A reply-bearing ticket whose bridge was reaped is stale and absent from the
58
- // canvas view. A standalone page has no bridge by construction.
59
- if (bridgeNodeId !== null && !byId.has(bridgeNodeId))
60
- return null;
61
- const asking = bridgeNodeId === null ? undefined : askingNodeFor(bridgeNodeId, byId);
33
+ const asking = summary.source.nodeId === undefined ? undefined : byId.get(summary.source.nodeId);
62
34
  const conversation = resolveConversation(asking, nodes);
63
35
  const sourceLabel = readableSourceLabel(summary.source);
64
36
  return {
65
- ticket_id: summary.id,
66
- ...(bridgeNodeId === null ? {} : { bridge_node_id: bridgeNodeId }),
67
- dir: summary.dir,
37
+ ticket_id: summary.request_id,
38
+ dir: summary.request_dir,
68
39
  title: summary.title,
69
- subtitle: summary.subtitle,
40
+ subtitle: summary.subtitle ?? '',
70
41
  kind: summary.kind,
71
- ...(summary.kind === 'page' ? { emitted_at: summary.emittedAt } : { blocked_since: summary.blockedSince }),
72
- ...(summary.claim !== undefined ? { claim_owner: summary.claim.owner } : {}),
73
- ...(sourceLabel !== undefined ? { source_label: sourceLabel } : {}),
74
- ...(asking !== undefined ? { asking_node_id: asking.node_id, asking_node_name: asking.name, cwd: asking.cwd } : {}),
75
- ...(conversation !== undefined ? { conversation_id: conversation.node_id, conversation_title: conversation.name } : {}),
42
+ ...(summary.kind === 'page' ? { emitted_at: summary.emitted_at } : { blocked_since: summary.blocked_since }),
43
+ ...(sourceLabel === undefined ? {} : { source_label: sourceLabel }),
44
+ ...(asking === undefined ? {} : { asking_node_id: asking.node_id, asking_node_name: asking.name, cwd: asking.cwd }),
45
+ ...(conversation === undefined ? {} : { conversation_id: conversation.node_id, conversation_title: conversation.name }),
76
46
  };
77
47
  }
78
- function interactionDirForTicket(ticketId, byId) {
79
- const dir = ticketDir(ticketId);
80
- if (!existsSync(pageManifestPath(dir)) && !existsSync(reviewPath(dir))) {
81
- throw new InputError({ error: 'not_found', message: `no interaction ticket: ${ticketId}`, field: 'ticket_id', next: 'Pass a ticket_id from `crtr human list`.' });
48
+ async function requireInteraction(ticketId) {
49
+ try {
50
+ const request = await cliClient().getHumanRequest(ticketId);
51
+ return { request_id: request.request_id, kind: request.kind };
82
52
  }
83
- const bridgeNodeId = replyBridgeId(dir);
84
- return { dir, bridge: bridgeNodeId === null ? null : byId.get(bridgeNodeId) ?? null };
85
- }
86
- function isTerminalBridge(status) {
87
- return status === 'done' || status === 'dead' || status === 'canceled';
53
+ catch (error) {
54
+ if (!(error instanceof ApiError) || error.status !== 404)
55
+ rethrowAsCliError(error);
56
+ }
57
+ const ticket = (await cliClient().listHumanInbox()).tickets.find((item) => item.ticket_id === ticketId);
58
+ if (ticket === undefined)
59
+ throw new InputError({ error: 'not_found', message: `no interaction ticket: ${ticketId}`, field: 'ticket_id', next: 'Pass a ticket_id from `crtr human list`.' });
60
+ return { request_id: ticket.request_id, kind: ticket.kind };
88
61
  }
89
- function isAlreadyResolvedConflict(error) {
90
- return error instanceof ApiError && error.status === 409 && error.code === 'ticket_already_resolved';
62
+ function isAlreadySettled(error) {
63
+ return error instanceof ApiError && error.status === 409 && (error.code === 'already_settled' || error.code === 'review_terminal_conflict');
91
64
  }
92
- // list (read-only, paginated)
93
65
  export const humanList = defineLeaf({
94
66
  name: 'list',
95
67
  description: 'enumerate pending interactions',
@@ -102,7 +74,7 @@ export const humanList = defineLeaf({
102
74
  { kind: 'flag', name: 'cursor', type: 'string', required: false, constraint: "Opaque token from a previous response's next_cursor. Omit on first call." },
103
75
  ],
104
76
  output: [
105
- { name: 'items', type: 'object[]', required: true, constraint: 'Each: {ticket_id, dir, title, subtitle, kind, emitted_at? | blocked_since?}; pages use emitted_at, reviews use blocked_since. Enriched when derivable with bridge_node_id, claim_owner, source_label, asking_node_id/name, conversation_id/title. Oldest first.' },
77
+ { name: 'items', type: 'object[]', required: true, constraint: 'Each: {ticket_id, dir, title, subtitle, kind, emitted_at? | blocked_since?}; pages use emitted_at, reviews use blocked_since. Enriched when derivable with claim_owner, source_label, asking_node_id/name, conversation_id/title. Oldest first.' },
106
78
  { name: 'next_cursor', type: 'string | null', required: true, constraint: 'Pass on the next call to continue. null means no more items.' },
107
79
  { name: 'total', type: 'integer | null', required: true, constraint: 'Total pending interactions.' },
108
80
  ],
@@ -110,47 +82,35 @@ export const humanList = defineLeaf({
110
82
  effects: ['None. Read-only.'],
111
83
  },
112
84
  run: async (input) => {
113
- const limitRaw = input['limit'];
114
- const limit = Math.min(Math.max(1, limitRaw), 100);
115
- const cursor = input['cursor'];
116
- const { tickets } = await cliClient().listHumanInbox();
117
- const raw = filterVisibleReviewTickets(scanInbox(), new Set(tickets.map((ticket) => ticket.ticket_id)));
85
+ const limit = Math.min(Math.max(1, input['limit']), 100);
118
86
  const nodes = await fetchNodeRefs();
119
- const byId = new Map(nodes.map((n) => [n.node_id, n]));
120
- const items = raw
121
- .flatMap((s) => {
122
- const item = summarizeTicket(s, nodes, byId);
123
- return item === null ? [] : [item];
124
- })
125
- .sort((a, b) => {
126
- const ka = `${a.emitted_at ?? a.blocked_since}|${a.ticket_id}`;
127
- const kb = `${b.emitted_at ?? b.blocked_since}|${b.ticket_id}`;
128
- return ka < kb ? -1 : ka > kb ? 1 : 0;
87
+ const byId = new Map(nodes.map((node) => [node.node_id, node]));
88
+ const items = (await cliClient().listHumanInbox()).tickets.map((summary) => summarizeTicket(summary, nodes, byId)).sort((a, b) => {
89
+ const left = `${a.emitted_at ?? a.blocked_since}|${a.ticket_id}`;
90
+ const right = `${b.emitted_at ?? b.blocked_since}|${b.ticket_id}`;
91
+ return left < right ? -1 : left > right ? 1 : 0;
129
92
  });
130
- const page = paginate(items, { limit, cursor }, {
93
+ const page = paginate(items, { limit, cursor: input['cursor'] }, {
131
94
  defaultLimit: 20,
132
95
  maxLimit: 100,
133
- keyOf: (i) => `${i.emitted_at ?? i.blocked_since}|${i.ticket_id}`,
96
+ keyOf: (item) => `${item.emitted_at ?? item.blocked_since}|${item.ticket_id}`,
134
97
  total: 'count',
135
98
  });
136
99
  return { items: page.items, next_cursor: page.next_cursor, total: page.total };
137
100
  },
138
101
  });
139
- // resolve — non-interactive human answer
140
- /** Decode the stdin envelope only; the daemon validates every response against the page manifest. */
141
102
  function parseResolveBody(raw) {
142
103
  let parsed;
143
104
  try {
144
105
  parsed = JSON.parse(raw);
145
106
  }
146
- catch (e) {
147
- throw new InputError({ error: 'invalid_json', message: `stdin is not valid JSON: ${String(e)}`, field: 'stdin', next: 'Pass {"responses":{"<component_id>":{...}}} on stdin.' });
107
+ catch (error) {
108
+ throw new InputError({ error: 'invalid_json', message: `stdin is not valid JSON: ${String(error)}`, field: 'stdin', next: 'Pass {"responses":{"<component_id>":{...}}} on stdin.' });
148
109
  }
149
- const responses = parsed.responses;
150
- if (typeof responses !== 'object' || responses === null || Array.isArray(responses)) {
110
+ if (typeof parsed.responses !== 'object' || parsed.responses === null || Array.isArray(parsed.responses)) {
151
111
  throw new InputError({ error: 'invalid_field', message: 'responses must be an object keyed by the id of a response-bearing component', field: 'responses', next: 'Pass {"responses":{"<component_id>":{...}}}, or {"responses":{}} to acknowledge a page with no response-bearing components.' });
152
112
  }
153
- return responses;
113
+ return parsed.responses;
154
114
  }
155
115
  export const humanResolve = defineLeaf({
156
116
  name: 'resolve',
@@ -166,76 +126,66 @@ export const humanResolve = defineLeaf({
166
126
  ],
167
127
  output: [
168
128
  { name: 'resolved', type: 'boolean', required: true, constraint: 'True when this call completed the page.' },
169
- { name: 'ticket_id', type: 'string', required: true, constraint: 'Interaction ticket id.' },
170
- { name: 'reason', type: 'string', required: false, constraint: 'already_resolved or claimed when resolved is false.' },
129
+ { name: 'ticket_id', type: 'string', required: true, constraint: 'Interaction request id.' },
130
+ { name: 'reason', type: 'string', required: false, constraint: 'already_settled when resolved is false.' },
171
131
  ],
172
132
  outputKind: 'object',
173
- effects: ['The daemon publishes the answer and, when the ticket has a reply route, retires its bridge and returns the completion to subscribers.'],
133
+ effects: ['The daemon settles the request and directly delivers a reply-bearing completion to its asking node.'],
174
134
  },
175
135
  run: async (input) => {
176
136
  const ticketId = input['ticket_id'];
177
- const nodes = await fetchNodeRefs();
178
- const byId = new Map(nodes.map((n) => [n.node_id, n]));
179
- const { dir, bridge } = interactionDirForTicket(ticketId, byId);
180
- if (!existsSync(pageManifestPath(dir))) {
181
- throw new InputError({ error: 'not_found', message: `no page for interaction: ${ticketId}`, field: 'ticket_id', next: 'Pass a pending ticket_id from `crtr human list`.' });
182
- }
183
- const responses = parseResolveBody(input['body']);
184
- // A published result on a live bridge may be an interrupted delivery. The
185
- // daemon re-drives that delivery before it returns its 409. A standalone
186
- // ticket or terminal bridge has nothing left to deliver.
187
- if (isResolved(dir) && (bridge === null || isTerminalBridge(bridge.status))) {
188
- return { resolved: false, ticket_id: ticketId, reason: 'already_resolved' };
189
- }
137
+ const ticket = await requireInteraction(ticketId);
138
+ if (ticket.kind !== 'page')
139
+ throw new InputError({ error: 'not_found', message: `no page for interaction: ${ticketId}`, field: 'ticket_id', next: 'Pass a pending page ticket_id from `crtr human list`.' });
190
140
  try {
191
- await cliClient().resolveHumanTicket(ticketId, { responses });
141
+ await cliClient().respondHumanRequest(ticket.request_id, { responses: parseResolveBody(input['body']), actor: envNodeId() ?? 'human' });
192
142
  }
193
- catch (e) {
194
- if (isAlreadyResolvedConflict(e))
195
- return { resolved: false, ticket_id: ticketId, reason: 'already_resolved' };
196
- rethrowAsCliError(e);
143
+ catch (error) {
144
+ if (isAlreadySettled(error))
145
+ return { resolved: false, ticket_id: ticketId, reason: 'already_settled' };
146
+ rethrowAsCliError(error);
197
147
  }
198
148
  return { resolved: true, ticket_id: ticketId };
199
149
  },
200
150
  });
201
- // cancel — retract a pending page/review
202
151
  export const humanCancel = defineLeaf({
203
152
  name: 'cancel',
204
153
  description: 'retract a pending page/review',
205
154
  whenToUse: 'a page or review went stale before the user completed it',
206
155
  help: {
207
156
  name: 'human cancel',
208
- summary: 'retract a pending page/review — drops it from the human inbox and retires its reply bridge when one exists. Reach for this the moment an interaction goes stale so the user is not left completing something that no longer matters',
209
- guide: 'Pass the page_id returned by `human send`, or a ticket_id from `human list`. Best-effort and idempotent: if the user already completed or canceled it, canceled:false with reason "already_resolved" changes nothing. Cancellation races the user\'s submit; whichever wins is authoritative. For a reply-bearing ticket, only subscribers other than the caller receive the cancellation notice.',
157
+ summary: 'retract a pending page or review',
210
158
  params: [
211
159
  { kind: 'positional', name: 'ticket_id', type: 'string', required: true, constraint: 'page_id returned by human send, or ticket_id returned by human list.' },
212
- { kind: 'flag', name: 'reason', type: 'string', required: false, constraint: 'Optional short note delivered to subscribers explaining why it was retracted.' },
160
+ { kind: 'flag', name: 'reason', type: 'string', required: false, constraint: 'Optional short note delivered to the asking node explaining why it was retracted.' },
213
161
  ],
214
162
  output: [
215
- { name: 'canceled', type: 'boolean', required: true, constraint: 'True when the interaction was retracted; false when there was nothing live to cancel (already answered/canceled).' },
216
- { name: 'ticket_id', type: 'string', required: true, constraint: 'The interaction ticket id.' },
217
- { name: 'reason', type: 'string', required: false, constraint: 'Why nothing was canceled (e.g. "already_resolved"), present when canceled is false.' },
163
+ { name: 'canceled', type: 'boolean', required: true, constraint: 'True when the interaction was retracted; false when it was already terminal.' },
164
+ { name: 'ticket_id', type: 'string', required: true, constraint: 'The interaction request id.' },
165
+ { name: 'reason', type: 'string', required: false, constraint: 'already_settled when canceled is false.' },
218
166
  ],
219
167
  outputKind: 'object',
220
- effects: [
221
- 'The daemon removes the ticket from the inbox; when it has a reply bridge, the bridge retires and subscribers other than the caller are notified that no answer is coming.',
222
- ],
168
+ effects: ['The daemon removes the request from the human inbox and directly notifies its asking node when it has one.'],
223
169
  },
224
170
  run: async (input) => {
225
171
  const ticketId = input['ticket_id'];
172
+ const ticket = await requireInteraction(ticketId);
226
173
  const reason = input['reason'];
227
- const actor = envNodeId() ?? 'human';
174
+ const body = { ...(reason === undefined || reason === '' ? {} : { reason }), actor: envNodeId() ?? 'human' };
228
175
  try {
229
- await cliClient().cancelHumanTicket(ticketId, { ...(reason !== undefined && reason !== '' ? { reason } : {}), actor });
176
+ if (ticket.kind === 'review')
177
+ await cliClient().cancelReview(ticket.request_id, body);
178
+ else
179
+ await cliClient().cancelHumanRequest(ticket.request_id, body);
230
180
  }
231
- catch (e) {
232
- if (isAlreadyResolvedConflict(e))
233
- return { canceled: false, ticket_id: ticketId, reason: 'already_resolved' };
234
- rethrowAsCliError(e);
181
+ catch (error) {
182
+ if (isAlreadySettled(error))
183
+ return { canceled: false, ticket_id: ticketId, reason: 'already_settled' };
184
+ rethrowAsCliError(error);
235
185
  }
236
186
  return { canceled: true, ticket_id: ticketId };
237
187
  },
238
- render: (r) => r['canceled'] === true
239
- ? `Canceled human interaction ${r['ticket_id']} — it left the human inbox.`
240
- : `Nothing to cancel for ${r['ticket_id']} — ${r['reason'] ?? 'nothing to cancel'}.`,
188
+ render: (result) => result['canceled'] === true
189
+ ? `Canceled human interaction ${result['ticket_id']} — it left the human inbox.`
190
+ : `Nothing to cancel for ${result['ticket_id']} — ${result['reason'] ?? 'nothing to cancel'}.`,
241
191
  });