@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,427 +1,17 @@
1
- import { envNodeId } from '../../shared/env.js';
2
- import { isRecord } from '../../shared/predicates.js';
3
- import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
4
- import { join } from 'node:path';
5
- import { jobDir } from '../canvas/paths.js';
6
- import { errorClassFromFault, } from '../fault-classifier.js';
7
- import { emitEvent } from '../events/emit.js';
8
- import { operationIdContext, validateOperationId } from '../events/operation-id.js';
9
1
  export const FAULT_QUIET_MS = 5 * 60_000;
10
- const MESSAGE_CAP = 500;
11
- function faultPath(nodeId) {
12
- return join(jobDir(nodeId), 'fault');
13
- }
14
- function providerRetryPath(nodeId) {
15
- return join(jobDir(nodeId), 'provider-retry');
16
- }
17
- function canonicalSessionFile(sessionFile) {
18
- try {
19
- return realpathSync(sessionFile);
20
- }
21
- catch {
22
- return sessionFile;
23
- }
24
- }
25
- function clampMessage(message) {
26
- return message.slice(0, MESSAGE_CAP);
27
- }
28
- function faultPriority(fault) {
29
- if (fault.link === 'pi→provider' && fault.retry.disposition === 'auto' && fault.retry.by === 'daemon')
30
- return 3;
31
- if (fault.retry.disposition === 'fatal')
32
- return 2;
33
- return 1;
34
- }
35
- const faultLinks = ['pi→provider', 'viewer↔broker', 'relay↔broker', 'viewer↔crtrd', 'broker↔crtrd', 'daemon→node', 'crtr→pi'];
36
- const faultKinds = ['rate-limit', 'overloaded', 'connection', 'auth', 'protocol', 'context-overflow', 'other', 'wedged', 'model-not-found'];
37
- function parseProviderError(value) {
38
- if (!isRecord(value) || typeof value.provider !== 'string' || value.provider === '' || typeof value.model !== 'string' || value.model === '')
39
- return undefined;
40
- const status = typeof value.status === 'number' && Number.isFinite(value.status) ? value.status : undefined;
41
- const code = typeof value.code === 'string' || typeof value.code === 'number' ? value.code : undefined;
42
- const errno = typeof value.errno === 'string' ? value.errno : undefined;
43
- const body = typeof value.body === 'string' ? value.body : undefined;
44
- const causes = Array.isArray(value.causes)
45
- ? value.causes.flatMap((item) => {
46
- if (!isRecord(item))
47
- return [];
48
- const cause = {
49
- ...(typeof item.message === 'string' ? { message: item.message } : {}),
50
- ...(typeof item.status === 'number' && Number.isFinite(item.status) ? { status: item.status } : {}),
51
- ...(typeof item.code === 'string' || typeof item.code === 'number' ? { code: item.code } : {}),
52
- ...(typeof item.errno === 'string' ? { errno: item.errno } : {}),
53
- ...(typeof item.body === 'string' ? { body: item.body } : {}),
54
- };
55
- return Object.keys(cause).length === 0 ? [] : [cause];
56
- })
57
- : undefined;
58
- return {
59
- provider: value.provider,
60
- model: value.model,
61
- ...(status === undefined ? {} : { status }),
62
- ...(code === undefined ? {} : { code }),
63
- ...(errno === undefined ? {} : { errno }),
64
- ...(body === undefined ? {} : { body }),
65
- ...(causes === undefined || causes.length === 0 ? {} : { causes }),
66
- };
67
- }
68
- function parseFault(raw) {
69
- try {
70
- const parsed = JSON.parse(raw);
71
- if (!faultLinks.includes(parsed.link))
72
- return null;
73
- if (typeof parsed.op !== 'string' || parsed.op === '')
74
- return null;
75
- if (!faultKinds.includes(parsed.kind))
76
- return null;
77
- if (typeof parsed.message !== 'string' || typeof parsed.since !== 'string')
78
- return null;
79
- const operationId = validateOperationId(parsed.operation_id);
80
- const retryRaw = parsed.retry;
81
- if (retryRaw === undefined)
82
- return null;
83
- if (retryRaw.disposition !== 'auto' && retryRaw.disposition !== 'manual' && retryRaw.disposition !== 'fatal')
84
- return null;
85
- if (retryRaw.by !== undefined && retryRaw.by !== 'sdk' && retryRaw.by !== 'daemon' && retryRaw.by !== 'client')
86
- return null;
87
- if (retryRaw.attempt !== undefined && typeof retryRaw.attempt !== 'number')
88
- return null;
89
- if (retryRaw.max !== undefined && typeof retryRaw.max !== 'number')
90
- return null;
91
- if (retryRaw.nextAt !== undefined && typeof retryRaw.nextAt !== 'string')
92
- return null;
93
- if (parsed.anchorEntryId !== undefined && (typeof parsed.anchorEntryId !== 'string' || parsed.anchorEntryId === ''))
94
- return null;
95
- const providerError = parsed.providerError === undefined ? undefined : parseProviderError(parsed.providerError);
96
- if (parsed.providerError !== undefined && providerError === undefined)
97
- return null;
98
- return {
99
- link: parsed.link,
100
- op: parsed.op,
101
- kind: parsed.kind,
102
- retry: {
103
- disposition: retryRaw.disposition,
104
- ...(retryRaw.by === undefined ? {} : { by: retryRaw.by }),
105
- ...(retryRaw.attempt === undefined ? {} : { attempt: retryRaw.attempt }),
106
- ...(retryRaw.max === undefined ? {} : { max: retryRaw.max }),
107
- ...(retryRaw.nextAt === undefined ? {} : { nextAt: retryRaw.nextAt }),
108
- },
109
- message: parsed.message,
110
- since: parsed.since,
111
- operation_id: operationId,
112
- ...(parsed.anchorEntryId === undefined ? {} : { anchorEntryId: parsed.anchorEntryId }),
113
- ...(providerError === undefined ? {} : { providerError }),
114
- };
115
- }
116
- catch {
117
- return null;
118
- }
119
- }
120
- function currentFault(nodeId) {
121
- try {
122
- return parseFault(readFileSync(faultPath(nodeId), 'utf8'));
123
- }
124
- catch {
125
- return null;
126
- }
127
- }
128
- function currentProviderRetryEpisode(nodeId) {
129
- try {
130
- const parsed = JSON.parse(readFileSync(providerRetryPath(nodeId), 'utf8'));
131
- if (parsed.state !== 'pending' && parsed.state !== 'admitted' && parsed.state !== 'invalidated')
132
- return null;
133
- if (typeof parsed.sessionFile !== 'string' || parsed.sessionFile === '')
134
- return null;
135
- if (parsed.anchorEntryId !== null && typeof parsed.anchorEntryId !== 'string')
136
- return null;
137
- const fault = parseFault(JSON.stringify(parsed.fault));
138
- if (fault === null ||
139
- fault.link !== 'pi→provider' ||
140
- fault.retry.disposition !== 'auto' ||
141
- fault.retry.by !== 'daemon' ||
142
- (fault.anchorEntryId ?? null) !== parsed.anchorEntryId)
143
- return null;
144
- return { state: parsed.state, sessionFile: canonicalSessionFile(parsed.sessionFile), anchorEntryId: parsed.anchorEntryId, fault };
145
- }
146
- catch {
147
- return null;
148
- }
149
- }
150
- function writeFault(nodeId, fault) {
151
- try {
152
- mkdirSync(jobDir(nodeId), { recursive: true });
153
- writeFileSync(faultPath(nodeId), JSON.stringify(fault));
154
- return true;
155
- }
156
- catch {
157
- return false;
158
- }
159
- }
160
- function writeProviderRetryEpisode(nodeId, episode) {
161
- try {
162
- mkdirSync(jobDir(nodeId), { recursive: true });
163
- writeFileSync(providerRetryPath(nodeId), JSON.stringify(episode));
164
- return true;
165
- }
166
- catch {
167
- return false;
168
- }
169
- }
170
- function resolvedMarker(nodeId, fault) {
171
- const current = currentFault(nodeId);
172
- if (current === null)
173
- return fault;
174
- if (current.link === fault.link && current.kind === fault.kind) {
175
- const retry = { ...current.retry, ...fault.retry };
176
- if (fault.retry.disposition === 'fatal' && !('nextAt' in fault.retry)) {
177
- delete retry.nextAt;
178
- }
179
- return { ...fault, since: current.since, anchorEntryId: current.anchorEntryId ?? fault.anchorEntryId, retry };
180
- }
181
- return faultPriority(current) > faultPriority(fault) ? null : fault;
182
- }
183
- function updateMarker(nodeId, fault) {
184
- const resolved = resolvedMarker(nodeId, fault);
185
- return resolved !== null && writeFault(nodeId, resolved) ? resolved : null;
186
- }
187
- function resolveOperationId(explicit) {
188
- if (explicit !== undefined)
189
- return validateOperationId(explicit);
190
- const current = operationIdContext.current();
191
- if (current !== undefined)
192
- return current;
193
- return operationIdContext.fresh((operationId) => operationId);
194
- }
195
- function storedFault(input) {
196
- return {
197
- link: input.link,
198
- op: input.op,
199
- kind: input.kind,
200
- retry: input.retry,
201
- message: clampMessage(input.message),
202
- since: input.since ?? new Date().toISOString(),
203
- operation_id: resolveOperationId(input.operation_id),
204
- ...(typeof input.anchorEntryId === 'string' && input.anchorEntryId !== '' ? { anchorEntryId: input.anchorEntryId } : {}),
205
- ...(input.providerError === undefined ? {} : { providerError: input.providerError }),
206
- };
207
- }
208
- function emitRecordedFault(nodeId, fault) {
209
- emitEvent({
210
- level: fault.retry.disposition === 'fatal' ? 'error' : 'warn',
211
- event: 'fault.recorded',
212
- operation_id: fault.operation_id,
213
- node_id: nodeId,
214
- error_class: errorClassFromFault(fault),
215
- fields: {
216
- link: fault.link,
217
- op: fault.op,
218
- kind: fault.kind,
219
- retry: fault.retry,
220
- message: fault.message,
221
- ...(fault.anchorEntryId === undefined ? {} : { anchor_entry_id: fault.anchorEntryId }),
222
- ...(fault.providerError === undefined ? {} : { provider_error: fault.providerError }),
223
- },
224
- });
225
- }
226
- export function recordFault(nodeId, input) {
227
- const fault = storedFault(input);
228
- emitRecordedFault(nodeId, fault);
229
- updateMarker(nodeId, fault);
230
- }
231
- /** Atomically establishes the crash-recovery authority before publishing its
232
- * ordinary marker, so a broker death can never leave only a retryable fault. */
233
- export function recordPendingProviderRetryFault(nodeId, sessionFile, input) {
234
- const fault = storedFault(input);
235
- emitRecordedFault(nodeId, fault);
236
- const marker = resolvedMarker(nodeId, fault);
237
- if (marker === null)
238
- return null;
239
- if (!writeProviderRetryEpisode(nodeId, {
240
- state: 'pending',
241
- sessionFile: canonicalSessionFile(sessionFile),
242
- anchorEntryId: marker.anchorEntryId ?? null,
243
- fault: marker,
244
- }))
245
- return null;
246
- return writeFault(nodeId, marker) ? marker : null;
247
- }
248
- export function recordFaultForCurrentNode(input) {
249
- const nodeId = envNodeId();
250
- if (nodeId === undefined || nodeId.trim() === '')
251
- return false;
252
- if (!existsSync(jobDir(nodeId)))
253
- return false;
254
- try {
255
- recordFault(nodeId, input);
256
- return true;
257
- }
258
- catch {
259
- return false;
260
- }
261
- }
262
- export function clearFault(nodeId, opts) {
263
- try {
264
- const current = currentFault(nodeId);
265
- if (current === null)
266
- return false;
267
- if (opts?.link !== undefined && current.link !== opts.link)
268
- return false;
269
- rmSync(faultPath(nodeId));
270
- const episode = currentProviderRetryEpisode(nodeId);
271
- // turn_end precedes agent_settled. An admitted retry keeps its durable
272
- // coordinate while the broker classifies an error settlement; an invalidated
273
- // coordinate also remains durable so replacement startup cannot replay it.
274
- if (opts?.preserveProviderRetryEpisode !== true &&
275
- episode?.fault.since === current.since &&
276
- episode.state === 'pending')
277
- rmSync(providerRetryPath(nodeId), { force: true });
278
- const operationId = resolveOperationId();
279
- emitEvent({
280
- level: 'info',
281
- event: 'fault.cleared',
282
- operation_id: operationId,
283
- node_id: nodeId,
284
- error_class: errorClassFromFault(current),
285
- fields: {
286
- cleared_fault_operation_id: current.operation_id,
287
- link: current.link,
288
- op: current.op,
289
- kind: current.kind,
290
- retry: current.retry,
291
- message: current.message,
292
- since: current.since,
293
- ...(current.anchorEntryId === undefined ? {} : { anchor_entry_id: current.anchorEntryId }),
294
- },
295
- });
296
- return true;
297
- }
298
- catch {
299
- return false;
300
- }
301
- }
302
- export function clearFaultForCurrentNode(opts) {
303
- const nodeId = envNodeId();
304
- if (nodeId === undefined || nodeId.trim() === '')
305
- return false;
306
- if (!existsSync(jobDir(nodeId)))
307
- return false;
308
- return clearFault(nodeId, opts);
309
- }
310
- export function readFault(nodeId) {
311
- return currentFault(nodeId);
312
- }
313
- export function readProviderRetryEpisode(nodeId) {
314
- return currentProviderRetryEpisode(nodeId);
315
- }
316
- /** Persist a newly scheduled provider retry before its broker-local timer is armed. */
317
- export function recordPendingProviderRetryEpisode(nodeId, episode) {
318
- return writeProviderRetryEpisode(nodeId, { ...episode, sessionFile: canonicalSessionFile(episode.sessionFile), state: 'pending' });
319
- }
320
- /** Admission is irreversible EXCEPT through a proven no-progress rollback: a
321
- * broker startup that finds no assistant content or tool activity past the
322
- * episode's anchor may reopen it via `reopenProviderRetryEpisode` below. Short
323
- * of that proof, an admitted episode is never replayed. */
324
- export function admitProviderRetryEpisode(nodeId, sessionFile, fault) {
325
- const current = currentProviderRetryEpisode(nodeId);
326
- if (current === null ||
327
- current.state !== 'pending' ||
328
- current.sessionFile !== canonicalSessionFile(sessionFile) ||
329
- current.fault.since !== fault.since ||
330
- current.anchorEntryId !== (fault.anchorEntryId ?? null))
331
- return false;
332
- return writeProviderRetryEpisode(nodeId, {
333
- state: 'admitted',
334
- sessionFile: current.sessionFile,
335
- anchorEntryId: current.anchorEntryId,
336
- fault,
337
- });
338
- }
339
- /** Reopens an `admitted` episode to `pending` so a surviving broker startup can
340
- * re-drive it exactly like a never-admitted one. Callable only under the same
341
- * no-progress proof `recoverOnStartup` establishes — the branch segment from
342
- * `anchorEntryId` to the current leaf carries no assistant content and no tool
343
- * activity. Guarded by the same transcript-identity coordinate `admitProviderRetryEpisode`
344
- * uses, so a foreign or already-invalidated episode is never reopened. */
345
- export function reopenProviderRetryEpisode(nodeId, sessionFile) {
346
- const current = currentProviderRetryEpisode(nodeId);
347
- if (current === null || current.state !== 'admitted' || current.sessionFile !== canonicalSessionFile(sessionFile))
348
- return false;
349
- return writeProviderRetryEpisode(nodeId, { ...current, state: 'pending' });
350
- }
351
- /** A replacement explicitly invalidates its old transcript's retry episode. */
352
- export function invalidateProviderRetryEpisode(nodeId, sessionFile) {
353
- const current = currentProviderRetryEpisode(nodeId);
354
- if (current === null || current.sessionFile !== canonicalSessionFile(sessionFile) || current.state === 'invalidated')
355
- return false;
356
- return writeProviderRetryEpisode(nodeId, { ...current, state: 'invalidated' });
357
- }
358
- export function clearProviderRetryEpisode(nodeId) {
359
- try {
360
- rmSync(providerRetryPath(nodeId), { force: true });
361
- }
362
- catch {
363
- /* best-effort */
364
- }
365
- }
366
- export async function readFaultAsync(nodeId) {
367
- return readFault(nodeId);
368
- }
369
- /** Snapshot fault state immediately before a broker launch begins, returning a
370
- * reader that is only trustworthy for a fault recorded by THIS attempt.
371
- *
372
- * `clearFault` can silently fail to remove a prior marker (its own removal
373
- * errors are swallowed, and its result goes unchecked by callers), so rather
374
- * than assume that call succeeded, this independently CONFIRMS no fault is
375
- * present at the moment of launch. Other processes (a viewer, a relay) can
376
- * still write THEIR OWN fault links after this snapshot, but `explanation()`
377
- * only ever accepts a fatal `crtr→pi` launch fault — the one shape only a
378
- * newly launched broker itself writes (`resolveLaunchModel` in `broker.ts`) —
379
- * so no ACCEPTED fault can exist before this snapshot without also surviving
380
- * a failed clear, which the snapshot already catches. When a stale fault
381
- * survives the clear, `explanation()` fails closed (returns null) rather than
382
- * risk surfacing an old, unrelated cause as if it were fresh.
383
- *
384
- * Call this the instant before the launch call it is guarding, so the window
385
- * in which an unrelated write could occur stays as small as possible. */
386
- export function beginBootFaultAttempt(nodeId) {
387
- const preLaunchClear = readFault(nodeId) === null;
388
- return {
389
- explanation() {
390
- if (!preLaunchClear)
391
- return null;
392
- const fault = readFault(nodeId);
393
- if (fault === null)
394
- return null;
395
- if (fault.link !== 'crtr\u2192pi' || fault.retry.disposition !== 'fatal')
396
- return null;
397
- const op = fault.op.trim();
398
- const message = fault.message.trim();
399
- if (op === '' || message === '')
400
- return null;
401
- return `${op}: ${message}`;
402
- },
403
- };
404
- }
405
- /** True when a raw engine error says pi could not resolve a credential for the model's
406
- * provider at all. pi raises `Provider is not configured: <id>` whenever auth resolution
407
- * yields nothing, and `Credential store read/modify failed for <id>` when the store itself
408
- * errored — for crouter's managed providers both mean "the pool did not hand over a usable
409
- * credential just now", which is frequently transient (a refresh in flight, a momentary read
410
- * failure) and never something the agent can fix from inside its own turn. Classified as a
411
- * plain `other` fault it is FATAL, so the node parks and every wake re-fails on the same
412
- * provider; the broker instead substitutes an authenticated provider and re-drives. */
2
+ /** True when Pi could not resolve credentials for a provider. */
413
3
  export function isProviderUnconfiguredError(message) {
414
- const m = message ?? '';
415
- if (/provider is not configured\b/i.test(m))
4
+ const value = message ?? '';
5
+ if (/provider is not configured\b/i.test(value))
416
6
  return true;
417
- return /credential store (?:read|modify) failed\b/i.test(m);
7
+ return /credential store (?:read|modify) failed\b/i.test(value);
418
8
  }
419
- /** True when a raw engine error is the provider 404 "model missing" signal. */
9
+ /** True when a provider reports that a requested model is unavailable. */
420
10
  export function isModelNotFoundError(message) {
421
- const m = message ?? '';
422
- if (/not_found_error/i.test(m))
11
+ const value = message ?? '';
12
+ if (/not_found_error/i.test(value))
423
13
  return true;
424
- if (/model[^.]{0,40}(not\s*found|does not exist|is not available|unavailable)|(no such|unknown) model/i.test(m))
14
+ if (/model[^.]{0,40}(not\s*found|does not exist|is not available|unavailable)|(no such|unknown) model/i.test(value))
425
15
  return true;
426
- return /\b404\b/.test(m) && /not\s*found|not available|does not exist/i.test(m);
16
+ return /\b404\b/.test(value) && /not\s*found|not available|does not exist/i.test(value);
427
17
  }
@@ -32,17 +32,14 @@ export declare function hasRetainedReports(nodeId: string): boolean;
32
32
  export interface ReviveBearings {
33
33
  /** The one-shot yield note left by the prior self, pending boot confirmation. */
34
34
  yieldMsg: string | null;
35
- /** Coalesced digest of unread reports, or null when the feed was empty. The
36
- * cursor now records the last physically covered entry. */
35
+ /** Coalesced wake mail, held by the execution until the turn settles. */
37
36
  unreadDigest: string | null;
38
37
  /** Persona-transition framing to surface when the node's role changed while
39
38
  * it was away. The drift remains pending until context-intro completes. */
40
39
  driftGuidance: string | null;
41
40
  }
42
- /** Capture revive bearings for `meta`: read the pending yield note, advance the
43
- * feed cursor past unread reports, and capture external persona framing.
44
- * Revive paths call it once, then pass the result to the pure kickoff builder. */
45
- export declare function drainBearings(meta: NodeMeta): ReviveBearings;
41
+ /** Claim wake mail for this launch without acknowledging it before a turn. */
42
+ export declare function drainBearings(meta: NodeMeta, executionId: string): ReviveBearings;
46
43
  /** Assemble the auto-injected first message for a FRESH revive of `meta` from its
47
44
  * already-drained `bearings` (see drainBearings) plus pure on-disk reads of the
48
45
  * node's goal, roadmap, and context dir, framed so the revived node can rebuild
@@ -25,7 +25,8 @@ import { activeBackgroundBashJobs, bashJobCommandLine, formatBashElapsed } from
25
25
  import { readRoadmap, roadmapPath } from './roadmap.js';
26
26
  import { buildWakeBearings } from './bearings.js';
27
27
  import { personaDrift } from './persona.js';
28
- import { readInboxSince, readCursor, writeCursor, coalesce, } from '../feed/inbox.js';
28
+ import { coalesce, nodeMailEntry } from '../feed/inbox.js';
29
+ import { claimKickoffMail } from '../canvas/deliveries.js';
29
30
  import { formatCard } from '../../shared/generated-context.js';
30
31
  /** The runtime-restart continuation exactly as delivered. `revive.ts` injects
31
32
  * it as the prompt of a strict resume that follows a cleanly aborted turn. */
@@ -105,20 +106,14 @@ export function hasRetainedReports(nodeId) {
105
106
  const dir = reportsDir(nodeId);
106
107
  return existsSync(dir) && readdirSync(dir).some((file) => file.endsWith('.md'));
107
108
  }
108
- /** Capture revive bearings for `meta`: read the pending yield note, advance the
109
- * feed cursor past unread reports, and capture external persona framing.
110
- * Revive paths call it once, then pass the result to the pure kickoff builder. */
111
- export function drainBearings(meta) {
109
+ /** Claim wake mail for this launch without acknowledging it before a turn. */
110
+ export function drainBearings(meta, executionId) {
112
111
  const nodeId = meta.node_id;
113
112
  // The note stays on disk until session_start confirms this launch.
114
113
  const yieldMsg = readYieldMessage(nodeId);
115
- // Drain the feed: read the physically ordered suffix after the cursor, then
116
- // record its final entry so only later physical entries remain unread.
117
- const cursorEntryId = readCursor(nodeId);
118
- const entries = readInboxSince(nodeId, cursorEntryId);
114
+ const entries = claimKickoffMail(nodeId, executionId).map(nodeMailEntry);
119
115
  let unreadDigest = null;
120
116
  if (entries.length > 0) {
121
- writeCursor(nodeId, entries.at(-1).entry_id);
122
117
  // The launch argv carries one message. Keep coalescing card-free runs, but
123
118
  // start a new batch at each entry with cards. A batch's cards are therefore
124
119
  // directly ahead of its coalesced text, so they cannot leap an earlier body.
@@ -144,8 +139,8 @@ export function drainBearings(meta) {
144
139
  return { yieldMsg, unreadDigest, driftGuidance };
145
140
  }
146
141
  // Fresh-revive catch-up bug: on a refresh-yield (resume:false) the old
147
- // conversation is gone AND the physical inbox cursor already covers everything
148
- // drained pre-yield, so the revived node loses sight of reports its
142
+ // conversation is gone AND earlier deliveries are already acknowledged,
143
+ // so the revived node loses sight of reports its
149
144
  // subscriptions pushed BEFORE the yield. The bodies are never lost — they persist
150
145
  // at reports/<ts>-<kind>.md forever — but a fresh revive needs a bounded index
151
146
  // that says history exists and how to expand it, not every path preloaded.
@@ -181,7 +176,7 @@ function reportHistorySources(nodeId) {
181
176
  }
182
177
  /** Render the <feed> block PURELY: the live "awaiting" roster (a read), the
183
178
  * already-drained unread digest (from drainBearings), and a progressive index
184
- * to on-disk report history. No cursor write. */
179
+ * to on-disk report history. No delivery mutation. */
185
180
  function feedBlock(nodeId, unreadDigest) {
186
181
  // Awaiting = active subscriptions whose publisher is still live (active|idle).
187
182
  const awaiting = subscriptionsOf(nodeId)
@@ -206,18 +201,17 @@ function feedBlock(nodeId, unreadDigest) {
206
201
  lines.push('Awaiting 0 nodes.');
207
202
  lines.push('', unreadDigest ?? '(no unread reports)');
208
203
  }
209
- // Catch-up history. The unread digest above only covers what arrived after the
210
- // physical cursor; on a refresh-revive it already covers everything drained
211
- // pre-yield, so preserve the breadcrumb without preloading every old report.
204
+ // Include a bounded history index because acknowledged reports are absent
205
+ // from the unread digest after a refresh.
212
206
  const history = reportHistorySources(nodeId);
213
207
  if (history.length > 0) {
214
208
  const visible = history.slice(0, REPORT_HISTORY_SOURCE_CAP);
215
209
  const hidden = history.length - visible.length;
216
210
  const total = history.reduce((sum, h) => sum + h.count, 0);
217
- lines.push('', `Report history on disk (collapsed): ${total} report${total === 1 ? '' : 's'} across ${history.length} subscribed node${history.length === 1 ? '' : 's'}. This is a catch-up index for reports your cursor already passed; read only what matters.`, ...visible.map((h) => {
211
+ lines.push('', `Report history on disk (collapsed): ${total} report${total === 1 ? '' : 's'} across ${history.length} subscribed node${history.length === 1 ? '' : 's'}. This is a catch-up index for earlier reports; read only what matters.`, ...visible.map((h) => {
218
212
  const ref = `${h.nodeId}:reports/${h.latest}`;
219
213
  return ` - ${h.name} (${h.nodeId}): ${h.count} report${h.count === 1 ? '' : 's'}; latest ${reportKind(h.latest)}: ${ref}`;
220
- }), ...(hidden > 0 ? [` - … ${hidden} more subscribed node${hidden === 1 ? '' : 's'} with reports hidden`] : []), 'Expand: `crtr canvas history read <ref>` for one listed report; `crtr node inspect artifacts <node-id> --type report` for that node\'s full report list; `crtr node inspect artifacts <node-id> --type inbox` for cursor-independent inbox history including full message bodies.');
214
+ }), ...(hidden > 0 ? [` - … ${hidden} more subscribed node${hidden === 1 ? '' : 's'} with reports hidden`] : []), 'Expand: `crtr canvas history read <ref>` for one listed report; `crtr node inspect artifacts <node-id> --type report` for that node\'s full report list; `crtr node inspect artifacts <node-id> --type inbox` for retained inbox history including full message bodies.');
221
215
  }
222
216
  return `<feed>\n${lines.join('\n')}\n</feed>`;
223
217
  }
@@ -1,7 +1,7 @@
1
1
  // The launch spec — how a node becomes (or comes back as) a running pi process.
2
2
  //
3
3
  // pi-only. No claude branch — we are a super-opinionated system. A node's
4
- // LaunchSpec (persisted in meta.json) is the canonical recipe the daemon
4
+ // LaunchSpec (stored with the node in SQL) is the canonical recipe the daemon
5
5
  // replays to revive it faithfully: `--session <path>` (the saved conversation's
6
6
  // absolute .jsonl) to wake a done/idle node — a refresh-yield reuses the SAME
7
7
  // path plus `--crtr-new-cycle`, resetting the session tree's leaf and rooting a
@@ -31,7 +31,9 @@
31
31
  // unknown-node/illegal-move/already-finalized-cancel branching — stays here;
32
32
  // only the raw conditional UPDATE lives in canvas.ts, keeping every `.prepare(`
33
33
  // inside `core/canvas/`).
34
- import { getNode, cancelCronsOnWake, composeOutcomeDeliveryDocument, readOutcomeDelivery, transitionStatusIntent } from '../canvas/index.js';
34
+ import { getNode, cancelCronsOnWake, transitionStatusIntent } from '../canvas/index.js';
35
+ import { readExternalActionDelivery } from '../canvas/deliveries.js';
36
+ import { composeNodeOutcomeDocument } from './outcome-document.js';
35
37
  const ANY = '*';
36
38
  const TERMINAL_REASONS = {
37
39
  finish: 'finished',
@@ -95,6 +97,37 @@ function detailJson(detail) {
95
97
  return serialized;
96
98
  return JSON.stringify({ schema: 'crtr.node-outcome-detail/v1', truncated: true, ...(message === undefined ? {} : { message }) });
97
99
  }
100
+ function armedOutcomeAction(nodeId) {
101
+ const delivery = readExternalActionDelivery(`node-outcome:${nodeId}`);
102
+ if (delivery === null || delivery.state !== 'armed')
103
+ return null;
104
+ const adapter = delivery.adapter;
105
+ if (adapter?.adapter !== 'subprocess' || !Array.isArray(adapter.argv) || !adapter.argv.every((entry) => typeof entry === 'string') || typeof adapter.cwd !== 'string') {
106
+ throw new Error(`outcome delivery ${delivery.deliveryId} has an invalid subprocess adapter`);
107
+ }
108
+ return { name: delivery.destinationId, payload: adapter.payload ?? null };
109
+ }
110
+ function nodeAtOutcome(node, reason) {
111
+ let status;
112
+ let intent;
113
+ if (reason === 'finished' || reason === 'declined') {
114
+ status = 'done';
115
+ intent = 'done';
116
+ }
117
+ else if (reason === 'parked') {
118
+ status = 'done';
119
+ intent = 'parked';
120
+ }
121
+ else if (reason === 'closed' || reason === 'retired' || reason === 'stranded' || reason === 'deadline_exceeded') {
122
+ status = 'canceled';
123
+ intent = null;
124
+ }
125
+ else {
126
+ status = 'dead';
127
+ intent = null;
128
+ }
129
+ return { ...node, status, intent, terminal_reason: reason };
130
+ }
98
131
  /** Enact a lifecycle event on a node: validate the from-status against the
99
132
  * table, then write status+intent in ONE atomic statement (so they can never
100
133
  * disagree). Returns the hydrated node view after the write.
@@ -121,13 +154,13 @@ export function transition(nodeId, event, opts) {
121
154
  const outcomeDetailJson = detailJson(opts?.outcome?.detail);
122
155
  let documentJson;
123
156
  if (recordsOutcome && current !== null && current.outcome_at === null) {
124
- const delivery = readOutcomeDelivery(nodeId);
125
- if (delivery?.state === 'armed') {
126
- documentJson = composeOutcomeDeliveryDocument({
127
- nodeId,
157
+ const action = armedOutcomeAction(nodeId);
158
+ if (action !== null) {
159
+ documentJson = JSON.stringify(composeNodeOutcomeDocument({
160
+ node: nodeAtOutcome(current, outcomeReason),
128
161
  outcome: { kind: 'failure', reason: outcomeReason, at: outcomeAt, revision: (current.outcome_revision ?? 0) + 1, detailJson: outcomeDetailJson },
129
- action: { name: delivery.actionName, payload: delivery.payload },
130
- });
162
+ action,
163
+ }));
131
164
  }
132
165
  }
133
166
  // A committed canonical result is protected from a concurrent close.
@@ -24,7 +24,7 @@
24
24
  // tmux half, the bindings owner, and tmux chrome.
25
25
  import { envNodeId } from '../../shared/env.js';
26
26
  import { getNode, fullName, deleteNode, listNodes, subscribersOf, openFocusRow, closeFocusRow, getFocusByNode, getFocusByPane, getFocusById, listFocuses as listFocusRows, } from '../canvas/index.js';
27
- import { nodeHasAssistantMessage, isStreaming } from '../canvas/render-source.js';
27
+ import { nodeHasAssistantMessage } from '../canvas/render-source.js';
28
28
  import { headlessBrokerHost } from './host.js';
29
29
  import { fanDoctrineWake } from './close.js';
30
30
  import { paneExists, paneLocation, closePane, currentTmux, switchClient, selectWindow, selectLayout, selectPane, getPaneOption, setPaneOption, respawnPaneSync, windowOfPane, renameWindow, } from './tmux-driver.js';
@@ -331,7 +331,7 @@ function isEmptyNode(nodeId) {
331
331
  const m = getNode(nodeId);
332
332
  if (m === null)
333
333
  return false;
334
- if (isStreaming(nodeId, getNode(nodeId)?.pi_pid ?? null))
334
+ if (m.streaming === true)
335
335
  return false;
336
336
  return !nodeHasAssistantMessage(m.pi_session_file);
337
337
  }