@north-light/crouter 0.3.311 → 0.3.313

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (520) hide show
  1. package/dist/api/client.d.ts +27 -30
  2. package/dist/api/client.js +80 -67
  3. package/dist/api/dto/broker-ops.d.ts +10 -20
  4. package/dist/api/dto/canvas.d.ts +1 -10
  5. package/dist/api/dto/health.d.ts +113 -0
  6. package/dist/api/dto/human-requests.d.ts +14 -3
  7. package/dist/api/dto/human.d.ts +2 -48
  8. package/dist/api/dto/human.js +3 -2
  9. package/dist/api/dto/inbox.d.ts +14 -0
  10. package/dist/api/dto/mail.d.ts +43 -0
  11. package/dist/api/dto/nodes.d.ts +11 -8
  12. package/dist/api/dto/recovery.d.ts +96 -0
  13. package/dist/api/dto/reviews.d.ts +2 -2
  14. package/dist/api/routes.d.ts +9 -6
  15. package/dist/api/routes.js +11 -11
  16. package/dist/build-root.d.ts +4 -0
  17. package/dist/build-root.js +11 -1
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
  20. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  22. package/dist/cli.js +10 -1
  23. package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
  24. package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
  25. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  26. package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
  27. package/dist/clients/attach/chrome/inbox-strip.js +17 -9
  28. package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
  29. package/dist/clients/attach/chrome/ticket-panel.js +77 -46
  30. package/dist/clients/attach/overlays/graph.js +6 -9
  31. package/dist/clients/attach/render/chat-view.d.ts +5 -4
  32. package/dist/clients/attach/render/chat-view.js +22 -13
  33. package/dist/clients/attach/render/page-block.d.ts +11 -7
  34. package/dist/clients/attach/render/page-block.js +64 -55
  35. package/dist/clients/attach/viewer.js +746 -735
  36. package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
  37. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
  38. package/dist/clients/inbox/controller.d.ts +42 -14
  39. package/dist/clients/inbox/controller.js +240 -124
  40. package/dist/clients/inbox/dto-adapt.d.ts +28 -0
  41. package/dist/clients/inbox/dto-adapt.js +93 -0
  42. package/dist/clients/inbox/page-adapter.d.ts +5 -4
  43. package/dist/clients/inbox/page-adapter.js +12 -12
  44. package/dist/clients/inbox/resolve.d.ts +10 -5
  45. package/dist/clients/inbox/resolve.js +13 -12
  46. package/dist/clients/inbox/review/review-client.d.ts +0 -1
  47. package/dist/clients/inbox/review/review-client.js +0 -4
  48. package/dist/clients/inbox/review-adapter.d.ts +2 -1
  49. package/dist/clients/inbox/review-adapter.js +3 -5
  50. package/dist/clients/inbox/surface.d.ts +2 -2
  51. package/dist/clients/inbox/surface.js +19 -8
  52. package/dist/clients/inbox/tui.d.ts +3 -3
  53. package/dist/commands/__tests__/human.test.js +31 -15
  54. package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
  55. package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
  56. package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
  57. package/dist/commands/api-client.d.ts +5 -4
  58. package/dist/commands/api-client.js +13 -7
  59. package/dist/commands/canvas-history/shared.js +1 -1
  60. package/dist/commands/canvas-history.js +1 -1
  61. package/dist/commands/canvas-roster.js +1 -1
  62. package/dist/commands/canvas.js +1 -2
  63. package/dist/commands/human/feedback.js +20 -17
  64. package/dist/commands/human/prompts.js +45 -36
  65. package/dist/commands/human/queue.js +73 -123
  66. package/dist/commands/human/review.js +6 -16
  67. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
  68. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
  69. package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
  70. package/dist/commands/node-inspect-artifacts.js +1 -1
  71. package/dist/commands/node-lifecycle-revive.d.ts +2 -2
  72. package/dist/commands/node-lifecycle-revive.js +9 -19
  73. package/dist/commands/pkg/plugin-manage.js +0 -1
  74. package/dist/commands/profile/show.js +4 -12
  75. package/dist/commands/surface-tmux-spread.js +1 -5
  76. package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
  77. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
  78. package/dist/commands/sys/branch.d.ts +2 -0
  79. package/dist/commands/sys/branch.js +19 -0
  80. package/dist/commands/sys/config.js +106 -33
  81. package/dist/commands/sys/daemon.d.ts +2 -0
  82. package/dist/commands/sys/daemon.js +130 -7
  83. package/dist/commands/sys/doctor.js +5 -15
  84. package/dist/commands/sys/migrate.js +21 -92
  85. package/dist/commands/sys/setup-settings-runner.js +4 -2
  86. package/dist/commands/sys.js +2 -15
  87. package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
  88. package/dist/core/__tests__/boot.test.js +32 -25
  89. package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
  90. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
  91. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
  92. package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
  93. package/dist/core/__tests__/canvas.test.js +18 -47
  94. package/dist/core/__tests__/child-death-wake.test.js +29 -7
  95. package/dist/core/__tests__/cli-help-contract.test.js +2 -0
  96. package/dist/core/__tests__/close.test.js +29 -10
  97. package/dist/core/__tests__/collectible.test.js +2 -1
  98. package/dist/core/__tests__/config-change-delta.test.js +2 -0
  99. package/dist/core/__tests__/context-intro.test.js +17 -5
  100. package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
  101. package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
  102. package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
  103. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
  104. package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
  105. package/dist/core/__tests__/daemon-boot.test.js +51 -14
  106. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  107. package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
  108. package/dist/core/__tests__/focuses.test.js +10 -5
  109. package/dist/core/__tests__/helpers/harness.js +50 -18
  110. package/dist/core/__tests__/history-inbox.test.js +12 -16
  111. package/dist/core/__tests__/human-action-delivery.test.js +48 -28
  112. package/dist/core/__tests__/human-actions.test.js +7 -3
  113. package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
  114. package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
  115. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
  116. package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
  117. package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
  118. package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
  119. package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
  120. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  121. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +312 -1
  122. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  123. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  124. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  125. package/dist/core/__tests__/integration/revive.test.js +67 -21
  126. package/dist/core/__tests__/integration/spawn-root.test.js +62 -19
  127. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  128. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  129. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  130. package/dist/core/__tests__/kickoff.test.js +36 -39
  131. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  132. package/dist/core/__tests__/lifecycle.test.js +2 -1
  133. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  134. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  135. package/dist/core/__tests__/migration.test.js +145 -31
  136. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  137. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  138. package/dist/core/__tests__/node-outcome.test.js +93 -26
  139. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +4 -3
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +8 -7
  141. package/dist/core/__tests__/on-read-nested-store.test.js +8 -7
  142. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  143. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  144. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  145. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  146. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  147. package/dist/core/__tests__/profile-project-memory-delivery.test.js +35 -32
  148. package/dist/core/__tests__/prune.test.js +11 -5
  149. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  150. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  151. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  152. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  153. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  154. package/dist/core/__tests__/revive-all.test.js +13 -3
  155. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  156. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  157. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  158. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  159. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  160. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  161. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  162. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  163. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  164. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  165. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +5 -4
  166. package/dist/core/__tests__/stop-guard.test.js +2 -1
  167. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  168. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +19 -5
  169. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  170. package/dist/core/bootstrap.js +2 -0
  171. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  172. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  173. package/dist/core/broker-client/transport-socket.js +13 -5
  174. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  175. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  176. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  177. package/dist/core/canvas/attention.d.ts +2 -5
  178. package/dist/core/canvas/attention.js +15 -64
  179. package/dist/core/canvas/boot.d.ts +6 -7
  180. package/dist/core/canvas/boot.js +5 -9
  181. package/dist/core/canvas/canvas.d.ts +13 -109
  182. package/dist/core/canvas/canvas.js +140 -401
  183. package/dist/core/canvas/crons.d.ts +2 -0
  184. package/dist/core/canvas/crons.js +16 -15
  185. package/dist/core/canvas/daemon-owner.js +10 -0
  186. package/dist/core/canvas/db.d.ts +2 -2
  187. package/dist/core/canvas/db.js +2 -18
  188. package/dist/core/canvas/deliveries.d.ts +104 -0
  189. package/dist/core/canvas/deliveries.js +354 -0
  190. package/dist/core/canvas/executions.d.ts +29 -0
  191. package/dist/core/canvas/executions.js +68 -0
  192. package/dist/core/canvas/history.js +35 -35
  193. package/dist/core/canvas/index.d.ts +3 -1
  194. package/dist/core/canvas/index.js +3 -1
  195. package/dist/core/canvas/migrations.js +20 -12
  196. package/dist/core/canvas/nav-model.js +1 -3
  197. package/dist/core/canvas/nav-render.d.ts +4 -4
  198. package/dist/core/canvas/nav-render.js +11 -21
  199. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  200. package/dist/core/canvas/node-sql-migration.js +261 -0
  201. package/dist/core/canvas/paths.d.ts +2 -16
  202. package/dist/core/canvas/paths.js +2 -31
  203. package/dist/core/canvas/recovery.d.ts +76 -0
  204. package/dist/core/canvas/recovery.js +588 -0
  205. package/dist/core/canvas/remote-canvas-source.js +4 -0
  206. package/dist/core/canvas/render-source.d.ts +16 -27
  207. package/dist/core/canvas/render-source.js +16 -45
  208. package/dist/core/canvas/sessions.d.ts +33 -0
  209. package/dist/core/canvas/sessions.js +78 -0
  210. package/dist/core/canvas/types.d.ts +33 -33
  211. package/dist/core/canvas/types.js +2 -5
  212. package/dist/core/canvas/waits.d.ts +18 -0
  213. package/dist/core/canvas/waits.js +70 -0
  214. package/dist/core/canvas/worktrees.d.ts +40 -0
  215. package/dist/core/canvas/worktrees.js +93 -0
  216. package/dist/core/command-hooks/artifact.d.ts +6 -0
  217. package/dist/core/command-hooks/artifact.js +22 -6
  218. package/dist/core/command-hooks/discovery.d.ts +2 -0
  219. package/dist/core/command-hooks/discovery.js +2 -0
  220. package/dist/core/command-hooks/lifecycle-catalog.d.ts +20 -2
  221. package/dist/core/command-hooks/lifecycle-catalog.js +7 -2
  222. package/dist/core/command-hooks/lifecycle-create.d.ts +4 -0
  223. package/dist/core/command-hooks/lifecycle-create.js +51 -0
  224. package/dist/core/command-hooks/schema.d.ts +2 -2
  225. package/dist/core/command-hooks/schema.js +5 -4
  226. package/dist/core/command-hooks/transport/exec-invoke.js +3 -1
  227. package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +18 -8
  228. package/dist/core/command-hooks/transport/exec-lifecycle.js +89 -30
  229. package/dist/core/config.d.ts +17 -12
  230. package/dist/core/config.js +106 -155
  231. package/dist/core/errors.d.ts +1 -0
  232. package/dist/core/errors.js +4 -0
  233. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  234. package/dist/core/feed/feed.d.ts +0 -6
  235. package/dist/core/feed/feed.js +68 -56
  236. package/dist/core/feed/inbox.d.ts +7 -81
  237. package/dist/core/feed/inbox.js +46 -378
  238. package/dist/core/feed/messages.d.ts +58 -0
  239. package/dist/core/feed/messages.js +217 -0
  240. package/dist/core/fs-utils.d.ts +4 -0
  241. package/dist/core/fs-utils.js +21 -1
  242. package/dist/core/human/answer.d.ts +1 -1
  243. package/dist/core/human/answer.js +1 -1
  244. package/dist/core/human/claim.d.ts +1 -1
  245. package/dist/core/human/claim.js +2 -4
  246. package/dist/core/human/component-docs.js +1 -1
  247. package/dist/core/human/feedback-companion.js +4 -3
  248. package/dist/core/human/feedback.d.ts +5 -8
  249. package/dist/core/human/feedback.js +57 -85
  250. package/dist/core/human/page-authoring.d.ts +30 -0
  251. package/dist/core/human/page-authoring.js +19 -0
  252. package/dist/core/human/requests.d.ts +136 -0
  253. package/dist/core/human/requests.js +515 -0
  254. package/dist/core/human/root.d.ts +0 -7
  255. package/dist/core/human/root.js +0 -17
  256. package/dist/core/human/scan.d.ts +2 -7
  257. package/dist/core/human/scan.js +48 -132
  258. package/dist/core/human/summary.d.ts +1 -1
  259. package/dist/core/human/summary.js +1 -1
  260. package/dist/core/human/tickets.d.ts +23 -83
  261. package/dist/core/human/tickets.js +92 -243
  262. package/dist/core/human/types.d.ts +5 -1
  263. package/dist/core/installed-plugins.js +8 -3
  264. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  265. package/dist/core/locked-file.d.ts +6 -3
  266. package/dist/core/locked-file.js +36 -9
  267. package/dist/core/memory/extensions.js +2 -0
  268. package/dist/core/preview-registry.js +0 -1
  269. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  270. package/dist/core/profiles/default-binding.d.ts +0 -4
  271. package/dist/core/profiles/default-binding.js +12 -58
  272. package/dist/core/profiles/env-store.js +8 -3
  273. package/dist/core/profiles/manifest.d.ts +2 -37
  274. package/dist/core/profiles/manifest.js +35 -161
  275. package/dist/core/profiles/select.js +3 -0
  276. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  277. package/dist/core/review/__tests__/comments.test.js +28 -10
  278. package/dist/core/review/__tests__/remap.test.js +28 -10
  279. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  280. package/dist/core/review/birth.d.ts +1 -1
  281. package/dist/core/review/birth.js +34 -5
  282. package/dist/core/review/comments.js +1 -1
  283. package/dist/core/review/companion.d.ts +1 -1
  284. package/dist/core/review/companion.js +2 -2
  285. package/dist/core/review/document.js +1 -1
  286. package/dist/core/review/project.d.ts +5 -1
  287. package/dist/core/review/project.js +14 -36
  288. package/dist/core/review/realize.d.ts +3 -6
  289. package/dist/core/review/realize.js +51 -168
  290. package/dist/core/review/remap.js +1 -1
  291. package/dist/core/review/stage.d.ts +3 -2
  292. package/dist/core/review/stage.js +2 -3
  293. package/dist/core/review/store.d.ts +3 -21
  294. package/dist/core/review/store.js +140 -186
  295. package/dist/core/review/types.d.ts +0 -1
  296. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  297. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  298. package/dist/core/runtime/broker/auth-reload.js +9 -10
  299. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  300. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  301. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  302. package/dist/core/runtime/broker/extension-abort.js +11 -0
  303. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  304. package/dist/core/runtime/broker/fault-retry.js +142 -95
  305. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  306. package/dist/core/runtime/broker/inbox.js +4 -92
  307. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  308. package/dist/core/runtime/broker/rebind.js +21 -12
  309. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  310. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  311. package/dist/core/runtime/broker.js +28 -22
  312. package/dist/core/runtime/close.d.ts +3 -9
  313. package/dist/core/runtime/close.js +16 -27
  314. package/dist/core/runtime/fault.d.ts +3 -93
  315. package/dist/core/runtime/fault.js +9 -419
  316. package/dist/core/runtime/kickoff.d.ts +3 -6
  317. package/dist/core/runtime/kickoff.js +12 -18
  318. package/dist/core/runtime/launch.js +1 -1
  319. package/dist/core/runtime/lifecycle.js +40 -7
  320. package/dist/core/runtime/nodes.d.ts +5 -1
  321. package/dist/core/runtime/nodes.js +26 -11
  322. package/dist/core/runtime/placement.js +2 -2
  323. package/dist/core/runtime/recycle.js +24 -14
  324. package/dist/core/runtime/reset.js +26 -7
  325. package/dist/core/runtime/revive.js +226 -217
  326. package/dist/core/runtime/spawn.d.ts +4 -1
  327. package/dist/core/runtime/spawn.js +43 -5
  328. package/dist/core/runtime/warm-pool.d.ts +2 -2
  329. package/dist/core/runtime/warm-pool.js +23 -3
  330. package/dist/core/scoped-state/db.d.ts +7 -0
  331. package/dist/core/scoped-state/db.js +92 -0
  332. package/dist/core/scoped-state/migrate.d.ts +11 -0
  333. package/dist/core/scoped-state/migrate.js +198 -0
  334. package/dist/core/scoped-state/paths.d.ts +9 -0
  335. package/dist/core/scoped-state/paths.js +27 -0
  336. package/dist/core/scoped-state/profiles.d.ts +12 -0
  337. package/dist/core/scoped-state/profiles.js +62 -0
  338. package/dist/core/scoped-state/providers.d.ts +16 -0
  339. package/dist/core/scoped-state/providers.js +8 -0
  340. package/dist/core/scoped-state/schema.d.ts +3 -0
  341. package/dist/core/scoped-state/schema.js +26 -0
  342. package/dist/core/scoped-state/settings.d.ts +28 -0
  343. package/dist/core/scoped-state/settings.js +82 -0
  344. package/dist/core/subscription-state.d.ts +29 -48
  345. package/dist/core/subscription-state.js +176 -712
  346. package/dist/core/worktree-close.js +4 -4
  347. package/dist/core/worktree-mutation-async.js +2 -2
  348. package/dist/core/worktree-sweep.js +11 -16
  349. package/dist/core/worktree.js +6 -12
  350. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  351. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  352. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  353. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  354. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  355. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +41 -17
  356. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  357. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  358. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  359. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  360. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  361. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  362. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  363. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  364. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  365. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +6 -3
  366. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  367. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  368. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  369. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  370. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  371. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +29 -43
  372. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  373. package/dist/daemon/api/bridge.js +1 -1
  374. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  375. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  376. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  377. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  378. package/dist/daemon/api/handlers/canvas.js +2 -44
  379. package/dist/daemon/api/handlers/daemon.js +41 -0
  380. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  381. package/dist/daemon/api/handlers/health.js +10 -5
  382. package/dist/daemon/api/handlers/human-requests.js +168 -188
  383. package/dist/daemon/api/handlers/human.js +3 -148
  384. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  385. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  386. package/dist/daemon/api/handlers/inbox.js +21 -120
  387. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  388. package/dist/daemon/api/handlers/messages.js +73 -15
  389. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  390. package/dist/daemon/api/handlers/nodes.d.ts +1 -0
  391. package/dist/daemon/api/handlers/nodes.js +41 -42
  392. package/dist/daemon/api/handlers/reviews.js +4 -29
  393. package/dist/daemon/api/map.d.ts +25 -3
  394. package/dist/daemon/api/map.js +84 -14
  395. package/dist/daemon/api/server.js +68 -9
  396. package/dist/daemon/control.d.ts +18 -0
  397. package/dist/daemon/cron/disposition.d.ts +2 -0
  398. package/dist/daemon/cron/disposition.js +8 -5
  399. package/dist/daemon/cron/settle.js +37 -13
  400. package/dist/daemon/cron/sinks.d.ts +18 -1
  401. package/dist/daemon/cron/sinks.js +47 -93
  402. package/dist/daemon/crtrd-cli.js +4 -19
  403. package/dist/daemon/crtrd.d.ts +2 -0
  404. package/dist/daemon/crtrd.js +206 -81
  405. package/dist/daemon/fleet.js +5 -6
  406. package/dist/daemon/human/finish.d.ts +12 -24
  407. package/dist/daemon/human/finish.js +88 -285
  408. package/dist/daemon/human/settle.d.ts +40 -0
  409. package/dist/daemon/human/settle.js +89 -0
  410. package/dist/daemon/human/sweep.d.ts +20 -5
  411. package/dist/daemon/human/sweep.js +56 -60
  412. package/dist/daemon/manage.d.ts +21 -16
  413. package/dist/daemon/manage.js +85 -51
  414. package/dist/daemon/messaging/node-message.d.ts +1 -1
  415. package/dist/daemon/messaging/node-message.js +9 -12
  416. package/dist/daemon/park-activity.js +3 -3
  417. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  418. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  419. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  420. package/dist/daemon/reconcilers/controller-death.js +6 -18
  421. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  422. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  423. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  424. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  425. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  426. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  427. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  428. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  429. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  430. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  431. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  432. package/dist/daemon/review/companion.js +5 -11
  433. package/dist/daemon/review/deliver.d.ts +1 -5
  434. package/dist/daemon/review/deliver.js +3 -52
  435. package/dist/daemon/review/finish.d.ts +2 -27
  436. package/dist/daemon/review/finish.js +79 -263
  437. package/dist/daemon/review/sweep.d.ts +3 -10
  438. package/dist/daemon/review/sweep.js +13 -131
  439. package/dist/daemon/startup-policy.d.ts +0 -5
  440. package/dist/daemon/startup-policy.js +0 -5
  441. package/dist/hook-authoring.d.ts +28 -2
  442. package/dist/hook-authoring.js +53 -1
  443. package/dist/index.d.ts +3 -2
  444. package/dist/index.js +1 -1
  445. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  446. package/dist/migrations/__tests__/activation.test.js +9 -6
  447. package/dist/migrations/activation.d.ts +3 -2
  448. package/dist/migrations/activation.js +2 -4
  449. package/dist/migrations/corpus.d.ts +2 -5
  450. package/dist/migrations/corpus.js +9 -20
  451. package/dist/migrations/runner.d.ts +3 -1
  452. package/dist/migrations/runner.js +6 -2
  453. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  454. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  455. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  456. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  457. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  458. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  459. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  460. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  461. package/dist/pi-extensions/canvas-stophook.js +47 -31
  462. package/dist/shared/env.d.ts +1 -0
  463. package/dist/shared/env.js +3 -0
  464. package/package.json +1 -1
  465. package/runtime.lock.json +5 -5
  466. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  467. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  468. package/dist/clients/inbox/review-visibility.js +0 -19
  469. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  470. package/dist/commands/canvas-rebuild-index.js +0 -46
  471. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  472. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  473. package/dist/core/__tests__/human-deliver.test.js +0 -256
  474. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  475. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  476. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  477. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  478. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  479. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  480. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  481. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  482. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  483. package/dist/core/canvas/human-deliveries.js +0 -75
  484. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  485. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  486. package/dist/core/feed/passive.d.ts +0 -3
  487. package/dist/core/feed/passive.js +0 -53
  488. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  489. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  490. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  491. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  492. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  493. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  494. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  495. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  496. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  497. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  498. package/dist/core/review/ticket-filter.d.ts +0 -17
  499. package/dist/core/review/ticket-filter.js +0 -21
  500. package/dist/core/runtime/broker/passive.d.ts +0 -17
  501. package/dist/core/runtime/broker/passive.js +0 -44
  502. package/dist/core/runtime/busy.d.ts +0 -46
  503. package/dist/core/runtime/busy.js +0 -150
  504. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  505. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  506. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  507. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  508. package/dist/daemon/human/deliver-action.d.ts +0 -3
  509. package/dist/daemon/human/deliver-action.js +0 -9
  510. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  511. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  512. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  513. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  514. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  515. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  516. package/dist/daemon/startup-block-marker.d.ts +0 -25
  517. package/dist/daemon/startup-block-marker.js +0 -99
  518. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  519. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  520. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -3,37 +3,70 @@
3
3
  // autostart another daemon.
4
4
  import { CrtrClient } from '../../../api/client.js';
5
5
  import { ApiError } from '../../../api/errors.js';
6
+ import { envExecutionId } from '../../../shared/env.js';
6
7
  function daemonClient() {
7
8
  return CrtrClient.forLocalSocket({ autostart: false });
8
9
  }
10
+ export function brokerExecutionId() {
11
+ const id = envExecutionId();
12
+ if (id === undefined || id === '')
13
+ throw new Error('broker launch is missing CRTR_EXECUTION_ID');
14
+ return id;
15
+ }
9
16
  export function bindBrokerSession(nodeId, request) {
10
- return daemonClient().bindBrokerSession(nodeId, request);
17
+ return daemonClient().bindBrokerSession(nodeId, { ...request, executionId: brokerExecutionId() });
11
18
  }
12
19
  export function settleBroker(nodeId, request) {
13
- return daemonClient().settleBroker(nodeId, request);
20
+ return daemonClient().settleBroker(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
14
21
  }
15
22
  export function completeBrokerPark(nodeId, request) {
16
- return daemonClient().completeBrokerPark(nodeId, request);
23
+ return daemonClient().completeBrokerPark(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
17
24
  }
18
25
  export function recordBrokerParkActivity(nodeId) {
19
- return daemonClient().recordBrokerParkActivity(nodeId);
26
+ return daemonClient().recordBrokerParkActivity(nodeId, { expected_execution_id: brokerExecutionId() });
27
+ }
28
+ export function claimBrokerMail(nodeId, channel) {
29
+ return daemonClient().claimNodeMail(nodeId, { execution_id: brokerExecutionId(), channel });
30
+ }
31
+ export function acknowledgeBrokerMail(nodeId, deliveries) {
32
+ return daemonClient().acknowledgeNodeMail(nodeId, { execution_id: brokerExecutionId(), deliveries });
33
+ }
34
+ export function recordBrokerTurn(nodeId, request) {
35
+ return daemonClient().recordBrokerTurn(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
36
+ }
37
+ export function mutateBrokerProviderRetry(nodeId, request) {
38
+ return daemonClient().mutateBrokerProviderRetry(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
39
+ }
40
+ export function mutateBrokerFault(nodeId, request) {
41
+ return daemonClient().mutateBrokerFault(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
42
+ }
43
+ export function brokerRecovery(nodeId) {
44
+ return daemonClient().getBrokerRecovery(nodeId, brokerExecutionId());
45
+ }
46
+ /** Node-scoped fault writes are for non-broker clients such as attach transport.
47
+ * Broker-owned recovery mutations use the execution-fenced wrappers above. */
48
+ export function recordNodeFault(nodeId, input) {
49
+ return daemonClient().recordNodeFault(nodeId, input);
20
50
  }
21
- export function advanceBrokerInboxCursor(nodeId, request) {
22
- return daemonClient().advanceBrokerInboxCursor(nodeId, request);
51
+ export function clearNodeFault(nodeId, opts = {}) {
52
+ return daemonClient().clearNodeFault(nodeId, {
53
+ ...(opts.link === undefined ? {} : { link: opts.link }),
54
+ ...(opts.preserveEpisode === undefined ? {} : { preserve_episode: opts.preserveEpisode }),
55
+ }).then((result) => result.cleared);
23
56
  }
24
57
  /** Fixed canvas projection for broker extension rendering. It deliberately
25
58
  * exposes no generic node/database facade. */
26
59
  export function commitBrokerModel(nodeId, request) {
27
- return daemonClient().commitBrokerModel(nodeId, request);
60
+ return daemonClient().commitBrokerModel(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
28
61
  }
29
62
  export function brokerExtensionState(nodeId) {
30
63
  return daemonClient().brokerExtensionState(nodeId);
31
64
  }
32
65
  export function commitBrokerGeneratedName(nodeId, request) {
33
- return daemonClient().commitBrokerGeneratedName(nodeId, request);
66
+ return daemonClient().commitBrokerGeneratedName(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
34
67
  }
35
68
  export function commitBrokerPersonaAck(nodeId, request) {
36
- return daemonClient().commitBrokerPersonaAck(nodeId, request);
69
+ return daemonClient().commitBrokerPersonaAck(nodeId, { ...request, expected_execution_id: brokerExecutionId() });
37
70
  }
38
71
  /** Reports still use the canonical daemon report handler; this broker adapter
39
72
  * only preserves terminal structured output's idempotent finalization result. */
@@ -2,6 +2,9 @@ interface AbortableSession {
2
2
  clearQueue(): unknown;
3
3
  abort(): Promise<unknown>;
4
4
  }
5
+ export declare function refuseTurn(abort: () => void): void;
6
+ export declare function wasTurnRefused(): boolean;
7
+ export declare function clearRefusedTurn(): void;
5
8
  /** Mark one synchronous ExtensionContext.abort() call as a refresh boundary. */
6
9
  export declare function runRefreshAbort(abort: () => void): void;
7
10
  /** Bound into Pi's extension context by the broker for every session generation. */
@@ -5,6 +5,17 @@
5
5
  // Other aborts (notably a critical inbox entry) leave queues intact because the
6
6
  // current conversation still owns those already-handed-off messages.
7
7
  let refreshAbortScope = false;
8
+ let refusedTurn = false;
9
+ export function refuseTurn(abort) {
10
+ refusedTurn = true;
11
+ abort();
12
+ }
13
+ export function wasTurnRefused() {
14
+ return refusedTurn;
15
+ }
16
+ export function clearRefusedTurn() {
17
+ refusedTurn = false;
18
+ }
8
19
  /** Mark one synchronous ExtensionContext.abort() call as a refresh boundary. */
9
20
  export function runRefreshAbort(abort) {
10
21
  refreshAbortScope = true;
@@ -1,5 +1,6 @@
1
1
  import type { AgentSessionServices, ModelRegistry } from '@earendil-works/pi-coding-agent';
2
- import { readFault } from '../fault.js';
2
+ import type { Fault } from '../../fault-classifier.js';
3
+ import type { RecoveryFaultDTO } from '../../../api/dto/recovery.js';
3
4
  import type { BrokerSdkConfig } from '../launch.js';
4
5
  import type { BrokerToClient } from '../broker-protocol.js';
5
6
  import type { BrokerSession } from './read-ops.js';
@@ -26,12 +27,21 @@ export declare class FaultRetry {
26
27
  private providerFallbackInFlight;
27
28
  private notFoundFallbackInFlight;
28
29
  private stagedCapacityFailure;
30
+ private personaGateFailed;
29
31
  constructor(deps: FaultRetryDeps);
30
32
  clearTimer(): void;
31
33
  reset(): void;
34
+ /** A failed daemon-backed persona gate is local evidence, not a provider fault. */
35
+ notePersonaGateFailure(): void;
36
+ private state;
37
+ private recordFault;
38
+ private clearFault;
39
+ private sessionId;
40
+ private clearEpisode;
32
41
  onEvent(event: BrokerToClient): void;
33
- isActiveAutoFault(fault: ReturnType<typeof readFault>): fault is NonNullable<ReturnType<typeof readFault>>;
34
- publishSettledProviderFault(generation: SubscriptionGeneration): void;
42
+ isActiveAutoFault(fault: RecoveryFaultDTO | null): fault is RecoveryFaultDTO;
43
+ isActiveAutoFault(fault: Fault | null): fault is Fault;
44
+ publishSettledProviderFault(generation: SubscriptionGeneration): Promise<void>;
35
45
  /** Startup re-drive reads the durable episode surviving from the process this
36
46
  * replaces. Revive deliberately clears the ordinary fault marker before this
37
47
  * boundary, so the episode is the recovery authority. A `pending` episode
@@ -44,7 +54,7 @@ export declare class FaultRetry {
44
54
  * it out below), so it is treated the same as "no progress" rather than
45
55
  * blocking the ladder. Returns 'retry-armed' when the timer now owns the
46
56
  * first turn, 'none' when the caller's default kickoff should proceed. */
47
- recoverOnStartup(): 'retry-armed' | 'none';
57
+ recoverOnStartup(): Promise<'retry-armed' | 'none'>;
48
58
  /** True iff the branch segment strictly after `anchorEntryId` (root→leaf,
49
59
  * anchor exclusive) contains an assistant message with non-empty content or
50
60
  * a tool result — the two shapes a runtime card and a `content: []`
@@ -52,14 +62,13 @@ export declare class FaultRetry {
52
62
  * branch cannot be distinguished from "no progress" here; callers treat it
53
63
  * as no progress rather than as a positive finding. */
54
64
  private hasProgressPastAnchor;
55
- schedule(): void;
65
+ schedule(): Promise<void>;
56
66
  private maybeSwitchProviderForRetry;
57
67
  private maybeFallbackOnUnusableModel;
58
68
  private nextEligibleFallbackRoute;
59
69
  private targetCannotFitContext;
60
70
  private stageCapacityFailure;
61
71
  private recordPendingProviderFault;
62
- private sessionFile;
63
72
  private pendingEpisodeMatches;
64
73
  private faultRetryDelayMs;
65
74
  private clearAndArmFaultRetry;
@@ -1,8 +1,8 @@
1
- import { realpathSync } from 'node:fs';
2
1
  import { envModelIntent, envNodeCwd, envProfileId } from '../../../shared/env.js';
3
2
  import { isContextOverflow } from '@earendil-works/pi-ai';
4
3
  import { classify } from '../../fault-classifier.js';
5
- import { admitProviderRetryEpisode, clearFault, clearProviderRetryEpisode, invalidateProviderRetryEpisode, isModelNotFoundError, isProviderUnconfiguredError, readFault, readProviderRetryEpisode, recordFault, recordPendingProviderRetryFault, reopenProviderRetryEpisode, } from '../fault.js';
4
+ import { isModelNotFoundError, isProviderUnconfiguredError } from '../fault.js';
5
+ import { brokerRecovery, mutateBrokerFault, mutateBrokerProviderRetry } from './daemon-ops.js';
6
6
  import { extractCoolingDeadline } from '../managed-provider-cooling.js';
7
7
  import { extractProviderFailureDetails } from '../../provider-failure.js';
8
8
  import { expandModelCandidates, modelRequestFromConfig, probeRouteAvailability } from '../../model-routes.js';
@@ -15,6 +15,32 @@ import { endedByAbort } from '../session-cycles.js';
15
15
  import { resolveProviderCandidates } from '../../subscription-state.js';
16
16
  import { announceMessageOrigin } from '../stamp/channel.js';
17
17
  const MAX_TIMEOUT_MS = 2_147_483_647;
18
+ function policyFault(fault) {
19
+ return {
20
+ link: fault.link,
21
+ op: fault.op,
22
+ kind: fault.kind,
23
+ retry: fault.retry,
24
+ message: fault.message,
25
+ since: fault.since,
26
+ operation_id: fault.operation_id,
27
+ ...(fault.anchor_entry_id === undefined ? {} : { anchorEntryId: fault.anchor_entry_id }),
28
+ ...(fault.provider_error === undefined ? {} : { providerError: fault.provider_error }),
29
+ };
30
+ }
31
+ function faultInput(fault) {
32
+ return {
33
+ link: fault.link,
34
+ op: fault.op,
35
+ kind: fault.kind,
36
+ retry: fault.retry,
37
+ message: fault.message,
38
+ ...(fault.operation_id === undefined ? {} : { operation_id: fault.operation_id }),
39
+ ...(fault.since === undefined ? {} : { since: fault.since }),
40
+ ...(fault.anchorEntryId === undefined ? {} : { anchor_entry_id: fault.anchorEntryId }),
41
+ ...(fault.providerError === undefined ? {} : { provider_error: fault.providerError }),
42
+ };
43
+ }
18
44
  /** Owns provider fallback and daemon-driven fault retry state for one broker session. */
19
45
  export class FaultRetry {
20
46
  deps;
@@ -28,6 +54,7 @@ export class FaultRetry {
28
54
  // agent_end staging field: message_end precedes agent_end, which resets that
29
55
  // field for the ordinary provider result.
30
56
  stagedCapacityFailure = null;
57
+ personaGateFailed = false;
31
58
  constructor(deps) {
32
59
  this.deps = deps;
33
60
  }
@@ -42,6 +69,29 @@ export class FaultRetry {
42
69
  this.providerFallbackInFlight = null;
43
70
  this.notFoundFallbackInFlight = null;
44
71
  this.stagedCapacityFailure = null;
72
+ this.personaGateFailed = false;
73
+ }
74
+ /** A failed daemon-backed persona gate is local evidence, not a provider fault. */
75
+ notePersonaGateFailure() {
76
+ this.personaGateFailed = true;
77
+ }
78
+ state() {
79
+ return brokerRecovery(this.deps.nodeId);
80
+ }
81
+ async recordFault(input) {
82
+ return (await mutateBrokerFault(this.deps.nodeId, { op: 'record', input })).fault;
83
+ }
84
+ async clearFault(link, preserveEpisode = false) {
85
+ await mutateBrokerFault(this.deps.nodeId, { op: 'clear', ...(link === undefined ? {} : { link }), ...(preserveEpisode ? { preserve_episode: true } : {}) });
86
+ }
87
+ sessionId(session) {
88
+ const id = session.sessionManager.getSessionId?.();
89
+ return typeof id === 'string' && id !== '' ? id : null;
90
+ }
91
+ async clearEpisode(session) {
92
+ const sessionId = this.sessionId(session);
93
+ if (sessionId !== null)
94
+ await mutateBrokerProviderRetry(this.deps.nodeId, { op: 'clear', session_id: sessionId });
45
95
  }
46
96
  onEvent(event) {
47
97
  this.maybeSwitchProviderForRetry(event);
@@ -56,13 +106,15 @@ export class FaultRetry {
56
106
  return false;
57
107
  return fault.kind === 'connection' || fault.kind === 'rate-limit' || fault.kind === 'overloaded';
58
108
  }
59
- publishSettledProviderFault(generation) {
109
+ async publishSettledProviderFault(generation) {
60
110
  const agentEnd = generation.stagedProviderAgentEnd;
61
111
  const watchdogAbort = generation.stagedWatchdogAbort;
62
112
  const refreshAbort = generation.stagedRefreshAbort;
63
113
  const overflowFailure = generation.stagedOverflowFailure;
64
114
  const capacityFailure = this.stagedCapacityFailure?.generation === generation ? this.stagedCapacityFailure : null;
115
+ const personaGateFailed = this.personaGateFailed;
65
116
  this.stagedCapacityFailure = null;
117
+ this.personaGateFailed = false;
66
118
  generation.stagedProviderAgentEnd = null;
67
119
  generation.stagedWatchdogAbort = false;
68
120
  generation.stagedRefreshAbort = false;
@@ -71,54 +123,52 @@ export class FaultRetry {
71
123
  // turn_end clears the ordinary marker before this settlement. A failed
72
124
  // admitted retry therefore carries from its durable episode, while an
73
125
  // initial provider failure still reads from the ordinary marker.
74
- const prior = readFault(this.deps.nodeId);
75
- // A persona gate runs before Pi contacts a provider. It records a typed
76
- // local API transport failure, then Pi represents that thrown error as a
77
- // generic provider-looking agent_end. Keep its actual boundary instead of
78
- // turning it into a fatal pi→provider `other` fault.
79
- if (prior?.link === 'broker↔crtrd' && prior.kind === 'connection') {
80
- clearProviderRetryEpisode(this.deps.nodeId);
81
- return;
82
- }
83
- const durableEpisode = readProviderRetryEpisode(this.deps.nodeId);
126
+ const initialState = await this.state();
127
+ const prior = initialState.fault;
128
+ const durableEpisode = initialState.episode;
129
+ const sessionId = this.sessionId(settledSession);
84
130
  const episodeFault = this.isActiveAutoFault(prior) && prior.link === 'pi→provider'
85
131
  ? prior
86
132
  : durableEpisode?.state === 'admitted' &&
87
- durableEpisode.sessionFile === this.sessionFile(settledSession) &&
133
+ durableEpisode.session_id === sessionId &&
88
134
  this.isActiveAutoFault(durableEpisode.fault)
89
135
  ? durableEpisode.fault
90
136
  : null;
91
137
  const episode = episodeFault === null
92
138
  ? null
93
- : { since: episodeFault.since, anchorEntryId: episodeFault.anchorEntryId, attempt: episodeFault.retry.attempt };
139
+ : { since: episodeFault.since, anchorEntryId: episodeFault.anchor_entry_id, attempt: episodeFault.retry.attempt };
94
140
  const messages = Array.isArray(agentEnd?.messages) ? agentEnd.messages : [];
95
141
  const last = [...messages].reverse().find((message) => typeof message === 'object' && message !== null && message.role === 'assistant');
142
+ if (personaGateFailed) {
143
+ await this.clearEpisode(settledSession);
144
+ return;
145
+ }
96
146
  if (capacityFailure !== null || overflowFailure !== null) {
97
- clearFault(this.deps.nodeId, { link: 'pi→provider' });
98
- clearProviderRetryEpisode(this.deps.nodeId);
99
- recordFault(this.deps.nodeId, {
147
+ await this.clearFault('pi→provider');
148
+ await this.clearEpisode(settledSession);
149
+ await this.recordFault({
100
150
  link: 'pi→provider', op: 'context overflow recovery', kind: 'context-overflow', retry: { disposition: 'fatal' },
101
- message: capacityFailure?.errorMessage ?? overflowFailure.errorMessage, anchorEntryId: settledSession.sessionManager.getLeafId?.() ?? undefined,
151
+ message: capacityFailure?.errorMessage ?? overflowFailure.errorMessage, anchor_entry_id: settledSession.sessionManager.getLeafId?.() ?? undefined,
102
152
  });
103
153
  return;
104
154
  }
105
155
  if (watchdogAbort && last?.stopReason === 'aborted') {
106
- this.recordPendingProviderFault(settledSession, {
156
+ await this.recordPendingProviderFault(settledSession, {
107
157
  link: 'pi→provider', op: 'generation turn', kind: 'connection',
108
158
  retry: { disposition: 'auto', by: 'daemon', ...(episode?.attempt !== undefined ? { attempt: episode.attempt } : {}) },
109
159
  message: typeof last.errorMessage === 'string' ? last.errorMessage : 'engine error (no errorMessage recorded)',
110
160
  ...(episode !== null ? { since: episode.since } : {}),
111
- anchorEntryId: episode?.anchorEntryId ?? settledSession.sessionManager.getLeafId?.() ?? undefined,
161
+ anchor_entry_id: episode?.anchorEntryId ?? settledSession.sessionManager.getLeafId?.() ?? undefined,
112
162
  });
113
163
  return;
114
164
  }
115
165
  if (last?.stopReason !== 'error') {
116
166
  if (last !== undefined)
117
- clearProviderRetryEpisode(this.deps.nodeId);
167
+ await this.clearEpisode(settledSession);
118
168
  return;
119
169
  }
120
170
  if (refreshAbort) {
121
- clearProviderRetryEpisode(this.deps.nodeId);
171
+ await this.clearEpisode(settledSession);
122
172
  return;
123
173
  }
124
174
  const raw = typeof last.errorMessage === 'string' ? last.errorMessage : 'engine error (no errorMessage recorded)';
@@ -126,7 +176,7 @@ export class FaultRetry {
126
176
  // and settles as an error rather than `aborted`. Something asked that turn
127
177
  // to stop, so there is no provider fault to record.
128
178
  if (endedByAbort({ stopReason: 'error', errorMessage: raw })) {
129
- clearProviderRetryEpisode(this.deps.nodeId);
179
+ await this.clearEpisode(settledSession);
130
180
  return;
131
181
  }
132
182
  const uncompactedOverflow = isContextOverflow(last, settledSession.model?.contextWindow);
@@ -139,8 +189,8 @@ export class FaultRetry {
139
189
  ? undefined
140
190
  : { provider, model, ...failure };
141
191
  if (uncompactedOverflow) {
142
- clearFault(this.deps.nodeId, { link: 'pi→provider' });
143
- clearProviderRetryEpisode(this.deps.nodeId);
192
+ await this.clearFault('pi→provider');
193
+ await this.clearEpisode(settledSession);
144
194
  }
145
195
  const faultInput = {
146
196
  link: 'pi→provider', op: 'generation turn', kind: uncompactedOverflow ? 'context-overflow' : classified.kind,
@@ -155,16 +205,16 @@ export class FaultRetry {
155
205
  : { disposition: classified.disposition },
156
206
  message: raw,
157
207
  ...(!uncompactedOverflow && classified.disposition === 'auto' && episode !== null ? { since: episode.since } : {}),
158
- anchorEntryId: !uncompactedOverflow && classified.disposition === 'auto' && episode?.anchorEntryId !== undefined
208
+ anchor_entry_id: !uncompactedOverflow && classified.disposition === 'auto' && episode?.anchorEntryId !== undefined
159
209
  ? episode.anchorEntryId
160
210
  : settledSession.sessionManager.getLeafId?.() ?? undefined,
161
- ...(providerError === undefined ? {} : { providerError }),
211
+ ...(providerError === undefined ? {} : { provider_error: providerError }),
162
212
  };
163
213
  if (faultInput.retry.disposition === 'auto')
164
- this.recordPendingProviderFault(settledSession, faultInput);
214
+ await this.recordPendingProviderFault(settledSession, faultInput);
165
215
  else {
166
- clearProviderRetryEpisode(this.deps.nodeId);
167
- recordFault(this.deps.nodeId, faultInput);
216
+ await this.clearEpisode(settledSession);
217
+ await this.recordFault(faultInput);
168
218
  }
169
219
  }
170
220
  /** Startup re-drive reads the durable episode surviving from the process this
@@ -179,42 +229,42 @@ export class FaultRetry {
179
229
  * it out below), so it is treated the same as "no progress" rather than
180
230
  * blocking the ladder. Returns 'retry-armed' when the timer now owns the
181
231
  * first turn, 'none' when the caller's default kickoff should proceed. */
182
- recoverOnStartup() {
232
+ async recoverOnStartup() {
183
233
  const generation = this.deps.installedGeneration();
184
234
  if (generation === undefined)
185
235
  return 'none';
186
- const episode = readProviderRetryEpisode(this.deps.nodeId);
187
- const sessionFile = this.sessionFile(generation.session);
236
+ const episode = (await this.state()).episode;
237
+ const sessionId = this.sessionId(generation.session);
188
238
  // A new-cycle launch deliberately roots a new transcript branch even though
189
239
  // it reuses the same .jsonl. Its old anchor is no longer a valid recovery
190
240
  // coordinate, so mark the episode invalid rather than inferring that from
191
241
  // the shared path or letting startup re-drive into the fresh cycle.
192
242
  if (this.deps.cfg.newCycle === true) {
193
- if (episode !== null && sessionFile !== null && episode.sessionFile === sessionFile) {
194
- invalidateProviderRetryEpisode(this.deps.nodeId, sessionFile);
243
+ if (episode !== null && sessionId !== null && episode.session_id === sessionId) {
244
+ await mutateBrokerProviderRetry(this.deps.nodeId, { op: 'invalidate', session_id: sessionId });
195
245
  }
196
246
  return 'none';
197
247
  }
198
- if (episode === null || sessionFile === null || episode.sessionFile !== sessionFile)
248
+ if (episode === null || sessionId === null || episode.session_id !== sessionId)
199
249
  return 'none';
200
250
  if (episode.state === 'admitted') {
201
- if (episode.anchorEntryId !== null && !this.hasProgressPastAnchor(generation.session, episode.anchorEntryId)) {
202
- if (!reopenProviderRetryEpisode(this.deps.nodeId, sessionFile))
251
+ if (episode.fault.anchor_entry_id !== undefined && !this.hasProgressPastAnchor(generation.session, episode.fault.anchor_entry_id)) {
252
+ if (!(await mutateBrokerProviderRetry(this.deps.nodeId, { op: 'reopen', session_id: sessionId })).applied)
203
253
  return 'none';
204
254
  }
205
255
  else {
206
256
  // Progress detected past the anchor (or no anchor to prove against) — the
207
257
  // dead attempt did real work. Never roll that back; clear the episode and
208
258
  // let the caller's default kickoff continue where it actually got to.
209
- clearProviderRetryEpisode(this.deps.nodeId);
259
+ await this.clearEpisode(generation.session);
210
260
  return 'none';
211
261
  }
212
262
  }
213
263
  else if (episode.state !== 'pending') {
214
264
  return 'none';
215
265
  }
216
- recordFault(this.deps.nodeId, episode.fault);
217
- this.schedule();
266
+ await this.recordFault(faultInput(policyFault(episode.fault)));
267
+ await this.schedule();
218
268
  return 'retry-armed';
219
269
  }
220
270
  /** True iff the branch segment strictly after `anchorEntryId` (root→leaf,
@@ -236,17 +286,17 @@ export class FaultRetry {
236
286
  return message.role === 'assistant' && Array.isArray(message.content) && message.content.length > 0;
237
287
  });
238
288
  }
239
- schedule() {
289
+ async schedule() {
240
290
  const generation = this.deps.installedGeneration();
241
291
  const session = this.deps.currentSession();
242
292
  if (generation === undefined || session.isStreaming || this.timer !== null)
243
293
  return;
244
- const fault = readFault(this.deps.nodeId);
245
- // A live session without a durable transcript path still owns its ordinary
246
- // in-process timer; it simply cannot survive broker death. Once a path
247
- // exists, require its pending episode so admitted/invalidated retries stay
248
- // inert even before a replacement broker starts.
249
- if (!this.isActiveAutoFault(fault) || (this.sessionFile(session) !== null && !this.pendingEpisodeMatches(session, fault))) {
294
+ const state = await this.state();
295
+ const fault = state.fault;
296
+ // A live session without a durable session binding still owns its ordinary
297
+ // in-process timer; once one exists, require its pending episode so
298
+ // admitted/invalidated retries stay inert before replacement startup.
299
+ if (!this.isActiveAutoFault(fault) || (this.sessionId(session) !== null && !this.pendingEpisodeMatches(session, fault, state))) {
250
300
  this.clearTimer();
251
301
  return;
252
302
  }
@@ -355,7 +405,7 @@ export class FaultRetry {
355
405
  if (this.deps.installedGeneration() !== generation || this.deps.currentSession() !== candidateSession)
356
406
  return;
357
407
  if (unconfigured)
358
- clearFault(this.deps.nodeId, { link: 'pi→provider' });
408
+ await this.clearFault('pi→provider');
359
409
  const fallbackCard = formatCard('recovery', {}, formatModelFallbackRecovery(currentSpec, resolved, unconfigured ? 'credential' : 'not-found'));
360
410
  announceMessageOrigin(fallbackCard, { type: 'runtime', kind: 'recovery' });
361
411
  await this.deps.turnAdmission.admit((release) => operationIdContext.fresh(() => candidateSession.prompt(fallbackCard, { preflightResult: release })));
@@ -399,42 +449,31 @@ export class FaultRetry {
399
449
  : 'All available fallback models have context windows smaller than the current conversation.',
400
450
  };
401
451
  }
402
- recordPendingProviderFault(session, input) {
403
- const sessionFile = this.sessionFile(session);
404
- // A pathless session cannot be crash-recovered, but its live broker still
405
- // owns the ordinary auto-retry timer.
406
- if (sessionFile === null) {
407
- recordFault(this.deps.nodeId, input);
452
+ async recordPendingProviderFault(session, input) {
453
+ const sessionId = this.sessionId(session);
454
+ // A session without a daemon binding cannot crash-recover, but its live
455
+ // broker still records its ordinary auto-retry fault.
456
+ if (sessionId === null) {
457
+ await this.recordFault(input);
408
458
  return;
409
459
  }
410
- recordPendingProviderRetryFault(this.deps.nodeId, sessionFile, input);
460
+ await mutateBrokerProviderRetry(this.deps.nodeId, { op: 'record-pending', session_id: sessionId, input });
411
461
  }
412
- sessionFile(session) {
413
- if (typeof session.sessionFile !== 'string' || session.sessionFile === '')
414
- return null;
415
- try {
416
- return realpathSync(session.sessionFile);
417
- }
418
- catch {
419
- return session.sessionFile;
420
- }
421
- }
422
- pendingEpisodeMatches(session, fault) {
423
- const sessionFile = this.sessionFile(session);
424
- const episode = readProviderRetryEpisode(this.deps.nodeId);
425
- return sessionFile !== null &&
462
+ pendingEpisodeMatches(session, fault, state) {
463
+ const sessionId = this.sessionId(session);
464
+ const episode = state.episode;
465
+ return sessionId !== null &&
426
466
  episode !== null &&
427
467
  episode.state === 'pending' &&
428
- episode.sessionFile === sessionFile &&
429
- episode.fault.since === fault.since &&
430
- episode.anchorEntryId === (fault.anchorEntryId ?? null);
468
+ episode.session_id === sessionId &&
469
+ episode.fault.fault_id === fault.fault_id;
431
470
  }
432
471
  faultRetryDelayMs(fault, now) {
433
472
  if (fault.retry.nextAt !== undefined) {
434
473
  const ms = Date.parse(fault.retry.nextAt) - now;
435
474
  return Number.isFinite(ms) ? Math.max(0, ms) : null;
436
475
  }
437
- return nextFaultRetry(fault, now)?.delayMs ?? null;
476
+ return nextFaultRetry(policyFault(fault), now)?.delayMs ?? null;
438
477
  }
439
478
  clearAndArmFaultRetry(delayMs, generation) {
440
479
  if (this.deps.installedGeneration() !== generation)
@@ -464,7 +503,7 @@ export class FaultRetry {
464
503
  const candidateSession = generation.session;
465
504
  if (candidateSession.isStreaming)
466
505
  return;
467
- const initial = readFault(this.deps.nodeId);
506
+ const initial = (await this.state()).fault;
468
507
  if (!this.isActiveAutoFault(initial))
469
508
  return;
470
509
  let fault = initial;
@@ -472,7 +511,7 @@ export class FaultRetry {
472
511
  const online = await this.deps.retryProbe();
473
512
  if (this.deps.installedGeneration() !== generation || this.deps.currentSession() !== candidateSession || candidateSession.isStreaming)
474
513
  return;
475
- const fresh = readFault(this.deps.nodeId);
514
+ const fresh = (await this.state()).fault;
476
515
  if (!this.isActiveAutoFault(fresh) || fresh.since !== initial.since)
477
516
  return;
478
517
  if (!online) {
@@ -484,20 +523,20 @@ export class FaultRetry {
484
523
  const attempt = fault.retry.attempt ?? 0;
485
524
  const policy = policyFor(fault.kind);
486
525
  if (attempt >= policy.max) {
487
- recordFault(this.deps.nodeId, markFatalOnExhaust(fault));
488
- clearProviderRetryEpisode(this.deps.nodeId);
526
+ await this.recordFault(faultInput(markFatalOnExhaust(policyFault(fault))));
527
+ await this.clearEpisode(candidateSession);
489
528
  return;
490
529
  }
491
- const plan = nextFaultRetry(fault, Date.now());
530
+ const plan = nextFaultRetry(policyFault(fault), Date.now());
492
531
  if (plan === null) {
493
- recordFault(this.deps.nodeId, markFatalOnExhaust(fault));
494
- clearProviderRetryEpisode(this.deps.nodeId);
532
+ await this.recordFault(faultInput(markFatalOnExhaust(policyFault(fault))));
533
+ await this.clearEpisode(candidateSession);
495
534
  return;
496
535
  }
497
- if (fault.anchorEntryId !== undefined) {
536
+ if (fault.anchor_entry_id !== undefined) {
498
537
  try {
499
- if ((candidateSession.sessionManager.getLeafId?.() ?? null) !== fault.anchorEntryId) {
500
- await candidateSession.navigateTree(fault.anchorEntryId);
538
+ if ((candidateSession.sessionManager.getLeafId?.() ?? null) !== fault.anchor_entry_id) {
539
+ await candidateSession.navigateTree(fault.anchor_entry_id);
501
540
  if (this.deps.installedGeneration() !== generation || this.deps.currentSession() !== candidateSession)
502
541
  return;
503
542
  }
@@ -508,27 +547,35 @@ export class FaultRetry {
508
547
  }
509
548
  if (this.deps.installedGeneration() !== generation || this.deps.currentSession() !== candidateSession)
510
549
  return;
511
- await this.deps.turnAdmission.admit((release) => {
512
- const current = readFault(this.deps.nodeId);
550
+ await this.deps.turnAdmission.admit(async (release) => {
551
+ const current = (await this.state()).fault;
513
552
  if (this.deps.installedGeneration() !== generation ||
514
553
  this.deps.currentSession() !== candidateSession ||
515
554
  candidateSession.isStreaming ||
516
555
  !this.isActiveAutoFault(current) ||
517
556
  current.since !== fault.since) {
518
557
  release();
519
- return Promise.resolve();
558
+ return;
520
559
  }
521
560
  // This is the admission point: all generation/session/streaming/fault
522
561
  // revalidation above has passed, and only now may the durable episode
523
- // become admitted. Writing it before prompt() fails closed across a broker
524
- // death in the narrow handoff to the SDK — replaying there could duplicate
525
- // an already accepted recovery turn.
526
- const sessionFile = this.sessionFile(candidateSession);
527
- if (sessionFile !== null && !admitProviderRetryEpisode(this.deps.nodeId, sessionFile, plan.fault)) {
562
+ // become admitted. A rejected or failed mutation releases without
563
+ // calling prompt, so stale brokers never run a recovery turn.
564
+ const sessionId = this.sessionId(candidateSession);
565
+ if (sessionId === null) {
528
566
  release();
529
- return Promise.resolve();
567
+ return;
568
+ }
569
+ const admitted = await mutateBrokerProviderRetry(this.deps.nodeId, {
570
+ op: 'admit',
571
+ session_id: sessionId,
572
+ fault_id: current.fault_id,
573
+ retry: plan.fault.retry,
574
+ });
575
+ if (!admitted.applied) {
576
+ release();
577
+ return;
530
578
  }
531
- recordFault(this.deps.nodeId, plan.fault);
532
579
  // The attempt facts are what makes a rolled-back retry legible: every
533
580
  // superseded attempt falls off the active branch, so this one card is the
534
581
  // whole visible record of the sequence, and without them a reader cannot