@north-light/crouter 0.3.233 → 0.3.234

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 (379) hide show
  1. package/dist/api/client.js +16 -15
  2. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +1 -1
  3. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +14 -17
  4. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -11
  5. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +8 -8
  6. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +10 -11
  7. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/pi-shell-runner.ts +2 -2
  8. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +3 -3
  9. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -7
  10. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +8 -8
  11. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +6 -6
  12. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/sysprompt-window.ts +3 -3
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +1 -1
  14. package/dist/clients/attach/chrome/ticket-panel.js +15 -2
  15. package/dist/clients/attach/overlays/graph.js +0 -3
  16. package/dist/clients/attach/overlays/mcp.js +1 -2
  17. package/dist/clients/attach/render/chat-view.js +0 -12
  18. package/dist/clients/attach/render/crtr-output.js +0 -2
  19. package/dist/clients/attach/render/tool-calls.js +0 -6
  20. package/dist/clients/attach/slash/dispatch.js +8 -11
  21. package/dist/clients/attach/viewer.js +532 -534
  22. package/dist/clients/conversation/projection.js +0 -2
  23. package/dist/clients/inbox/controller.js +22 -3
  24. package/dist/clients/inbox/review/companion-pane.js +0 -4
  25. package/dist/clients/inbox/review-adapter.js +0 -7
  26. package/dist/clients/inbox/tui/slots.d.ts +3 -3
  27. package/dist/clients/inbox/tui/types.d.ts +2 -2
  28. package/dist/commands/api-client.js +0 -8
  29. package/dist/commands/attention.js +0 -8
  30. package/dist/commands/canvas-browse.js +2 -1
  31. package/dist/commands/canvas-config.js +0 -10
  32. package/dist/commands/canvas-history.js +2 -1
  33. package/dist/commands/cron.js +5 -18
  34. package/dist/commands/dashboard.js +0 -2
  35. package/dist/commands/human/feedback.js +2 -1
  36. package/dist/commands/human/prompts.js +2 -5
  37. package/dist/commands/human/queue.js +2 -7
  38. package/dist/commands/human/request.js +0 -14
  39. package/dist/commands/human/review.js +2 -1
  40. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
  41. package/dist/commands/memory/find.js +2 -2
  42. package/dist/commands/memory/lint.js +2 -1
  43. package/dist/commands/memory/read.d.ts +2 -7
  44. package/dist/commands/memory/read.js +5 -16
  45. package/dist/commands/memory/shared.js +4 -7
  46. package/dist/commands/memory/write.js +4 -3
  47. package/dist/commands/node/bash.js +2 -1
  48. package/dist/commands/node/create.js +2 -1
  49. package/dist/commands/node/inspect.js +0 -6
  50. package/dist/commands/node/lifecycle.js +4 -13
  51. package/dist/commands/node/message.js +3 -2
  52. package/dist/commands/node/subscription.js +2 -1
  53. package/dist/commands/node/wait.js +4 -3
  54. package/dist/commands/node-context.js +2 -1
  55. package/dist/commands/node-lifecycle-revive.js +0 -2
  56. package/dist/commands/node-worktree.js +2 -1
  57. package/dist/commands/pkg/browse/actions.d.ts +1 -1
  58. package/dist/commands/pkg/browse/actions.js +4 -4
  59. package/dist/commands/pkg/browse/catalog.d.ts +1 -1
  60. package/dist/commands/pkg/browse/catalog.js +15 -15
  61. package/dist/commands/pkg/browse/command-view.d.ts +1 -1
  62. package/dist/commands/pkg/browse/command-view.js +3 -3
  63. package/dist/commands/pkg/browse/detail-panel.d.ts +1 -1
  64. package/dist/commands/pkg/browse/detail-panel.js +8 -8
  65. package/dist/commands/pkg/browse/doc-view.d.ts +1 -1
  66. package/dist/commands/pkg/browse/doc-view.js +3 -3
  67. package/dist/commands/pkg/browse/list-panel.d.ts +1 -1
  68. package/dist/commands/pkg/browse/list-panel.js +3 -3
  69. package/dist/commands/pkg/browse/model.d.ts +3 -3
  70. package/dist/commands/pkg/browse/shell.d.ts +2 -2
  71. package/dist/commands/pkg/browse/shell.js +13 -13
  72. package/dist/commands/pkg/plugin-manage.js +2 -1
  73. package/dist/commands/pkg/shared.js +0 -2
  74. package/dist/commands/profile/delete.js +2 -1
  75. package/dist/commands/push.js +2 -3
  76. package/dist/commands/revive.js +0 -2
  77. package/dist/commands/surface/node/placement.js +2 -1
  78. package/dist/commands/surface-edit.js +2 -1
  79. package/dist/commands/sys/config.js +0 -4
  80. package/dist/commands/sys/daemon.js +0 -10
  81. package/dist/commands/sys/doctor.js +3 -2
  82. package/dist/commands/sys/panels/broker-limits-panel.d.ts +2 -2
  83. package/dist/commands/sys/panels/broker-limits-panel.js +4 -4
  84. package/dist/commands/sys/panels/dependencies-panel.d.ts +2 -2
  85. package/dist/commands/sys/panels/dependencies-panel.js +2 -2
  86. package/dist/commands/sys/panels/keybindings-panel.d.ts +1 -1
  87. package/dist/commands/sys/panels/keybindings-panel.js +5 -5
  88. package/dist/commands/sys/panels/models-panel.d.ts +4 -4
  89. package/dist/commands/sys/panels/models-panel.js +11 -10
  90. package/dist/commands/sys/panels/plugins-panel.d.ts +2 -2
  91. package/dist/commands/sys/panels/plugins-panel.js +2 -2
  92. package/dist/commands/sys/panels/prefs-panel.d.ts +2 -2
  93. package/dist/commands/sys/panels/prefs-panel.js +4 -4
  94. package/dist/commands/sys/panels/profiles-panel.d.ts +1 -1
  95. package/dist/commands/sys/panels/profiles-panel.js +14 -13
  96. package/dist/commands/sys/panels/provider-panel.d.ts +3 -3
  97. package/dist/commands/sys/panels/provider-panel.js +8 -8
  98. package/dist/commands/sys/settings-shell.d.ts +3 -3
  99. package/dist/commands/sys/settings-shell.js +15 -15
  100. package/dist/commands/sys/setup-settings-runner.d.ts +3 -3
  101. package/dist/commands/sys/setup-settings-runner.js +5 -5
  102. package/dist/commands/sys/setup-wizard.d.ts +4 -4
  103. package/dist/commands/sys/setup-wizard.js +8 -8
  104. package/dist/commands/sys/support/prepare.js +4 -4
  105. package/dist/commands/sys/support/submit.js +2 -2
  106. package/dist/commands/sys/sync-deps.js +0 -2
  107. package/dist/commands/sys/sync-project-guidance.js +2 -1
  108. package/dist/commands/sys/sysprompt.js +2 -1
  109. package/dist/core/__tests__/boot.test.js +0 -8
  110. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +23 -1
  111. package/dist/core/__tests__/canvas-db-boundary.test.d.ts +1 -0
  112. package/dist/core/__tests__/canvas-db-boundary.test.js +46 -0
  113. package/dist/core/__tests__/canvas.test.js +1 -1
  114. package/dist/core/__tests__/child-death-wake.test.js +0 -2
  115. package/dist/core/__tests__/cron-armed-context-failure.test.d.ts +1 -0
  116. package/dist/core/__tests__/cron-armed-context-failure.test.js +145 -0
  117. package/dist/core/__tests__/daemon-wedge.test.js +0 -4
  118. package/dist/core/__tests__/dead-node-policy-table.test.js +0 -4
  119. package/dist/core/__tests__/fixtures/fake-engine.js +4 -14
  120. package/dist/core/__tests__/fixtures/fake-pi-host.js +5 -5
  121. package/dist/core/__tests__/focuses.test.js +0 -6
  122. package/dist/core/__tests__/fs-utils.test.d.ts +1 -0
  123. package/dist/core/__tests__/fs-utils.test.js +50 -0
  124. package/dist/core/__tests__/helpers/harness.js +1 -1
  125. package/dist/core/__tests__/inline-memory-refs.test.js +0 -2
  126. package/dist/core/__tests__/integration/broker-fork-seam.test.js +0 -2
  127. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +3 -16
  128. package/dist/core/__tests__/integration/command-plugins.test.js +0 -24
  129. package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -6
  130. package/dist/core/__tests__/integration/flagship-lifecycle.test.js +0 -26
  131. package/dist/core/__tests__/integration/live-mutation-verbs.test.js +0 -4
  132. package/dist/core/__tests__/integration/live-mutation.test.js +1 -5
  133. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +1 -5
  134. package/dist/core/__tests__/integration/revive.test.js +0 -12
  135. package/dist/core/__tests__/integration/subscription-delivery.test.js +0 -6
  136. package/dist/core/__tests__/integration/tmux-surface.test.js +24 -8
  137. package/dist/core/__tests__/integration/worktree-reap.test.js +0 -6
  138. package/dist/core/__tests__/lifecycle.test.js +0 -18
  139. package/dist/core/__tests__/memory-resolver-precedence.test.js +1 -1
  140. package/dist/core/__tests__/migration.test.js +2 -2
  141. package/dist/core/__tests__/on-read-dedup-resume.test.js +3 -3
  142. package/dist/core/__tests__/pid-identity-match.test.js +1 -1
  143. package/dist/core/__tests__/plugin-kinds.test.js +1 -1
  144. package/dist/core/__tests__/plugin-page-components.test.js +1 -1
  145. package/dist/core/__tests__/preview-registry-sync.test.js +42 -1
  146. package/dist/core/__tests__/respawn-throttle.test.js +0 -4
  147. package/dist/core/__tests__/revive-all.test.js +5 -5
  148. package/dist/core/__tests__/revive-parked-fresh.test.js +62 -11
  149. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +0 -8
  150. package/dist/core/__tests__/seam/broker-attach-stream.test.js +0 -8
  151. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +0 -2
  152. package/dist/core/__tests__/tmux-driver-boundary.test.d.ts +1 -0
  153. package/dist/core/__tests__/tmux-driver-boundary.test.js +50 -0
  154. package/dist/core/activity-signal.d.ts +7 -0
  155. package/dist/core/activity-signal.js +38 -0
  156. package/dist/core/auto-update.js +2 -1
  157. package/dist/core/bootstrap.js +2 -1
  158. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
  159. package/dist/core/canvas/__tests__/remote-transport.test.js +1 -1
  160. package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
  161. package/dist/core/canvas/browse/app.js +3 -2
  162. package/dist/core/canvas/browse/model.d.ts +1 -1
  163. package/dist/core/canvas/browse/model.js +0 -4
  164. package/dist/core/canvas/canvas.d.ts +22 -0
  165. package/dist/core/canvas/canvas.js +50 -18
  166. package/dist/core/canvas/crons.js +0 -6
  167. package/dist/core/canvas/db.d.ts +14 -20
  168. package/dist/core/canvas/db.js +30 -1332
  169. package/dist/core/canvas/focuses.js +0 -4
  170. package/dist/core/canvas/history.js +2 -1
  171. package/dist/core/canvas/migrations.d.ts +20 -0
  172. package/dist/core/canvas/migrations.js +1339 -0
  173. package/dist/core/canvas/nav-model.js +1 -11
  174. package/dist/core/canvas/nav-render.js +0 -8
  175. package/dist/core/canvas/remote-canvas-source.js +0 -2
  176. package/dist/core/canvas/render-source.js +31 -13
  177. package/dist/core/command-manifests/manifest.js +0 -10
  178. package/dist/core/command-manifests/schema.js +0 -4
  179. package/dist/core/command-plugins/transport/http-invoke.js +0 -14
  180. package/dist/core/command.js +0 -4
  181. package/dist/core/config.js +2 -1
  182. package/dist/core/feed/inbox.d.ts +10 -0
  183. package/dist/core/feed/inbox.js +18 -12
  184. package/dist/core/fs-utils.d.ts +9 -2
  185. package/dist/core/fs-utils.js +7 -15
  186. package/dist/core/help.js +0 -8
  187. package/dist/core/host-exports/export.d.ts +2 -2
  188. package/dist/core/host-exports/export.js +9 -12
  189. package/dist/core/human/answer.d.ts +2 -2
  190. package/dist/core/human/convention.d.ts +2 -5
  191. package/dist/core/human/convention.js +3 -13
  192. package/dist/core/human/page-schema.d.ts +2 -2
  193. package/dist/core/human/page-schema.js +2 -2
  194. package/dist/core/human/root.js +6 -23
  195. package/dist/core/human/scan.js +24 -7
  196. package/dist/core/human/tickets.d.ts +4 -1
  197. package/dist/core/human/tickets.js +20 -6
  198. package/dist/core/io.js +0 -12
  199. package/dist/core/memory/identity.js +0 -4
  200. package/dist/core/memory/inline-ref-inventory.js +3 -2
  201. package/dist/core/memory/lint.js +3 -6
  202. package/dist/core/memory-resolver.d.ts +5 -0
  203. package/dist/core/memory-resolver.js +14 -12
  204. package/dist/core/pagination.js +0 -2
  205. package/dist/core/preview-registry.d.ts +14 -0
  206. package/dist/core/preview-registry.js +2 -4
  207. package/dist/core/preview-result-path.js +2 -1
  208. package/dist/core/profiles/manifest.d.ts +37 -5
  209. package/dist/core/profiles/manifest.js +44 -24
  210. package/dist/core/profiles/select.js +0 -4
  211. package/dist/core/review/signal.js +4 -23
  212. package/dist/core/review/ticket-filter.d.ts +3 -3
  213. package/dist/core/review/ticket-filter.js +4 -9
  214. package/dist/core/runtime/__tests__/engine-route.test.js +1 -1
  215. package/dist/core/runtime/__tests__/spawn-env.test.js +0 -8
  216. package/dist/core/runtime/bearings-render.d.ts +56 -0
  217. package/dist/core/runtime/bearings-render.js +241 -0
  218. package/dist/core/runtime/bearings.d.ts +2 -17
  219. package/dist/core/runtime/bearings.js +12 -198
  220. package/dist/core/runtime/broker/auth-reload.d.ts +39 -0
  221. package/dist/core/runtime/broker/auth-reload.js +149 -0
  222. package/dist/core/runtime/broker/engine-drive.d.ts +26 -0
  223. package/dist/core/runtime/broker/engine-drive.js +257 -0
  224. package/dist/core/runtime/broker/engine-routing.d.ts +143 -0
  225. package/dist/core/runtime/broker/engine-routing.js +211 -0
  226. package/dist/core/runtime/broker/event-projection.js +3 -2
  227. package/dist/core/runtime/broker/fault-retry.js +5 -4
  228. package/dist/core/runtime/broker/frame-client.d.ts +38 -0
  229. package/dist/core/runtime/broker/frame-client.js +129 -0
  230. package/dist/core/runtime/broker/frame-dispatch.d.ts +12 -189
  231. package/dist/core/runtime/broker/frame-dispatch.js +89 -1476
  232. package/dist/core/runtime/broker/frame-engine-control.d.ts +20 -0
  233. package/dist/core/runtime/broker/frame-engine-control.js +231 -0
  234. package/dist/core/runtime/broker/frame-memory-refs.d.ts +19 -0
  235. package/dist/core/runtime/broker/frame-memory-refs.js +41 -0
  236. package/dist/core/runtime/broker/frame-model-query.d.ts +13 -0
  237. package/dist/core/runtime/broker/frame-model-query.js +69 -0
  238. package/dist/core/runtime/broker/frame-read.d.ts +19 -0
  239. package/dist/core/runtime/broker/frame-read.js +116 -0
  240. package/dist/core/runtime/broker/frame-replies.d.ts +24 -0
  241. package/dist/core/runtime/broker/frame-replies.js +64 -0
  242. package/dist/core/runtime/broker/frame-session.d.ts +34 -0
  243. package/dist/core/runtime/broker/frame-session.js +203 -0
  244. package/dist/core/runtime/broker/turn-ignition.d.ts +42 -0
  245. package/dist/core/runtime/broker/turn-ignition.js +74 -0
  246. package/dist/core/runtime/broker-extension-render.js +9 -85
  247. package/dist/core/runtime/broker-protocol.js +0 -4
  248. package/dist/core/runtime/broker-sdk.js +2 -1
  249. package/dist/core/runtime/broker.d.ts +1 -1
  250. package/dist/core/runtime/broker.js +5 -23
  251. package/dist/core/runtime/fault.js +3 -2
  252. package/dist/core/runtime/headless-pi.js +3 -2
  253. package/dist/core/runtime/host.js +2 -3
  254. package/dist/core/runtime/kickoff.js +0 -4
  255. package/dist/core/runtime/launch-target.js +2 -1
  256. package/dist/core/runtime/launch.js +3 -6
  257. package/dist/core/runtime/lifecycle.js +17 -26
  258. package/dist/core/runtime/managed-provider-cooling.d.ts +1 -1
  259. package/dist/core/runtime/naming.js +0 -2
  260. package/dist/core/runtime/nodes.js +2 -1
  261. package/dist/core/runtime/persona.js +0 -6
  262. package/dist/core/runtime/placement-tmux.js +2 -5
  263. package/dist/core/runtime/placement.js +4 -11
  264. package/dist/core/runtime/recap.js +4 -3
  265. package/dist/core/runtime/reopen.js +4 -7
  266. package/dist/core/runtime/reset.js +0 -4
  267. package/dist/core/runtime/resume-root.js +0 -2
  268. package/dist/core/runtime/revive.js +17 -13
  269. package/dist/core/runtime/session-list-cache.js +2 -2
  270. package/dist/core/runtime/shell-expansion.js +0 -2
  271. package/dist/core/runtime/spawn.js +0 -2
  272. package/dist/core/runtime/stream-watchdog.d.ts +0 -1
  273. package/dist/core/runtime/stream-watchdog.js +13 -18
  274. package/dist/core/runtime/tmux-bindings.js +0 -2
  275. package/dist/core/runtime/tmux-driver.d.ts +12 -0
  276. package/dist/core/runtime/tmux-driver.js +18 -13
  277. package/dist/core/runtime/warm-pool.js +0 -4
  278. package/dist/core/scope.js +4 -3
  279. package/dist/core/spawn.js +2 -3
  280. package/dist/core/subscription-state.js +10 -17
  281. package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +3 -3
  282. package/dist/core/substrate/render.js +0 -12
  283. package/dist/core/substrate/schema.js +0 -12
  284. package/dist/core/substrate/surface-match.js +0 -2
  285. package/dist/core/termrender/display.js +2 -1
  286. package/dist/core/tui/page-host.d.ts +3 -3
  287. package/dist/core/tui/page-host.js +4 -4
  288. package/dist/core/tui/panel.js +3 -3
  289. package/dist/core/wake.js +0 -12
  290. package/dist/core/worktree.js +0 -2
  291. package/dist/daemon/__tests__/reconciler-signature.test.d.ts +1 -0
  292. package/dist/daemon/__tests__/reconciler-signature.test.js +36 -0
  293. package/dist/daemon/api/__tests__/seam/api-server.test.js +2 -16
  294. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +3 -11
  295. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +0 -6
  296. package/dist/daemon/api/handlers/attach.js +2 -1
  297. package/dist/daemon/api/handlers/broker-ops.js +3 -6
  298. package/dist/daemon/api/handlers/canvas.js +12 -18
  299. package/dist/daemon/api/handlers/feedback-comments.js +0 -8
  300. package/dist/daemon/api/handlers/health.js +3 -2
  301. package/dist/daemon/api/handlers/human-requests.js +0 -10
  302. package/dist/daemon/api/handlers/human.js +1 -5
  303. package/dist/daemon/api/handlers/inbox.js +0 -18
  304. package/dist/daemon/api/handlers/memory.js +1 -1
  305. package/dist/daemon/api/handlers/messages.js +4 -8
  306. package/dist/daemon/api/handlers/nodes.js +10 -21
  307. package/dist/daemon/api/handlers/reports.js +1 -1
  308. package/dist/daemon/api/handlers/reviews.js +1 -6
  309. package/dist/daemon/api/map.js +0 -6
  310. package/dist/daemon/api/router.js +0 -4
  311. package/dist/daemon/api/server.js +2 -5
  312. package/dist/daemon/boot-migrations.d.ts +14 -0
  313. package/dist/daemon/boot-migrations.js +31 -0
  314. package/dist/daemon/cron/armed-context.d.ts +21 -0
  315. package/dist/daemon/cron/armed-context.js +100 -0
  316. package/dist/daemon/cron/capture.d.ts +20 -0
  317. package/dist/daemon/cron/capture.js +45 -0
  318. package/dist/daemon/cron/disposition.d.ts +19 -0
  319. package/dist/daemon/cron/disposition.js +52 -0
  320. package/dist/daemon/cron/escalation.d.ts +12 -0
  321. package/dist/daemon/cron/escalation.js +124 -0
  322. package/dist/daemon/cron/held.d.ts +4 -0
  323. package/dist/daemon/cron/held.js +20 -0
  324. package/dist/daemon/cron/lease-recovery.d.ts +3 -0
  325. package/dist/daemon/cron/lease-recovery.js +119 -0
  326. package/dist/daemon/cron/live-runs.d.ts +11 -0
  327. package/dist/daemon/cron/live-runs.js +23 -0
  328. package/dist/daemon/cron/passes.d.ts +11 -0
  329. package/dist/daemon/cron/passes.js +78 -0
  330. package/dist/daemon/cron/settle.d.ts +28 -0
  331. package/dist/daemon/cron/settle.js +37 -0
  332. package/dist/daemon/cron/sinks.d.ts +2 -0
  333. package/dist/daemon/cron/sinks.js +100 -0
  334. package/dist/daemon/cron-run.d.ts +5 -41
  335. package/dist/daemon/cron-run.js +193 -882
  336. package/dist/daemon/crtrd-cli.js +2 -1
  337. package/dist/daemon/crtrd.js +22 -22
  338. package/dist/daemon/fleet.js +0 -4
  339. package/dist/daemon/human/deliver-action.js +2 -1
  340. package/dist/daemon/human/sweep.js +15 -4
  341. package/dist/daemon/manage.d.ts +2 -2
  342. package/dist/daemon/manage.js +4 -13
  343. package/dist/daemon/pidfile.js +2 -2
  344. package/dist/daemon/reconcilers/broker-supervision.js +26 -29
  345. package/dist/daemon/reconcilers/dormant-inbox.d.ts +1 -1
  346. package/dist/daemon/reconcilers/dormant-inbox.js +2 -1
  347. package/dist/daemon/reconcilers/live-obligation.js +2 -2
  348. package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
  349. package/dist/daemon/reconcilers/pending-review-submit.js +2 -1
  350. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -1
  351. package/dist/daemon/reconcilers/storage-maintenance.js +2 -1
  352. package/dist/daemon/review/companion.js +2 -2
  353. package/dist/daemon/review/sweep.js +8 -3
  354. package/dist/migrations/001-surfaces-frontmatter.js +0 -6
  355. package/dist/migrations/corpus.js +2 -5
  356. package/dist/migrations/registry.js +15 -2
  357. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +0 -4
  358. package/dist/pi-extensions/broker-local.js +2 -1
  359. package/dist/pi-extensions/canvas-bash-valve.js +3 -17
  360. package/dist/pi-extensions/canvas-context-intro.js +2 -7
  361. package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
  362. package/dist/pi-extensions/canvas-goal-capture.js +2 -1
  363. package/dist/pi-extensions/canvas-inbox-watcher.js +5 -12
  364. package/dist/pi-extensions/canvas-passive-context.js +2 -5
  365. package/dist/pi-extensions/canvas-preview-result.js +2 -1
  366. package/dist/pi-extensions/canvas-prompt-scrub.js +2 -1
  367. package/dist/pi-extensions/canvas-recap.js +3 -16
  368. package/dist/pi-extensions/canvas-review-boundary.js +2 -5
  369. package/dist/pi-extensions/canvas-stophook.js +2 -26
  370. package/dist/pi-extensions/canvas-structured-output.js +2 -1
  371. package/dist/pi-extensions/canvas-tool-guide.js +2 -1
  372. package/dist/shared/__tests__/env-boundary.test.d.ts +1 -0
  373. package/dist/shared/__tests__/env-boundary.test.js +93 -0
  374. package/dist/shared/env.d.ts +68 -0
  375. package/dist/shared/env.js +171 -0
  376. package/package.json +3 -1
  377. package/runtime.lock.json +2 -2
  378. package/dist/core/canvas/render.d.ts +0 -47
  379. package/dist/core/canvas/render.js +0 -245
@@ -0,0 +1,20 @@
1
+ import type { AgentSessionServices, ModelRegistry } from '@earendil-works/pi-coding-agent';
2
+ import type { ClientToBroker } from '../broker-protocol.js';
3
+ import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
4
+ import type { FrameReplies } from './frame-replies.js';
5
+ import type { BrokerSession } from './read-ops.js';
6
+ /** The engine-command ops — every one of them a controller-gated mutation of
7
+ * the live engine's model, thinking level, queue, or session settings. */
8
+ export type EngineControlFrame = Extract<ClientToBroker, {
9
+ type: 'set_model' | 'deliver_custom_message' | 'cycle_model' | 'cycle_ladder' | 'cycle_thinking' | 'dequeue' | 'set_thinking_level' | 'set_auto_retry' | 'set_auto_compaction' | 'compact' | 'set_session_name';
10
+ }>;
11
+ export declare function createEngineControlHandler(deps: {
12
+ registry: BrokerClientRegistry;
13
+ replies: FrameReplies;
14
+ session: () => BrokerSession;
15
+ services: () => AgentSessionServices;
16
+ registryOf: (services: AgentSessionServices) => ModelRegistry;
17
+ persistModelChoice: (pinnedOverride?: boolean, userSelected?: boolean) => Promise<string | null>;
18
+ broadcastModelChanged: (persist?: boolean, pinnedOverride?: boolean, userSelected?: boolean) => Promise<void>;
19
+ formatModelSpec: (model: BrokerSession['model'], thinkingLevel?: string) => string | null;
20
+ }): (client: BrokerClient, frame: EngineControlFrame) => void;
@@ -0,0 +1,231 @@
1
+ import { configuredLadderProviders, isProviderPinnedModelToken, nextLadderModel, normalizeModel, } from '../launch.js';
2
+ import { isManagedProvider, readSubscriptionPool, } from '../../subscription-state.js';
3
+ import { parseModelSpec } from '../model-selection.js';
4
+ import { resolveModelQuery } from './frame-model-query.js';
5
+ export function createEngineControlHandler(deps) {
6
+ const { registry, replies, session, services, registryOf, persistModelChoice, broadcastModelChanged, formatModelSpec, } = deps;
7
+ const { notWritable, ackTo, engineErrorTo } = replies;
8
+ const modelRegistry = () => registryOf(services());
9
+ const noSuchModel = (client, message) => registry.sendFrame(client, { type: 'error', code: 'engine_error', message });
10
+ const setModel = (client, frame) => {
11
+ if (notWritable(client, 'set the model'))
12
+ return;
13
+ const requested = parseModelSpec(frame.model);
14
+ let model;
15
+ let normalized;
16
+ try {
17
+ // Tier/alias specs (`opus`, `strong`, `anthropic/strong`, …) MUST resolve
18
+ // through the config ladder before touching the registry: the fuzzy
19
+ // registry search rank-ties `opus` between claude-3-opus-20240229 and
20
+ // claude-opus-4-8, and the deprecated 2024 model wins on catalog order.
21
+ // Concrete `provider/id` specs and genuine substrings (`fable`) pass
22
+ // through normalizeModel unchanged and take the exact/search path below.
23
+ normalized = parseModelSpec(normalizeModel(requested.modelSpec));
24
+ model = resolveModelQuery(modelRegistry(), normalized.modelSpec);
25
+ }
26
+ catch (err) {
27
+ // N2: registry.find should never throw on the real SDK, but a degenerate
28
+ // engine must still get a reply rather than a silently-dropped frame.
29
+ engineErrorTo(client)(err);
30
+ return;
31
+ }
32
+ if (model === undefined) {
33
+ noSuchModel(client, `no model matching '${frame.model}' in the registry`);
34
+ return;
35
+ }
36
+ void session()
37
+ .setModel(model)
38
+ .then(async () => {
39
+ // The caller's explicit `:thinking` wins; else the ladder cell's.
40
+ const thinking = requested.thinkingLevel ?? normalized.thinkingLevel;
41
+ if (thinking !== undefined)
42
+ session().setThinkingLevel(thinking);
43
+ // frame.pinned, when the sender pre-normalized `frame.model` (e.g.
44
+ // setModelLive), carries the RAW-token pin decision explicitly.
45
+ // Otherwise frame.model itself IS the raw/explicit token (a
46
+ // slash-command arg, or a concrete picker pick) and derives it.
47
+ await broadcastModelChanged(true, frame.pinned !== undefined
48
+ ? frame.pinned
49
+ : isProviderPinnedModelToken(frame.model), true);
50
+ ackTo(client, 'set_model');
51
+ })
52
+ .catch(engineErrorTo(client));
53
+ };
54
+ const deliverCustomMessage = (client, frame) => {
55
+ if (notWritable(client, 'deliver a custom message'))
56
+ return;
57
+ // Never triggers a turn either way. `nextTurn` folds the message into
58
+ // whatever turn comes next (situational context); omitting it pushes
59
+ // straight onto the message list so it lands FIRST in a still-empty
60
+ // session (a freshly-claimed spare's bearings). The inbox watcher's own
61
+ // situational send uses `followUp` + triggerTurn because there its job
62
+ // IS to wake the node.
63
+ void session()
64
+ .sendCustomMessage({
65
+ customType: frame.customType,
66
+ content: frame.content,
67
+ display: true,
68
+ ...(frame.details !== undefined ? { details: frame.details } : {}),
69
+ }, ...(frame.deliverAs !== undefined ? [{ deliverAs: frame.deliverAs }] : []))
70
+ .then(() => ackTo(client, 'deliver_custom_message'))
71
+ .catch(engineErrorTo(client));
72
+ };
73
+ const cycleModel = (client, frame) => {
74
+ if (notWritable(client, 'cycle the model'))
75
+ return;
76
+ void session()
77
+ .cycleModel(frame.direction)
78
+ .then(async () => {
79
+ await broadcastModelChanged(true, undefined, true);
80
+ ackTo(client, 'cycle_model');
81
+ })
82
+ .catch(engineErrorTo(client));
83
+ };
84
+ const cycleLadder = (client, frame) => {
85
+ if (notWritable(client, 'cycle the model ladder'))
86
+ return;
87
+ // Resolve the next interleaved-ladder rung from the current model+thinking
88
+ // spec, then reuse the set_model path (registry resolve + thinking apply).
89
+ // The cycle only offers signed-in provider columns — same rule as the
90
+ // setup wizard's Models panel: pool occupancy for a managed provider
91
+ // (read live, so a login that landed mid-session counts immediately),
92
+ // else the registry's auth status. When nobody is signed in the helper
93
+ // returns every provider, so the cycle stays walkable pre-login.
94
+ const offeredProviders = (() => {
95
+ try {
96
+ const models = modelRegistry();
97
+ return configuredLadderProviders((providerId) => (isManagedProvider(providerId) &&
98
+ readSubscriptionPool(providerId).length > 0) ||
99
+ models.getProviderAuthStatus(providerId).configured);
100
+ }
101
+ catch {
102
+ return undefined; // auth state unreadable — cycle the full ladder
103
+ }
104
+ })();
105
+ const currentSpec = formatModelSpec(session().model, session().thinkingLevel) ?? '';
106
+ const nextSpec = parseModelSpec(nextLadderModel(currentSpec, frame.direction === 'backward' ? -1 : 1, offeredProviders));
107
+ let ladderModel;
108
+ try {
109
+ ladderModel = resolveModelQuery(modelRegistry(), nextSpec.modelSpec);
110
+ }
111
+ catch (err) {
112
+ engineErrorTo(client)(err);
113
+ return;
114
+ }
115
+ if (ladderModel === undefined) {
116
+ noSuchModel(client, `ladder model '${nextSpec.modelSpec}' not in the registry`);
117
+ return;
118
+ }
119
+ void session()
120
+ .setModel(ladderModel)
121
+ .then(async () => {
122
+ if (nextSpec.thinkingLevel !== undefined)
123
+ session().setThinkingLevel(nextSpec.thinkingLevel);
124
+ await broadcastModelChanged(true, undefined, true);
125
+ ackTo(client, 'cycle_ladder');
126
+ })
127
+ .catch(engineErrorTo(client));
128
+ };
129
+ const cycleThinking = (client) => {
130
+ if (notWritable(client, 'cycle the thinking level'))
131
+ return;
132
+ void (async () => {
133
+ session().cycleThinkingLevel();
134
+ await persistModelChoice(undefined, true);
135
+ ackTo(client, 'cycle_thinking');
136
+ })().catch(engineErrorTo(client));
137
+ };
138
+ const dequeue = (client, frame) => {
139
+ if (notWritable(client, 'dequeue messages', frame.id))
140
+ return;
141
+ try {
142
+ const { steering, followUp } = session().clearQueue();
143
+ registry.sendFrame(client, {
144
+ type: 'data',
145
+ id: frame.id,
146
+ kind: 'dequeue',
147
+ steering,
148
+ followUp,
149
+ });
150
+ }
151
+ catch (err) {
152
+ engineErrorTo(client, frame.id)(err);
153
+ }
154
+ };
155
+ const setThinkingLevel = (client, frame) => {
156
+ if (notWritable(client, 'set the thinking level'))
157
+ return;
158
+ void (async () => {
159
+ session().setThinkingLevel(frame.level);
160
+ await persistModelChoice(undefined, true);
161
+ ackTo(client, 'set_thinking_level');
162
+ })().catch(engineErrorTo(client));
163
+ };
164
+ const setAutoRetry = (client, frame) => {
165
+ if (notWritable(client, 'set auto-retry'))
166
+ return;
167
+ try {
168
+ session().setAutoRetryEnabled(frame.enabled);
169
+ ackTo(client, 'set_auto_retry');
170
+ }
171
+ catch (err) {
172
+ engineErrorTo(client)(err);
173
+ }
174
+ };
175
+ const setAutoCompaction = (client, frame) => {
176
+ if (notWritable(client, 'set auto-compaction'))
177
+ return;
178
+ try {
179
+ session().setAutoCompactionEnabled(frame.enabled);
180
+ ackTo(client, 'set_auto_compaction');
181
+ }
182
+ catch (err) {
183
+ engineErrorTo(client)(err);
184
+ }
185
+ };
186
+ const compact = (client, frame) => {
187
+ if (notWritable(client, 'compact the session'))
188
+ return;
189
+ void session()
190
+ .compact(frame.instructions)
191
+ .then(() => ackTo(client, 'compact'))
192
+ .catch(engineErrorTo(client));
193
+ };
194
+ const setSessionName = (client, frame) => {
195
+ if (notWritable(client, 'rename the session'))
196
+ return;
197
+ try {
198
+ session().setSessionName(frame.name);
199
+ ackTo(client, 'set_session_name');
200
+ }
201
+ catch (err) {
202
+ engineErrorTo(client)(err);
203
+ }
204
+ };
205
+ return (client, frame) => {
206
+ switch (frame.type) {
207
+ case 'set_model':
208
+ return setModel(client, frame);
209
+ case 'deliver_custom_message':
210
+ return deliverCustomMessage(client, frame);
211
+ case 'cycle_model':
212
+ return cycleModel(client, frame);
213
+ case 'cycle_ladder':
214
+ return cycleLadder(client, frame);
215
+ case 'cycle_thinking':
216
+ return cycleThinking(client);
217
+ case 'dequeue':
218
+ return dequeue(client, frame);
219
+ case 'set_thinking_level':
220
+ return setThinkingLevel(client, frame);
221
+ case 'set_auto_retry':
222
+ return setAutoRetry(client, frame);
223
+ case 'set_auto_compaction':
224
+ return setAutoCompaction(client, frame);
225
+ case 'compact':
226
+ return compact(client, frame);
227
+ case 'set_session_name':
228
+ return setSessionName(client, frame);
229
+ }
230
+ };
231
+ }
@@ -0,0 +1,19 @@
1
+ import type { RefMeta } from '../broker-protocol.js';
2
+ import type { BrokerSession } from './read-ops.js';
3
+ /**
4
+ * Inline memory-reference inventory (design: inline memory references) — a
5
+ * single broker-level memo shared by BOTH the `list_memory_refs` read-op reply
6
+ * and per-submission guidance resolution, so a burst of reads + submissions
7
+ * between reloads never re-walks the corpus per call. Invalidated only when the
8
+ * corpus is KNOWN to have changed (a successful `session.reload()`); a failed
9
+ * reload leaves the memo intact.
10
+ */
11
+ export type MemoryRefGuidance = {
12
+ /** The memoized inventory rows the `list_memory_refs` read-op replies with. */
13
+ refs(): RefMeta[];
14
+ /** The hidden guidance text for a human frame's verbatim text, or `null`. */
15
+ guidanceFor(text: string): string | null;
16
+ /** Drop the memo AND the shared substrate parse cache it reads through. */
17
+ invalidate(): void;
18
+ };
19
+ export declare function createMemoryRefGuidance(session: () => BrokerSession): MemoryRefGuidance;
@@ -0,0 +1,41 @@
1
+ import { emitEvent } from '../../events/emit.js';
2
+ import { buildRefInventory } from '../../memory/inline-ref-inventory.js';
3
+ import { buildGuidance } from '../../memory/inline-ref-guidance.js';
4
+ import { clearSessionCache } from '../../substrate/session-cache.js';
5
+ import { isLeadingEngineCommand } from './engine-routing.js';
6
+ export function createMemoryRefGuidance(session) {
7
+ let memoized = null;
8
+ const inventory = () => {
9
+ if (memoized === null)
10
+ memoized = buildRefInventory();
11
+ return memoized;
12
+ };
13
+ return {
14
+ refs: () => inventory().refs,
15
+ /** `null` when no guidance should be sent (a leading engine command/skill/
16
+ * template, no resolvable refs, or a degraded inventory/guidance build —
17
+ * work item 6: any failure here degrades to "no guidance", logged once as a
18
+ * structured warning, and NEVER blocks/alters the human delivery). */
19
+ guidanceFor(text) {
20
+ try {
21
+ if (isLeadingEngineCommand(text, session()))
22
+ return null;
23
+ return buildGuidance(text, inventory().names);
24
+ }
25
+ catch (err) {
26
+ emitEvent({
27
+ level: 'warn',
28
+ event: 'broker.memory_refs.guidance_resolve_failed',
29
+ error: err,
30
+ });
31
+ return null;
32
+ }
33
+ },
34
+ invalidate() {
35
+ memoized = null;
36
+ // Without this, a fresh buildRefInventory() would still read the stale
37
+ // cached docs out of the shared substrate parse cache.
38
+ clearSessionCache();
39
+ },
40
+ };
41
+ }
@@ -0,0 +1,13 @@
1
+ import type { ModelRegistry } from '@earendil-works/pi-coding-agent';
2
+ /** A model resolved out of the registry, or undefined when nothing matches. */
3
+ export type ResolvedModel = ReturnType<ModelRegistry['find']>;
4
+ /** Resolve a `provider/id` model spec against the LIVE services registry (which
5
+ * carries any extension-registered providers). Pure: returns undefined on a
6
+ * malformed spec or an unknown model. */
7
+ export declare function findModelSpec(registry: ModelRegistry, spec: string): ResolvedModel;
8
+ /** Resolve a model from a free-text query — `/model opus`, `/model fable`. Tries
9
+ * the exact `provider/id` spec first; failing that, searches the registry the
10
+ * way the picker's search box does (case-insensitive substring on the model id
11
+ * and the `provider/id` key) and returns the BEST match, preferring authed/
12
+ * available models. Returns undefined only when nothing matches. */
13
+ export declare function resolveModelQuery(registry: ModelRegistry, query: string): ResolvedModel;
@@ -0,0 +1,69 @@
1
+ import { parseModelSpec } from '../model-selection.js';
2
+ import { modelKey } from './read-ops.js';
3
+ /** Resolve a `provider/id` model spec against the LIVE services registry (which
4
+ * carries any extension-registered providers). Pure: returns undefined on a
5
+ * malformed spec or an unknown model. */
6
+ export function findModelSpec(registry, spec) {
7
+ const { modelSpec } = parseModelSpec(spec);
8
+ const slash = modelSpec.indexOf('/');
9
+ if (slash <= 0)
10
+ return undefined;
11
+ return registry.find(modelSpec.slice(0, slash), modelSpec.slice(slash + 1));
12
+ }
13
+ /** Resolve a model from a free-text query — `/model opus`, `/model fable`. Tries
14
+ * the exact `provider/id` spec first; failing that, searches the registry the
15
+ * way the picker's search box does (case-insensitive substring on the model id
16
+ * and the `provider/id` key) and returns the BEST match, preferring authed/
17
+ * available models. Returns undefined only when nothing matches. */
18
+ export function resolveModelQuery(registry, query) {
19
+ const exact = findModelSpec(registry, query);
20
+ if (exact)
21
+ return exact;
22
+ const q = query.trim().toLowerCase();
23
+ if (q === '')
24
+ return undefined;
25
+ let all;
26
+ try {
27
+ all = registry.getAll();
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ let availableKeys;
33
+ try {
34
+ availableKeys = new Set(registry.getAvailable().map(modelKey));
35
+ }
36
+ catch {
37
+ availableKeys = new Set();
38
+ }
39
+ // Rank: exact id (100) > exact provider/id (90) > id prefix (80) > id
40
+ // substring (60) > provider/id substring (40); +5 if the model is available.
41
+ const rank = (m) => {
42
+ const id = m.id.toLowerCase();
43
+ const key = modelKey(m).toLowerCase();
44
+ let base = -1;
45
+ if (id === q)
46
+ base = 100;
47
+ else if (key === q)
48
+ base = 90;
49
+ else if (id.startsWith(q))
50
+ base = 80;
51
+ else if (id.includes(q))
52
+ base = 60;
53
+ else if (key.includes(q))
54
+ base = 40;
55
+ else
56
+ return -1;
57
+ return base + (availableKeys.has(modelKey(m)) ? 5 : 0);
58
+ };
59
+ let best;
60
+ let bestRank = 0;
61
+ for (const m of all) {
62
+ const r = rank(m);
63
+ if (r > bestRank) {
64
+ bestRank = r;
65
+ best = m;
66
+ }
67
+ }
68
+ return best;
69
+ }
@@ -0,0 +1,19 @@
1
+ import type { ClientToBroker } from '../broker-protocol.js';
2
+ import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
3
+ import type { FrameReplies } from './frame-replies.js';
4
+ import type { MemoryRefGuidance } from './frame-memory-refs.js';
5
+ import { type BrokerSession } from './read-ops.js';
6
+ /** The read ops — none of them controller-gated: they are pure reads that
7
+ * drive nothing in the engine, so terminal observer connections populate their
8
+ * palettes and pickers through these too. */
9
+ export type ReadFrame = Extract<ClientToBroker, {
10
+ type: 'get_commands' | 'list_models' | 'list_scoped_models' | 'list_memory_refs' | 'get_tree' | 'get_settings' | 'list_sessions' | 'get_human_fork_coordinates';
11
+ }>;
12
+ export declare function createReadHandler(deps: {
13
+ registry: BrokerClientRegistry;
14
+ replies: FrameReplies;
15
+ session: () => BrokerSession;
16
+ boundaryReviewId: string | undefined;
17
+ memoryRefs: MemoryRefGuidance;
18
+ formatExactModelSpec: (model: BrokerSession['model'], thinkingLevel?: string) => string | null;
19
+ }): (client: BrokerClient, frame: ReadFrame) => void;
@@ -0,0 +1,116 @@
1
+ import { SessionListCache } from '../session-list-cache.js';
2
+ import { sessionListCachePath } from '../../canvas/paths.js';
3
+ import { engineLeadingCommandTokens } from './engine-routing.js';
4
+ import { buildCommandList, buildGetTreeData, buildListMemoryRefsData, buildListModelsData, buildScopedModelsData, buildSessionsData, buildSettingsData, } from './read-ops.js';
5
+ export function createReadHandler(deps) {
6
+ const { registry, replies, session, boundaryReviewId, memoryRefs, formatExactModelSpec } = deps;
7
+ // Cache per-session wire info keyed by (path, mtime, size) so reopening the
8
+ // picker does not re-read the whole corpus (see session-list-cache.ts).
9
+ const sessionListCache = new SessionListCache(sessionListCachePath());
10
+ sessionListCache.prewarm(session().sessionManager.getSessionDir());
11
+ return (client, frame) => {
12
+ switch (frame.type) {
13
+ case 'get_commands': {
14
+ // OBSERVERS may call this: the merged command inventory is static
15
+ // (extensions + templates + skills + builtins) and drives nothing in the
16
+ // engine, so it is not a controller-gated op. Terminal observer
17
+ // connections use it to populate the command palette.
18
+ // The merged command list rides in ack.detail as JSON (the foundation's
19
+ // AckFrame.detail field) — the viewer JSON.parses it when for ===
20
+ // 'get_commands'. Keeps every command op a uniform ack reply.
21
+ try {
22
+ replies.ackTo(client, 'get_commands', true, JSON.stringify(buildCommandList(session())));
23
+ }
24
+ catch (err) {
25
+ replies.engineErrorTo(client)(err);
26
+ }
27
+ break;
28
+ }
29
+ // read-op picker data (§5 Unit A)
30
+ // NOT controller-gated: read-only, like get_commands. Terminal observer
31
+ // connections populate pickers through these too.
32
+ case 'list_models': {
33
+ try {
34
+ registry.sendFrame(client, buildListModelsData(frame.id, session()));
35
+ }
36
+ catch (err) {
37
+ replies.engineErrorTo(client, frame.id)(err);
38
+ }
39
+ break;
40
+ }
41
+ case 'list_scoped_models': {
42
+ try {
43
+ registry.sendFrame(client, buildScopedModelsData(frame.id, session()));
44
+ }
45
+ catch (err) {
46
+ replies.engineErrorTo(client, frame.id)(err);
47
+ }
48
+ break;
49
+ }
50
+ case 'list_memory_refs': {
51
+ try {
52
+ registry.sendFrame(client, buildListMemoryRefsData(frame.id, memoryRefs.refs(), engineLeadingCommandTokens(session())));
53
+ }
54
+ catch (err) {
55
+ replies.engineErrorTo(client, frame.id)(err);
56
+ }
57
+ break;
58
+ }
59
+ case 'get_tree': {
60
+ try {
61
+ registry.sendFrame(client, buildGetTreeData(frame.id, session(), boundaryReviewId));
62
+ }
63
+ catch (err) {
64
+ replies.engineErrorTo(client, frame.id)(err);
65
+ }
66
+ break;
67
+ }
68
+ case 'get_settings': {
69
+ try {
70
+ registry.sendFrame(client, buildSettingsData(frame.id, session()));
71
+ }
72
+ catch (err) {
73
+ replies.engineErrorTo(client, frame.id)(err);
74
+ }
75
+ break;
76
+ }
77
+ case 'list_sessions': {
78
+ // Session listing reads the session dir off disk — async; reply when it
79
+ // resolves (or relay the error, correlated by id so the viewer doesn't hang).
80
+ void buildSessionsData(frame.id, frame.scope ?? 'cwd', session(), sessionListCache)
81
+ .then((d) => registry.sendFrame(client, d))
82
+ .catch(replies.engineErrorTo(client, frame.id));
83
+ break;
84
+ }
85
+ case 'get_human_fork_coordinates': {
86
+ // NOT controller-gated: this is a pure observer read. Branch creation is
87
+ // intentionally delegated to a detached SDK manager in the ask publisher;
88
+ // calling createBranchedSession here corrupts this live manager's identity.
89
+ try {
90
+ const sm = session().sessionManager;
91
+ const sourceFile = sm.getSessionFile();
92
+ const leafId = sm.getLeafId();
93
+ if (sourceFile === undefined ||
94
+ sourceFile === '' ||
95
+ leafId === null) {
96
+ replies.engineErrorTo(client, frame.id)(new Error('no persisted source session leaf'));
97
+ break;
98
+ }
99
+ registry.sendFrame(client, {
100
+ type: 'data',
101
+ id: frame.id,
102
+ kind: 'human_fork_coordinates',
103
+ sourceFile,
104
+ sourceSessionId: sm.getSessionId(),
105
+ leafId,
106
+ modelSpec: formatExactModelSpec(session().model, session().thinkingLevel),
107
+ });
108
+ }
109
+ catch (err) {
110
+ replies.engineErrorTo(client, frame.id)(err);
111
+ }
112
+ break;
113
+ }
114
+ }
115
+ };
116
+ }
@@ -0,0 +1,24 @@
1
+ import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
2
+ import type { BrokerSession } from './read-ops.js';
3
+ export declare const REVIEW_BOUNDARY_NAVIGATION_CODE = "review_boundary_navigation";
4
+ export declare class ReviewBoundaryNavigationError extends Error {
5
+ readonly targetId: string;
6
+ readonly code = "review_boundary_navigation";
7
+ constructor(targetId: string);
8
+ }
9
+ /** The reply vocabulary every frame handler answers a client with, bound once
10
+ * to the client registry that owns the sockets. */
11
+ export type FrameReplies = {
12
+ /** Reject a read-only client for a mutating op. The decision reads this
13
+ * client's OWN fixed role — there is no shared writer slot, so any number of
14
+ * writable clients pass concurrently. Returns true when rejected (the caller
15
+ * should `break`). */
16
+ notWritable(client: BrokerClient, what: string, id?: string): boolean;
17
+ ackTo(client: BrokerClient, op: string, ok?: boolean, detail?: string, id?: string): void;
18
+ engineErrorTo(client: BrokerClient, id?: string): (err: unknown) => void;
19
+ reviewBoundaryRefusalTo(client: BrokerClient, targetId: string): void;
20
+ };
21
+ export declare function createFrameReplies(registry: BrokerClientRegistry): FrameReplies;
22
+ /** True iff `entryId` sits BEFORE this review companion's visible transcript
23
+ * boundary — the navigation an ordinary node has no boundary for. */
24
+ export declare function isPreBoundaryReviewEntry(session: BrokerSession, boundaryReviewId: string | undefined, entryId: string): boolean;
@@ -0,0 +1,64 @@
1
+ import { isReviewBoundary } from '../session-visibility.js';
2
+ export const REVIEW_BOUNDARY_NAVIGATION_CODE = 'review_boundary_navigation';
3
+ export class ReviewBoundaryNavigationError extends Error {
4
+ targetId;
5
+ code = REVIEW_BOUNDARY_NAVIGATION_CODE;
6
+ constructor(targetId) {
7
+ super(`review companion cannot navigate before the visible transcript boundary (target: ${targetId})`);
8
+ this.targetId = targetId;
9
+ this.name = 'ReviewBoundaryNavigationError';
10
+ }
11
+ }
12
+ export function createFrameReplies(registry) {
13
+ return {
14
+ notWritable(client, what, id) {
15
+ if (client.role === 'controller')
16
+ return false;
17
+ registry.sendFrame(client, {
18
+ type: 'error',
19
+ code: 'read_only',
20
+ message: `a read-only client may not ${what}`,
21
+ // M1: echo a correlated request's id so its pending-by-id promise rejects
22
+ // rather than hanging (e.g. a read-only `dequeue`).
23
+ ...(id !== undefined ? { id } : {}),
24
+ });
25
+ return true;
26
+ },
27
+ ackTo(client, op, ok = true, detail, id) {
28
+ registry.sendFrame(client, {
29
+ type: 'ack',
30
+ for: op,
31
+ ok,
32
+ ...(detail !== undefined ? { detail } : {}),
33
+ ...(id !== undefined ? { id } : {}),
34
+ });
35
+ },
36
+ engineErrorTo(client, id) {
37
+ return (err) => registry.sendFrame(client, {
38
+ type: 'error',
39
+ code: 'engine_error',
40
+ message: String(err),
41
+ // M1: a correlated read-op / dequeue failure echoes its id so the viewer's
42
+ // pending-by-id promise rejects instead of hanging; uncorrelated callers
43
+ // pass no id (unchanged behavior).
44
+ ...(id !== undefined ? { id } : {}),
45
+ });
46
+ },
47
+ reviewBoundaryRefusalTo(client, targetId) {
48
+ const refusal = new ReviewBoundaryNavigationError(targetId);
49
+ registry.sendFrame(client, {
50
+ type: 'error',
51
+ code: refusal.code,
52
+ message: refusal.message,
53
+ });
54
+ },
55
+ };
56
+ }
57
+ /** True iff `entryId` sits BEFORE this review companion's visible transcript
58
+ * boundary — the navigation an ordinary node has no boundary for. */
59
+ export function isPreBoundaryReviewEntry(session, boundaryReviewId, entryId) {
60
+ return (boundaryReviewId !== undefined &&
61
+ !session.sessionManager
62
+ .getBranch(entryId)
63
+ .some((entry) => isReviewBoundary(entry, boundaryReviewId)));
64
+ }
@@ -0,0 +1,34 @@
1
+ import type { ClientToBroker } from '../broker-protocol.js';
2
+ import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
3
+ import type { FrameReplies } from './frame-replies.js';
4
+ import type { MemoryRefGuidance } from './frame-memory-refs.js';
5
+ import type { RebindDriver } from './rebind.js';
6
+ import type { BrokerSession } from './read-ops.js';
7
+ /** True iff a parsed JSONL branch-entry line is a hidden (`display:false`)
8
+ * custom message — the general display boundary crossed if left in an
9
+ * exported branch (not just `crtr-memory-refs` guidance; any hidden custom
10
+ * content). Covers both persisted shapes a branch entry can take: a
11
+ * `type:'custom_message'` entry (display at the entry's own top level) and a
12
+ * `type:'message'` entry wrapping a `role:'custom'` message (display nested
13
+ * under `.message`). */
14
+ export declare function isHiddenJsonlBranchEntry(entry: unknown): boolean;
15
+ /** Rewrite an already-exported JSONL file in place, dropping every hidden
16
+ * (`display:false`) branch entry while re-chaining `parentId` so the
17
+ * remaining entries stay a single valid linear branch — applying the general
18
+ * display:false boundary to `session.exportToJsonl`'s own output, which
19
+ * serializes every entry verbatim regardless of display. The header line
20
+ * (line 1) is copied through untouched. */
21
+ export declare function stripHiddenEntriesFromJsonlExport(filePath: string): void;
22
+ /** The ops that navigate, replace, or publish this node's session. */
23
+ export type SessionFrame = Extract<ClientToBroker, {
24
+ type: 'navigate_tree' | 'reload' | 'export' | 'new_session' | 'switch_session' | 'fork' | 'clone' | 'share';
25
+ }>;
26
+ export declare function createSessionHandler(deps: {
27
+ registry: BrokerClientRegistry;
28
+ replies: FrameReplies;
29
+ rebind: RebindDriver;
30
+ session: () => BrokerSession;
31
+ boundaryReviewId: string | undefined;
32
+ memoryRefs: MemoryRefGuidance;
33
+ reWelcomeAll: () => void;
34
+ }): (client: BrokerClient, frame: SessionFrame) => void;