@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
@@ -8,15 +8,14 @@ import { contextDir, isSafeNodeId } from '../../core/canvas/paths.js';
8
8
  import { fullName } from '../../core/canvas/labels.js';
9
9
  import { recordedPidLiveness } from '../../core/canvas/pid.js';
10
10
  import { transition } from '../../core/runtime/lifecycle.js';
11
- import { isBusy, busySince } from '../../core/runtime/busy.js';
12
- import { readFault, recordFault } from '../../core/runtime/fault.js';
11
+ import { readFault, readLiveTurn, recordFault } from '../../core/canvas/recovery.js';
13
12
  import { nextFaultRetry } from '../../core/runtime/fault-recovery.js';
14
13
  import { fanDoctrineWake } from '../../core/runtime/close.js';
15
14
  import { headlessBrokerHost } from '../../core/runtime/host.js';
16
15
  import { reloadAuthLive } from '../../core/runtime/auth-reload.js';
17
16
  import { emitEvent } from '../../core/events/emit.js';
18
17
  import { operationIdContext } from '../../core/events/operation-id.js';
19
- import { hasUnseenWakeMail } from '../../core/feed/inbox.js';
18
+ import { hasUnseenWakeMail, resetStaleExecutionMail } from '../../core/canvas/deliveries.js';
20
19
  import { activeBackgroundBashJobs } from '../../core/bash-jobs.js';
21
20
  import { focusOf } from '../../core/runtime/placement.js';
22
21
  import { hasAttachedBrokerViewers } from '../../core/runtime/broker/client-registry.js';
@@ -25,18 +24,32 @@ import { BrokerUnreachableError } from '../../core/runtime/broker-request.js';
25
24
  import { formatCard, PARK_SUMMARY_PROMPT } from '../../shared/generated-context.js';
26
25
  import { brokerThresholdsForDaemon } from '../../core/runtime/fleet.js';
27
26
  import { readConfig } from '../../core/config.js';
27
+ import { scopedStoreExists } from '../../core/scoped-state/db.js';
28
+ import { providerStore } from '../../core/scoped-state/paths.js';
29
+ import { providerRevision } from '../../core/scoped-state/providers.js';
28
30
  import { beginPendingPark, consumePendingPark, pendingParkIds, pendingParkStartedAt } from '../park-pending.js';
29
31
  import { shouldParkAfterSummary } from '../park-activity.js';
30
32
  import { applyParkEvent, parkEvent } from './live-obligation.js';
31
33
  import { envNoDaemonAutostart, envTestUnattendedParkMs, parkSummaryGraceMs, envUnattendedParkMs, } from '../../shared/env.js';
32
34
  const execFileAsync = promisify(execFile);
35
+ function liveBusy(nodeId) {
36
+ return readLiveTurn(nodeId)?.busy === true;
37
+ }
38
+ function liveTurnProgressMs(nodeId) {
39
+ const progressAt = readLiveTurn(nodeId)?.progressAt;
40
+ if (progressAt === null || progressAt === undefined)
41
+ return null;
42
+ const progressMs = Date.parse(progressAt);
43
+ return Number.isNaN(progressMs) ? null : progressMs;
44
+ }
33
45
  function incarnationOf(pid, identity) {
34
46
  return identity === null ? null : `${pid}:${identity}`;
35
47
  }
36
48
  function busySinceForIncarnation(row) {
37
- const since = busySince(row.node_id);
49
+ const turn = readLiveTurn(row.node_id);
50
+ const since = turn?.busy === true && turn.progressAt !== null ? Date.parse(turn.progressAt) : Number.NaN;
38
51
  const launchedAt = row.launched_at === null || row.launched_at === undefined ? Number.NaN : Date.parse(row.launched_at);
39
- if (since === null || !Number.isFinite(launchedAt) || since < launchedAt)
52
+ if (!Number.isFinite(since) || !Number.isFinite(launchedAt) || since < launchedAt)
40
53
  return null;
41
54
  return since;
42
55
  }
@@ -131,13 +144,17 @@ export function treeHasDescendants(psOutput, rootPid) {
131
144
  return null;
132
145
  return (childrenOf.get(rootPid) ?? []).length > 0;
133
146
  }
134
- function piCredentialPaths() {
135
- const env = process.env['PI_CODING_AGENT_DIR'];
136
- const agentDir = env !== undefined && env !== '' ? env : join(homedir(), '.pi', 'agent');
147
+ function piAgentDir() {
148
+ const configured = process.env['PI_CODING_AGENT_DIR'];
149
+ if (configured !== undefined && configured !== '')
150
+ return configured === '~' || configured.startsWith('~/') ? join(homedir(), configured.slice(1)) : configured;
151
+ return join(homedir(), '.pi', 'agent');
152
+ }
153
+ function piCredentialPaths(agentDir) {
137
154
  return [
138
155
  join(agentDir, 'auth.json'),
139
- join(agentDir, 'anthropic-subscriptions.json'),
140
- join(agentDir, 'openai-codex-subscriptions.json'),
156
+ join(agentDir, 'anthropic-credentials.json'),
157
+ join(agentDir, 'openai-codex-credentials.json'),
141
158
  ];
142
159
  }
143
160
  export class BrokerSupervisionReconciler {
@@ -147,7 +164,7 @@ export class BrokerSupervisionReconciler {
147
164
  unattendedSince = new Map();
148
165
  sampleTree;
149
166
  signal;
150
- knownAuthMtimeMs = null;
167
+ knownCredentialRevision = null;
151
168
  lastOverCapWarnAt = Number.NEGATIVE_INFINITY;
152
169
  constructor(options = {}) {
153
170
  this.sampleTree = options.readTreeSample ?? ((pid) => this.readTreeSample(pid));
@@ -209,6 +226,7 @@ export class BrokerSupervisionReconciler {
209
226
  if (!ctx.fleet.has(id))
210
227
  consumePendingPark(id);
211
228
  }
229
+ resetStaleExecutionMail(now);
212
230
  }
213
231
  /** Every condition that resets the unattended clock EXCEPT `isBusy` — the
214
232
  * node is unavailable to park, attended, or has work waiting. Short-circuits
@@ -278,7 +296,7 @@ export class BrokerSupervisionReconciler {
278
296
  headlessBrokerHost.teardown(id);
279
297
  return;
280
298
  }
281
- if (reset || isBusy(id)) {
299
+ if (reset || liveBusy(id)) {
282
300
  this.unattendedSince.delete(id);
283
301
  return;
284
302
  }
@@ -682,8 +700,9 @@ export class BrokerSupervisionReconciler {
682
700
  });
683
701
  }
684
702
  async handleAuthReload(fleet) {
703
+ const agentDir = piAgentDir();
685
704
  let mtimeMs = 0;
686
- for (const path of piCredentialPaths()) {
705
+ for (const path of piCredentialPaths(agentDir)) {
687
706
  try {
688
707
  mtimeMs = Math.max(mtimeMs, statSync(path).mtimeMs);
689
708
  }
@@ -691,15 +710,18 @@ export class BrokerSupervisionReconciler {
691
710
  /* not written yet — nothing to observe for this file */
692
711
  }
693
712
  }
694
- if (mtimeMs === 0)
695
- return;
696
- if (this.knownAuthMtimeMs === null) {
697
- this.knownAuthMtimeMs = mtimeMs;
713
+ let revision = 0;
714
+ const store = providerStore(agentDir);
715
+ if (scopedStoreExists(store))
716
+ revision = providerRevision(agentDir);
717
+ const current = `${mtimeMs}:${revision}`;
718
+ if (this.knownCredentialRevision === null) {
719
+ this.knownCredentialRevision = current;
698
720
  return;
699
721
  }
700
- if (mtimeMs === this.knownAuthMtimeMs)
722
+ if (current === this.knownCredentialRevision)
701
723
  return;
702
- this.knownAuthMtimeMs = mtimeMs;
724
+ this.knownCredentialRevision = current;
703
725
  await operationIdContext.fresh(async () => {
704
726
  try {
705
727
  const live = Array.from(fleet.entries(), ([id]) => id);
@@ -1,25 +1,13 @@
1
- import { getRow } from '../../core/canvas/index.js';
1
+ import { liveControllerWaits } from '../../core/canvas/waits.js';
2
2
  import { operationIdContext } from '../../core/events/operation-id.js';
3
- import { appendControllerDeathNotice } from '../../core/feed/inbox.js';
3
+ import { enqueueControllerDeathNotice } from '../../core/feed/messages.js';
4
4
  export class ControllerDeathReconciler {
5
5
  run(now, ctx) {
6
- void now; // no time-gating here — the death check is already-controller-status-driven; the param is kept for six-way call-site uniformity, matching every other reconciler's run(now, ctx) shape.
7
- // Settle every wait whose controller has ended, independent of the waiter's
8
- // current status. Active and focused terminal nodes own a live inbox
9
- // watcher; an idle-released node is revived by the node-lifecycle tick.
10
- for (const row of ctx.rows) {
11
- const current = getRow(row.node_id);
12
- const controllerId = current?.waiting_for;
13
- if (controllerId === null || controllerId === undefined)
14
- continue;
15
- // Read-gate before the write boundary: only a dead controller warrants a
16
- // notice. appendControllerDeathNotice re-checks liveness inside its
17
- // transaction, so this gate cannot misfire.
18
- const controller = getRow(controllerId);
19
- if (controller !== null && (controller.status === 'active' || controller.status === 'idle'))
20
- continue;
6
+ void now;
7
+ void ctx;
8
+ for (const wait of liveControllerWaits()) {
21
9
  operationIdContext.fresh(() => {
22
- appendControllerDeathNotice(row.node_id, controllerId);
10
+ enqueueControllerDeathNotice(wait.nodeId, wait.controllerId);
23
11
  });
24
12
  }
25
13
  }
@@ -0,0 +1,9 @@
1
+ import type { DetachedWorkLifecycle } from './broker-supervision.js';
2
+ export declare const deliveryDaemonInstanceId: `${string}-${string}-${string}-${string}-${string}`;
3
+ export interface DeliveryLaneContext {
4
+ lifecycle: DetachedWorkLifecycle;
5
+ }
6
+ /** The one daemon lane for every configured external action. Node-inbox rows are claimed by their destination. */
7
+ export declare class DeliveryLaneReconciler {
8
+ run(now: number, ctx: DeliveryLaneContext): void;
9
+ }
@@ -0,0 +1,130 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { rmSync } from 'node:fs';
3
+ import { claimExternalActionDelivery, dueExternalActionDeliveries, messageBodyForDelivery, settleDelivery } from '../../core/canvas/deliveries.js';
4
+ import { getNode } from '../../core/canvas/canvas.js';
5
+ import { emitEvent } from '../../core/events/emit.js';
6
+ import { operationIdContext } from '../../core/events/operation-id.js';
7
+ import { createPageRequest, getRequest } from '../../core/human/requests.js';
8
+ import { synthesizedPageDocument, writeSynthesizedPage } from '../../core/human/page-synth.js';
9
+ import { preparePage } from '../../core/human/page-authoring.js';
10
+ import { resolvePageComponents } from '../../core/config.js';
11
+ import { spawnChild } from '../../core/runtime/spawn.js';
12
+ import { runDelivery } from '../delivery/run-delivery.js';
13
+ export const deliveryDaemonInstanceId = randomUUID();
14
+ function adapterFor(delivery) {
15
+ if (delivery.adapter === null || typeof delivery.adapter['adapter'] !== 'string') {
16
+ throw new Error(`delivery ${delivery.deliveryId} has no adapter`);
17
+ }
18
+ return delivery.adapter;
19
+ }
20
+ function failed(delivery, owner, message) {
21
+ settleDelivery(delivery.deliveryId, delivery.attempts, owner, {
22
+ state: 'permanent_failed', failure: { kind: 'spawn_error', message },
23
+ }, Date.now());
24
+ }
25
+ async function deliverSpawn(delivery, owner, adapter) {
26
+ const existing = getNode(adapter.nodeId);
27
+ if (existing !== null) {
28
+ if (existing.frozen_at !== null || existing.pi_session_id !== null || existing.status === 'idle' || existing.status === 'done' || existing.status === 'canceled') {
29
+ settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
30
+ return;
31
+ }
32
+ failed(delivery, owner, existing.status === 'dead'
33
+ ? `cron spawn ${adapter.nodeId} recorded a failed birth`
34
+ : `cron spawn ${adapter.nodeId} found an incomplete birth`);
35
+ return;
36
+ }
37
+ try {
38
+ await spawnChild({
39
+ nodeId: adapter.nodeId, kind: adapter.kind, cwd: adapter.cwd, prompt: adapter.prompt,
40
+ ...(adapter.root ? { root: true, rootLifecycle: adapter.rootLifecycle } : { parent: adapter.parent }),
41
+ readiness: 'launch', profile: adapter.profile, wakeOrigin: adapter.wakeOrigin,
42
+ });
43
+ settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
44
+ }
45
+ catch (error) {
46
+ failed(delivery, owner, `cron spawn ${adapter.nodeId} failed: ${error instanceof Error ? error.message : String(error)}`);
47
+ operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.cron_spawn_failed', node_id: adapter.nodeId, error, fields: { delivery_id: delivery.deliveryId } }));
48
+ }
49
+ }
50
+ function preparedCronPage(adapter, stdout) {
51
+ const synthesized = writeSynthesizedPage(synthesizedPageDocument(adapter.title, adapter.summary, stdout, false));
52
+ try {
53
+ return preparePage({
54
+ sourceFile: synthesized.path,
55
+ source: { sessionName: adapter.sessionName, ...(adapter.profileName === null ? {} : { profileName: adapter.profileName }) },
56
+ productKinds: resolvePageComponents(),
57
+ delivery: { placement: 'inline', inbox: true, reply: false },
58
+ });
59
+ }
60
+ finally {
61
+ rmSync(synthesized.dir, { recursive: true, force: true });
62
+ }
63
+ }
64
+ function deliverHuman(delivery, owner, adapter, body) {
65
+ if (getRequest(adapter.requestId) !== null) {
66
+ settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
67
+ return;
68
+ }
69
+ const stdout = typeof body?.['stdout'] === 'string'
70
+ ? body['stdout']
71
+ : null;
72
+ if (stdout === null) {
73
+ failed(delivery, owner, `cron human ${adapter.requestId} has no frozen stdout`);
74
+ return;
75
+ }
76
+ try {
77
+ createPageRequest({ request_id: adapter.requestId, prepared: preparedCronPage(adapter, stdout) });
78
+ settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
79
+ }
80
+ catch (error) {
81
+ failed(delivery, owner, `cron human ${adapter.requestId} failed: ${error instanceof Error ? error.message : String(error)}`);
82
+ }
83
+ }
84
+ async function deliverExternal(delivery, owner) {
85
+ const adapter = adapterFor(delivery);
86
+ const body = messageBodyForDelivery(delivery.deliveryId);
87
+ if (body === null) {
88
+ failed(delivery, owner, `delivery ${delivery.deliveryId} has no message body`);
89
+ return;
90
+ }
91
+ if (adapter.adapter === 'subprocess') {
92
+ await runDelivery({ argv: adapter.argv, cwd: adapter.cwd, document: body, attempt: delivery.attempts }, (settlement) => {
93
+ settleDelivery(delivery.deliveryId, delivery.attempts, owner, settlement, Date.now());
94
+ });
95
+ return;
96
+ }
97
+ if (adapter.adapter === 'cron_spawn') {
98
+ await deliverSpawn(delivery, owner, adapter);
99
+ return;
100
+ }
101
+ deliverHuman(delivery, owner, adapter, body);
102
+ }
103
+ /** The one daemon lane for every configured external action. Node-inbox rows are claimed by their destination. */
104
+ export class DeliveryLaneReconciler {
105
+ run(now, ctx) {
106
+ if (!ctx.lifecycle.acceptsDetachedWork())
107
+ return;
108
+ let due;
109
+ try {
110
+ due = dueExternalActionDeliveries(now);
111
+ }
112
+ catch (error) {
113
+ operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.scan_failed', error }));
114
+ return;
115
+ }
116
+ for (const candidate of due) {
117
+ try {
118
+ const claimed = claimExternalActionDelivery(candidate.deliveryId, now, deliveryDaemonInstanceId);
119
+ if (claimed === null)
120
+ continue;
121
+ ctx.lifecycle.registerDetached(deliverExternal(claimed, deliveryDaemonInstanceId).catch((error) => {
122
+ operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.failed', error, fields: { delivery_id: claimed.deliveryId } }));
123
+ }));
124
+ }
125
+ catch (error) {
126
+ operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.claim_failed', error, fields: { delivery_id: candidate.deliveryId } }));
127
+ }
128
+ }
129
+ }
130
+ }
@@ -1,12 +1,12 @@
1
1
  import { newestReportRef, hasActiveLiveSubscription, hasLiveMessageWait, hasLiveSubscriber, hasPendingCronWake, subscribersOf, } from '../../core/canvas/index.js';
2
2
  import { fullName } from '../../core/canvas/labels.js';
3
3
  import { countTickets } from '../../core/canvas/attention.js';
4
- import { hasUnseenWakeMail, readCursor, readInboxSince } from '../../core/feed/inbox.js';
4
+ import { hasUnseenWakeMail } from '../../core/canvas/deliveries.js';
5
5
  import { readOutputRequest } from '../../core/runtime/structured-output.js';
6
6
  import { focusOf } from '../../core/runtime/placement.js';
7
7
  import { hasAttachedBrokerViewers } from '../../core/runtime/broker/client-registry.js';
8
8
  import { fanDoctrineWake } from '../../core/runtime/close.js';
9
- import { readFault } from '../../core/runtime/fault.js';
9
+ import { readFault } from '../../core/canvas/recovery.js';
10
10
  import { transition } from '../../core/runtime/lifecycle.js';
11
11
  /** Durable work that requires a node to remain automatically wakeable. */
12
12
  export function hasLiveObligation(nodeId) {
@@ -56,7 +56,7 @@ export function parkEvent(nodeId, lifecycle) {
56
56
  return 'release';
57
57
  if (lifecycle === 'resident')
58
58
  return hasUnseenWakeMail(nodeId) ? 'release' : 'park';
59
- if (readInboxSince(nodeId, readCursor(nodeId)).length > 0)
59
+ if (hasUnseenWakeMail(nodeId))
60
60
  return 'release';
61
61
  return hasLiveSubscriber(nodeId) ? 'release' : 'cancel';
62
62
  }
@@ -4,7 +4,7 @@ import { isProfilePaused } from '../../core/profiles/manifest.js';
4
4
  import { operationIdContext } from '../../core/events/operation-id.js';
5
5
  import { errorClassFromFault } from '../../core/fault-classifier.js';
6
6
  import { fanDoctrineWake } from '../../core/runtime/close.js';
7
- import { readFault } from '../../core/runtime/fault.js';
7
+ import { readFault } from '../../core/canvas/recovery.js';
8
8
  import { requestBrokerTeardown } from '../../core/runtime/host.js';
9
9
  import { transition } from '../../core/runtime/lifecycle.js';
10
10
  /** Enforce absolute spawn deadlines independently of the tick's live-row scan. */
@@ -39,7 +39,7 @@ export declare function compareThaw(a: ThawEntry, b: ThawEntry): number;
39
39
  export declare class FreezeLaneReconciler {
40
40
  private readonly relaunchState;
41
41
  /** End a stuck-delivery episode the tick decided is over — the node's profile
42
- * paused, or nothing wake-worthy is left in its inbox. */
42
+ * paused, or it has no wake-worthy mail left. */
43
43
  endEpisode(nodeId: string): void;
44
44
  run(now: number, ctx: FreezeLaneContext): Promise<FrozenCounts>;
45
45
  private enact;
@@ -13,16 +13,14 @@ import { fullName } from '../../../core/canvas/labels.js';
13
13
  import { isSafeNodeId } from '../../../core/canvas/paths.js';
14
14
  import { emitEvent } from '../../../core/events/emit.js';
15
15
  import { operationIdContext } from '../../../core/events/operation-id.js';
16
- import { readCursor } from '../../../core/feed/inbox.js';
16
+ import { acknowledgedNodeMailCount } from '../../../core/canvas/deliveries.js';
17
17
  import { fanDoctrineWake } from '../../../core/runtime/close.js';
18
18
  import { transition } from '../../../core/runtime/lifecycle.js';
19
19
  import { reviveNode } from '../../../core/runtime/revive.js';
20
20
  import { hasPendingWake } from '../live-obligation.js';
21
- // Idle-release/inbox retry cap (2026-07-10 diagnosis, round 2): the SAME
22
- // livelock shape as the stranded-relaunch case, but on the pass that resumes a
23
- // dormant idle-released node when its inbox gains an unseen entry. The counter
24
- // is keyed on inbox-cursor progress, not row-state residency or pid liveness: a
25
- // transiently-live broker does not prove that delivery completed.
21
+ // Idle-release delivery retry cap. The counter is keyed on acknowledged
22
+ // delivery progress, not row-state residency or pid liveness: a transiently
23
+ // live broker does not prove that a destination received its mail.
26
24
  const IDLE_RELEASE_RELAUNCH_MAX_ATTEMPTS = 6;
27
25
  const IDLE_RELEASE_RELAUNCH_MAX_BACKOFF_MS = 10 * 60_000;
28
26
  const IDLE_RELEASE_RELAUNCH_BASE_MS = 20_000;
@@ -57,7 +55,7 @@ export function compareThaw(a, b) {
57
55
  export class FreezeLaneReconciler {
58
56
  relaunchState = new Map();
59
57
  /** End a stuck-delivery episode the tick decided is over — the node's profile
60
- * paused, or nothing wake-worthy is left in its inbox. */
58
+ * paused, or it has no wake-worthy mail left. */
61
59
  endEpisode(nodeId) {
62
60
  this.relaunchState.delete(nodeId);
63
61
  }
@@ -118,10 +116,10 @@ export class FreezeLaneReconciler {
118
116
  }
119
117
  async enactIdleRelease(now, row) {
120
118
  const id = row.node_id;
121
- const cursorNow = readCursor(id);
119
+ const acknowledgedCount = acknowledgedNodeMailCount(id);
122
120
  const prior = this.relaunchState.get(id);
123
- const cursorChanged = prior === undefined || prior.cursor !== cursorNow;
124
- const attempts = cursorChanged ? 0 : prior.attempts;
121
+ const deliveryProgressed = prior === undefined || prior.acknowledgedCount !== acknowledgedCount;
122
+ const attempts = deliveryProgressed ? 0 : prior.attempts;
125
123
  if (attempts >= IDLE_RELEASE_RELAUNCH_MAX_ATTEMPTS) {
126
124
  this.relaunchState.delete(id);
127
125
  emitEvent({
@@ -148,7 +146,7 @@ export class FreezeLaneReconciler {
148
146
  `dead until you revive it — \`crtr node lifecycle revive ${id}\`.`, { reason: 'child-crashed', child: id });
149
147
  return 'skipped';
150
148
  }
151
- const lastAttempt = cursorChanged ? undefined : prior?.lastAttempt;
149
+ const lastAttempt = deliveryProgressed ? undefined : prior?.lastAttempt;
152
150
  const backoff = Math.min(IDLE_RELEASE_RELAUNCH_BASE_MS * 2 ** attempts, IDLE_RELEASE_RELAUNCH_MAX_BACKOFF_MS);
153
151
  if (lastAttempt !== undefined && now - lastAttempt < backoff)
154
152
  return 'skipped';
@@ -157,7 +155,7 @@ export class FreezeLaneReconciler {
157
155
  // actually happened advances the episode toward exhaustion.
158
156
  if ((await reviveNode(id, { resume: true, capacity: 'freeze' })).outcome === 'frozen')
159
157
  return 'frozen';
160
- this.relaunchState.set(id, { cursor: cursorNow, attempts: attempts + 1, lastAttempt: now });
158
+ this.relaunchState.set(id, { acknowledgedCount, attempts: attempts + 1, lastAttempt: now });
161
159
  emitEvent({
162
160
  level: 'info',
163
161
  event: 'broker.revive.idle_release',
@@ -7,12 +7,12 @@
7
7
  // to the freeze lane, which orders them by tier and spends the free slots in
8
8
  // that order. Splitting the phases is what makes the tier order mean anything —
9
9
  // a single interleaved pass would give slots out in row-creation order.
10
- import { clearFrozen, clearRecordedPid, getNode, } from '../../../core/canvas/index.js';
10
+ import { clearFrozen, endLiveExecution, getNode, } from '../../../core/canvas/index.js';
11
11
  import { isSafeNodeId } from '../../../core/canvas/paths.js';
12
12
  import { recordedPidLiveness } from '../../../core/canvas/pid.js';
13
13
  import { emitEvent } from '../../../core/events/emit.js';
14
14
  import { operationIdContext } from '../../../core/events/operation-id.js';
15
- import { hasCleanAbort, isBusy } from '../../../core/runtime/busy.js';
15
+ import { readInterruptedTurn } from '../../../core/canvas/recovery.js';
16
16
  import { isProfilePaused } from '../../../core/profiles/manifest.js';
17
17
  import { classifyDeadNode } from '../../fleet.js';
18
18
  import { applyParkEvent, hasPendingWake, parkEvent } from '../live-obligation.js';
@@ -51,7 +51,7 @@ export class NodeLifecycleReconciler {
51
51
  if (handleTerminatingRow(row, ctx.fleet.has(id)))
52
52
  return;
53
53
  if (row.pi_pid != null && recordedPidLiveness(row.pi_pid, row.pi_pid_identity) === 'dead') {
54
- clearRecordedPid(id);
54
+ endLiveExecution(id, { reason: 'observed-dead' });
55
55
  }
56
56
  if (row.frozen_at != null)
57
57
  candidates.push({ row, enact: 'thaw' });
@@ -114,11 +114,11 @@ export class NodeLifecycleReconciler {
114
114
  // its marker without failure or launch, and retaining the PID lets the
115
115
  // second recognize the same dead row for ordinary recovery.
116
116
  if (!ctx.fleet.hasProfilePauseInterruption(id))
117
- clearRecordedPid(id);
117
+ endLiveExecution(id, { reason: 'observed-dead' });
118
118
  // 7. The invariant-5 policy table, on the same inputs an exit event gives
119
119
  // it. 8. A verdict that needs no broker is enacted right here, because it
120
120
  // costs no slot; a respawn becomes a candidate for the freeze lane.
121
- const action = classifyDeadNode(getNode(id), isBusy(id) || hasCleanAbort(id), hasPendingWake(id));
121
+ const action = classifyDeadNode(getNode(id), readInterruptedTurn(id)?.interrupted === true, hasPendingWake(id));
122
122
  if (action === 'respawn-fresh' || action === 'respawn-cycle' || action === 'respawn-resume') {
123
123
  candidates.push({ row, enact: 'policy' });
124
124
  return;
@@ -1,4 +1,4 @@
1
- import { hasUnseenWakeMail } from '../../../core/feed/inbox.js';
1
+ import { hasUnseenWakeMail } from '../../../core/canvas/deliveries.js';
2
2
  /** Whether the lifecycle tick must ensure this row has an engine. The durable
3
3
  * message handler calls this after appending its entry: that append makes the
4
4
  * idle branch's unseen-wake-mail check true for a wake-capable entry. */
@@ -8,7 +8,7 @@
8
8
  // never coming.
9
9
  import { emitEvent } from '../../core/events/emit.js';
10
10
  import { operationIdContext } from '../../core/events/operation-id.js';
11
- import { listSubmitRequestedReviews } from '../../core/review/store.js';
11
+ import { listSubmitRequestedRequests } from '../../core/human/requests.js';
12
12
  import { completeRequestedSubmit } from '../review/finish.js';
13
13
  export class PendingReviewSubmitReconciler {
14
14
  run(now, ctx) {
@@ -17,7 +17,7 @@ export class PendingReviewSubmitReconciler {
17
17
  return;
18
18
  let pending;
19
19
  try {
20
- pending = listSubmitRequestedReviews();
20
+ pending = listSubmitRequestedRequests();
21
21
  }
22
22
  catch (error) {
23
23
  operationIdContext.fresh(() => {
@@ -26,7 +26,7 @@ export class PendingReviewSubmitReconciler {
26
26
  return;
27
27
  }
28
28
  for (const review of pending) {
29
- ctx.lifecycle.registerDetached(completeRequestedSubmit(review.review_id, { atCompanionTurnEnd: false }).then(() => { }, (error) => {
29
+ ctx.lifecycle.registerDetached(completeRequestedSubmit(review.request_id, { atCompanionTurnEnd: false }).then(() => { }, (error) => {
30
30
  operationIdContext.fresh(() => {
31
31
  emitEvent({ level: 'error', event: 'review.deferred_submit.failed', error });
32
32
  });
@@ -12,17 +12,11 @@ export interface StorageMaintenanceContext {
12
12
  * managed-worktree reconciliation gets its start-up pass. */
13
13
  export declare class StorageMaintenanceReconciler {
14
14
  private lastSpareSweepAt;
15
- private lastGhostSweepAt;
16
15
  private focusGcInFlight;
17
16
  private readonly worktreeSweep;
18
17
  run(now: number, ctx: StorageMaintenanceContext): void;
19
- /** Drop rows whose on-disk node dir is gone. Such a row can never be revived,
20
- * focused, or closed — every one of those resolves the node through meta.json —
21
- * so it sits in the roster jamming the surfaces that try. One readdir per sweep. */
22
- private reapGhostRows;
23
- /** Milliseconds since meta/telemetry was last written. Inbox activity is
24
- * deliberately excluded: children can keep writing a dead manager's inbox. */
25
- private msSinceDiskActivity;
18
+ /** Milliseconds since terminal state, launch/settlement, or disposable telemetry changed. Inbox activity is deliberately excluded: children can keep writing a dead manager's inbox. */
19
+ private msSinceTerminalActivity;
26
20
  /** Reap only physical placement residue of terminal nodes. The durable row,
27
21
  * graph, and on-disk state remain available for a later manual revive. */
28
22
  private reapDeadResidue;
@@ -1,7 +1,7 @@
1
1
  import { statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { closeFocusRow, getFocusById, listFocuses, listNodes, reapGhostRows, } from '../../core/canvas/index.js';
4
- import { isSafeNodeId, jobDir, nodeDir } from '../../core/canvas/paths.js';
3
+ import { closeFocusRow, getFocusById, listFocuses, listNodes, } from '../../core/canvas/index.js';
4
+ import { isSafeNodeId, jobDir } from '../../core/canvas/paths.js';
5
5
  import { emitEvent } from '../../core/events/emit.js';
6
6
  import { operationIdContext } from '../../core/events/operation-id.js';
7
7
  import { listLivePanesAsync, tearDownNode } from '../../core/runtime/placement.js';
@@ -11,13 +11,11 @@ import { ManagedWorktreeSweepReconciler } from './managed-worktree-sweep.js';
11
11
  // placement is reaped. A fresh crash keeps its pane for inspection.
12
12
  export const DEAD_REAP_GRACE_MS = 10 * 60_000;
13
13
  const SPARE_SWEEP_INTERVAL_MS = 60 * 1000;
14
- const GHOST_SWEEP_INTERVAL_MS = 60 * 1000;
15
14
  /** Recurring storage cleanup. The in-memory throttle clocks intentionally reset
16
15
  * on daemon boot, so the first tick runs every full sweep — which is also how
17
16
  * managed-worktree reconciliation gets its start-up pass. */
18
17
  export class StorageMaintenanceReconciler {
19
18
  lastSpareSweepAt = Number.NEGATIVE_INFINITY;
20
- lastGhostSweepAt = Number.NEGATIVE_INFINITY;
21
19
  focusGcInFlight = false;
22
20
  worktreeSweep = new ManagedWorktreeSweepReconciler();
23
21
  run(now, ctx) {
@@ -28,49 +26,21 @@ export class StorageMaintenanceReconciler {
28
26
  // observes the post-reap state this ordering exists to give it.
29
27
  this.reapDeadResidue(now);
30
28
  this.scheduleFocusGc(ctx);
31
- this.reapGhostRows(now);
32
29
  this.gcWarmPool(now);
33
30
  // Registers detached async Git work and returns immediately: this lane is
34
31
  // called un-awaited from a 2-second supervision tick and must not block it.
35
32
  this.worktreeSweep.run(now, ctx);
36
33
  }
37
- /** Drop rows whose on-disk node dir is gone. Such a row can never be revived,
38
- * focused, or closed — every one of those resolves the node through meta.json —
39
- * so it sits in the roster jamming the surfaces that try. One readdir per sweep. */
40
- reapGhostRows(now) {
41
- if (now - this.lastGhostSweepAt < GHOST_SWEEP_INTERVAL_MS)
42
- return;
43
- this.lastGhostSweepAt = now;
44
- operationIdContext.fresh(() => {
45
- try {
46
- const reaped = reapGhostRows();
47
- if (reaped.length > 0) {
48
- emitEvent({
49
- level: 'info',
50
- event: 'canvas.ghost_rows.reaped',
51
- outcome: 'succeeded',
52
- fields: { reaped_count: reaped.length, reaped_ids: reaped },
53
- });
54
- }
55
- }
56
- catch (err) {
57
- emitEvent({ level: 'error', event: 'canvas.ghost_rows.reap_failed', error: err });
58
- }
59
- });
60
- }
61
- /** Milliseconds since meta/telemetry was last written. Inbox activity is
62
- * deliberately excluded: children can keep writing a dead manager's inbox. */
63
- msSinceDiskActivity(nodeId, now) {
64
- let latest = 0;
65
- for (const path of [join(nodeDir(nodeId), 'meta.json'), join(jobDir(nodeId), 'telemetry.json')]) {
66
- try {
67
- const mtime = statSync(path).mtimeMs;
68
- if (mtime > latest)
69
- latest = mtime;
70
- }
71
- catch {
72
- /* absent — contributes nothing */
73
- }
34
+ /** Milliseconds since terminal state, launch/settlement, or disposable telemetry changed. Inbox activity is deliberately excluded: children can keep writing a dead manager's inbox. */
35
+ msSinceTerminalActivity(row, now) {
36
+ const timestamps = [row.created, row.launched_at, row.last_settled_at, row.finalized_at, row.outcome_at]
37
+ .map((value) => value === null || value === undefined ? Number.NaN : Date.parse(value));
38
+ let latest = Math.max(0, ...timestamps.filter(Number.isFinite));
39
+ try {
40
+ latest = Math.max(latest, statSync(join(jobDir(row.node_id), 'telemetry.json')).mtimeMs);
41
+ }
42
+ catch {
43
+ /* telemetry is disposable */
74
44
  }
75
45
  return latest === 0 ? Number.POSITIVE_INFINITY : now - latest;
76
46
  }
@@ -91,7 +61,7 @@ export class StorageMaintenanceReconciler {
91
61
  if (row.pane == null && row.window == null && row.tmux_session == null)
92
62
  continue;
93
63
  try {
94
- if (this.msSinceDiskActivity(row.node_id, now) < DEAD_REAP_GRACE_MS)
64
+ if (this.msSinceTerminalActivity(row, now) < DEAD_REAP_GRACE_MS)
95
65
  continue;
96
66
  }
97
67
  catch (err) {
@@ -3,25 +3,19 @@
3
3
  // Two facts, both already maintained by the runtime, and neither one enough
4
4
  // alone:
5
5
  //
6
- // - MID-TURN — the `<jobDir>/busy` marker AND-ed with a live broker pid, the
7
- // canonical "pi is inside a turn" signal (`core/runtime/busy.ts`). A stale
8
- // marker from a crashed broker reads false through the AND.
6
+ // - MID-TURN — the recovery store's live-turn witness. A cleared turn reads
7
+ // false without consulting a node-local runtime file.
9
8
  // - QUEUED — an undelivered non-deferred inbox entry. Comments are pushed to
10
9
  // the companion as inbox messages, so the ordinary rhythm is: the human
11
10
  // comments, the entry lands, the companion's turn starts a moment later.
12
11
  // Between those two moments the busy marker is cold while the companion is
13
12
  // unmistakably about to work, and submitting into that gap kills the edit
14
13
  // the wait exists to protect.
15
- import { getNode } from '../../core/canvas/canvas.js';
16
- import { isPidAlive } from '../../core/canvas/pid.js';
17
- import { hasUnseenWakeMail } from '../../core/feed/inbox.js';
18
- import { isBusy } from '../../core/runtime/busy.js';
14
+ import { readLiveTurn } from '../../core/canvas/recovery.js';
15
+ import { hasUnseenWakeMail } from '../../core/canvas/deliveries.js';
19
16
  /** Is the companion inside a turn right now? */
20
17
  export function companionMidTurn(nodeId) {
21
- const node = getNode(nodeId);
22
- if (node === null || !isPidAlive(node.pi_pid))
23
- return false;
24
- return isBusy(nodeId);
18
+ return readLiveTurn(nodeId)?.busy ?? false;
25
19
  }
26
20
  /** Does the companion hold work it has not yet consumed? A deferred entry is
27
21
  * excluded for the same reason the settlement policy excludes it: it never