@north-light/crouter 0.3.312 → 0.3.313

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (495) hide show
  1. package/dist/api/client.d.ts +27 -30
  2. package/dist/api/client.js +80 -67
  3. package/dist/api/dto/broker-ops.d.ts +10 -20
  4. package/dist/api/dto/canvas.d.ts +1 -10
  5. package/dist/api/dto/health.d.ts +113 -0
  6. package/dist/api/dto/human-requests.d.ts +14 -3
  7. package/dist/api/dto/human.d.ts +2 -48
  8. package/dist/api/dto/human.js +3 -2
  9. package/dist/api/dto/inbox.d.ts +14 -0
  10. package/dist/api/dto/mail.d.ts +43 -0
  11. package/dist/api/dto/nodes.d.ts +11 -8
  12. package/dist/api/dto/recovery.d.ts +96 -0
  13. package/dist/api/dto/reviews.d.ts +2 -2
  14. package/dist/api/routes.d.ts +9 -6
  15. package/dist/api/routes.js +11 -11
  16. package/dist/build-root.d.ts +4 -0
  17. package/dist/build-root.js +11 -1
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
  20. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  22. package/dist/cli.js +10 -1
  23. package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
  24. package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
  25. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  26. package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
  27. package/dist/clients/attach/chrome/inbox-strip.js +17 -9
  28. package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
  29. package/dist/clients/attach/chrome/ticket-panel.js +77 -46
  30. package/dist/clients/attach/overlays/graph.js +6 -9
  31. package/dist/clients/attach/render/chat-view.d.ts +5 -4
  32. package/dist/clients/attach/render/chat-view.js +22 -13
  33. package/dist/clients/attach/render/page-block.d.ts +11 -7
  34. package/dist/clients/attach/render/page-block.js +64 -55
  35. package/dist/clients/attach/viewer.js +746 -735
  36. package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
  37. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
  38. package/dist/clients/inbox/controller.d.ts +42 -14
  39. package/dist/clients/inbox/controller.js +240 -124
  40. package/dist/clients/inbox/dto-adapt.d.ts +28 -0
  41. package/dist/clients/inbox/dto-adapt.js +93 -0
  42. package/dist/clients/inbox/page-adapter.d.ts +5 -4
  43. package/dist/clients/inbox/page-adapter.js +12 -12
  44. package/dist/clients/inbox/resolve.d.ts +10 -5
  45. package/dist/clients/inbox/resolve.js +13 -12
  46. package/dist/clients/inbox/review/review-client.d.ts +0 -1
  47. package/dist/clients/inbox/review/review-client.js +0 -4
  48. package/dist/clients/inbox/review-adapter.d.ts +2 -1
  49. package/dist/clients/inbox/review-adapter.js +3 -5
  50. package/dist/clients/inbox/surface.d.ts +2 -2
  51. package/dist/clients/inbox/surface.js +19 -8
  52. package/dist/clients/inbox/tui.d.ts +3 -3
  53. package/dist/commands/__tests__/human.test.js +31 -15
  54. package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
  55. package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
  56. package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
  57. package/dist/commands/api-client.d.ts +5 -4
  58. package/dist/commands/api-client.js +13 -7
  59. package/dist/commands/canvas-history/shared.js +1 -1
  60. package/dist/commands/canvas-history.js +1 -1
  61. package/dist/commands/canvas-roster.js +1 -1
  62. package/dist/commands/canvas.js +1 -2
  63. package/dist/commands/human/feedback.js +20 -17
  64. package/dist/commands/human/prompts.js +45 -36
  65. package/dist/commands/human/queue.js +73 -123
  66. package/dist/commands/human/review.js +6 -16
  67. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
  68. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
  69. package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
  70. package/dist/commands/node-inspect-artifacts.js +1 -1
  71. package/dist/commands/node-lifecycle-revive.d.ts +2 -2
  72. package/dist/commands/node-lifecycle-revive.js +9 -19
  73. package/dist/commands/pkg/plugin-manage.js +0 -1
  74. package/dist/commands/profile/show.js +4 -12
  75. package/dist/commands/surface-tmux-spread.js +1 -5
  76. package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
  77. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
  78. package/dist/commands/sys/branch.d.ts +2 -0
  79. package/dist/commands/sys/branch.js +19 -0
  80. package/dist/commands/sys/config.js +106 -33
  81. package/dist/commands/sys/daemon.d.ts +2 -0
  82. package/dist/commands/sys/daemon.js +130 -7
  83. package/dist/commands/sys/doctor.js +5 -15
  84. package/dist/commands/sys/migrate.js +21 -92
  85. package/dist/commands/sys/setup-settings-runner.js +4 -2
  86. package/dist/commands/sys.js +2 -15
  87. package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
  88. package/dist/core/__tests__/boot.test.js +32 -25
  89. package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
  90. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
  91. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
  92. package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
  93. package/dist/core/__tests__/canvas.test.js +18 -47
  94. package/dist/core/__tests__/child-death-wake.test.js +29 -7
  95. package/dist/core/__tests__/cli-help-contract.test.js +2 -0
  96. package/dist/core/__tests__/close.test.js +29 -10
  97. package/dist/core/__tests__/collectible.test.js +2 -1
  98. package/dist/core/__tests__/config-change-delta.test.js +2 -0
  99. package/dist/core/__tests__/context-intro.test.js +13 -1
  100. package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
  101. package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
  102. package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
  103. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
  104. package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
  105. package/dist/core/__tests__/daemon-boot.test.js +51 -14
  106. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  107. package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
  108. package/dist/core/__tests__/focuses.test.js +10 -5
  109. package/dist/core/__tests__/helpers/harness.js +50 -18
  110. package/dist/core/__tests__/history-inbox.test.js +12 -16
  111. package/dist/core/__tests__/human-action-delivery.test.js +48 -28
  112. package/dist/core/__tests__/human-actions.test.js +7 -3
  113. package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
  114. package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
  115. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
  116. package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
  117. package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
  118. package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
  119. package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
  120. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  121. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
  122. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  123. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  124. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  125. package/dist/core/__tests__/integration/revive.test.js +67 -21
  126. package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
  127. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  128. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  129. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  130. package/dist/core/__tests__/kickoff.test.js +36 -39
  131. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  132. package/dist/core/__tests__/lifecycle.test.js +2 -1
  133. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  134. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  135. package/dist/core/__tests__/migration.test.js +145 -31
  136. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  137. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  138. package/dist/core/__tests__/node-outcome.test.js +93 -26
  139. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
  141. package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
  142. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  143. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  144. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  145. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  146. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  147. package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
  148. package/dist/core/__tests__/prune.test.js +11 -5
  149. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  150. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  151. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  152. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  153. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  154. package/dist/core/__tests__/revive-all.test.js +13 -3
  155. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  156. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  157. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  158. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  159. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  160. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  161. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  162. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  163. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  164. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  165. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
  166. package/dist/core/__tests__/stop-guard.test.js +2 -1
  167. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  168. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
  169. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  170. package/dist/core/bootstrap.js +2 -0
  171. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  172. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  173. package/dist/core/broker-client/transport-socket.js +13 -5
  174. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  175. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  176. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  177. package/dist/core/canvas/attention.d.ts +2 -5
  178. package/dist/core/canvas/attention.js +15 -64
  179. package/dist/core/canvas/boot.d.ts +6 -7
  180. package/dist/core/canvas/boot.js +5 -9
  181. package/dist/core/canvas/canvas.d.ts +13 -109
  182. package/dist/core/canvas/canvas.js +140 -401
  183. package/dist/core/canvas/crons.d.ts +2 -0
  184. package/dist/core/canvas/crons.js +16 -15
  185. package/dist/core/canvas/daemon-owner.js +10 -0
  186. package/dist/core/canvas/db.d.ts +2 -2
  187. package/dist/core/canvas/db.js +2 -18
  188. package/dist/core/canvas/deliveries.d.ts +104 -0
  189. package/dist/core/canvas/deliveries.js +354 -0
  190. package/dist/core/canvas/executions.d.ts +29 -0
  191. package/dist/core/canvas/executions.js +68 -0
  192. package/dist/core/canvas/history.js +35 -35
  193. package/dist/core/canvas/index.d.ts +3 -1
  194. package/dist/core/canvas/index.js +3 -1
  195. package/dist/core/canvas/migrations.js +20 -12
  196. package/dist/core/canvas/nav-model.js +1 -3
  197. package/dist/core/canvas/nav-render.d.ts +4 -4
  198. package/dist/core/canvas/nav-render.js +11 -21
  199. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  200. package/dist/core/canvas/node-sql-migration.js +261 -0
  201. package/dist/core/canvas/paths.d.ts +2 -16
  202. package/dist/core/canvas/paths.js +2 -31
  203. package/dist/core/canvas/recovery.d.ts +76 -0
  204. package/dist/core/canvas/recovery.js +588 -0
  205. package/dist/core/canvas/remote-canvas-source.js +4 -0
  206. package/dist/core/canvas/render-source.d.ts +16 -27
  207. package/dist/core/canvas/render-source.js +16 -45
  208. package/dist/core/canvas/sessions.d.ts +33 -0
  209. package/dist/core/canvas/sessions.js +78 -0
  210. package/dist/core/canvas/types.d.ts +33 -33
  211. package/dist/core/canvas/types.js +2 -5
  212. package/dist/core/canvas/waits.d.ts +18 -0
  213. package/dist/core/canvas/waits.js +70 -0
  214. package/dist/core/canvas/worktrees.d.ts +40 -0
  215. package/dist/core/canvas/worktrees.js +93 -0
  216. package/dist/core/config.d.ts +17 -12
  217. package/dist/core/config.js +106 -155
  218. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  219. package/dist/core/feed/feed.d.ts +0 -6
  220. package/dist/core/feed/feed.js +68 -56
  221. package/dist/core/feed/inbox.d.ts +7 -81
  222. package/dist/core/feed/inbox.js +46 -378
  223. package/dist/core/feed/messages.d.ts +58 -0
  224. package/dist/core/feed/messages.js +217 -0
  225. package/dist/core/fs-utils.d.ts +4 -0
  226. package/dist/core/fs-utils.js +21 -1
  227. package/dist/core/human/answer.d.ts +1 -1
  228. package/dist/core/human/answer.js +1 -1
  229. package/dist/core/human/claim.d.ts +1 -1
  230. package/dist/core/human/claim.js +2 -4
  231. package/dist/core/human/component-docs.js +1 -1
  232. package/dist/core/human/feedback-companion.js +3 -2
  233. package/dist/core/human/feedback.d.ts +5 -8
  234. package/dist/core/human/feedback.js +57 -85
  235. package/dist/core/human/page-authoring.d.ts +30 -0
  236. package/dist/core/human/page-authoring.js +19 -0
  237. package/dist/core/human/requests.d.ts +136 -0
  238. package/dist/core/human/requests.js +515 -0
  239. package/dist/core/human/root.d.ts +0 -7
  240. package/dist/core/human/root.js +0 -17
  241. package/dist/core/human/scan.d.ts +2 -7
  242. package/dist/core/human/scan.js +48 -132
  243. package/dist/core/human/summary.d.ts +1 -1
  244. package/dist/core/human/summary.js +1 -1
  245. package/dist/core/human/tickets.d.ts +23 -83
  246. package/dist/core/human/tickets.js +92 -243
  247. package/dist/core/human/types.d.ts +5 -1
  248. package/dist/core/installed-plugins.js +8 -3
  249. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  250. package/dist/core/locked-file.d.ts +6 -3
  251. package/dist/core/locked-file.js +36 -9
  252. package/dist/core/memory/extensions.js +2 -0
  253. package/dist/core/preview-registry.js +0 -1
  254. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  255. package/dist/core/profiles/default-binding.d.ts +0 -4
  256. package/dist/core/profiles/default-binding.js +12 -58
  257. package/dist/core/profiles/env-store.js +8 -3
  258. package/dist/core/profiles/manifest.d.ts +2 -37
  259. package/dist/core/profiles/manifest.js +35 -161
  260. package/dist/core/profiles/select.js +3 -0
  261. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  262. package/dist/core/review/__tests__/comments.test.js +28 -10
  263. package/dist/core/review/__tests__/remap.test.js +28 -10
  264. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  265. package/dist/core/review/birth.d.ts +1 -1
  266. package/dist/core/review/birth.js +34 -5
  267. package/dist/core/review/comments.js +1 -1
  268. package/dist/core/review/document.js +1 -1
  269. package/dist/core/review/project.d.ts +5 -1
  270. package/dist/core/review/project.js +14 -36
  271. package/dist/core/review/realize.d.ts +3 -6
  272. package/dist/core/review/realize.js +51 -168
  273. package/dist/core/review/remap.js +1 -1
  274. package/dist/core/review/stage.d.ts +3 -2
  275. package/dist/core/review/stage.js +2 -3
  276. package/dist/core/review/store.d.ts +3 -21
  277. package/dist/core/review/store.js +140 -186
  278. package/dist/core/review/types.d.ts +0 -1
  279. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  280. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  281. package/dist/core/runtime/broker/auth-reload.js +9 -10
  282. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  283. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  284. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  285. package/dist/core/runtime/broker/extension-abort.js +11 -0
  286. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  287. package/dist/core/runtime/broker/fault-retry.js +142 -95
  288. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  289. package/dist/core/runtime/broker/inbox.js +4 -92
  290. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  291. package/dist/core/runtime/broker/rebind.js +21 -12
  292. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  293. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  294. package/dist/core/runtime/broker.js +28 -22
  295. package/dist/core/runtime/close.d.ts +3 -9
  296. package/dist/core/runtime/close.js +16 -27
  297. package/dist/core/runtime/fault.d.ts +3 -93
  298. package/dist/core/runtime/fault.js +9 -419
  299. package/dist/core/runtime/kickoff.d.ts +3 -6
  300. package/dist/core/runtime/kickoff.js +12 -18
  301. package/dist/core/runtime/launch.js +1 -1
  302. package/dist/core/runtime/lifecycle.js +40 -7
  303. package/dist/core/runtime/placement.js +2 -2
  304. package/dist/core/runtime/recycle.js +8 -2
  305. package/dist/core/runtime/reset.js +22 -6
  306. package/dist/core/runtime/revive.js +226 -218
  307. package/dist/core/runtime/spawn.js +14 -3
  308. package/dist/core/scoped-state/db.d.ts +7 -0
  309. package/dist/core/scoped-state/db.js +92 -0
  310. package/dist/core/scoped-state/migrate.d.ts +11 -0
  311. package/dist/core/scoped-state/migrate.js +198 -0
  312. package/dist/core/scoped-state/paths.d.ts +9 -0
  313. package/dist/core/scoped-state/paths.js +27 -0
  314. package/dist/core/scoped-state/profiles.d.ts +12 -0
  315. package/dist/core/scoped-state/profiles.js +62 -0
  316. package/dist/core/scoped-state/providers.d.ts +16 -0
  317. package/dist/core/scoped-state/providers.js +8 -0
  318. package/dist/core/scoped-state/schema.d.ts +3 -0
  319. package/dist/core/scoped-state/schema.js +26 -0
  320. package/dist/core/scoped-state/settings.d.ts +28 -0
  321. package/dist/core/scoped-state/settings.js +82 -0
  322. package/dist/core/subscription-state.d.ts +29 -48
  323. package/dist/core/subscription-state.js +176 -712
  324. package/dist/core/worktree-close.js +4 -4
  325. package/dist/core/worktree-mutation-async.js +2 -2
  326. package/dist/core/worktree-sweep.js +11 -16
  327. package/dist/core/worktree.js +6 -12
  328. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  329. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  330. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  331. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  332. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  333. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
  334. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  335. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  336. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  337. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  338. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  339. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  340. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  341. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  342. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  343. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
  344. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  345. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  346. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  347. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  348. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  349. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
  350. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  351. package/dist/daemon/api/bridge.js +1 -1
  352. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  353. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  354. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  355. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  356. package/dist/daemon/api/handlers/canvas.js +2 -44
  357. package/dist/daemon/api/handlers/daemon.js +41 -0
  358. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  359. package/dist/daemon/api/handlers/health.js +10 -5
  360. package/dist/daemon/api/handlers/human-requests.js +168 -188
  361. package/dist/daemon/api/handlers/human.js +3 -151
  362. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  363. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  364. package/dist/daemon/api/handlers/inbox.js +21 -120
  365. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  366. package/dist/daemon/api/handlers/messages.js +73 -15
  367. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  368. package/dist/daemon/api/handlers/nodes.js +7 -13
  369. package/dist/daemon/api/handlers/reviews.js +4 -29
  370. package/dist/daemon/api/map.d.ts +25 -3
  371. package/dist/daemon/api/map.js +84 -14
  372. package/dist/daemon/api/server.js +68 -9
  373. package/dist/daemon/control.d.ts +18 -0
  374. package/dist/daemon/cron/disposition.d.ts +2 -0
  375. package/dist/daemon/cron/disposition.js +8 -5
  376. package/dist/daemon/cron/settle.js +37 -13
  377. package/dist/daemon/cron/sinks.d.ts +18 -1
  378. package/dist/daemon/cron/sinks.js +47 -93
  379. package/dist/daemon/crtrd-cli.js +4 -19
  380. package/dist/daemon/crtrd.d.ts +2 -0
  381. package/dist/daemon/crtrd.js +206 -81
  382. package/dist/daemon/fleet.js +5 -6
  383. package/dist/daemon/human/finish.d.ts +12 -24
  384. package/dist/daemon/human/finish.js +88 -285
  385. package/dist/daemon/human/settle.d.ts +40 -0
  386. package/dist/daemon/human/settle.js +89 -0
  387. package/dist/daemon/human/sweep.d.ts +20 -5
  388. package/dist/daemon/human/sweep.js +56 -60
  389. package/dist/daemon/manage.d.ts +21 -16
  390. package/dist/daemon/manage.js +85 -51
  391. package/dist/daemon/messaging/node-message.d.ts +1 -1
  392. package/dist/daemon/messaging/node-message.js +9 -12
  393. package/dist/daemon/park-activity.js +3 -3
  394. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  395. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  396. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  397. package/dist/daemon/reconcilers/controller-death.js +6 -18
  398. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  399. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  400. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  401. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  402. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  403. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  404. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  405. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  406. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  407. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  408. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  409. package/dist/daemon/review/companion.js +5 -11
  410. package/dist/daemon/review/deliver.d.ts +1 -5
  411. package/dist/daemon/review/deliver.js +3 -52
  412. package/dist/daemon/review/finish.d.ts +2 -27
  413. package/dist/daemon/review/finish.js +79 -263
  414. package/dist/daemon/review/sweep.d.ts +3 -10
  415. package/dist/daemon/review/sweep.js +13 -131
  416. package/dist/daemon/startup-policy.d.ts +0 -5
  417. package/dist/daemon/startup-policy.js +0 -5
  418. package/dist/index.d.ts +2 -1
  419. package/dist/index.js +1 -1
  420. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  421. package/dist/migrations/__tests__/activation.test.js +9 -6
  422. package/dist/migrations/activation.d.ts +3 -2
  423. package/dist/migrations/activation.js +2 -4
  424. package/dist/migrations/corpus.d.ts +2 -5
  425. package/dist/migrations/corpus.js +9 -20
  426. package/dist/migrations/runner.d.ts +3 -1
  427. package/dist/migrations/runner.js +6 -2
  428. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  429. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  430. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  431. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  432. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  433. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  434. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  435. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  436. package/dist/pi-extensions/canvas-stophook.js +47 -31
  437. package/dist/shared/env.d.ts +1 -0
  438. package/dist/shared/env.js +3 -0
  439. package/package.json +1 -1
  440. package/runtime.lock.json +5 -5
  441. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  442. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  443. package/dist/clients/inbox/review-visibility.js +0 -19
  444. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  445. package/dist/commands/canvas-rebuild-index.js +0 -46
  446. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  447. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  448. package/dist/core/__tests__/human-deliver.test.js +0 -256
  449. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  450. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  451. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  452. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  453. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  454. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  455. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  456. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  457. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  458. package/dist/core/canvas/human-deliveries.js +0 -75
  459. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  460. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  461. package/dist/core/feed/passive.d.ts +0 -3
  462. package/dist/core/feed/passive.js +0 -53
  463. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  464. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  465. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  466. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  467. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  468. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  469. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  470. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  471. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  472. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  473. package/dist/core/review/ticket-filter.d.ts +0 -17
  474. package/dist/core/review/ticket-filter.js +0 -21
  475. package/dist/core/runtime/broker/passive.d.ts +0 -17
  476. package/dist/core/runtime/broker/passive.js +0 -44
  477. package/dist/core/runtime/busy.d.ts +0 -46
  478. package/dist/core/runtime/busy.js +0 -150
  479. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  480. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  481. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  482. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  483. package/dist/daemon/human/deliver-action.d.ts +0 -3
  484. package/dist/daemon/human/deliver-action.js +0 -9
  485. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  486. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  487. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  488. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  489. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  490. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  491. package/dist/daemon/startup-block-marker.d.ts +0 -25
  492. package/dist/daemon/startup-block-marker.js +0 -99
  493. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  494. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  495. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -1,23 +1,5 @@
1
- // Per-node inbox.jsonl primitive for the pi-native canvas runtime.
2
- //
3
- // An inbox entry is a lightweight pointer for a push (~30 tokens, body lives
4
- // in nodes/<id>/reports/) or carries a direct message inline. A direct
5
- // message short enough to inline stays entirely in the jsonl line; one that
6
- // would clip spills its full body to nodes/<id>/messages/ and the entry
7
- // carries `ref` to it alongside a bounded inline preview.
8
- //
9
- // Layout:
10
- // nodes/<id>/inbox.jsonl — one JSON line per entry, append-only
11
- // nodes/<id>/inbox.jsonl.cursor — exact identity of the last successfully
12
- // settled/covered entry (sidecar), distinct from the in-memory read position
13
- import { appendFileSync, existsSync, readFileSync, writeFileSync, renameSync, mkdirSync, } from 'node:fs';
14
- import { dirname, join } from 'node:path';
15
- import { randomBytes, randomUUID } from 'node:crypto';
16
- import { deliverAndSettleMessageWait, getRow, inboxPath, messagesDir, settleDeadMessageWait } from '../canvas/index.js';
17
1
  import { resolveRef } from '../canvas/history.js';
18
- import { emitEvent } from '../events/emit.js';
19
- import { errorClassFromError } from '../events/errors.js';
20
- import { generateOperationId, operationIdContext, validateOperationId } from '../events/operation-id.js';
2
+ import { validateOperationId } from '../events/operation-id.js';
21
3
  import { formatInboxCard, formatInboxRefInstruction } from '../../shared/generated-context.js';
22
4
  import { renderBirthAnnouncementBody } from '../../shared/birth-announcement.js';
23
5
  const INBOX_ENTRY_ID = /^(?!0{32}$)[0-9a-f]{32}$/;
@@ -26,321 +8,27 @@ function validateInboxEntryId(value) {
26
8
  throw new TypeError('entry_id must be 32 lowercase non-zero hexadecimal characters');
27
9
  return value;
28
10
  }
29
- function generateInboxEntryId() {
30
- let value;
31
- do
32
- value = randomBytes(16).toString('hex');
33
- while (value === '00000000000000000000000000000000');
34
- return validateInboxEntryId(value);
35
- }
36
- /** Complete an entry exactly once before any spill or append work. */
37
- export function finalizeInboxEntry(input) {
38
- const operationId = input.operation_id === undefined
39
- ? operationIdContext.current() ?? generateOperationId()
40
- : validateOperationId(input.operation_id);
11
+ /** Project the immutable message body and destination-specific urgency into the canonical inbox rendering shape. */
12
+ export function nodeMailEntry(delivery) {
13
+ const message = delivery.message;
41
14
  return {
42
- ...input,
43
- entry_id: generateInboxEntryId(),
44
- operation_id: operationId,
45
- ts: new Date().toISOString(),
15
+ entry_id: validateInboxEntryId(message.messageId),
16
+ operation_id: validateOperationId(message.operationId),
17
+ ts: message.createdAt,
18
+ from: message.senderNodeId,
19
+ ...(message.senderName === null ? {} : { from_name: message.senderName }),
20
+ tier: delivery.urgency,
21
+ kind: message.kind,
22
+ ...(message.bodyRef === null ? {} : { ref: message.bodyRef }),
23
+ label: message.label,
24
+ ...(message.bodyJson === null ? {} : { data: message.bodyJson }),
25
+ ...(message.disposition === null ? {} : { disposition: message.disposition }),
46
26
  };
47
27
  }
48
- // Cursor sidecar path
49
- function cursorPath(nodeId) {
50
- return `${inboxPath(nodeId)}.cursor`;
51
- }
52
- // Append
53
- /** Spill an oversized inline body to a durable file — the SAME atomic
54
- * tmp+rename pattern a push report uses — and return its absolute path.
55
- * Called only when the body would clip in the digest; short bodies never
56
- * touch disk beyond the jsonl line itself. */
57
- function writeMessageBody(nodeId, ts, body) {
58
- const dir = messagesDir(nodeId);
59
- if (!existsSync(dir))
60
- mkdirSync(dir, { recursive: true });
61
- const stamp = ts.replace(/[^0-9]/g, '');
62
- const finalPath = join(dir, `${stamp}-${randomUUID().slice(0, 6)}.md`);
63
- const tmpPath = `${finalPath}.tmp`;
64
- writeFileSync(tmpPath, body, 'utf8');
65
- renameSync(tmpPath, finalPath);
66
- return finalPath;
67
- }
68
- /** The tier a live watcher injects into the turn a node is ALREADY running
69
- * (`visibleRoute`, canvas-inbox-watcher). `normal` and `deferred` both wait
70
- * for a later cycle instead. */
71
- const STEERING_TIER = 'urgent';
72
- /** The tier an entry is actually appended at. A terminal node owes one final
73
- * result and reaps on it, so the later cycle `normal` and `deferred` wait for
74
- * is one it may never take — mail handed to such a node is raised to the
75
- * steering tier so the turn it is running now carries it, and so a dormant one
76
- * is woken for it rather than holding it forever. */
77
- function effectiveTier(nodeId, tier) {
78
- if (tier !== 'normal' && tier !== 'deferred')
79
- return tier;
80
- return getRow(nodeId)?.lifecycle === 'terminal' ? STEERING_TIER : tier;
81
- }
82
- /** Atomically append one finalized inbox entry to `nodes/<nodeId>/inbox.jsonl`,
83
- * at the tier `effectiveTier` resolves for this target. */
84
- export function appendInbox(nodeId, entry) {
85
- const full = finalizeInboxEntry({ ...entry, tier: effectiveTier(nodeId, entry.tier) });
86
- try {
87
- // A push pointer already carries its canonical history `ref` — its body
88
- // lives in the report file, never inline. A direct message / system notice has no ref yet: when
89
- // its inline body would clip in the digest, spill the FULL text to a durable
90
- // file (same atomic tmp+rename as a report) and point `ref` at it, exactly
91
- // like a push, then keep only the bounded preview inline. A short body is
92
- // left fully inline, untouched — uniform with how a push's body always
93
- // lives in a file, never inline, dereferenced by reading `ref`.
94
- if (full.ref === undefined) {
95
- const body = inlineBody(full);
96
- const { text, clipped } = clipBody(body);
97
- if (clipped) {
98
- full.ref = writeMessageBody(nodeId, full.ts, body);
99
- full.data = { ...full.data, body: text };
100
- }
101
- }
102
- const line = JSON.stringify(full) + '\n';
103
- // Ensure the parent directory exists (inbox.jsonl lives directly under the
104
- // node dir, which ensureNodeDirs creates — but guard anyway for callers that
105
- // haven't yet scaffolded the node).
106
- const dir = dirname(inboxPath(nodeId));
107
- if (!existsSync(dir))
108
- mkdirSync(dir, { recursive: true });
109
- // The append and matching wait settlement share SQLite's write boundary.
110
- // A re-arm therefore cannot land between this delivery and its clear.
111
- return deliverAndSettleMessageWait(nodeId, entry.from, () => {
112
- // appendFileSync is atomic within a single process (a single write(2) call for
113
- // a short line is atomic on POSIX). For multi-process safety we rely on the
114
- // OS-level append guarantee (O_APPEND) which Node honours via 'a' flag.
115
- appendFileSync(inboxPath(nodeId), line, { encoding: 'utf8', flag: 'a' });
116
- return full;
117
- });
118
- }
119
- catch (error) {
120
- emitActiveAppendFailure(nodeId, full, error);
121
- throw error;
122
- }
123
- }
124
- function emitActiveAppendFailure(nodeId, entry, error) {
125
- try {
126
- emitEvent({
127
- level: 'error',
128
- event: 'inbox.delivery.failed',
129
- operation_id: entry.operation_id,
130
- outcome: 'failed',
131
- error,
132
- error_class: errorClassFromError(error),
133
- fields: { stage: 'append', channel: 'active', target_node_id: nodeId },
134
- });
135
- }
136
- catch {
137
- // Event emission is observational; the original append failure remains authoritative.
138
- }
139
- }
140
- /** Deliver the one system notice for a controller that can no longer satisfy a
141
- * wait. Returns null when another durable operation already settled or re-armed
142
- * that wait. */
143
- export function appendControllerDeathNotice(nodeId, controller) {
144
- const full = finalizeInboxEntry({
145
- from: null,
146
- tier: 'urgent',
147
- kind: 'urgent',
148
- label: 'declared controller is no longer live',
149
- data: { body: `Controller ${controller} is no longer live; the declared message wait was cleared.` },
150
- });
151
- try {
152
- const line = JSON.stringify(full) + '\n';
153
- const dir = dirname(inboxPath(nodeId));
154
- if (!existsSync(dir))
155
- mkdirSync(dir, { recursive: true });
156
- return settleDeadMessageWait(nodeId, controller, () => {
157
- appendFileSync(inboxPath(nodeId), line, { encoding: 'utf8', flag: 'a' });
158
- return full;
159
- });
160
- }
161
- catch (error) {
162
- emitActiveAppendFailure(nodeId, full, error);
163
- throw error;
164
- }
165
- }
166
- // Read
167
- function readPhysicalInbox(nodeId) {
168
- const p = inboxPath(nodeId);
169
- if (!existsSync(p))
170
- return [];
171
- const raw = readFileSync(p, 'utf8');
172
- if (raw === '')
173
- return [];
174
- const lines = raw.split('\n');
175
- if (lines.at(-1) === '')
176
- lines.pop();
177
- if (lines.some((line) => line.trim() === ''))
178
- throw new Error('inbox contains a blank physical line');
179
- const entries = lines.map((line) => {
180
- const entry = JSON.parse(line);
181
- entry.entry_id = validateInboxEntryId(entry.entry_id);
182
- entry.operation_id = validateOperationId(entry.operation_id);
183
- return entry;
184
- });
185
- const seen = new Set();
186
- for (const entry of entries) {
187
- if (seen.has(entry.entry_id))
188
- throw new Error(`duplicate inbox entry_id: ${entry.entry_id}`);
189
- seen.add(entry.entry_id);
190
- }
191
- return entries;
192
- }
193
- function cursorIndex(entries, rawCursor) {
194
- const cursor = validateInboxEntryId(rawCursor);
195
- return entries.findIndex((entry) => entry.entry_id === cursor);
196
- }
197
- /** Return the physical suffix strictly after the exact cursor identity. */
198
- export function readInboxSince(nodeId, cursorEntryId) {
199
- const entries = readPhysicalInbox(nodeId);
200
- if (cursorEntryId === undefined)
201
- return entries;
202
- const index = cursorIndex(entries, cursorEntryId);
203
- if (index < 0) {
204
- throw new Error(`inbox cursor entry_id not found: ${validateInboxEntryId(cursorEntryId)}`);
205
- }
206
- return entries.slice(index + 1);
207
- }
208
- /** Read one exact contiguous physical range after an exclusive boundary through an inclusive boundary. */
209
- export function readInboxRange(nodeId, afterEntryId, throughEntryId) {
210
- const after = afterEntryId === undefined ? undefined : validateInboxEntryId(afterEntryId);
211
- const through = validateInboxEntryId(throughEntryId);
212
- const entries = readPhysicalInbox(nodeId);
213
- const throughIndex = entries.findIndex((entry) => entry.entry_id === through);
214
- if (throughIndex < 0)
215
- throw new Error(`inbox range through entry_id not found: ${through}`);
216
- if (after === undefined)
217
- return entries.slice(0, throughIndex + 1);
218
- const afterIndex = entries.findIndex((entry) => entry.entry_id === after);
219
- if (afterIndex < 0)
220
- throw new Error(`inbox range after entry_id not found: ${after}`);
221
- if (afterIndex >= throughIndex)
222
- throw new Error(`inbox range is reversed: ${after} through ${through}`);
223
- return entries.slice(afterIndex + 1, throughIndex + 1);
224
- }
225
- // Cancel set (interrupt-canceled entries)
226
- function canceledPath(nodeId) {
227
- return `${inboxPath(nodeId)}.canceled`;
228
- }
229
- /** Entry ids a human interrupt canceled before delivery. Append-only sidecar
230
- * (the inbox itself is append-only — never mutated in place): one entry_id
231
- * per line. The watcher skips canceled entries at plan time; a missing file
232
- * is an empty set. */
233
- export function readCanceledEntryIds(nodeId) {
234
- const p = canceledPath(nodeId);
235
- if (!existsSync(p))
236
- return new Set();
237
- const out = new Set();
238
- for (const line of readFileSync(p, 'utf8').split('\n')) {
239
- const trimmed = line.trim();
240
- if (trimmed === '')
241
- continue;
242
- out.add(validateInboxEntryId(trimmed));
243
- }
244
- return out;
245
- }
246
- /** Mark every not-yet-settled HUMAN message entry canceled — the boot-window
247
- * half of a first-class interrupt (`POST /v1/nodes/{id}/interrupt`): a send
248
- * that rode the durable inbox (dormant target, or the mid-revive fallback)
249
- * must not be injected after the human pressed Esc. Scope: `from === 'human'`
250
- * `kind: 'message'` entries ONLY — a human Esc never cancels spine/agent
251
- * messages or system notices. Entries past the durable cursor that a live
252
- * watcher already handed to the engine may be over-canceled; that is safe —
253
- * the interrupt's abort lands right behind, and covered handoffs are never
254
- * re-delivered anyway. Returns the newly canceled entry ids. */
255
- export function cancelPendingHumanMessages(nodeId) {
256
- return cancelPendingEntries(nodeId, (e) => e.from === 'human' && e.kind === 'message');
257
- }
258
- /** Cancel every not-yet-settled entry the predicate selects. Retraction, not
259
- * deletion: the append-only inbox is untouched and the sidecar grows. Safe to
260
- * call late — an entry already past the cursor is not pending, and the watcher
261
- * re-reads the set at plan time so a cancel racing a buffered entry still
262
- * lands. */
263
- export function cancelPendingEntries(nodeId, select) {
264
- const cursor = readCursor(nodeId);
265
- const pending = readInboxSince(nodeId, cursor);
266
- const already = readCanceledEntryIds(nodeId);
267
- const targets = pending
268
- .filter((e) => !already.has(e.entry_id) && select(e))
269
- .map((e) => e.entry_id);
270
- if (targets.length === 0)
271
- return [];
272
- const dir = dirname(inboxPath(nodeId));
273
- if (!existsSync(dir))
274
- mkdirSync(dir, { recursive: true });
275
- appendFileSync(canceledPath(nodeId), targets.join('\n') + '\n', { encoding: 'utf8', flag: 'a' });
276
- return targets;
277
- }
278
- // Cursor persistence
279
- /** Read and validate the persisted exact cursor identity. */
280
- export function readCursor(nodeId) {
281
- const p = cursorPath(nodeId);
282
- if (!existsSync(p))
283
- return undefined;
284
- const raw = readFileSync(p, 'utf8').trim();
285
- if (raw === '')
286
- return undefined;
287
- return validateInboxEntryId(raw);
288
- }
289
- /** Persist one validated exact cursor identity (atomic tmp+rename). */
290
- export function writeCursor(nodeId, entryId) {
291
- const value = validateInboxEntryId(entryId);
292
- const p = cursorPath(nodeId);
293
- const tmp = `${p}.tmp`;
294
- const dir = dirname(p);
295
- if (!existsSync(dir))
296
- mkdirSync(dir, { recursive: true });
297
- writeFileSync(tmp, value, 'utf8');
298
- renameSync(tmp, p);
299
- }
300
- /** Is there mail this node has not seen that would drive a turn? The one
301
- * authority for "wake-worthy inbox state", read by the park clock, the
302
- * settlement policy, the review-companion guard, and reviveNode's parked
303
- * re-entry rule. Deferred entries are excluded everywhere for the same
304
- * reason: a deferred entry rides the next natural cycle and never starts one.
305
- * A cursor that no longer resolves against the physical inbox is a fault, not
306
- * an answer — every caller's conservative reading of this predicate is `true`
307
- * (do not park, do not shut down, do not resume quietly), so report it and
308
- * return that. */
309
- export function hasUnseenWakeMail(nodeId) {
310
- try {
311
- return readInboxSince(nodeId, readCursor(nodeId)).some((entry) => entry.tier !== 'deferred');
312
- }
313
- catch (error) {
314
- emitEvent({ level: 'warn', event: 'inbox.wake_scan.failed', node_id: nodeId, error });
315
- return true;
316
- }
317
- }
318
- /** Whether wake-capable mail arrived at or after a parking turn began. Unlike
319
- * `hasUnseenWakeMail`, this deliberately includes mail the main engine already
320
- * consumed: a shadow completion must not undo activity that occurred while it
321
- * ran. A failed scan conservatively keeps the node live. */
322
- export function hasWakeMailSince(nodeId, sinceMs) {
323
- try {
324
- return readPhysicalInbox(nodeId).some((entry) => entry.tier !== 'deferred' && Date.parse(entry.ts) >= sinceMs);
325
- }
326
- catch (error) {
327
- emitEvent({ level: 'warn', event: 'inbox.wake_scan.failed', node_id: nodeId, error });
328
- return true;
329
- }
330
- }
331
- // Coalesce
332
- /** Bounds for inlining a message body's preview in the digest. */
333
28
  const BODY_MAX_LINES = 12;
334
29
  const BODY_MAX_CHARS = 1000;
335
30
  const INLINE_REPORT_MAX_CHARS = 1000;
336
- /** The inline body an entry carries (a direct msg / system alert), trimmed.
337
- * Empty for a push pointer (whose body lives at `ref`) or a one-line entry
338
- * whose `label` already IS the whole message. */
339
- function inlineBody(e) {
340
- const body = typeof e.data?.['body'] === 'string' ? e.data['body'].trim() : '';
341
- return body === '' || body === e.label ? '' : body;
342
- }
343
- /** Clip a body to a bounded preview, reporting whether anything was dropped. */
31
+ /** Clip a body to the bounded preview stored beside a spilled message body. */
344
32
  export function clipBody(body) {
345
33
  let text = body;
346
34
  let clipped = false;
@@ -355,83 +43,65 @@ export function clipBody(body) {
355
43
  }
356
44
  return { text: text.trimEnd(), clipped };
357
45
  }
358
- /** Resolve a short pushed report body for inlining. Larger reports stay
359
- * pointers, carried by the entry's `ref` attribute alone. */
360
- function inlineReport(e) {
361
- if (e.ref === undefined || e.ref.startsWith('/'))
46
+ function inlineBody(entry) {
47
+ const body = typeof entry.data?.['body'] === 'string' ? entry.data['body'].trim() : '';
48
+ return body === '' || body === entry.label ? '' : body;
49
+ }
50
+ function inlineReport(entry) {
51
+ if (entry.ref === undefined || entry.ref.startsWith('/'))
362
52
  return null;
363
53
  try {
364
- const report = resolveRef(e.ref);
54
+ const report = resolveRef(entry.ref);
365
55
  if (report?.source !== 'report' || report.path === null || report.body.length >= INLINE_REPORT_MAX_CHARS)
366
56
  return null;
367
57
  return report.body;
368
58
  }
369
59
  catch {
370
- // A malformed or unavailable history ref remains a pointer in the digest.
371
60
  return null;
372
61
  }
373
62
  }
374
- /**
375
- * One entry's card body.
376
- *
377
- * A short pushed report is inlined as its own body; the label is NOT repeated
378
- * above it, because a report's label is just its first line (feed.ts
379
- * `firstLine`), so carrying both reads as a truncated preview stacked on the
380
- * full text it previews. A larger report has only its label, with `ref`
381
- * carrying the pointer. Direct messages keep their bounded inline-body/spill
382
- * behavior.
383
- */
384
- function entryBody(e) {
385
- const birth = renderBirthAnnouncementBody(e.data, e.ts);
63
+ function entryBody(entry) {
64
+ const birth = renderBirthAnnouncementBody(entry.data, entry.ts);
386
65
  if (birth !== null)
387
66
  return birth;
388
- const report = inlineReport(e);
67
+ const report = inlineReport(entry);
389
68
  if (report !== null)
390
- return `${report.trimEnd()}\n\n${formatInboxRefInstruction(e.ref)}`;
391
- const body = inlineBody(e);
69
+ return `${report.trimEnd()}\n\n${formatInboxRefInstruction(entry.ref)}`;
70
+ const body = inlineBody(entry);
392
71
  if (body === '')
393
- return e.ref === undefined ? e.label : `${e.label}\n\n${formatInboxRefInstruction(e.ref)}`;
72
+ return entry.ref === undefined ? entry.label : `${entry.label}\n\n${formatInboxRefInstruction(entry.ref)}`;
394
73
  const { text, clipped } = clipBody(body);
395
- if (e.ref !== undefined)
396
- return `${text}\n\n${formatInboxRefInstruction(e.ref)}`;
74
+ if (entry.ref !== undefined)
75
+ return `${text}\n\n${formatInboxRefInstruction(entry.ref)}`;
397
76
  return clipped ? `${text}\n… (body clipped)` : text;
398
77
  }
399
- function cardEntry(e) {
78
+ function cardEntry(entry) {
400
79
  return {
401
- kind: e.kind,
402
- body: entryBody(e),
403
- ...(e.ref === undefined ? {} : { ref: e.ref }),
404
- ...(e.disposition === undefined ? {} : { disposition: e.disposition }),
80
+ kind: entry.kind,
81
+ body: entryBody(entry),
82
+ ...(entry.ref === undefined ? {} : { ref: entry.ref }),
83
+ ...(entry.disposition === undefined ? {} : { disposition: entry.disposition }),
405
84
  };
406
85
  }
407
- /** Split unread inbox pointers into ordered deliveries: each human entry
408
- * verbatim, and every other sender in one `<runtime kind="inbox">` card. */
86
+ /** Split delivered entries into verbatim human messages and sender-grouped inbox cards. */
409
87
  export function coalesce(entries) {
410
88
  if (entries.length === 0)
411
89
  return [];
412
- // Group by `from` (null → 'system').
413
90
  const groups = new Map();
414
- for (const e of entries) {
415
- const key = e.from ?? 'system';
416
- if (!groups.has(key))
417
- groups.set(key, []);
418
- groups.get(key).push(e);
91
+ for (const entry of entries) {
92
+ const sender = entry.from ?? 'system';
93
+ if (!groups.has(sender))
94
+ groups.set(sender, []);
95
+ groups.get(sender).push(entry);
419
96
  }
420
- // Human words stay outside the card, so a batch mixing both keeps the card
421
- // where the first node sender fell rather than reordering the digest.
422
97
  const deliveries = [];
423
98
  const sections = [];
424
99
  let cardSlot = -1;
425
100
  for (const [sender, items] of groups) {
426
- // A human typing directly into a node's box is a CONVERSATION TURN, not a
427
- // notification the node is being woken to triage. Deliver those words
428
- // verbatim as the woken turn's user message — no card, no `<from>` section.
429
- // This makes the dormant path identical to the live path (type "hi" → the
430
- // node just reads "hi"). Every other sender is digest content.
431
101
  if (sender === 'human') {
432
- for (const e of items) {
433
- const body = inlineBody(e);
434
- deliveries.push({ kind: 'human', text: body === '' ? e.label : body, entries: [e] });
102
+ for (const entry of items) {
103
+ const body = inlineBody(entry);
104
+ deliveries.push({ kind: 'human', text: body === '' ? entry.label : body, entries: [entry] });
435
105
  }
436
106
  continue;
437
107
  }
@@ -442,12 +112,10 @@ export function coalesce(entries) {
442
112
  sections.push({ id: sender, entries: items.map(cardEntry) });
443
113
  }
444
114
  if (cardSlot >= 0) {
445
- // Card sections group by sender, so its entry list comes from the input
446
- // array instead, keeping physical inbox order.
447
115
  deliveries[cardSlot] = {
448
116
  kind: 'card',
449
117
  text: formatInboxCard(sections),
450
- entries: entries.filter((e) => (e.from ?? 'system') !== 'human'),
118
+ entries: entries.filter((entry) => (entry.from ?? 'system') !== 'human'),
451
119
  };
452
120
  }
453
121
  return deliveries;
@@ -0,0 +1,58 @@
1
+ import { type InboxDisposition, type InboxKind, type InboxTier } from './inbox.js';
2
+ export type { InboxDisposition, InboxKind, InboxTier } from './inbox.js';
3
+ export interface NodeMailInput {
4
+ kind: InboxKind;
5
+ label: string;
6
+ senderNodeId?: string | null;
7
+ senderName?: string | null;
8
+ bodyRef?: string | null;
9
+ bodyJson?: unknown;
10
+ disposition?: InboxDisposition | null;
11
+ urgency: InboxTier;
12
+ wake: 0 | 1;
13
+ operationId?: string;
14
+ /** Optional caller-owned obligation identity, used by cron settlement replay. */
15
+ idempotencyKey?: string;
16
+ }
17
+ export interface ExternalActionAdapter {
18
+ adapter: 'subprocess' | 'cron_spawn' | 'cron_human';
19
+ [key: string]: unknown;
20
+ }
21
+ export interface ExternalActionInput {
22
+ destinationId: string;
23
+ adapter: ExternalActionAdapter;
24
+ bodyJson: unknown;
25
+ idempotencyKey: string;
26
+ label?: string;
27
+ kind?: InboxKind;
28
+ }
29
+ export interface EnqueuedNodeMail {
30
+ messageId: string;
31
+ deliveryId: string;
32
+ urgency: InboxTier;
33
+ createdAt: string;
34
+ }
35
+ export interface EnqueuedExternalAction {
36
+ messageId: string;
37
+ deliveryId: string;
38
+ }
39
+ type Subscriber = {
40
+ node_id: string;
41
+ active: boolean;
42
+ };
43
+ /** Insert one immutable message and one node-inbox obligation into the caller's write transaction. */
44
+ export declare function enqueueNodeMail(destinationNodeId: string, input: NodeMailInput): EnqueuedNodeMail;
45
+ /** Fan out one immutable report message to the supplied subscriber snapshot. */
46
+ export declare function enqueueSubscriberMail(subscribers: readonly Subscriber[], input: Omit<NodeMailInput, 'wake'>): Array<EnqueuedNodeMail & {
47
+ destinationNodeId: string;
48
+ }>;
49
+ /** Insert one frozen external-action obligation. A duplicate idempotency key returns its original identity. */
50
+ export declare function enqueueExternalAction(input: ExternalActionInput): EnqueuedExternalAction;
51
+ /** Retract an obligation without fabricating a terminal receipt. */
52
+ export declare function cancelDelivery(deliveryId: string, now?: number): boolean;
53
+ export declare function cancelPendingNodeMail(nodeId: string, select?: {
54
+ senderNodeId?: string | null;
55
+ kind?: InboxKind;
56
+ }): string[];
57
+ /** Enqueue the one notice only if the same live controller wait is still dead. */
58
+ export declare function enqueueControllerDeathNotice(nodeId: string, controllerId: string): EnqueuedNodeMail | null;