@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,211 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { emitEvent } from '../../events/emit.js';
3
+ /**
4
+ * Route a writable client's `prompt`/`follow_up` frame against the LIVE session
5
+ * state. The client picks its frame type off a possibly-STALE `isStreaming`
6
+ * snapshot, so the broker is authoritative and the client's choice is a HINT:
7
+ *
8
+ * - m-B (streaming-safe prompt): a `prompt` arriving mid-stream needs
9
+ * PromptOptions.streamingBehavior ("Required if streaming"; prompt() throws
10
+ * without it). "steer" mirrors pi interactive-mode's own
11
+ * Enter-while-streaming submit (`prompt(text, { streamingBehavior: "steer" })`).
12
+ * - m-C (idle-safe follow_up): pi's followUp() only enqueues into a RUNNING
13
+ * agent loop — on an idle session nothing ever drains the queue and the text
14
+ * is silently lost. An idle `follow_up` therefore runs as a prompt now.
15
+ *
16
+ * driveEngine calls this synchronously with `session.isStreaming`, so the
17
+ * routed state cannot go stale between the check and the engine call.
18
+ * C1: images ride as a field on PromptOptions (pi: `prompt(text, { images })`).
19
+ */
20
+ export function resolveEngineRoute(frame, isStreaming) {
21
+ if (frame.type === 'follow_up' && isStreaming)
22
+ return { call: 'followUp' };
23
+ const options = {};
24
+ if (frame.images !== undefined)
25
+ options.images = frame.images;
26
+ if (isStreaming)
27
+ options.streamingBehavior = 'steer';
28
+ return { call: 'prompt', options };
29
+ }
30
+ /**
31
+ * True iff `text` would be consumed by pi's OWN leading-name preflight before
32
+ * `session.prompt/steer/followUp` ever sees it as prose — an extension command
33
+ * (`session.extensionRunner.getCommand`), a `/skill:name` skill, or a `/name`
34
+ * prompt template (mirrors `_tryExecuteExtensionCommand` / `_expandSkillCommand`
35
+ * / `expandPromptTemplate` in the pinned SDK verbatim, including the exact
36
+ * name-parsing substrings). Inline memory-reference guidance must NEVER fire
37
+ * for these — the leading token routes to the engine's own command surface,
38
+ * not prose referencing a memory doc. Each lookup is independently wrapped so
39
+ * an engine missing one accessor (the fake, or a degraded session) degrades
40
+ * that check to "not a match" rather than aborting the whole classification —
41
+ * matching `buildCommandList`'s existing "no live extensionRunner" tolerance.
42
+ */
43
+ export function isLeadingEngineCommand(text, session) {
44
+ if (text.startsWith('/')) {
45
+ const spaceIndex = text.indexOf(' ');
46
+ const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
47
+ try {
48
+ if (session.extensionRunner?.getCommand(commandName))
49
+ return true;
50
+ }
51
+ catch {
52
+ /* engine without a live extensionRunner (e.g. the fake) — not a match */
53
+ }
54
+ }
55
+ if (text.startsWith('/skill:')) {
56
+ const spaceIndex = text.indexOf(' ');
57
+ const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
58
+ try {
59
+ if (session.resourceLoader?.getSkills().skills.some((s) => s.name === skillName))
60
+ return true;
61
+ }
62
+ catch {
63
+ /* engine without a live resourceLoader — not a match */
64
+ }
65
+ }
66
+ if (text.startsWith('/')) {
67
+ const match = text.match(/^\/([^\s]+)(?:\s+([\s\S]*))?$/);
68
+ const templateName = match?.[1];
69
+ if (templateName !== undefined) {
70
+ try {
71
+ if (session.promptTemplates?.some((t) => t.name === templateName))
72
+ return true;
73
+ }
74
+ catch {
75
+ /* engine without a live promptTemplates getter — not a match */
76
+ }
77
+ }
78
+ }
79
+ return false;
80
+ }
81
+ /** A `steer` frame normally maps directly to pi's steer queue. Engine commands,
82
+ * skills, and templates must instead pass through prompt()'s leading-command
83
+ * preflight; with streamingBehavior:'steer', ordinary expanded text still lands
84
+ * in the same queue. */
85
+ export function resolveSteerCall(text, session) {
86
+ return isLeadingEngineCommand(text, session) ? 'prompt' : 'steer';
87
+ }
88
+ /**
89
+ * Enumerate every leading-token STRING this session's engine would
90
+ * independently consume as a leading command/skill/template dispatch BEFORE
91
+ * inline memory-reference guidance ever applies — the read-op sibling of
92
+ * `isLeadingEngineCommand`'s point-lookup, gathered as a full enumeration for
93
+ * viewer-side suppression instead of a per-submission membership check. Same
94
+ * three sources, same shape of the leading match `isLeadingEngineCommand`
95
+ * checks (bare extension-command invocation names and prompt-template names,
96
+ * `skill:<name>` for skills), so `/${token}` round-trips through
97
+ * `isLeadingEngineCommand(text, session)` for every emitted token (locked by
98
+ * a test). Kept STRICTLY separate from `buildCommandList` (command
99
+ * completion) and the ref inventory (`RefMeta` rows) — this is neither.
100
+ */
101
+ export function engineLeadingCommandTokens(session) {
102
+ const out = [];
103
+ try {
104
+ for (const c of session.extensionRunner?.getRegisteredCommands() ?? [])
105
+ out.push(c.invocationName);
106
+ }
107
+ catch {
108
+ /* engine without a live extensionRunner (e.g. the fake) — none */
109
+ }
110
+ try {
111
+ for (const s of session.resourceLoader?.getSkills().skills ?? [])
112
+ out.push(`skill:${s.name}`);
113
+ }
114
+ catch {
115
+ /* engine without a live resourceLoader — none */
116
+ }
117
+ try {
118
+ for (const t of session.promptTemplates ?? [])
119
+ out.push(t.name);
120
+ }
121
+ catch {
122
+ /* engine without a live promptTemplates getter — none */
123
+ }
124
+ return out;
125
+ }
126
+ /**
127
+ * Choose how the hidden `crtr-memory-refs` guidance custom message rides
128
+ * alongside a human frame's own engine call (design Flow B). Pure over the
129
+ * frame's routing outcome — never reads live session state itself, so a
130
+ * caller can decide the delivery mode from the exact same `route`
131
+ * `resolveEngineRoute` already computed for the human call.
132
+ *
133
+ * - `steer` frame -> 'steer' (always; no route call exists).
134
+ * - a TRUE mid-stream follow_up (`routeCall === 'followUp'`) -> 'followUp'.
135
+ * - every other `prompt`/`follow_up` routed as `routeCall === 'prompt'`:
136
+ * idle -> 'nextTurn' (the ONLY case guidance precedes the human call —
137
+ * pi's `prompt()` reads+clears its pending-nextTurn queue synchronously
138
+ * into the SAME turn as the new user message); streaming (m-B
139
+ * steer-while-streaming) -> 'steer', never 'nextTurn' (that would attach
140
+ * to a later, unrelated turn).
141
+ */
142
+ export function chooseGuidanceDeliveryMode(frameType, routeCall, isStreaming) {
143
+ if (frameType === 'steer')
144
+ return 'steer';
145
+ if (routeCall === 'followUp')
146
+ return 'followUp';
147
+ return isStreaming ? 'steer' : 'nextTurn';
148
+ }
149
+ /**
150
+ * Deliver a `nextTurn` guidance custom message ATOMICALLY around the idle-
151
+ * prompt call it's meant to ride alongside. Pi's `_pendingNextTurnMessages`
152
+ * queue (the only public seam for `deliverAs:'nextTurn'`) is drained ONLY deep
153
+ * inside `session.prompt()`, well after several await points — extension-
154
+ * command interception, the `input` extension transform, a streaming reroute,
155
+ * model/auth validation, pre-turn compaction — that can return or throw BEFORE
156
+ * the drain ever runs. `sendCustomMessage`'s nextTurn branch pushes
157
+ * synchronously and unconditionally, so any of those early exits strands the
158
+ * pushed guidance in the queue to attach itself to whichever LATER, unrelated
159
+ * prompt happens to reach the drain first.
160
+ *
161
+ * There is no public SDK seam to observe or cancel a pending nextTurn message
162
+ * (`_pendingNextTurnMessages` is TS-`private` only — a plain runtime field, not
163
+ * `#private`), so this reaches into that one field and removes EXACTLY the
164
+ * entry this call pushed, identified by object identity via a locally-
165
+ * generated marker riding the message's own `details` field (never surfaced to
166
+ * the model, and irrelevant if the message WAS consumed normally — `details`
167
+ * is just carried through like any other field once folded into a real turn).
168
+ * Runs UNCONDITIONALLY after the human call settles, success or failure/early-
169
+ * return alike — a "successful" `prompt()` promise can just as easily be an
170
+ * early return that skipped the drain (an intercepted extension command, an
171
+ * `input` transform, a streaming reroute) as a genuine consumed turn, so only
172
+ * checking `.catch` is not enough.
173
+ */
174
+ export async function promptWithNextTurnGuidance(session, guidance, runPrompt) {
175
+ // Must survive Pi's structured-clone step when the queued custom message is
176
+ // folded into the turn. A Symbol reaches that step in `details` and aborts
177
+ // the real prompt with "could not be cloned" before the provider runs.
178
+ const marker = `crtr-memory-refs-pending:${randomUUID()}`;
179
+ let queued = false;
180
+ try {
181
+ await session.sendCustomMessage({ customType: 'crtr-memory-refs', content: guidance, display: false, details: marker }, { deliverAs: 'nextTurn' });
182
+ queued = true;
183
+ }
184
+ catch (err) {
185
+ emitEvent({
186
+ level: 'warn',
187
+ event: 'broker.memory_refs.guidance_send_failed',
188
+ error: err,
189
+ });
190
+ }
191
+ try {
192
+ await runPrompt();
193
+ }
194
+ finally {
195
+ if (queued)
196
+ clearOwnPendingNextTurnGuidance(session, marker);
197
+ }
198
+ }
199
+ /** Splice out exactly the pending-nextTurn entry `marker` identifies (object-
200
+ * identity match on `details`), if it is still present — a no-op (and safe)
201
+ * if `session.prompt()` already drained it normally, or if the session/field
202
+ * shape doesn't match (defensive against an engine without the field, e.g. a
203
+ * future SDK bump or a structurally-different test double). */
204
+ function clearOwnPendingNextTurnGuidance(session, marker) {
205
+ const pending = session._pendingNextTurnMessages;
206
+ if (!Array.isArray(pending))
207
+ return;
208
+ const idx = pending.findIndex((m) => m?.details === marker);
209
+ if (idx !== -1)
210
+ pending.splice(idx, 1);
211
+ }
@@ -1,6 +1,7 @@
1
1
  import { readConfig } from '../../config.js';
2
+ import { envStreamWatchdogMs } from '../../../shared/env.js';
2
3
  import { emitEvent } from '../../events/emit.js';
3
- import { releaseKnownStreamWait, resolveStreamWatchdogMs, StreamWatchdog } from '../stream-watchdog.js';
4
+ import { releaseKnownStreamWait, StreamWatchdog } from '../stream-watchdog.js';
4
5
  import { withoutYieldAbort } from '../session-cycles.js';
5
6
  import { DEFAULT_WORKING_ACTIVITY, randomWorkingActivity } from '../../../shared/working-activity.js';
6
7
  import { hydrateLiveAdvertisedCommandMessage, prepareAdvertisedCommandPersistence, } from '../advertised-command-invocation.js';
@@ -12,7 +13,7 @@ export class EventProjection {
12
13
  pendingUpdateTimer = null;
13
14
  workingActivity = DEFAULT_WORKING_ACTIVITY;
14
15
  streamWatchdog;
15
- streamWatchdogMs = resolveStreamWatchdogMs();
16
+ streamWatchdogMs = envStreamWatchdogMs();
16
17
  pendingBroadcast = null;
17
18
  broadcastGeneration = 0;
18
19
  constructor(deps) {
@@ -1,3 +1,4 @@
1
+ import { envModelIntent, envNodeCwd, envProfileId } from '../../../shared/env.js';
1
2
  import { isContextOverflow } from '@earendil-works/pi-ai';
2
3
  import { classify } from '../../fault-classifier.js';
3
4
  import { clearFault, isModelNotFoundError, isProviderUnconfiguredError, readFault, recordFault } from '../fault.js';
@@ -140,10 +141,10 @@ export class FaultRetry {
140
141
  const currentSpec = this.deps.formatModelSpec(candidateSession.model, candidateSession.thinkingLevel);
141
142
  if (currentSpec === null)
142
143
  return;
143
- const routeRequest = modelRequestFromConfig(this.deps.cfg.model, process.env['CRTR_MODEL_INTENT'], false);
144
+ const routeRequest = modelRequestFromConfig(this.deps.cfg.model, envModelIntent(), false);
144
145
  if (routeRequest === undefined)
145
146
  return;
146
- const routes = expandModelCandidates(routeRequest, process.env['CRTR_NODE_CWD'] ?? this.deps.cfg.cwd, process.env['CRTR_PROFILE_ID']);
147
+ const routes = expandModelCandidates(routeRequest, envNodeCwd() ?? this.deps.cfg.cwd, envProfileId());
147
148
  const current = parseModelSpec(currentSpec);
148
149
  const liveRank = routes.find((candidate) => `${candidate.providerId}/${candidate.modelId}` === current.modelSpec && candidate.thinkingLevel === current.thinkingLevel)?.rank;
149
150
  const nextRoute = liveRank === undefined ? undefined : routes.slice(liveRank + 1).find((candidate) => !this.failedRetryRoutes.has(candidate.routeId) && resolveProviderCandidates([{ ...candidate, ...probeRouteAvailability(candidate, this.deps.registryOf(candidateServices)), automaticFallback: true }]).candidates.length > 0);
@@ -183,8 +184,8 @@ export class FaultRetry {
183
184
  return;
184
185
  const current = parseModelSpec(currentSpec);
185
186
  this.notFoundModels.add(current.modelSpec);
186
- const routeRequest = modelRequestFromConfig(this.deps.cfg.model, process.env['CRTR_MODEL_INTENT'], false);
187
- const routes = routeRequest ? expandModelCandidates(routeRequest, process.env['CRTR_NODE_CWD'] ?? this.deps.cfg.cwd, process.env['CRTR_PROFILE_ID']) : [];
187
+ const routeRequest = modelRequestFromConfig(this.deps.cfg.model, envModelIntent(), false);
188
+ const routes = routeRequest ? expandModelCandidates(routeRequest, envNodeCwd() ?? this.deps.cfg.cwd, envProfileId()) : [];
188
189
  const liveRank = routes.find((candidate) => `${candidate.providerId}/${candidate.modelId}` === current.modelSpec && candidate.thinkingLevel === current.thinkingLevel)?.rank;
189
190
  const nextRoute = liveRank === undefined ? undefined : routes.slice(liveRank + 1).find((candidate) => !this.notFoundModels.has(`${candidate.providerId}/${candidate.modelId}`) && resolveProviderCandidates([{ ...candidate, ...probeRouteAvailability(candidate, this.deps.registryOf(candidateServices)), automaticFallback: true }]).candidates.length > 0);
190
191
  if (!nextRoute)
@@ -0,0 +1,38 @@
1
+ import { type ClientToBroker } from '../broker-protocol.js';
2
+ import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
3
+ import type { EngineDriver } from './engine-drive.js';
4
+ import type { FrameReplies } from './frame-replies.js';
5
+ import type { BrokerSession } from './read-ops.js';
6
+ import type { TurnAcceptanceGate } from './turn-ignition.js';
7
+ /** An extension dialog fanned out to writable clients, awaiting the first answer. */
8
+ export type PendingDialog = {
9
+ resolve: (response: Extract<ClientToBroker, {
10
+ type: 'extension_ui_response';
11
+ }>) => void;
12
+ };
13
+ /** The frames about a client's own connection — admission, one-shot delivery,
14
+ * dialog answers, and the two ways a socket or the broker ends. */
15
+ export type ClientFrameHandlers = {
16
+ hello(client: BrokerClient, frame: Extract<ClientToBroker, {
17
+ type: 'hello';
18
+ }>): void;
19
+ deliver(client: BrokerClient, frame: Extract<ClientToBroker, {
20
+ type: 'deliver';
21
+ }>): void;
22
+ dialog(client: BrokerClient, frame: Extract<ClientToBroker, {
23
+ type: 'extension_ui_response';
24
+ }>): void;
25
+ bye(client: BrokerClient): void;
26
+ shutdown(): void;
27
+ };
28
+ export declare function createClientHandlers(deps: {
29
+ registry: BrokerClientRegistry;
30
+ replies: FrameReplies;
31
+ session: () => BrokerSession;
32
+ driveEngine: EngineDriver;
33
+ turnAcceptance: TurnAcceptanceGate;
34
+ pendingDialogs: Map<string, PendingDialog>;
35
+ sendWelcome: (client: BrokerClient) => void;
36
+ replayExtraPendingDialogsTo: (client: BrokerClient) => void;
37
+ disposeAndExit: (reason: string, status?: number) => void;
38
+ }): ClientFrameHandlers;
@@ -0,0 +1,129 @@
1
+ import { SITUATIONAL_CONTEXT_CUSTOM_TYPE } from '../../../shared/generated-context.js';
2
+ import { emitEvent } from '../../events/emit.js';
3
+ import { normalizeSnapshotTail } from '../broker-protocol.js';
4
+ export function createClientHandlers(deps) {
5
+ const { registry, replies, session, driveEngine, turnAcceptance, pendingDialogs, sendWelcome, replayExtraPendingDialogsTo, disposeAndExit, } = deps;
6
+ const { notWritable, ackTo } = replies;
7
+ return {
8
+ hello(client, frame) {
9
+ // Admit at the REQUESTED role — unconditionally. `controller` is a
10
+ // per-client write capability, not a singleton slot, so an arriving
11
+ // writable client never demotes (or is demoted by) an already-attached
12
+ // one: attached terminal panes are all writable at once.
13
+ //
14
+ // A repeated hello on an already-helloed socket must not RE-role it: the
15
+ // role is fixed for the socket's lifetime, so every downstream gate and
16
+ // every already-fanned-out dialog keep the authority the first welcome
17
+ // stated. A duplicate hello still re-sends the catch-up snapshot.
18
+ const firstHello = !client.helloed;
19
+ client.id = frame.client_id;
20
+ client.snapshotTail = normalizeSnapshotTail(frame.snapshot_tail);
21
+ if (firstHello) {
22
+ client.helloed = true;
23
+ client.role = frame.role === 'controller' ? 'controller' : 'observer';
24
+ client.attends = frame.attends !== false;
25
+ }
26
+ sendWelcome(client);
27
+ if (firstHello)
28
+ registry.persistAttachState(); // a helloed viewer arrived
29
+ // welcome carried the FIRST pending dialog; replay any extras so a writable
30
+ // client attaching mid-dialog can answer every in-flight dialog.
31
+ if (client.role === 'controller')
32
+ replayExtraPendingDialogsTo(client);
33
+ },
34
+ deliver(client, frame) {
35
+ // Interactive delivery from a one-shot server client (crtrd's
36
+ // `POST /messages` `delivery:'interactive'` path). Routed HERE,
37
+ // broker-side, because only the broker authoritatively knows streaming
38
+ // state: the synthesized `prompt` frame reuses resolveEngineRoute's m-B
39
+ // routing (idle → prompt(); mid-stream → prompt({streamingBehavior:
40
+ // 'steer'}), pi's own Enter-while-streaming submit) plus the memory-refs
41
+ // guidance path — byte-identical behavior to a viewer's Enter. Acked
42
+ // once ACCEPTED (correlated by the frame's id) so the daemon returns
43
+ // deterministically; `detail` reports which route was taken.
44
+ //
45
+ // The ack reports the engine call's OUTCOME, never merely the fact that
46
+ // a call was made: the session's prompt() rejects outright when the
47
+ // model's provider has no credential, and acking unconditionally reported
48
+ // `delivered:true` for a message the engine had refused — the caller's
49
+ // one-shot client then closed before the error frame arrived and the
50
+ // message vanished with no trace. A prompt route therefore acks on the
51
+ // race between acceptance (agent_start) and rejection; a steer route
52
+ // joins a turn already running, so no new agent_start is coming and its
53
+ // ack stays at routing time.
54
+ if (notWritable(client, 'drive the engine', frame.id))
55
+ return;
56
+ const via = session().isStreaming ? 'steer' : 'prompt';
57
+ // Ordered context cards ahead of the body, inside this ONE frame handling,
58
+ // so no other client's send can interleave between a card and its body.
59
+ // Nothing is awaited: `sendCustomMessage` completes synchronously on both
60
+ // branches that apply here — idle appends to the session and starts NO turn
61
+ // (the body is the sole trigger), streaming queues on the same steer queue
62
+ // the body's `prompt({streamingBehavior:'steer'})` uses, preserving order.
63
+ for (const card of frame.cards ?? []) {
64
+ void session()
65
+ .sendCustomMessage({
66
+ customType: SITUATIONAL_CONTEXT_CUSTOM_TYPE,
67
+ content: card,
68
+ display: true,
69
+ }, via === 'steer' ? { deliverAs: 'steer' } : undefined)
70
+ .catch((error) => emitEvent({
71
+ level: 'error',
72
+ event: 'broker.deliver.card_failed',
73
+ error,
74
+ }));
75
+ }
76
+ let delivered = false;
77
+ const ackAccepted = () => {
78
+ if (delivered)
79
+ return;
80
+ delivered = true;
81
+ ackTo(client, 'deliver', true, via, frame.id);
82
+ };
83
+ if (via === 'prompt')
84
+ turnAcceptance.waitFor(ackAccepted);
85
+ const outcome = driveEngine(client, {
86
+ type: 'prompt',
87
+ text: frame.text,
88
+ ...(frame.images !== undefined ? { images: frame.images } : {}),
89
+ });
90
+ if (via === 'steer' || outcome === undefined) {
91
+ turnAcceptance.cancel(ackAccepted);
92
+ ackAccepted();
93
+ return;
94
+ }
95
+ void outcome.then((failure) => {
96
+ turnAcceptance.cancel(ackAccepted);
97
+ if (failure === undefined) {
98
+ ackAccepted();
99
+ return;
100
+ }
101
+ // The error frame already went to the client, but a one-shot deliver
102
+ // client is gone by then — record it where it survives.
103
+ emitEvent({
104
+ level: 'error',
105
+ event: 'broker.prompt.deliver_failed',
106
+ error: failure,
107
+ });
108
+ if (delivered)
109
+ return;
110
+ delivered = true;
111
+ ackTo(client, 'deliver', false, String(failure), frame.id);
112
+ });
113
+ },
114
+ dialog(client, frame) {
115
+ if (notWritable(client, 'answer dialogs'))
116
+ return;
117
+ // First response for this id wins: `resolve` settles the entry and deletes
118
+ // it, so every later response from a peer that was also fanned this dialog
119
+ // finds nothing here and is a silent no-op.
120
+ pendingDialogs.get(frame.id)?.resolve(frame);
121
+ },
122
+ bye(client) {
123
+ client.socket.end();
124
+ },
125
+ shutdown() {
126
+ disposeAndExit('shutdown-frame');
127
+ },
128
+ };
129
+ }
@@ -1,180 +1,13 @@
1
- import type { AgentSessionServices, ModelRegistry, PromptOptions } from '@earendil-works/pi-coding-agent';
2
- import type { ImageContent } from '@earendil-works/pi-ai';
3
- export { promptWithAdvertisedCommandInvocation } from '../advertised-command-invocation.js';
4
- import { type BrokerSdkConfig } from '../launch.js';
5
- import { type ClientToBroker } from '../broker-protocol.js';
1
+ import type { AgentSessionServices, ModelRegistry } from '@earendil-works/pi-coding-agent';
2
+ import type { BrokerSdkConfig } from '../launch.js';
3
+ import type { ClientToBroker } from '../broker-protocol.js';
6
4
  import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
7
- import { type BrokerSession } from './read-ops.js';
5
+ import type { BrokerSession } from './read-ops.js';
6
+ import { type DriveFrame } from './engine-drive.js';
7
+ import { type PendingDialog } from './frame-client.js';
8
8
  import type { ToolGroupTracker } from './tool-groups.js';
9
9
  import type { EventProjection } from './event-projection.js';
10
10
  import type { RebindDriver } from './rebind.js';
11
- export declare const REVIEW_BOUNDARY_NAVIGATION_CODE = "review_boundary_navigation";
12
- export declare class ReviewBoundaryNavigationError extends Error {
13
- readonly targetId: string;
14
- readonly code = "review_boundary_navigation";
15
- constructor(targetId: string);
16
- }
17
- /**
18
- * Route a writable client's `prompt`/`follow_up` frame against the LIVE session
19
- * state. The client picks its frame type off a possibly-STALE `isStreaming`
20
- * snapshot, so the broker is authoritative and the client's choice is a HINT:
21
- *
22
- * - m-B (streaming-safe prompt): a `prompt` arriving mid-stream needs
23
- * PromptOptions.streamingBehavior ("Required if streaming"; prompt() throws
24
- * without it). "steer" mirrors pi interactive-mode's own
25
- * Enter-while-streaming submit (`prompt(text, { streamingBehavior: "steer" })`).
26
- * - m-C (idle-safe follow_up): pi's followUp() only enqueues into a RUNNING
27
- * agent loop — on an idle session nothing ever drains the queue and the text
28
- * is silently lost. An idle `follow_up` therefore runs as a prompt now.
29
- *
30
- * driveEngine calls this synchronously with `session.isStreaming`, so the
31
- * routed state cannot go stale between the check and the engine call.
32
- * C1: images ride as a field on PromptOptions (pi: `prompt(text, { images })`).
33
- */
34
- export declare function resolveEngineRoute(frame: {
35
- type: 'prompt' | 'follow_up';
36
- images?: ImageContent[];
37
- }, isStreaming: boolean): {
38
- call: 'prompt';
39
- options: PromptOptions;
40
- } | {
41
- call: 'followUp';
42
- };
43
- /**
44
- * True iff `text` would be consumed by pi's OWN leading-name preflight before
45
- * `session.prompt/steer/followUp` ever sees it as prose — an extension command
46
- * (`session.extensionRunner.getCommand`), a `/skill:name` skill, or a `/name`
47
- * prompt template (mirrors `_tryExecuteExtensionCommand` / `_expandSkillCommand`
48
- * / `expandPromptTemplate` in the pinned SDK verbatim, including the exact
49
- * name-parsing substrings). Inline memory-reference guidance must NEVER fire
50
- * for these — the leading token routes to the engine's own command surface,
51
- * not prose referencing a memory doc. Each lookup is independently wrapped so
52
- * an engine missing one accessor (the fake, or a degraded session) degrades
53
- * that check to "not a match" rather than aborting the whole classification —
54
- * matching `buildCommandList`'s existing "no live extensionRunner" tolerance.
55
- */
56
- export declare function isLeadingEngineCommand(text: string, session: {
57
- extensionRunner?: {
58
- getCommand(name: string): unknown;
59
- };
60
- resourceLoader?: {
61
- getSkills(): {
62
- skills: Array<{
63
- name: string;
64
- }>;
65
- };
66
- };
67
- promptTemplates?: ReadonlyArray<{
68
- name: string;
69
- }>;
70
- }): boolean;
71
- /** A `steer` frame normally maps directly to pi's steer queue. Engine commands,
72
- * skills, and templates must instead pass through prompt()'s leading-command
73
- * preflight; with streamingBehavior:'steer', ordinary expanded text still lands
74
- * in the same queue. */
75
- export declare function resolveSteerCall(text: string, session: Parameters<typeof isLeadingEngineCommand>[1]): 'prompt' | 'steer';
76
- /**
77
- * Enumerate every leading-token STRING this session's engine would
78
- * independently consume as a leading command/skill/template dispatch BEFORE
79
- * inline memory-reference guidance ever applies — the read-op sibling of
80
- * `isLeadingEngineCommand`'s point-lookup, gathered as a full enumeration for
81
- * viewer-side suppression instead of a per-submission membership check. Same
82
- * three sources, same shape of the leading match `isLeadingEngineCommand`
83
- * checks (bare extension-command invocation names and prompt-template names,
84
- * `skill:<name>` for skills), so `/${token}` round-trips through
85
- * `isLeadingEngineCommand(text, session)` for every emitted token (locked by
86
- * a test). Kept STRICTLY separate from `buildCommandList` (command
87
- * completion) and the ref inventory (`RefMeta` rows) — this is neither.
88
- */
89
- export declare function engineLeadingCommandTokens(session: {
90
- extensionRunner?: {
91
- getRegisteredCommands(): Array<{
92
- invocationName: string;
93
- }>;
94
- };
95
- resourceLoader?: {
96
- getSkills(): {
97
- skills: Array<{
98
- name: string;
99
- }>;
100
- };
101
- };
102
- promptTemplates?: ReadonlyArray<{
103
- name: string;
104
- }>;
105
- }): string[];
106
- /**
107
- * Choose how the hidden `crtr-memory-refs` guidance custom message rides
108
- * alongside a human frame's own engine call (design Flow B). Pure over the
109
- * frame's routing outcome — never reads live session state itself, so a
110
- * caller can decide the delivery mode from the exact same `route`
111
- * `resolveEngineRoute` already computed for the human call.
112
- *
113
- * - `steer` frame -> 'steer' (always; no route call exists).
114
- * - a TRUE mid-stream follow_up (`routeCall === 'followUp'`) -> 'followUp'.
115
- * - every other `prompt`/`follow_up` routed as `routeCall === 'prompt'`:
116
- * idle -> 'nextTurn' (the ONLY case guidance precedes the human call —
117
- * pi's `prompt()` reads+clears its pending-nextTurn queue synchronously
118
- * into the SAME turn as the new user message); streaming (m-B
119
- * steer-while-streaming) -> 'steer', never 'nextTurn' (that would attach
120
- * to a later, unrelated turn).
121
- */
122
- export declare function chooseGuidanceDeliveryMode(frameType: 'prompt' | 'follow_up' | 'steer', routeCall: 'prompt' | 'followUp' | undefined, isStreaming: boolean): 'steer' | 'followUp' | 'nextTurn';
123
- /**
124
- * Deliver a `nextTurn` guidance custom message ATOMICALLY around the idle-
125
- * prompt call it's meant to ride alongside. Pi's `_pendingNextTurnMessages`
126
- * queue (the only public seam for `deliverAs:'nextTurn'`) is drained ONLY deep
127
- * inside `session.prompt()`, well after several await points — extension-
128
- * command interception, the `input` extension transform, a streaming reroute,
129
- * model/auth validation, pre-turn compaction — that can return or throw BEFORE
130
- * the drain ever runs. `sendCustomMessage`'s nextTurn branch pushes
131
- * synchronously and unconditionally, so any of those early exits strands the
132
- * pushed guidance in the queue to attach itself to whichever LATER, unrelated
133
- * prompt happens to reach the drain first.
134
- *
135
- * There is no public SDK seam to observe or cancel a pending nextTurn message
136
- * (`_pendingNextTurnMessages` is TS-`private` only — a plain runtime field, not
137
- * `#private`), so this reaches into that one field and removes EXACTLY the
138
- * entry this call pushed, identified by object identity via a locally-
139
- * generated marker riding the message's own `details` field (never surfaced to
140
- * the model, and irrelevant if the message WAS consumed normally — `details`
141
- * is just carried through like any other field once folded into a real turn).
142
- * Runs UNCONDITIONALLY after the human call settles, success or failure/early-
143
- * return alike — a "successful" `prompt()` promise can just as easily be an
144
- * early return that skipped the drain (an intercepted extension command, an
145
- * `input` transform, a streaming reroute) as a genuine consumed turn, so only
146
- * checking `.catch` is not enough.
147
- */
148
- export declare function promptWithNextTurnGuidance(session: {
149
- sendCustomMessage(message: {
150
- customType: string;
151
- content: unknown;
152
- display?: boolean;
153
- details?: unknown;
154
- }, options?: {
155
- deliverAs?: 'steer' | 'followUp' | 'nextTurn';
156
- }): Promise<void>;
157
- }, guidance: string, runPrompt: () => Promise<void>): Promise<void>;
158
- /** True iff a parsed JSONL branch-entry line is a hidden (`display:false`)
159
- * custom message — the general display boundary crossed if left in an
160
- * exported branch (not just `crtr-memory-refs` guidance; any hidden custom
161
- * content). Covers both persisted shapes a branch entry can take: a
162
- * `type:'custom_message'` entry (display at the entry's own top level) and a
163
- * `type:'message'` entry wrapping a `role:'custom'` message (display nested
164
- * under `.message`). */
165
- export declare function isHiddenJsonlBranchEntry(entry: unknown): boolean;
166
- /** Rewrite an already-exported JSONL file in place, dropping every hidden
167
- * (`display:false`) branch entry while re-chaining `parentId` so the
168
- * remaining entries stay a single valid linear branch — applying the general
169
- * display:false boundary to `session.exportToJsonl`'s own output, which
170
- * serializes every entry verbatim regardless of display. The header line
171
- * (line 1) is copied through untouched. */
172
- export declare function stripHiddenEntriesFromJsonlExport(filePath: string): void;
173
- type PendingDialog = {
174
- resolve: (response: Extract<ClientToBroker, {
175
- type: 'extension_ui_response';
176
- }>) => void;
177
- };
178
11
  export type FrameDispatchDeps = {
179
12
  nodeId: string;
180
13
  cfg: BrokerSdkConfig;
@@ -207,31 +40,21 @@ export declare function createFrameDispatchContext(deps: FrameDispatchDeps): {
207
40
  hello: (client: BrokerClient, frame: Extract<ClientToBroker, {
208
41
  type: "hello";
209
42
  }>) => void;
210
- drive: (client: BrokerClient, frame: Extract<ClientToBroker, {
211
- type: "prompt" | "steer" | "follow_up" | "abort" | "bash";
212
- }>) => void;
43
+ drive: (client: BrokerClient, frame: DriveFrame) => void;
213
44
  deliver: (client: BrokerClient, frame: Extract<ClientToBroker, {
214
45
  type: "deliver";
215
46
  }>) => void;
216
47
  dialog: (client: BrokerClient, frame: Extract<ClientToBroker, {
217
48
  type: "extension_ui_response";
218
49
  }>) => void;
219
- engineControl: (client: BrokerClient, frame: Extract<ClientToBroker, {
220
- 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";
221
- }>) => void;
222
- read: (client: BrokerClient, frame: Extract<ClientToBroker, {
223
- type: "get_commands" | "list_models" | "list_scoped_models" | "list_memory_refs" | "get_tree" | "get_settings" | "list_sessions" | "get_human_fork_coordinates";
224
- }>) => void;
225
- session: (client: BrokerClient, frame: Extract<ClientToBroker, {
226
- type: "navigate_tree" | "reload" | "export" | "new_session" | "switch_session" | "fork" | "clone" | "share";
227
- }>) => void;
228
- authentication: (client: BrokerClient, frame: Extract<ClientToBroker, {
229
- type: "reload_auth";
230
- }>) => void;
50
+ engineControl: (client: BrokerClient, frame: import("./frame-engine-control.js").EngineControlFrame) => void;
51
+ read: (client: BrokerClient, frame: import("./frame-read.js").ReadFrame) => void;
52
+ session: (client: BrokerClient, frame: import("./frame-session.js").SessionFrame) => void;
53
+ authentication: (client: BrokerClient) => void;
231
54
  bye: (client: BrokerClient) => void;
232
55
  shutdown: () => void;
233
56
  };
234
- kickoff(prompt: string): void;
57
+ kickoff: (prompt: string) => void;
235
58
  notifyTurnAccepted: () => void;
236
59
  pendingPromptDispatches: () => number;
237
60
  resetForSession: () => void;