@north-light/crouter 0.3.173 → 0.3.174

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 (673) hide show
  1. package/bin/runtime-selector.mjs +8 -1
  2. package/dist/api/__tests__/{client.test.js → serial/client.test.js} +2 -135
  3. package/dist/api/client.d.ts +12 -21
  4. package/dist/api/client.js +22 -31
  5. package/dist/api/dto/broker-ops.d.ts +158 -0
  6. package/dist/api/dto/broker-ops.js +6 -0
  7. package/dist/api/dto/crons.d.ts +6 -0
  8. package/dist/api/dto/human.d.ts +7 -1
  9. package/dist/api/dto/inbox.d.ts +1 -71
  10. package/dist/api/dto/inbox.js +1 -9
  11. package/dist/api/dto/nodes.d.ts +6 -7
  12. package/dist/api/dto/reviews.d.ts +1 -11
  13. package/dist/api/routes.d.ts +7 -4
  14. package/dist/api/routes.js +8 -5
  15. package/dist/build-root.js +3 -3
  16. package/dist/builtin-memory/04-base-worker.md +1 -4
  17. package/dist/builtin-memory/05-kinds/advisor/00-base.md +1 -5
  18. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +2 -7
  19. package/dist/builtin-memory/05-kinds/design/00-base.md +1 -6
  20. package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -6
  21. package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -4
  22. package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -2
  23. package/dist/builtin-memory/05-kinds/general/00-base.md +1 -3
  24. package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -6
  25. package/dist/builtin-memory/05-kinds/plan/reviewers/00-base.md +1 -2
  26. package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -3
  27. package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -3
  28. package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -3
  29. package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -3
  30. package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -4
  31. package/dist/builtin-memory/05-kinds/review/companion/00-base.md +2 -3
  32. package/dist/builtin-memory/05-kinds/spec/00-base.md +1 -2
  33. package/dist/builtin-memory/advisor/council.md +1 -6
  34. package/dist/builtin-memory/design.md +2 -7
  35. package/dist/builtin-memory/development.md +9 -14
  36. package/dist/builtin-memory/internal/marketplaces.md +2 -6
  37. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  38. package/dist/builtin-memory/internal/plugins.md +2 -8
  39. package/dist/builtin-memory/internal/storage-tiers.md +1 -2
  40. package/dist/builtin-memory/testing.md +41 -0
  41. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{provider-rotation.test.ts → serial/provider-rotation.test.ts} +27 -179
  42. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +5 -15
  43. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +1 -28
  44. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +43 -88
  45. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -1
  46. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +9 -1
  47. package/dist/cli.js +2 -2
  48. package/dist/clients/attach/__tests__/attach-remote-readonly.test.js +37 -131
  49. package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +71 -152
  50. package/dist/clients/attach/__tests__/context-message.test.js +16 -174
  51. package/dist/clients/attach/__tests__/crtr-output.test.js +36 -2069
  52. package/dist/clients/attach/__tests__/diagram.test.js +2 -2
  53. package/dist/clients/attach/__tests__/edit-diff.test.js +0 -4
  54. package/dist/clients/attach/__tests__/file-review-focus.test.js +2 -1
  55. package/dist/clients/attach/__tests__/group-activity.test.js +40 -229
  56. package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.js +26 -76
  57. package/dist/clients/attach/__tests__/prompt-preview.test.js +4 -70
  58. package/dist/clients/attach/__tests__/ref-autocomplete.test.js +11 -125
  59. package/dist/clients/attach/__tests__/titled-editor-preview.test.js +14 -205
  60. package/dist/clients/attach/command.d.ts +2 -1
  61. package/dist/clients/attach/command.js +81 -76
  62. package/dist/clients/attach/input/capabilities.d.ts +27 -0
  63. package/dist/clients/attach/input/capabilities.js +96 -0
  64. package/dist/clients/attach/input/controller.d.ts +20 -182
  65. package/dist/clients/attach/input/controller.js +128 -537
  66. package/dist/clients/attach/input/overlay-owner.d.ts +41 -0
  67. package/dist/clients/attach/input/overlay-owner.js +199 -0
  68. package/dist/clients/attach/overlays/file-review.d.ts +0 -4
  69. package/dist/clients/attach/overlays/file-review.js +13 -5
  70. package/dist/clients/attach/overlays/graph.js +2 -10
  71. package/dist/clients/attach/render/chat-view.d.ts +4 -3
  72. package/dist/clients/attach/render/chat-view.js +39 -33
  73. package/dist/clients/attach/render/group-activity.d.ts +1 -0
  74. package/dist/clients/attach/render/group-activity.js +20 -13
  75. package/dist/clients/attach/render/group-recap.js +12 -3
  76. package/dist/clients/attach/session/connection.js +9 -15
  77. package/dist/clients/attach/session/editor-inventory.d.ts +6 -5
  78. package/dist/clients/attach/session/editor-inventory.js +7 -8
  79. package/dist/clients/attach/session/file-links.d.ts +13 -0
  80. package/dist/clients/attach/session/file-links.js +107 -0
  81. package/dist/clients/attach/session/frame.d.ts +3 -0
  82. package/dist/clients/attach/session/frame.js +10 -1
  83. package/dist/clients/attach/session/frames.js +2 -3
  84. package/dist/clients/attach/session/input-wiring.d.ts +4 -1
  85. package/dist/clients/attach/session/input-wiring.js +15 -14
  86. package/dist/clients/attach/session/mouse.d.ts +6 -1
  87. package/dist/clients/attach/session/mouse.js +8 -5
  88. package/dist/clients/attach/session/profile-files.js +18 -12
  89. package/dist/clients/attach/session/reconnect.js +1 -1
  90. package/dist/clients/attach/session/state-sync.js +1 -2
  91. package/dist/clients/attach/session/surface-host.d.ts +3 -2
  92. package/dist/clients/attach/session/surface-host.js +4 -4
  93. package/dist/clients/attach/slash/dispatch.d.ts +8 -31
  94. package/dist/clients/attach/slash/dispatch.js +34 -81
  95. package/dist/clients/attach/viewer.js +820 -860
  96. package/dist/clients/conversation/projection.d.ts +79 -0
  97. package/dist/clients/conversation/projection.js +341 -0
  98. package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +159 -0
  99. package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +151 -0
  100. package/dist/clients/inbox/controller.d.ts +0 -4
  101. package/dist/clients/inbox/controller.js +0 -2
  102. package/dist/clients/inbox/review/__tests__/review-core.test.js +4 -293
  103. package/dist/clients/inbox/review/__tests__/teardown-notice.test.js +100 -0
  104. package/dist/clients/inbox/review/comments-client.d.ts +3 -2
  105. package/dist/clients/inbox/review/comments-client.js +12 -12
  106. package/dist/clients/inbox/review/companion-pane.d.ts +30 -0
  107. package/dist/clients/inbox/review/companion-pane.js +86 -0
  108. package/dist/clients/inbox/review/document-surface.d.ts +182 -0
  109. package/dist/clients/inbox/review/{document-region.js → document-surface.js} +252 -48
  110. package/dist/clients/inbox/review/frame.js +5 -4
  111. package/dist/clients/inbox/review/launch.d.ts +8 -8
  112. package/dist/clients/inbox/review/launch.js +52 -92
  113. package/dist/clients/inbox/review/nvim.d.ts +2 -0
  114. package/dist/clients/inbox/review/nvim.js +14 -12
  115. package/dist/clients/inbox/review/review-client.d.ts +12 -8
  116. package/dist/clients/inbox/review/review-client.js +46 -35
  117. package/dist/clients/inbox/review-adapter.d.ts +3 -10
  118. package/dist/clients/inbox/review-adapter.js +27 -19
  119. package/dist/clients/inbox/surface.d.ts +0 -4
  120. package/dist/clients/inbox/surface.js +0 -2
  121. package/dist/clients/surfaces/host.d.ts +11 -3
  122. package/dist/commands/__tests__/api-client-cold-start-diagnostic.test.js +7 -34
  123. package/dist/commands/__tests__/human.test.js +11 -322
  124. package/dist/commands/api-client.d.ts +5 -5
  125. package/dist/commands/api-client.js +6 -85
  126. package/dist/commands/canvas-browse.js +3 -29
  127. package/dist/commands/canvas-config.js +1 -1
  128. package/dist/commands/canvas-use.js +3 -3
  129. package/dist/commands/canvas.js +1 -1
  130. package/dist/commands/cron.js +5 -1
  131. package/dist/commands/human/queue.d.ts +0 -4
  132. package/dist/commands/human/queue.js +7 -40
  133. package/dist/commands/memory/list.d.ts +14 -0
  134. package/dist/commands/memory/list.js +35 -31
  135. package/dist/commands/memory/read.d.ts +7 -0
  136. package/dist/commands/memory/read.js +14 -8
  137. package/dist/commands/node/bash.d.ts +1 -0
  138. package/dist/commands/node/bash.js +198 -0
  139. package/dist/commands/node/create.d.ts +23 -0
  140. package/dist/commands/node/create.js +207 -0
  141. package/dist/commands/node/inspect.d.ts +2 -0
  142. package/dist/commands/node/inspect.js +302 -0
  143. package/dist/commands/node/lifecycle.d.ts +4 -0
  144. package/dist/commands/node/lifecycle.js +415 -0
  145. package/dist/commands/node/message.d.ts +1 -0
  146. package/dist/commands/node/message.js +164 -0
  147. package/dist/commands/node/pyramid.d.ts +2 -0
  148. package/dist/commands/node/pyramid.js +79 -0
  149. package/dist/commands/node/subscription.d.ts +1 -0
  150. package/dist/commands/node/subscription.js +82 -0
  151. package/dist/commands/node/wait.d.ts +1 -0
  152. package/dist/commands/node/wait.js +159 -0
  153. package/dist/commands/node-lifecycle-revive.js +10 -57
  154. package/dist/commands/node.d.ts +0 -42
  155. package/dist/commands/node.js +11 -2065
  156. package/dist/commands/search.js +1 -1
  157. package/dist/commands/surface/node/cycle.d.ts +1 -0
  158. package/dist/commands/surface/node/cycle.js +83 -0
  159. package/dist/commands/surface/node/focus.d.ts +1 -0
  160. package/dist/commands/surface/node/focus.js +67 -0
  161. package/dist/commands/surface/node/id.d.ts +1 -0
  162. package/dist/commands/surface/node/id.js +38 -0
  163. package/dist/commands/surface/node/pin.d.ts +1 -0
  164. package/dist/commands/surface/node/pin.js +42 -0
  165. package/dist/commands/surface/node/placement.d.ts +46 -0
  166. package/dist/commands/surface/node/placement.js +124 -0
  167. package/dist/commands/surface-node.js +4 -1
  168. package/dist/commands/surface-reload.js +1 -2
  169. package/dist/commands/surface-tmux-spread.js +1 -1
  170. package/dist/commands/surface.js +5 -20
  171. package/dist/commands/sys/__tests__/setup-core.test.js +18 -271
  172. package/dist/commands/sys/config.js +6 -6
  173. package/dist/commands/sys/logs.js +1 -1
  174. package/dist/commands/sys/panels/broker-limits-panel.d.ts +17 -0
  175. package/dist/commands/sys/panels/broker-limits-panel.js +104 -0
  176. package/dist/commands/sys/panels/dependencies-panel.d.ts +14 -0
  177. package/dist/commands/sys/panels/dependencies-panel.js +80 -0
  178. package/dist/commands/sys/panels/keybindings-panel.d.ts +56 -0
  179. package/dist/commands/sys/panels/keybindings-panel.js +703 -0
  180. package/dist/commands/sys/panels/keys-panel.d.ts +16 -0
  181. package/dist/commands/sys/panels/keys-panel.js +60 -0
  182. package/dist/commands/sys/panels/models-panel.d.ts +77 -0
  183. package/dist/commands/sys/panels/models-panel.js +647 -0
  184. package/dist/commands/sys/panels/panel.d.ts +45 -0
  185. package/dist/commands/sys/panels/panel.js +137 -0
  186. package/dist/commands/sys/panels/plugins-panel.d.ts +16 -0
  187. package/dist/commands/sys/panels/plugins-panel.js +80 -0
  188. package/dist/commands/sys/panels/prefs-panel.d.ts +21 -0
  189. package/dist/commands/sys/panels/prefs-panel.js +85 -0
  190. package/dist/commands/sys/panels/provider-panel.d.ts +62 -0
  191. package/dist/commands/sys/panels/provider-panel.js +538 -0
  192. package/dist/commands/sys/settings-shell.d.ts +55 -0
  193. package/dist/commands/sys/settings-shell.js +400 -0
  194. package/dist/commands/sys/settings.d.ts +2 -0
  195. package/dist/commands/sys/settings.js +31 -0
  196. package/dist/commands/sys/setup-core.d.ts +3 -33
  197. package/dist/commands/sys/setup-core.js +27 -78
  198. package/dist/commands/sys/setup-settings-runner.d.ts +24 -0
  199. package/dist/commands/sys/setup-settings-runner.js +63 -0
  200. package/dist/commands/sys/setup-wizard.d.ts +26 -125
  201. package/dist/commands/sys/setup-wizard.js +209 -2473
  202. package/dist/commands/sys/setup.js +5 -7
  203. package/dist/commands/sys.js +3 -2
  204. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +50 -0
  205. package/dist/core/__tests__/broker-launch-candidates.test.js +20 -9
  206. package/dist/core/__tests__/broker-preflight.test.js +1 -1
  207. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +108 -27
  208. package/dist/core/__tests__/canvas-inbox-watcher.test.js +14 -0
  209. package/dist/core/__tests__/canvas.test.js +2 -36
  210. package/dist/core/__tests__/close.test.js +14 -3
  211. package/dist/core/__tests__/context-intro.test.js +32 -143
  212. package/dist/core/__tests__/cron-broker-capacity.test.js +22 -3
  213. package/dist/core/__tests__/daemon-boot.test.js +2 -20
  214. package/dist/core/__tests__/daemon-wedge.test.js +1 -1
  215. package/dist/core/__tests__/dead-node-policy-table.test.js +1 -18
  216. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  217. package/dist/core/__tests__/fixtures/fake-engine.js +2 -2
  218. package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
  219. package/dist/core/__tests__/focuses.test.js +4 -98
  220. package/dist/core/__tests__/helpers/broker-clients.d.ts +0 -3
  221. package/dist/core/__tests__/helpers/broker-clients.js +0 -14
  222. package/dist/core/__tests__/helpers/harness.d.ts +0 -8
  223. package/dist/core/__tests__/helpers/harness.js +7 -87
  224. package/dist/core/__tests__/helpers/viewer-panes.js +1 -1
  225. package/dist/core/__tests__/inline-memory-refs.test.js +5 -134
  226. package/dist/core/__tests__/kickoff.test.js +1 -1
  227. package/dist/core/__tests__/memory-resolver-precedence.test.js +29 -1
  228. package/dist/core/__tests__/migration.test.js +35 -48
  229. package/dist/core/__tests__/on-read-dedup-resume.test.js +22 -12
  230. package/dist/core/__tests__/passive-subscription.test.js +26 -49
  231. package/dist/core/__tests__/preview-registry-sync.test.js +1 -2
  232. package/dist/core/__tests__/prune.test.js +94 -1
  233. package/dist/core/__tests__/review-model-floor.test.js +0 -32
  234. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +255 -0
  235. package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.js +39 -7
  236. package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.js +30 -29
  237. package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.js +54 -5
  238. package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.js +134 -82
  239. package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.js +15 -4
  240. package/dist/core/__tests__/{full/idle-release-settlement.test.js → seam/dormancy-release.test.js} +69 -17
  241. package/dist/core/__tests__/{full/yield-message-transaction.test.js → seam/yield-refresh-transaction.test.js} +31 -9
  242. package/dist/core/__tests__/secrets.test.js +1 -7
  243. package/dist/core/__tests__/{broker-fork-seam.test.js → serial/broker-fork-seam.test.js} +58 -7
  244. package/dist/core/__tests__/{broker-sdk-wiring.test.js → serial/broker-sdk-wiring.test.js} +18 -162
  245. package/dist/core/__tests__/{broker-snapshot-history.test.js → serial/broker-snapshot-history.test.js} +9 -9
  246. package/dist/core/__tests__/{command-plugins.test.js → serial/command-plugins.test.js} +105 -15
  247. package/dist/core/__tests__/{deferred-no-wake.test.js → serial/deferred-no-wake.test.js} +23 -20
  248. package/dist/core/__tests__/{flagship-lifecycle.test.js → serial/flagship-lifecycle.test.js} +7 -7
  249. package/dist/core/__tests__/{host-teardown-process-group.test.js → serial/host-teardown-process-group.test.js} +6 -6
  250. package/dist/core/__tests__/{human-deliver-e2e.test.js → serial/human-deliver-e2e.test.js} +12 -12
  251. package/dist/core/__tests__/{live-mutation.test.js → serial/live-mutation.test.js} +5 -5
  252. package/dist/core/__tests__/{refresh-stall-recycle.test.js → serial/refresh-stall-recycle.test.js} +5 -14
  253. package/dist/core/__tests__/{revive.test.js → serial/revive.test.js} +15 -100
  254. package/dist/core/__tests__/{spawn-root.test.js → serial/spawn-root.test.js} +32 -15
  255. package/dist/core/__tests__/serial/subscription-delivery.test.js +126 -0
  256. package/dist/core/__tests__/serial/tmux-surface.test.js +256 -0
  257. package/dist/core/__tests__/{worktree.test.js → serial/worktree.test.js} +15 -15
  258. package/dist/core/__tests__/session-model.test.js +0 -18
  259. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +9 -0
  260. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +50 -0
  261. package/dist/core/broker-client/__tests__/transport-relay.test.js +15 -16
  262. package/dist/core/broker-client/__tests__/transport-socket.test.js +28 -0
  263. package/dist/core/broker-client/client.d.ts +2 -2
  264. package/dist/core/broker-client/client.js +4 -7
  265. package/dist/core/broker-client/transport-relay.d.ts +13 -19
  266. package/dist/core/broker-client/transport-relay.js +19 -34
  267. package/dist/core/broker-client/transport-socket.d.ts +2 -2
  268. package/dist/core/broker-client/transport-socket.js +9 -2
  269. package/dist/core/broker-client/transport.d.ts +7 -3
  270. package/dist/core/broker-client/transport.js +4 -4
  271. package/dist/core/canvas/__tests__/attention.test.js +1 -18
  272. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +7 -64
  273. package/dist/core/canvas/__tests__/remote-transport.test.js +2 -3
  274. package/dist/core/canvas/__tests__/render-remote.test.js +7 -52
  275. package/dist/core/canvas/browse/app.d.ts +2 -17
  276. package/dist/core/canvas/browse/app.js +16 -88
  277. package/dist/core/canvas/browse/model.d.ts +0 -4
  278. package/dist/core/canvas/browse/model.js +1 -12
  279. package/dist/core/canvas/browse/render.js +6 -3
  280. package/dist/core/canvas/crons.d.ts +8 -5
  281. package/dist/core/canvas/crons.js +7 -6
  282. package/dist/core/canvas/extensions.d.ts +7 -3
  283. package/dist/core/canvas/extensions.js +16 -4
  284. package/dist/core/canvas/nav-model.d.ts +0 -4
  285. package/dist/core/canvas/nav-model.js +2 -14
  286. package/dist/core/canvas/node-order.d.ts +3 -0
  287. package/dist/core/canvas/node-order.js +12 -0
  288. package/dist/core/canvas/remote-canvas-source.d.ts +3 -6
  289. package/dist/core/canvas/remote-canvas-source.js +8 -15
  290. package/dist/core/command.js +11 -4
  291. package/dist/core/config.js +3 -1
  292. package/dist/core/events/envelope.js +1 -1
  293. package/dist/core/events/read.js +7 -21
  294. package/dist/core/events/source.d.ts +0 -2
  295. package/dist/core/events/source.js +0 -16
  296. package/dist/core/events/types.d.ts +1 -1
  297. package/dist/core/fault-classifier.d.ts +1 -1
  298. package/dist/core/feed/passive.d.ts +0 -11
  299. package/dist/core/feed/passive.js +1 -62
  300. package/dist/core/human/__tests__/{inbox-core.test.js → serial/inbox-core.test.js} +6 -6
  301. package/dist/core/keybindings/__tests__/resolve.test.js +1 -96
  302. package/dist/core/keybindings/background-bash.js +3 -4
  303. package/dist/core/keybindings/catalog.js +1 -1
  304. package/dist/core/memory/inline-ref-grammar.js +3 -3
  305. package/dist/core/memory-resolver.d.ts +11 -25
  306. package/dist/core/memory-resolver.js +51 -33
  307. package/dist/core/model-routes.d.ts +15 -0
  308. package/dist/core/model-routes.js +12 -0
  309. package/dist/core/pool-credential-store.js +1 -1
  310. package/dist/core/preview-registry.js +8 -9
  311. package/dist/core/profiles/__tests__/root-profile.test.js +4 -14
  312. package/dist/core/review/realize.d.ts +0 -13
  313. package/dist/core/review/realize.js +0 -40
  314. package/dist/core/review/types.d.ts +1 -1
  315. package/dist/core/runtime/__tests__/engine-route.test.js +2 -2
  316. package/dist/core/runtime/bearings.js +1 -11
  317. package/dist/core/runtime/broker/client-registry.d.ts +33 -0
  318. package/dist/core/runtime/broker/client-registry.js +164 -0
  319. package/dist/core/runtime/broker/daemon-ops.d.ts +19 -0
  320. package/dist/core/runtime/broker/daemon-ops.js +49 -0
  321. package/dist/core/runtime/broker/event-projection.d.ts +42 -0
  322. package/dist/core/runtime/broker/event-projection.js +178 -0
  323. package/dist/core/runtime/broker/fault-retry.d.ts +39 -0
  324. package/dist/core/runtime/broker/fault-retry.js +277 -0
  325. package/dist/core/runtime/broker/frame-dispatch.d.ts +238 -0
  326. package/dist/core/runtime/broker/frame-dispatch.js +1537 -0
  327. package/dist/core/runtime/broker/inbox.d.ts +22 -0
  328. package/dist/core/runtime/broker/inbox.js +167 -0
  329. package/dist/core/runtime/broker/passive.d.ts +16 -0
  330. package/dist/core/runtime/broker/passive.js +44 -0
  331. package/dist/core/runtime/broker/read-ops.d.ts +23 -0
  332. package/dist/core/runtime/broker/read-ops.js +150 -0
  333. package/dist/core/runtime/broker/rebind.d.ts +74 -0
  334. package/dist/core/runtime/broker/rebind.js +211 -0
  335. package/dist/core/runtime/broker/tool-groups.d.ts +39 -0
  336. package/dist/core/runtime/broker/tool-groups.js +244 -0
  337. package/dist/core/runtime/broker-extension-render.d.ts +11 -0
  338. package/dist/core/runtime/broker-extension-render.js +181 -0
  339. package/dist/core/runtime/broker-persona-guidance.d.ts +13 -0
  340. package/dist/core/runtime/broker-persona-guidance.js +58 -0
  341. package/dist/core/runtime/broker-protocol.d.ts +1 -1
  342. package/dist/core/runtime/broker-protocol.js +1 -1
  343. package/dist/core/runtime/broker.d.ts +5 -196
  344. package/dist/core/runtime/broker.js +276 -3059
  345. package/dist/core/runtime/canvas-extensions.js +11 -2
  346. package/dist/core/runtime/fault.js +1 -1
  347. package/dist/core/runtime/model-selection.d.ts +9 -0
  348. package/dist/core/runtime/model-selection.js +12 -0
  349. package/dist/core/runtime/model-swap.d.ts +3 -3
  350. package/dist/core/runtime/model-swap.js +33 -25
  351. package/dist/core/runtime/placement-tmux.d.ts +3 -14
  352. package/dist/core/runtime/placement-tmux.js +11 -80
  353. package/dist/core/runtime/placement.d.ts +4 -2
  354. package/dist/core/runtime/placement.js +7 -12
  355. package/dist/core/runtime/promote.d.ts +14 -1
  356. package/dist/core/runtime/promote.js +1 -1
  357. package/dist/core/runtime/reset.d.ts +1 -1
  358. package/dist/core/runtime/roadmap.js +1 -1
  359. package/dist/core/runtime/spawn.d.ts +8 -0
  360. package/dist/core/runtime/spawn.js +36 -6
  361. package/dist/core/runtime/stop-guard.d.ts +4 -3
  362. package/dist/core/runtime/stop-guard.js +36 -18
  363. package/dist/core/runtime/stop-signals.d.ts +4 -1
  364. package/dist/core/runtime/stream-watchdog.js +4 -5
  365. package/dist/core/runtime/structured-output.d.ts +15 -2
  366. package/dist/core/runtime/structured-output.js +35 -12
  367. package/dist/core/runtime/tmux-bindings.d.ts +25 -0
  368. package/dist/core/runtime/tmux-bindings.js +682 -0
  369. package/dist/core/runtime/tmux-chrome.d.ts +2 -1
  370. package/dist/core/runtime/tmux-chrome.js +3 -2
  371. package/dist/core/runtime/{tmux.d.ts → tmux-driver.d.ts} +18 -26
  372. package/dist/core/runtime/tmux-driver.js +598 -0
  373. package/dist/core/runtime/view-socket.d.ts +14 -0
  374. package/dist/core/runtime/view-socket.js +83 -0
  375. package/dist/core/session-model/session-state.d.ts +1 -2
  376. package/dist/core/session-model/session-state.js +5 -8
  377. package/dist/core/spawn.d.ts +1 -1
  378. package/dist/core/spawn.js +2 -2
  379. package/dist/core/substrate/gate.d.ts +1 -1
  380. package/dist/core/substrate/on-read-pure.d.ts +7 -0
  381. package/dist/core/substrate/on-read-pure.js +272 -0
  382. package/dist/core/termrender/termrender.js +32 -2
  383. package/dist/core/user-settings.d.ts +23 -1
  384. package/dist/core/user-settings.js +18 -1
  385. package/dist/daemon/api/__tests__/node-model-validation.test.js +134 -0
  386. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +98 -0
  387. package/dist/daemon/api/__tests__/{full → seam}/api-server.test.js +6 -48
  388. package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.js +17 -44
  389. package/dist/daemon/api/__tests__/{full/push-parity.test.js → seam/leaf-api-parity.test.js} +61 -42
  390. package/dist/daemon/api/bridge.js +40 -81
  391. package/dist/daemon/api/handlers/broker-ops.d.ts +2 -0
  392. package/dist/daemon/api/handlers/broker-ops.js +383 -0
  393. package/dist/daemon/api/handlers/crons.js +9 -4
  394. package/dist/daemon/api/handlers/files.js +6 -7
  395. package/dist/daemon/api/handlers/inbox.js +2 -340
  396. package/dist/daemon/api/handlers/nodes.d.ts +10 -1
  397. package/dist/daemon/api/handlers/nodes.js +74 -39
  398. package/dist/daemon/api/handlers/review-comments.js +5 -1
  399. package/dist/daemon/api/handlers/reviews.js +0 -19
  400. package/dist/daemon/api/map.js +25 -4
  401. package/dist/daemon/api/router.js +2 -3
  402. package/dist/daemon/api/server.js +5 -7
  403. package/dist/daemon/cron-run.d.ts +4 -0
  404. package/dist/daemon/cron-run.js +30 -13
  405. package/dist/daemon/crtrd.d.ts +3 -58
  406. package/dist/daemon/crtrd.js +28 -1244
  407. package/dist/daemon/manage.js +0 -1
  408. package/dist/daemon/reconcilers/broker-supervision.d.ts +50 -0
  409. package/dist/daemon/reconcilers/broker-supervision.js +549 -0
  410. package/dist/daemon/reconcilers/cron-lane.d.ts +10 -0
  411. package/dist/daemon/reconcilers/cron-lane.js +32 -0
  412. package/dist/daemon/reconcilers/dormant-inbox.d.ts +18 -0
  413. package/dist/daemon/reconcilers/dormant-inbox.js +143 -0
  414. package/dist/daemon/reconcilers/storage-maintenance.d.ts +22 -0
  415. package/dist/daemon/reconcilers/storage-maintenance.js +213 -0
  416. package/dist/daemon/review/finish.js +2 -5
  417. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +34 -53
  418. package/dist/pi-extensions/__tests__/canvas-recap.test.js +16 -113
  419. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +72 -99
  420. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +29 -12
  421. package/dist/pi-extensions/broker-local.d.ts +20 -0
  422. package/dist/pi-extensions/broker-local.js +89 -0
  423. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -3
  424. package/dist/pi-extensions/canvas-context-intro.js +22 -11
  425. package/dist/pi-extensions/canvas-doc-substrate.js +17 -34
  426. package/dist/pi-extensions/canvas-goal-capture.js +7 -12
  427. package/dist/pi-extensions/canvas-inbox-watcher.js +124 -139
  428. package/dist/pi-extensions/canvas-passive-context.d.ts +3 -3
  429. package/dist/pi-extensions/canvas-passive-context.js +22 -18
  430. package/dist/pi-extensions/canvas-recap.d.ts +2 -2
  431. package/dist/pi-extensions/canvas-recap.js +48 -21
  432. package/dist/pi-extensions/canvas-review-boundary.js +10 -4
  433. package/dist/pi-extensions/canvas-stophook.d.ts +0 -5
  434. package/dist/pi-extensions/canvas-stophook.js +149 -431
  435. package/dist/pi-extensions/canvas-structured-output.js +52 -30
  436. package/dist/pi-extensions/envelope-merge.d.ts +13 -0
  437. package/dist/pi-extensions/envelope-merge.js +29 -0
  438. package/dist/shared/working-activity.d.ts +3 -2
  439. package/dist/shared/working-activity.js +3 -2
  440. package/dist/types.d.ts +6 -5
  441. package/dist/types.js +2 -2
  442. package/docs/compat/hearth-crtr-v1.md +4 -12
  443. package/docs/compat/hearth-crtr-v2.md +3 -11
  444. package/docs/compat/hearth-crtr-v3.md +4 -12
  445. package/docs/compat/hearth-crtr-v4.md +3 -11
  446. package/docs/compat/hearth-crtr-v5.md +3 -11
  447. package/docs/public-api.md +3 -7
  448. package/package.json +7 -49
  449. package/runtime.lock.json +2766 -14533
  450. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/claude-plugin-commands.test.ts +0 -49
  451. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/memory-slash-commands.test.ts +0 -194
  452. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/statusline.test.ts +0 -68
  453. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/test.ts +0 -119
  454. package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +0 -439
  455. package/dist/clients/attach/__tests__/attach-keybindings.test.js +0 -170
  456. package/dist/clients/attach/__tests__/crtr-output-coverage.test.js +0 -57
  457. package/dist/clients/attach/__tests__/editor-frame-title.test.js +0 -19
  458. package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +0 -32
  459. package/dist/clients/attach/__tests__/markdown-headings.test.js +0 -35
  460. package/dist/clients/attach/__tests__/session-identity-refresh.test.js +0 -32
  461. package/dist/clients/conversation/conversation.d.ts +0 -65
  462. package/dist/clients/conversation/conversation.js +0 -318
  463. package/dist/clients/conversation/frames.d.ts +0 -30
  464. package/dist/clients/conversation/frames.js +0 -52
  465. package/dist/clients/conversation/region.d.ts +0 -51
  466. package/dist/clients/conversation/region.js +0 -265
  467. package/dist/clients/inbox/__tests__/inbox-controller.test.js +0 -247
  468. package/dist/clients/inbox/__tests__/mount-panel.test.js +0 -776
  469. package/dist/clients/inbox/review/__tests__/remap.test.js +0 -196
  470. package/dist/clients/inbox/review/columns.d.ts +0 -22
  471. package/dist/clients/inbox/review/columns.js +0 -40
  472. package/dist/clients/inbox/review/composition.d.ts +0 -60
  473. package/dist/clients/inbox/review/composition.js +0 -278
  474. package/dist/clients/inbox/review/document-region.d.ts +0 -120
  475. package/dist/clients/surfaces/region.d.ts +0 -16
  476. package/dist/clients/web/__tests__/push-engine.test.js +0 -158
  477. package/dist/clients/web/__tests__/push-registry.test.d.ts +0 -1
  478. package/dist/clients/web/__tests__/push-registry.test.js +0 -177
  479. package/dist/clients/web/dev-server.d.ts +0 -7
  480. package/dist/clients/web/dev-server.js +0 -46
  481. package/dist/clients/web/events.d.ts +0 -19
  482. package/dist/clients/web/events.js +0 -163
  483. package/dist/clients/web/push-engine.d.ts +0 -44
  484. package/dist/clients/web/push-engine.js +0 -125
  485. package/dist/clients/web/push-registry.d.ts +0 -85
  486. package/dist/clients/web/push-registry.js +0 -373
  487. package/dist/clients/web/server.d.ts +0 -19
  488. package/dist/clients/web/server.js +0 -661
  489. package/dist/clients/web/web-client/shared/protocol.d.ts +0 -214
  490. package/dist/clients/web/web-client/shared/protocol.js +0 -8
  491. package/dist/clients/web/web-cmd.d.ts +0 -2
  492. package/dist/clients/web/web-cmd.js +0 -141
  493. package/dist/commands/__tests__/api-canvas-source.test.d.ts +0 -1
  494. package/dist/commands/__tests__/api-canvas-source.test.js +0 -37
  495. package/dist/commands/__tests__/canvas-config.test.d.ts +0 -1
  496. package/dist/commands/__tests__/canvas-config.test.js +0 -107
  497. package/dist/commands/__tests__/node-no-kickoff-flag.test.d.ts +0 -1
  498. package/dist/commands/__tests__/node-no-kickoff-flag.test.js +0 -73
  499. package/dist/commands/__tests__/node-worktree-flag.test.d.ts +0 -1
  500. package/dist/commands/__tests__/node-worktree-flag.test.js +0 -49
  501. package/dist/commands/__tests__/search-contents.test.d.ts +0 -1
  502. package/dist/commands/__tests__/search-contents.test.js +0 -52
  503. package/dist/commands/memory/__tests__/lint.test.d.ts +0 -1
  504. package/dist/commands/memory/__tests__/lint.test.js +0 -15
  505. package/dist/commands/sys/__tests__/config-broker-thresholds.test.d.ts +0 -1
  506. package/dist/commands/sys/__tests__/config-broker-thresholds.test.js +0 -95
  507. package/dist/commands/sys/__tests__/config-keybindings.test.d.ts +0 -1
  508. package/dist/commands/sys/__tests__/config-keybindings.test.js +0 -55
  509. package/dist/commands/sys/__tests__/helpers/setup-runtime.d.ts +0 -5
  510. package/dist/commands/sys/__tests__/helpers/setup-runtime.js +0 -19
  511. package/dist/commands/sys/__tests__/setup-front-door.test.d.ts +0 -1
  512. package/dist/commands/sys/__tests__/setup-front-door.test.js +0 -102
  513. package/dist/commands/sys/__tests__/setup-models.test.d.ts +0 -1
  514. package/dist/commands/sys/__tests__/setup-models.test.js +0 -205
  515. package/dist/commands/sys/__tests__/setup-providers-viewport.test.d.ts +0 -1
  516. package/dist/commands/sys/__tests__/setup-providers-viewport.test.js +0 -155
  517. package/dist/commands/sys/__tests__/sync-deps.test.d.ts +0 -1
  518. package/dist/commands/sys/__tests__/sync-deps.test.js +0 -179
  519. package/dist/commands/sys/__tests__/sync-import.test.d.ts +0 -1
  520. package/dist/commands/sys/__tests__/sync-import.test.js +0 -315
  521. package/dist/core/__tests__/base-worker-prompt.test.d.ts +0 -1
  522. package/dist/core/__tests__/base-worker-prompt.test.js +0 -57
  523. package/dist/core/__tests__/command-plugins-surfaces.test.d.ts +0 -1
  524. package/dist/core/__tests__/command-plugins-surfaces.test.js +0 -359
  525. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +0 -1
  526. package/dist/core/__tests__/connection-reconnect-resume.test.js +0 -36
  527. package/dist/core/__tests__/finalization-migration.test.d.ts +0 -1
  528. package/dist/core/__tests__/finalization-migration.test.js +0 -38
  529. package/dist/core/__tests__/fixtures/extra-extension.d.ts +0 -1
  530. package/dist/core/__tests__/fixtures/extra-extension.js +0 -3
  531. package/dist/core/__tests__/fixtures/spawn-proof-target.test.d.ts +0 -1
  532. package/dist/core/__tests__/fixtures/spawn-proof-target.test.js +0 -15
  533. package/dist/core/__tests__/fork.test.d.ts +0 -1
  534. package/dist/core/__tests__/fork.test.js +0 -108
  535. package/dist/core/__tests__/full/broker-attach-limits.test.d.ts +0 -1
  536. package/dist/core/__tests__/full/broker-attach-limits.test.js +0 -147
  537. package/dist/core/__tests__/full/broker-deliver-ack.test.d.ts +0 -1
  538. package/dist/core/__tests__/full/broker-deliver-ack.test.js +0 -73
  539. package/dist/core/__tests__/full/broker-dialogs.test.d.ts +0 -1
  540. package/dist/core/__tests__/full/broker-dialogs.test.js +0 -180
  541. package/dist/core/__tests__/full/broker-display-snapshot.test.d.ts +0 -1
  542. package/dist/core/__tests__/full/broker-display-snapshot.test.js +0 -75
  543. package/dist/core/__tests__/full/broker-dormant-wake.test.d.ts +0 -1
  544. package/dist/core/__tests__/full/broker-dormant-wake.test.js +0 -130
  545. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +0 -1
  546. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +0 -57
  547. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +0 -1
  548. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +0 -80
  549. package/dist/core/__tests__/full/cascade-close.test.d.ts +0 -1
  550. package/dist/core/__tests__/full/cascade-close.test.js +0 -240
  551. package/dist/core/__tests__/full/detach-focus.test.d.ts +0 -1
  552. package/dist/core/__tests__/full/detach-focus.test.js +0 -126
  553. package/dist/core/__tests__/full/idle-release-settlement.test.d.ts +0 -1
  554. package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.d.ts +0 -1
  555. package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.js +0 -107
  556. package/dist/core/__tests__/full/park-clock-release.test.d.ts +0 -1
  557. package/dist/core/__tests__/full/park-clock-release.test.js +0 -56
  558. package/dist/core/__tests__/full/yield-message-transaction.test.d.ts +0 -1
  559. package/dist/core/__tests__/memory-index-render.test.d.ts +0 -1
  560. package/dist/core/__tests__/memory-index-render.test.js +0 -60
  561. package/dist/core/__tests__/model-routes.test.d.ts +0 -1
  562. package/dist/core/__tests__/model-routes.test.js +0 -41
  563. package/dist/core/__tests__/on-read-identity.test.d.ts +0 -1
  564. package/dist/core/__tests__/on-read-identity.test.js +0 -61
  565. package/dist/core/__tests__/phase4-review-migration.test.d.ts +0 -1
  566. package/dist/core/__tests__/phase4-review-migration.test.js +0 -100
  567. package/dist/core/__tests__/prune-to-limit.test.d.ts +0 -1
  568. package/dist/core/__tests__/prune-to-limit.test.js +0 -126
  569. package/dist/core/__tests__/reset.test.d.ts +0 -1
  570. package/dist/core/__tests__/reset.test.js +0 -69
  571. package/dist/core/__tests__/spawn-no-kickoff.test.d.ts +0 -1
  572. package/dist/core/__tests__/spawn-no-kickoff.test.js +0 -115
  573. package/dist/core/__tests__/steer-note.test.d.ts +0 -1
  574. package/dist/core/__tests__/steer-note.test.js +0 -41
  575. package/dist/core/__tests__/subcommand-tier.test.d.ts +0 -1
  576. package/dist/core/__tests__/subcommand-tier.test.js +0 -99
  577. package/dist/core/__tests__/subscription-delivery.test.js +0 -211
  578. package/dist/core/__tests__/test-runner-cheap-sharding.test.d.ts +0 -1
  579. package/dist/core/__tests__/test-runner-cheap-sharding.test.js +0 -97
  580. package/dist/core/__tests__/tmux-surface.test.js +0 -484
  581. package/dist/core/__tests__/wake-bearings.test.d.ts +0 -1
  582. package/dist/core/__tests__/wake-bearings.test.js +0 -102
  583. package/dist/core/canvas/__tests__/remote-event-stream.test.d.ts +0 -1
  584. package/dist/core/canvas/__tests__/remote-event-stream.test.js +0 -146
  585. package/dist/core/canvas/__tests__/source-resolve.test.d.ts +0 -1
  586. package/dist/core/canvas/__tests__/source-resolve.test.js +0 -77
  587. package/dist/core/canvas/browse/__tests__/model.test.d.ts +0 -1
  588. package/dist/core/canvas/browse/__tests__/model.test.js +0 -290
  589. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.d.ts +0 -1
  590. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +0 -134
  591. package/dist/core/canvas/browse/__tests__/render.test.d.ts +0 -1
  592. package/dist/core/canvas/browse/__tests__/render.test.js +0 -111
  593. package/dist/core/canvas/browse/rebuild-coalescer.d.ts +0 -7
  594. package/dist/core/canvas/browse/rebuild-coalescer.js +0 -61
  595. package/dist/core/canvas/remote-event-stream.d.ts +0 -24
  596. package/dist/core/canvas/remote-event-stream.js +0 -94
  597. package/dist/core/feed/__tests__/coalesce-human.test.d.ts +0 -1
  598. package/dist/core/feed/__tests__/coalesce-human.test.js +0 -36
  599. package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +0 -1
  600. package/dist/core/feed/__tests__/empty-node-guard.test.js +0 -45
  601. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +0 -1
  602. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +0 -84
  603. package/dist/core/human/__tests__/visible-paths.test.d.ts +0 -1
  604. package/dist/core/human/__tests__/visible-paths.test.js +0 -28
  605. package/dist/core/keybindings/__tests__/inbox-affordance.test.d.ts +0 -1
  606. package/dist/core/keybindings/__tests__/inbox-affordance.test.js +0 -38
  607. package/dist/core/review/__tests__/project.test.d.ts +0 -1
  608. package/dist/core/review/__tests__/project.test.js +0 -71
  609. package/dist/core/runtime/__tests__/command-expansion.test.d.ts +0 -1
  610. package/dist/core/runtime/__tests__/command-expansion.test.js +0 -25
  611. package/dist/core/runtime/__tests__/session-visibility.test.d.ts +0 -1
  612. package/dist/core/runtime/__tests__/session-visibility.test.js +0 -119
  613. package/dist/core/runtime/__tests__/spawn-at-given-id.test.d.ts +0 -1
  614. package/dist/core/runtime/__tests__/spawn-at-given-id.test.js +0 -34
  615. package/dist/core/runtime/tmux.js +0 -1314
  616. package/dist/daemon/api/__tests__/full/push-parity.test.d.ts +0 -1
  617. package/dist/daemon/api/__tests__/full/worktree-parity.test.d.ts +0 -1
  618. package/dist/daemon/api/__tests__/full/worktree-parity.test.js +0 -160
  619. package/dist/daemon/api/__tests__/inbox.test.d.ts +0 -1
  620. package/dist/daemon/api/__tests__/inbox.test.js +0 -489
  621. package/dist/web-client/apple-touch-icon.png +0 -0
  622. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  623. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  624. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  625. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  626. package/dist/web-client/assets/index-CCPaLeW7.css +0 -2
  627. package/dist/web-client/assets/index-kyOWsnZK.js +0 -86
  628. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  629. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  630. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  631. package/dist/web-client/assets/workbox-window.prod.es5-Bd17z0YL.js +0 -2
  632. package/dist/web-client/icon-192.png +0 -0
  633. package/dist/web-client/icon-512.png +0 -0
  634. package/dist/web-client/icon-maskable-512.png +0 -0
  635. package/dist/web-client/index.html +0 -17
  636. package/dist/web-client/manifest.webmanifest +0 -1
  637. package/dist/web-client/sw.js +0 -1
  638. /package/dist/api/__tests__/{client.test.d.ts → serial/client.test.d.ts} +0 -0
  639. /package/dist/clients/inbox/__tests__/{inbox-controller.test.d.ts → serial/inbox-controller.test.d.ts} +0 -0
  640. /package/dist/clients/inbox/__tests__/{mount-panel.test.d.ts → serial/mount-panel.test.d.ts} +0 -0
  641. /package/dist/clients/{attach/__tests__/attach-chrome-remote.test.d.ts → inbox/review/__tests__/teardown-notice.test.d.ts} +0 -0
  642. /package/dist/{clients/attach/__tests__/attach-keybindings.test.d.ts → core/__tests__/broker-extension-canvas-db-boundary.test.d.ts} +0 -0
  643. /package/dist/{clients/attach/__tests__/crtr-output-coverage.test.d.ts → core/__tests__/seam/broker-attach-multiclient.test.d.ts} +0 -0
  644. /package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.d.ts +0 -0
  645. /package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.d.ts +0 -0
  646. /package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.d.ts +0 -0
  647. /package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.d.ts +0 -0
  648. /package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.d.ts +0 -0
  649. /package/dist/{clients/attach/__tests__/editor-frame-title.test.d.ts → core/__tests__/seam/dormancy-release.test.d.ts} +0 -0
  650. /package/dist/{clients/attach/__tests__/input-controller-extension-command.test.d.ts → core/__tests__/seam/yield-refresh-transaction.test.d.ts} +0 -0
  651. /package/dist/core/__tests__/{broker-fork-seam.test.d.ts → serial/broker-fork-seam.test.d.ts} +0 -0
  652. /package/dist/core/__tests__/{broker-sdk-wiring.test.d.ts → serial/broker-sdk-wiring.test.d.ts} +0 -0
  653. /package/dist/core/__tests__/{broker-snapshot-history.test.d.ts → serial/broker-snapshot-history.test.d.ts} +0 -0
  654. /package/dist/core/__tests__/{command-plugins.test.d.ts → serial/command-plugins.test.d.ts} +0 -0
  655. /package/dist/core/__tests__/{deferred-no-wake.test.d.ts → serial/deferred-no-wake.test.d.ts} +0 -0
  656. /package/dist/core/__tests__/{flagship-lifecycle.test.d.ts → serial/flagship-lifecycle.test.d.ts} +0 -0
  657. /package/dist/core/__tests__/{host-teardown-process-group.test.d.ts → serial/host-teardown-process-group.test.d.ts} +0 -0
  658. /package/dist/core/__tests__/{human-deliver-e2e.test.d.ts → serial/human-deliver-e2e.test.d.ts} +0 -0
  659. /package/dist/core/__tests__/{live-mutation.test.d.ts → serial/live-mutation.test.d.ts} +0 -0
  660. /package/dist/core/__tests__/{refresh-stall-recycle.test.d.ts → serial/refresh-stall-recycle.test.d.ts} +0 -0
  661. /package/dist/core/__tests__/{revive.test.d.ts → serial/revive.test.d.ts} +0 -0
  662. /package/dist/core/__tests__/{spawn-root.test.d.ts → serial/spawn-root.test.d.ts} +0 -0
  663. /package/dist/core/__tests__/{subscription-delivery.test.d.ts → serial/subscription-delivery.test.d.ts} +0 -0
  664. /package/dist/core/__tests__/{tmux-surface.test.d.ts → serial/tmux-surface.test.d.ts} +0 -0
  665. /package/dist/core/__tests__/{worktree.test.d.ts → serial/worktree.test.d.ts} +0 -0
  666. /package/dist/{clients/attach/__tests__/markdown-headings.test.d.ts → core/__tests__/watchdog-abort-arms-retry.test.d.ts} +0 -0
  667. /package/dist/{clients/attach/__tests__/session-identity-refresh.test.d.ts → core/broker-client/__tests__/transport-socket.test.d.ts} +0 -0
  668. /package/dist/core/human/__tests__/{inbox-core.test.d.ts → serial/inbox-core.test.d.ts} +0 -0
  669. /package/dist/{clients/inbox/review/__tests__/remap.test.d.ts → daemon/api/__tests__/node-model-validation.test.d.ts} +0 -0
  670. /package/dist/{clients/surfaces/region.js → daemon/api/__tests__/review-terminal-conflict.test.d.ts} +0 -0
  671. /package/dist/daemon/api/__tests__/{full → seam}/api-server.test.d.ts +0 -0
  672. /package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.d.ts +0 -0
  673. /package/dist/{clients/web/__tests__/push-engine.test.d.ts → daemon/api/__tests__/seam/leaf-api-parity.test.d.ts} +0 -0
@@ -1,192 +1,85 @@
1
- // input/controller.ts — the input half of `crtr surface attach` (T6).
2
- //
3
- // Owns the editor's behavior: turns keystrokes + submits into `ClientToBroker`
4
- // command frames (sent via `hooks.onCommand`), reads pasted clipboard images to
5
- // attach to the next message (review M1), and renders blocking extension dialogs
6
- // (delegating to T5's `renderDialog`, routing the answer through
7
- // `hooks.onDialogResponse`). It NEVER touches the session or spawns pi — it only
8
- // produces frames; T7's `viewer.ts` constructs it and wires the socket.
9
- //
10
- // Keybinding matching is delegated to CustomEditor + its KeybindingsManager
11
- // (built by config.ts with the vendored `app.*` defs + user overrides) — that
12
- // is what calls `matchesKey` internally, so the controller wires intent via the
13
- // editor's `onSubmit`/`onEscape`/`onPasteImage` hooks + `onAction(app.*)`
14
- // registrations rather than matching raw keys itself.
1
+ // InputController owns editor intent and submission. Transient picker and dialog
2
+ // lifetime belongs to InputOverlayOwner; viewer permission belongs to the single
3
+ // ViewerCapabilities value supplied by session/input-wiring.
15
4
  import { spawn } from 'node:child_process';
16
5
  import { readFileSync, unlinkSync, writeFileSync } from 'node:fs';
17
6
  import { tmpdir } from 'node:os';
18
7
  import { join } from 'node:path';
19
8
  import { BROKER_READ_CAPS, encodeFrame, } from '../../../core/runtime/broker-protocol.js';
20
- import { isBlockingDialog, renderDialog } from '../overlays/dialogs.js';
21
- import { readClipboardImage, writeClipboardImageToFile } from './clipboard-image.js';
22
- import { dispatchSlashCommand, isSlashCommand, readOnlySlashRefusal } from '../slash/dispatch.js';
23
9
  import { classifySubmit } from '../../conversation/submit.js';
24
- import { buildForkPicker, buildModelPicker, buildScopedModelsPicker, buildSessionPicker, buildSettingsPicker, buildTreePicker, } from '../overlays/pickers.js';
25
- import { buildMcpPicker, readMcpServers, startMcpServerAction, MCP_EMPTY_STATE_HINT } from '../overlays/mcp.js';
26
- import { buildFileReviewPicker } from '../overlays/file-review.js';
27
- /** Overlay geometry for the native pickers — a touch wider/taller than the
28
- * extension-dialog default since the model/session/tree lists are denser. */
29
- const PICKER_OVERLAY_OPTIONS = { anchor: 'center', width: '80%', maxHeight: '85%' };
30
- /** Max gap between two Esc presses to count as a double-tap (pi uses 500ms). */
10
+ import { dispatchSlashCommand, isSlashCommand } from '../slash/dispatch.js';
11
+ import { viewerOperationForFrame } from './capabilities.js';
12
+ import { readClipboardImage, writeClipboardImageToFile } from './clipboard-image.js';
13
+ import { InputOverlayOwner } from './overlay-owner.js';
31
14
  const DOUBLE_ESCAPE_MS = 500;
32
- /** Largest drive frame the controller will emit. The broker DESTROYS the viewer
33
- * socket on any client line over BROKER_READ_CAPS.maxLineBytes (24 MiB), so we
34
- * refuse to emit within a 4 MiB margin of that. `emitDrive` measures the ACTUAL
35
- * encoded frame (text + JSON envelope), so the cap holds however the bytes
36
- * split. Pasted images go to a temp file and enter the prompt as a path, never
37
- * as inline base64, so a paste can no longer push a frame toward this ceiling. */
38
15
  const MAX_FRAME_BYTES = BROKER_READ_CAPS.maxLineBytes - 4 * 1024 * 1024;
39
- /** ClientToBroker frame types that mutate the engine/session — everything the
40
- * broker gates behind a writable role (`read_only` in broker.ts), plus
41
- * `reload_auth`
42
- * (open to any role server-side, but viewer-local login/logout are unwired for
43
- * a read-only viewer — this entry is belt-and-suspenders in case a
44
- * future path routes it through onCommand). A read-only viewer — an observer
45
- * (`--observer`) or a REMOTE attach (`--canvas`, always an observer) — is
46
- * a strictly VIEW-ONLY surface: even though the broker would itself reject
47
- * these from an observer, the input layer must never even emit one —
48
- * server-side rejection alone is not the contract.
49
- * `dequeue` is listed for documentation completeness even though it never
50
- * reaches `emitCommand` (it rides the separate `onRequest` read-op channel —
51
- * see `handleDequeue`'s own capability guard). Read-only frames (get_commands,
52
- * list_models, list_sessions, get_tree, get_settings, list_scoped_models)
53
- * and hello/bye/shutdown/extension_ui_response are deliberately absent —
54
- * reads stay live for remote (pickers just never get the chance to select,
55
- * see slash/dispatch.ts's READ_ONLY_SAFE_BUILTIN_NAMES gate). */
56
- const DRIVE_FRAME_TYPES = new Set([
57
- 'prompt', 'steer', 'follow_up', 'abort', 'bash',
58
- 'set_model', 'cycle_model', 'cycle_ladder', 'cycle_thinking', 'set_thinking_level', 'dequeue',
59
- 'set_auto_retry', 'set_auto_compaction', 'compact', 'new_session',
60
- 'switch_session', 'fork', 'set_session_name', 'navigate_tree', 'reload',
61
- 'export', 'clone', 'share', 'reload_auth',
62
- ]);
63
16
  export class InputController {
64
17
  tui;
65
18
  editor;
66
19
  keybindings;
67
20
  hooks;
68
- /** The currently-rendered blocking dialog, if any (for supersede/dismiss). */
69
- dialog;
70
- /** The request id of {@link dialog} — so a broker-driven `extension_ui_dismiss`
71
- * tears down ONLY the matching overlay (correlated dismissal), never an
72
- * unrelated one. */
73
- dialogId;
74
- /** The currently-open native picker overlay, if any (for supersede/dismiss). */
75
- picker;
76
- /** Monotonic open counter — a slow read-op reply whose `seq` is no longer the
77
- * latest is dropped, so an earlier picker never pops over a later request. */
78
- openSeq = 0;
79
- /** Latest engine state from `welcome`/`session_info_changed` (for `/session`). */
21
+ capabilities;
22
+ overlays;
80
23
  state;
81
- /** Timestamp of the last lone Esc, for double-tap → tree detection (0 = none). */
82
24
  lastEscapeAt = 0;
83
- /** Pasted-image registry: placeholder ordinal → temp-file path. The editor
84
- * shows a clean `[Image #N]` token instead of the ugly temp path; on submit
85
- * each token is expanded back to its path so the agent reads it off disk.
86
- * Cleared whenever the editor is emptied (send / clear). */
87
25
  pastedImages = new Map();
88
- /** Monotonic paste counter — the `N` in `[Image #N]`. */
89
26
  pasteSeq = 0;
90
- constructor(tui, editor, keybindings, hooks) {
27
+ constructor(tui, editor, keybindings, hooks, capabilities) {
91
28
  this.tui = tui;
92
29
  this.editor = editor;
93
30
  this.keybindings = keybindings;
94
31
  this.hooks = hooks;
32
+ this.capabilities = capabilities;
33
+ this.overlays = new InputOverlayOwner(tui, editor, keybindings, {
34
+ send: (frame) => this.emitCommand(frame),
35
+ notify: (message) => this.notify(message),
36
+ onDialogResponse: hooks.onDialogResponse,
37
+ onRequest: hooks.onRequest,
38
+ onMountPicker: hooks.onMountPicker,
39
+ onUnmountPicker: hooks.onUnmountPicker,
40
+ state: () => this.state,
41
+ });
95
42
  this.wire();
96
43
  }
97
- /** Why this viewer may not mutate the engine, or `undefined` when it may. The
98
- * ONE capability gate: a remote attach and an `--observer` attach are both
99
- * read-only, and the role is fixed at hello, so this answer cannot change
100
- * mid-session. */
101
- readOnlyReason() {
102
- if (this.hooks.remote === true)
103
- return 'Read-only remote attach';
104
- if ((this.hooks.role ?? 'controller') !== 'controller')
105
- return 'Read-only observer attach';
106
- return undefined;
107
- }
108
- /** Central choke point for every command frame the input layer emits (direct
109
- * keybindings, emitDrive, and the slash-command/picker `send` sink). Returns
110
- * false when the gate refused the frame, so callers stop without emitting
111
- * it. A writable local attach is byte-for-byte unchanged. */
112
- emitCommand(frame) {
113
- const reason = this.readOnlyReason();
114
- if (reason !== undefined && DRIVE_FRAME_TYPES.has(frame.type)) {
115
- this.notify(`${reason} — cannot drive the engine`);
116
- return false;
117
- }
118
- this.hooks.onCommand(frame);
119
- return true;
120
- }
121
- /** The model-ladder keybinding (alt+m / alt+shift+m), which viewer.ts's global
122
- * key listener owns rather than the editor's action table. Public so that
123
- * binding rides the gate above instead of going straight to the socket. */
124
44
  cycleLadder(direction) {
125
45
  this.emitCommand({ type: 'cycle_ladder', direction });
126
46
  }
127
- /** Send viewer-owned text without touching the editor or its history. The
128
- * broker aborts an active turn before starting this prompt, so a whip never
129
- * waits in pi's steering queue behind the work it is meant to replace. */
130
47
  sendInterrupting(text) {
131
48
  return this.emitDrive({ type: 'prompt', text, interrupt: true });
132
49
  }
133
- /** Render extension UI requests from the broker. Blocking dialogs route their
134
- * answer back to the broker; non-blocking notify requests become the viewer's
135
- * normal notice line and NOTHING else — a notify must never tear down an
136
- * unrelated blocking dialog whose broker request is still pending. A dialog is
137
- * torn down only by (a) a NEW blocking dialog superseding it, (b) its own
138
- * answer, or (c) a correlated {@link dismissDialog}
139
- * from the broker (the request was aborted/timed out out-of-band). */
140
- attachDialog(req) {
141
- if (req.method === 'notify') {
142
- const prefix = req.notifyType === 'error' ? 'Error: ' : req.notifyType === 'warning' ? 'Warning: ' : '';
143
- this.notify(`${prefix}${req.message}`);
144
- this.tui.requestRender();
145
- return;
146
- }
147
- // Everything past here TEARS DOWN the showing dialog, so a non-blocking
148
- // method must never reach it: pi's envelope carries display/editor methods
149
- // this viewer handles elsewhere (or not at all), and letting one through
150
- // would orphan an in-flight blocking request whose overlay it dismissed
151
- // without answering. `renderDialog` is inert for them anyway — the guard is
152
- // about the teardown above it, not the overlay below it.
153
- if (!isBlockingDialog(req))
154
- return;
155
- this.dialog?.dismiss();
156
- this.dialogId = req.id;
157
- this.dialog = renderDialog(this.tui, req, (resp) => {
158
- this.dialog = undefined;
159
- this.dialogId = undefined;
160
- this.hooks.onDialogResponse(resp);
161
- }, this.keybindings);
162
- this.tui.requestRender();
50
+ attachDialog(request) {
51
+ this.overlays.attachDialog(request);
163
52
  }
164
- /** Broker-driven correlated dismissal: the broker resolved this request itself
165
- * (its request was aborted out-of-band — e.g. a local OAuth loopback callback
166
- * won the race against this manual-paste dialog — or its timeout fired). Tear
167
- * down ONLY the overlay whose id matches; leave any other dialog untouched. */
168
53
  dismissDialog(id) {
169
- if (this.dialogId !== id)
170
- return;
171
- this.dialog?.dismiss();
172
- this.dialog = undefined;
173
- this.dialogId = undefined;
174
- this.tui.requestRender();
54
+ this.overlays.dismissDialog(id);
175
55
  }
176
- /** Feed the latest engine state so read-only commands (e.g. `/session`) report
177
- * current values. Optional; the controller works without it. */
178
56
  setState(state) {
179
57
  this.state = state;
180
58
  }
181
- // -------------------------------------------------------------------------
59
+ openFileReviewPicker(candidates, cwd, onChoose) {
60
+ this.overlays.openFileReviewPicker(candidates, cwd, onChoose);
61
+ }
62
+ /** Retained as a narrow delegate for auth affordances wired by the session. */
63
+ openAuthPicker(prepare) {
64
+ return this.overlays.openAuthPicker(prepare);
65
+ }
66
+ send = (frame) => {
67
+ this.emitCommand(frame);
68
+ };
69
+ emitCommand(frame) {
70
+ const operation = viewerOperationForFrame(frame);
71
+ if (operation === undefined || this.capabilities.permits(operation)) {
72
+ this.hooks.onCommand(frame);
73
+ return true;
74
+ }
75
+ this.notify(`${this.readOnlyLabel()} — cannot drive the engine`);
76
+ return false;
77
+ }
78
+ readOnlyLabel() {
79
+ return this.capabilities.remote ? 'Read-only remote attach' : 'Read-only observer attach';
80
+ }
182
81
  wire() {
183
82
  this.editor.onSubmit = (text) => this.handleSubmit(text);
184
- // Esc — mirror pi's native interactive escape (interactive-mode
185
- // setupKeyHandlers): while streaming, abort the turn; with an EMPTY editor
186
- // and idle, a double-tap (≤500ms) opens the session-tree navigator to rewind
187
- // to an earlier point (a lone tap just arms it); a non-empty editor swallows
188
- // Esc (it never clears text). openTreePicker is the same get_tree →
189
- // navigate_tree path that `/tree` and app.session.tree use.
190
83
  this.editor.onEscape = () => {
191
84
  if (this.state?.isStreaming) {
192
85
  this.emitCommand({ type: 'abort' });
@@ -197,55 +90,36 @@ export class InputController {
197
90
  const now = Date.now();
198
91
  if (now - this.lastEscapeAt < DOUBLE_ESCAPE_MS) {
199
92
  this.lastEscapeAt = 0;
200
- void this.openTreePicker();
93
+ void this.overlays.openTreePicker();
201
94
  }
202
95
  else {
203
96
  this.lastEscapeAt = now;
204
97
  }
205
98
  };
206
- // Ctrl+V / Alt+V → read clipboard image, save it to a temp file, and splice
207
- // the file PATH into the editor at the caret (so the agent can read it).
208
- this.editor.onPasteImage = () => {
209
- void this.handlePaste();
210
- };
211
- // A file DRAGGED into the pane arrives as bracketed-paste text (its path), not
212
- // an image event — TitledEditor recognizes an image path and routes it here so
213
- // it becomes an `[Image #N]` token just like a Ctrl+V paste.
99
+ this.editor.onPasteImage = () => { void this.handlePaste(); };
214
100
  const dropEditor = this.editor;
215
- dropEditor.onPasteImagePath = (path) => {
216
- this.attachImage(path);
217
- return true;
218
- };
219
- // Keyboard shortcuts that map 1:1 to a frame needing no engine-side data.
101
+ dropEditor.onPasteImagePath = (path) => { this.attachImage(path); return true; };
220
102
  this.editor.onAction('app.session.new', () => this.emitCommand({ type: 'new_session' }));
221
103
  this.editor.onAction('app.model.cycleForward', () => this.emitCommand({ type: 'cycle_model', direction: 'forward' }));
222
104
  this.editor.onAction('app.model.cycleBackward', () => this.emitCommand({ type: 'cycle_model', direction: 'backward' }));
223
105
  this.editor.onAction('app.thinking.cycle', () => this.emitCommand({ type: 'cycle_thinking' }));
224
106
  this.editor.onAction('app.message.followUp', () => this.handleFollowUp());
225
107
  this.editor.onAction('app.message.dequeue', () => void this.handleDequeue());
226
- // Pure client-side render toggles (ChatView owns the components).
227
108
  this.editor.onAction('app.tools.expand', () => this.hooks.onToggleToolsExpand?.());
228
109
  this.editor.onAction('app.thinking.toggle', () => this.hooks.onToggleThinking?.());
229
- // Ctrl+G — compose the prompt in $VISUAL/$EDITOR, then load it back.
230
110
  this.editor.onAction('app.editor.external', () => void this.openExternalEditor());
231
- // Native pickers via keybinding. `app.model.select` is bound to ctrl+l by
232
- // default (vendored in config.ts); the session.* actions ship with no
233
- // default key but fire if the user binds one — wiring them keeps parity.
234
- this.editor.onAction('app.model.select', () => void this.openModelPicker());
235
- this.editor.onAction('app.session.tree', () => void this.openTreePicker());
236
- this.editor.onAction('app.session.fork', () => void this.openForkPicker());
237
- this.editor.onAction('app.session.resume', () => void this.openSessionPicker());
111
+ this.editor.onAction('app.model.select', () => void this.overlays.openModelPicker());
112
+ this.editor.onAction('app.session.tree', () => void this.overlays.openTreePicker());
113
+ this.editor.onAction('app.session.fork', () => void this.overlays.openForkPicker());
114
+ this.editor.onAction('app.session.resume', () => void this.overlays.openSessionPicker());
238
115
  }
239
116
  slashContext() {
240
117
  return {
241
- send: (frame) => this.emitCommand(frame),
118
+ send: this.send,
242
119
  notify: (message) => this.notify(message),
120
+ capabilities: this.capabilities,
243
121
  state: this.state,
244
122
  cwd: process.cwd(),
245
- nodeId: this.hooks.nodeId,
246
- profileId: this.hooks.profileId,
247
- remote: this.hooks.remote,
248
- role: this.hooks.role,
249
123
  onGraph: this.hooks.onGraph,
250
124
  onInbox: this.hooks.onInbox,
251
125
  onHelp: this.hooks.onHelp,
@@ -256,257 +130,38 @@ export class InputController {
256
130
  onColor: this.hooks.onColor,
257
131
  onNodeMetadata: this.hooks.onNodeMetadata,
258
132
  onFoldTools: this.hooks.onFoldTools,
259
- openModelPicker: () => void this.openModelPicker(),
260
- openSessionPicker: () => void this.openSessionPicker(),
261
- openForkPicker: () => void this.openForkPicker(),
262
- openTreePicker: () => void this.openTreePicker(),
263
- openSettingsPicker: () => void this.openSettingsPicker(),
264
- openScopedModelsPicker: () => void this.openScopedModelsPicker(),
265
- openMcpPicker: (serverName) => this.openMcpPicker(serverName),
266
- openLoginPicker: this.hooks.openLoginPicker ? () => this.hooks.openLoginPicker() : undefined,
267
- openLogoutPicker: this.hooks.openLogoutPicker ? () => this.hooks.openLogoutPicker() : undefined,
133
+ openModelPicker: () => void this.overlays.openModelPicker(),
134
+ openSessionPicker: () => void this.overlays.openSessionPicker(),
135
+ openForkPicker: () => void this.overlays.openForkPicker(),
136
+ openTreePicker: () => void this.overlays.openTreePicker(),
137
+ openSettingsPicker: () => void this.overlays.openSettingsPicker(),
138
+ openScopedModelsPicker: () => void this.overlays.openScopedModelsPicker(),
139
+ openMcpPicker: (serverName) => this.overlays.openMcpPicker(serverName),
140
+ openLoginPicker: this.hooks.openLoginPicker,
141
+ openLogoutPicker: this.hooks.openLogoutPicker,
268
142
  };
269
143
  }
270
- // -------------------------------------------------------------------------
271
- // Native interactive pickers (overlays). Each fetches its payload via the
272
- // correlated read-op, builds the REAL pi selector, and routes selection to the
273
- // same command frame the text-arg slash path sends. Opened by slash command
274
- // (via slashContext) AND by keybinding (onAction, below).
275
- // -------------------------------------------------------------------------
276
- /** Send a command frame to the broker (picker selection sink). */
277
- send = (frame) => {
278
- this.emitCommand(frame);
279
- };
280
- /** Fetch a picker payload (narrowed on `kind`), then build + show its overlay
281
- * iff this open is still the latest (m4: a stale slow reply is dropped). Any
282
- * failure — unwired channel, wrong kind, request error, or a throwing builder
283
- * — surfaces a notice instead of an unhandled rejection (m5). */
284
- async openPicker(frame, kind, label, build) {
285
- if (this.hooks.onRequest === undefined) {
286
- this.notify(`The ${label} picker isn't available in this viewer`);
287
- return;
288
- }
289
- const seq = ++this.openSeq;
290
- let reply;
291
- try {
292
- reply = await this.hooks.onRequest(frame);
293
- }
294
- catch (err) {
295
- this.notify(`Could not open the ${label} picker: ${err instanceof Error ? err.message : String(err)}`);
296
- return;
297
- }
298
- if (seq !== this.openSeq)
299
- return; // a newer picker open superseded this one
300
- if (reply.kind !== kind) {
301
- this.notify(`Unexpected reply building the ${label} picker`);
302
- return;
303
- }
304
- this.showPicker((controls) => build(reply, controls.close));
305
- }
306
- /** Show a picker built by `build` (given a `close` that tears it down).
307
- * Mounted INLINE under the editor when viewer.ts wires onMountPicker/
308
- * onUnmountPicker (the integrated chrome path); otherwise falls back to a
309
- * centered overlay. Supersedes any picker already on screen. Routes focus to
310
- * the builder's declared focus target (an inner list for fork/settings) and
311
- * ALWAYS restores editor focus on close — `OverlayHandle.hide()` only
312
- * auto-restores when the OUTER component held focus, which is not the case
313
- * once we focus an inner child. A throwing builder is caught into a notice (m5). */
314
- showPicker(build) {
315
- this.picker?.dismiss();
316
- const inline = this.hooks.onMountPicker !== undefined && this.hooks.onUnmountPicker !== undefined;
317
- let handle;
318
- let current;
319
- let done = false;
320
- const disposeCurrent = () => {
321
- const disposable = current?.component;
322
- try {
323
- disposable?.dispose?.();
324
- }
325
- catch {
326
- /* ignore dispose errors during teardown */
327
- }
328
- };
329
- const mount = (p) => {
330
- if (inline)
331
- this.hooks.onMountPicker(p.component);
332
- else
333
- handle = this.tui.showOverlay(p.component, PICKER_OVERLAY_OPTIONS);
334
- this.tui.setFocus(p.focus);
335
- };
336
- const unmount = () => {
337
- if (inline)
338
- this.hooks.onUnmountPicker?.();
339
- else
340
- handle?.hide();
341
- };
342
- // Swap the mounted component IN PLACE (same inline slot) — multi-step flows
343
- // like /login replace the provider selector with the login dialog without
344
- // ever popping a centered overlay. Does NOT restore editor focus (only a
345
- // genuine close does); focus moves to the new component.
346
- const replace = (component, focus) => {
347
- if (done)
348
- return;
349
- disposeCurrent();
350
- unmount();
351
- current = { component, focus };
352
- mount(current);
353
- this.tui.requestRender();
354
- };
355
- const close = () => {
356
- if (done)
357
- return;
358
- done = true;
359
- disposeCurrent();
360
- unmount();
361
- this.picker = undefined;
362
- this.tui.setFocus(this.editor);
363
- this.tui.requestRender();
364
- };
365
- try {
366
- current = build({ close, replace });
367
- }
368
- catch (err) {
369
- this.notify(`Could not open picker: ${err instanceof Error ? err.message : String(err)}`);
370
- return;
371
- }
372
- mount(current);
373
- this.picker = {
374
- // Supersede: tear down without restoring editor focus — the new picker
375
- // grabs focus next. Genuine close restores the editor (above).
376
- dismiss: () => {
377
- if (done)
378
- return;
379
- done = true;
380
- disposeCurrent();
381
- unmount();
382
- },
383
- };
384
- this.tui.requestRender();
385
- }
386
- openModelPicker() {
387
- return this.openPicker({ type: 'list_models' }, 'list_models', 'model', (data, close) => buildModelPicker(this.tui, data, this.send, close));
388
- }
389
- /** Unlike the other pickers, the session picker opens IMMEDIATELY — its
390
- * component owns async loaders (with a Loading header), so the multi-second
391
- * session-dir scan runs off the open path instead of blocking it. */
392
- openSessionPicker() {
393
- const request = this.hooks.onRequest;
394
- if (request === undefined) {
395
- this.notify("The session picker isn't available in this viewer");
396
- return Promise.resolve();
397
- }
398
- this.openSeq++; // supersede any in-flight fetch-first picker open
399
- this.showPicker((controls) => buildSessionPicker(this.tui, this.state?.sessionFile, request, this.keybindings, this.send, controls.close));
400
- return Promise.resolve();
401
- }
402
- openTreePicker() {
403
- return this.openPicker({ type: 'get_tree' }, 'get_tree', 'tree', (data, close) => buildTreePicker(this.tui, data, this.send, close));
404
- }
405
- openForkPicker() {
406
- return this.openPicker({ type: 'get_tree' }, 'get_tree', 'fork', (data, close) => buildForkPicker(data, this.send, close));
407
- }
408
- openSettingsPicker() {
409
- return this.openPicker({ type: 'get_settings' }, 'get_settings', 'settings', (data, close) => buildSettingsPicker(data, this.send, close, (m) => this.notify(m)));
410
- }
411
- openScopedModelsPicker() {
412
- return this.openPicker({ type: 'list_scoped_models' }, 'list_scoped_models', 'scoped-models', (data, close) => buildScopedModelsPicker(data, close, (m) => this.notify(m)));
413
- }
414
- /** `/mcp` — the MCP server status panel. Viewer-local (no read-op): the rows
415
- * come from the adapter's on-disk config sources + tool cache
416
- * (overlays/mcp.ts), and the actions (connect / auth-start) ride a `prompt`
417
- * frame to the engine, where the adapter runtime actually lives. With a
418
- * server name (`/mcp <server>`), act on that server directly. */
419
- openMcpPicker(serverName) {
420
- const rows = readMcpServers();
421
- if (rows.length === 0) {
422
- this.notify(MCP_EMPTY_STATE_HINT);
423
- return;
424
- }
425
- if (serverName !== undefined) {
426
- const row = rows.find((r) => r.name === serverName);
427
- if (row === undefined) {
428
- this.notify(`No MCP server named ${JSON.stringify(serverName)} — usage: /mcp [server]`);
429
- return;
430
- }
431
- startMcpServerAction(row, this.send, (m) => this.notify(m));
432
- return;
433
- }
434
- this.showPicker((controls) => buildMcpPicker(rows, this.send, controls.close, (m) => this.notify(m)));
435
- }
436
- /** Open the transcript-backed file-review picker. The caller owns the popup
437
- * process and cursor insertion; this controller only owns picker lifetime. */
438
- openFileReviewPicker(candidates, cwd, onChoose) {
439
- this.showPicker((controls) => buildFileReviewPicker(candidates, cwd, (file) => {
440
- // The review mounts a full-pane surface and takes focus. Close this
441
- // picker first so its editor-focus restoration cannot steal focus
442
- // back from the newly mounted review.
443
- controls.close();
444
- onChoose(file);
445
- }, controls.close, (message) => this.notify(message)));
446
- }
447
- /** Open a viewer-local auth picker (login/logout) — uses the same showPicker
448
- * machinery as read-op pickers, but with a locally-built component (no read-op
449
- * request; auth operations are viewer-local in tmux-local attach). `prepare`
450
- * is async because provider status is async; superseded opens drop on openSeq. */
451
- async openAuthPicker(prepare) {
452
- const seq = ++this.openSeq;
453
- let build;
454
- try {
455
- build = await prepare();
456
- }
457
- catch (err) {
458
- this.notify(`Could not open the auth picker: ${err instanceof Error ? err.message : String(err)}`);
459
- return;
460
- }
461
- if (seq !== this.openSeq)
462
- return; // a newer picker open superseded this one
463
- this.showPicker(build);
464
- }
465
144
  handleSubmit(text) {
466
145
  const trimmed = text.trim();
467
146
  if (!trimmed)
468
147
  return;
469
- if (isSlashCommand(trimmed)) {
470
- const slashCtx = this.slashContext();
471
- // A read-only viewer's refusal is surfaced BEFORE dispatch, so no command
472
- // or engine frame is emitted.
473
- const refusal = readOnlySlashRefusal(trimmed, slashCtx);
474
- if (refusal !== undefined) {
475
- this.notify(refusal);
476
- return;
477
- }
478
- // Recognized builtin/scoped-out → handled here; unrecognized falls through
479
- // and is sent to the engine as a prompt (extension command).
480
- if (dispatchSlashCommand(trimmed, slashCtx)) {
481
- this.editor.setText('');
482
- return;
483
- }
148
+ if (isSlashCommand(trimmed) && dispatchSlashCommand(trimmed, this.slashContext())) {
149
+ this.editor.setText('');
150
+ return;
484
151
  }
485
- // Classify the remaining cases: dormant, bash, prompt/steer.
486
152
  const isDormant = this.hooks.isDormant?.() === true;
487
- const onDormantSubmit = this.hooks.onDormantSubmit;
488
153
  const classification = classifySubmit(text, {
489
154
  isDormant,
490
- canWakeDormant: isDormant && onDormantSubmit !== undefined,
155
+ canWakeDormant: isDormant && this.capabilities.permits('wake-dormant') && this.hooks.onDormantSubmit !== undefined,
491
156
  isStreaming: this.state?.isStreaming === true,
492
157
  });
493
158
  if (classification.kind === 'empty')
494
159
  return;
495
160
  if (classification.kind === 'dormant') {
496
- // Node dormant → there is NO broker to drive. Rather than emit a prompt/steer
497
- // frame into a dead socket (the old "I typed and nothing happened" trap),
498
- // deliver the text as a wake message: it revives the node and lands as the
499
- // first thing it reads, and the reconnect supervisor repaints the live pane.
500
- // Local-only — remote attach leaves onDormantSubmit unwired, so prompts fall
501
- // through unchanged there. Slash commands are dispatched above and don't reach
502
- // here, so `/quit`-style local commands keep working while dormant.
503
- // Raw text (whitespace and all) — not `trimmed` — per Major 4: emptiness is
504
- // judged on the trimmed view above, but what's actually delivered is the
505
- // human's exact typed content, same as the prompt/steer frame below.
506
- if (onDormantSubmit === undefined)
161
+ const submit = this.hooks.onDormantSubmit;
162
+ if (submit === undefined)
507
163
  return;
508
- const expanded = this.expandImagePlaceholders(classification.text);
509
- onDormantSubmit(expanded);
164
+ submit(this.expandImagePlaceholders(classification.text));
510
165
  this.editor.addToHistory(classification.trimmed);
511
166
  this.editor.setText('');
512
167
  this.clearPastedImages();
@@ -514,37 +169,20 @@ export class InputController {
514
169
  return;
515
170
  }
516
171
  if (classification.kind === 'bash') {
517
- // `!cmd` / `!!cmd` → run bash in the engine and inject output into context,
518
- // WITHOUT starting an agent turn (pi-native `!` semantics). `!!` withholds
519
- // the output from the LLM. The broker relays the run as bash_* frames.
520
172
  if (!this.emitDrive({ type: 'bash', command: classification.command, excludeFromContext: classification.excludeFromContext }))
521
173
  return;
522
174
  this.editor.addToHistory(classification.trimmed);
523
175
  this.editor.setText('');
524
176
  return;
525
177
  }
526
- // An unrecognized slash command belongs to the engine's extension runner.
527
- // `dispatchSlashCommand` returned false above, so route it exactly like
528
- // ordinary text instead of dropping the classifier's intermediate `slash`
529
- // result. Submit-while-streaming = steer (pi-native parity): interject into
530
- // the running turn instead of queueing a fresh prompt. `isStreaming` is the
531
- // broker snapshot's busy signal; the broker routes `steer` → session.steer()
532
- // and `prompt` → session.prompt(). Idle / no state yet → prompt (safe default).
533
- // Classification above (slash/dormant/bash/emptiness) reads the TRIMMED
534
- // view, but the frame itself carries the exact editor text (post image-
535
- // placeholder substitution) — leading/trailing whitespace is the human's,
536
- // not ours to discard.
537
178
  const delivery = classification.kind === 'slash'
538
179
  ? (this.state?.isStreaming === true ? 'steer' : 'prompt')
539
180
  : classification.kind;
540
181
  if (delivery !== 'steer' && delivery !== 'prompt')
541
182
  return;
542
183
  const expanded = this.expandImagePlaceholders(classification.kind === 'slash' ? text : classification.text);
543
- const frame = delivery === 'steer'
544
- ? { type: 'steer', text: expanded }
545
- : { type: 'prompt', text: expanded };
546
- if (!this.emitDrive(frame))
547
- return; // refused or too large — no frame emitted
184
+ if (!this.emitDrive(delivery === 'steer' ? { type: 'steer', text: expanded } : { type: 'prompt', text: expanded }))
185
+ return;
548
186
  this.editor.addToHistory(classification.trimmed);
549
187
  this.editor.setText('');
550
188
  this.clearPastedImages();
@@ -555,99 +193,28 @@ export class InputController {
555
193
  const trimmed = text.trim();
556
194
  if (!trimmed)
557
195
  return;
558
- // Same trimmed-classification/original-content split as handleSubmit's
559
- // prompt/steer frame: emptiness is judged on the trimmed view, but the
560
- // sent text is the exact editor content (post image-placeholder
561
- // substitution), whitespace and all.
562
196
  if (!this.emitDrive({ type: 'follow_up', text: this.expandImagePlaceholders(text) }))
563
197
  return;
564
198
  this.editor.addToHistory(trimmed);
565
199
  this.editor.setText('');
566
200
  this.clearPastedImages();
567
201
  }
568
- /** Replace every `[Image #N]` token with the temp-file path of the image the
569
- * user pasted, so the agent receives a readable path even though the editor
570
- * only ever showed the clean placeholder. Unknown ordinals are left as-is. */
571
- expandImagePlaceholders(text) {
572
- if (this.pastedImages.size === 0)
573
- return text;
574
- return text.replace(/\[Image #(\d+)\]/g, (match, digits) => {
575
- const path = this.pastedImages.get(Number(digits));
576
- return path ?? match;
577
- });
578
- }
579
- /** Drop the pasted-image registry and reset the ordinal counter — called once
580
- * a submit has consumed the editor text. */
581
- clearPastedImages() {
582
- this.pastedImages.clear();
583
- this.pasteSeq = 0;
584
- }
585
- /** Ctrl+G / app.editor.external — open the current editor text in $VISUAL/$EDITOR
586
- * and load the saved result back. Ported from pi's `openExternalEditor`: stop
587
- * the TUI to release the terminal, run the editor with inherited stdio, and on
588
- * a clean exit (status 0) replace the editor text; the socket keeps running in
589
- * the background throughout. */
590
- async openExternalEditor() {
591
- const editorCmd = process.env.VISUAL || process.env.EDITOR;
592
- if (!editorCmd) {
593
- this.notify('No editor configured — set $VISUAL or $EDITOR');
594
- return;
595
- }
596
- const editorWithExpanded = this.editor;
597
- const currentText = editorWithExpanded.getExpandedText?.() ?? this.editor.getText();
598
- const tmpFile = join(tmpdir(), `crtr-attach-editor-${Date.now()}.pi.md`);
599
- try {
600
- writeFileSync(tmpFile, currentText, 'utf-8');
601
- this.tui.stop();
602
- // Split on spaces so `code --wait`-style commands keep their args.
603
- const [cmd, ...args] = editorCmd.split(' ');
604
- process.stdout.write(`Launching external editor: ${editorCmd}\nThe viewer resumes when it exits.\n`);
605
- const status = await new Promise((resolve) => {
606
- const child = spawn(cmd, [...args, tmpFile], {
607
- stdio: 'inherit',
608
- shell: process.platform === 'win32',
609
- });
610
- child.on('error', () => resolve(null));
611
- child.on('close', (code) => resolve(code));
612
- });
613
- if (status === 0) {
614
- this.editor.setText(readFileSync(tmpFile, 'utf-8').replace(/\n$/, ''));
615
- }
616
- }
617
- catch (err) {
618
- this.notify(`External editor failed: ${err instanceof Error ? err.message : String(err)}`);
619
- }
620
- finally {
621
- try {
622
- unlinkSync(tmpFile);
623
- }
624
- catch {
625
- /* ignore temp-file cleanup errors */
626
- }
627
- this.tui.start();
628
- this.tui.requestRender(true);
629
- }
630
- }
631
- /** Alt+Up / app.message.dequeue — clear the broker's queued steering + follow-up
632
- * messages and restore them to the editor, prepended to any in-progress text
633
- * (pi's `restoreQueuedMessagesToEditor`: queued text first, joined by blank
634
- * lines). A read-AND-mutate op over the correlated request channel. */
635
202
  async handleDequeue() {
636
- const reason = this.readOnlyReason();
637
- if (reason !== undefined) {
638
- this.notify(`${reason} — cannot restore queued messages`);
203
+ if (!this.capabilities.permits('dequeue')) {
204
+ this.notify(`${this.readOnlyLabel()} — cannot restore queued messages`);
639
205
  return;
640
206
  }
641
- if (this.hooks.onRequest === undefined) {
207
+ const request = this.hooks.onRequest;
208
+ if (request === undefined) {
642
209
  this.notify("Restoring queued messages isn't available in this viewer");
643
210
  return;
644
211
  }
645
212
  let reply;
646
213
  try {
647
- reply = await this.hooks.onRequest({ type: 'dequeue' });
214
+ reply = await request({ type: 'dequeue' });
648
215
  }
649
- catch (err) {
650
- this.notify(`Could not restore queued messages: ${err instanceof Error ? err.message : String(err)}`);
216
+ catch (error) {
217
+ this.notify(`Could not restore queued messages: ${error instanceof Error ? error.message : String(error)}`);
651
218
  return;
652
219
  }
653
220
  if (reply.kind !== 'dequeue') {
@@ -659,28 +226,22 @@ export class InputController {
659
226
  this.notify('No queued messages to restore');
660
227
  return;
661
228
  }
662
- const combined = [queued.join('\n\n'), this.editor.getText()].filter((t) => t.trim()).join('\n\n');
663
- this.editor.setText(combined);
229
+ this.editor.setText([queued.join('\n\n'), this.editor.getText()].filter((value) => value.trim()).join('\n\n'));
664
230
  this.tui.requestRender();
665
231
  this.notify(`Restored ${queued.length} queued message${queued.length > 1 ? 's' : ''} to editor`);
666
232
  }
667
- /** Send a drive frame iff the WHOLE encoded frame fits under MAX_FRAME_BYTES
668
- * (the broker destroys the socket on any line over its 24 MiB read cap). Over
669
- * the ceiling → notify + refuse without sending a socket-destroying overflow. */
670
233
  emitDrive(frame) {
671
- let bytes;
672
234
  try {
673
- bytes = Buffer.byteLength(encodeFrame(frame));
235
+ const bytes = Buffer.byteLength(encodeFrame(frame));
236
+ if (bytes > MAX_FRAME_BYTES) {
237
+ this.notify(`Message too large to send (${Math.round(bytes / (1024 * 1024))} MiB) — shorten the text or remove an attached image`);
238
+ return false;
239
+ }
674
240
  }
675
241
  catch {
676
242
  this.notify('Message could not be encoded');
677
243
  return false;
678
244
  }
679
- if (bytes > MAX_FRAME_BYTES) {
680
- const mib = Math.round(bytes / (1024 * 1024));
681
- this.notify(`Message too large to send (${mib} MiB) — shorten the text or remove an attached image`);
682
- return false;
683
- }
684
245
  return this.emitCommand(frame);
685
246
  }
686
247
  async handlePaste() {
@@ -690,34 +251,64 @@ export class InputController {
690
251
  this.notify('No image in the clipboard');
691
252
  return;
692
253
  }
693
- // The clipboard layer read an image but DROPPED it — surface the reason.
694
254
  if (!result.image) {
695
255
  this.notify(result.note ?? 'Image not pasted');
696
256
  return;
697
257
  }
698
- // Persist to a temp file (the agent reads the image off disk instead of
699
- // relying on inline base64 the viewer can't render), then attach it as an
700
- // `[Image #N]` token (see attachImage).
701
- const path = writeClipboardImageToFile(result.image);
702
- this.attachImage(path, result.note);
258
+ this.attachImage(writeClipboardImageToFile(result.image), result.note);
703
259
  }
704
260
  catch {
705
261
  this.notify('Could not read the clipboard image');
706
262
  }
707
263
  }
708
- /** Register an image path in the paste registry and splice a clean `[Image #N]`
709
- * token into the editor at the caret instead of the raw path. On submit the
710
- * token is expanded back to the path (see expandImagePlaceholders), so the
711
- * agent reads the image off disk. Shared by clipboard paste and drag-drop. */
712
264
  attachImage(path, note) {
713
265
  const n = ++this.pasteSeq;
714
266
  this.pastedImages.set(n, path);
715
- const cur = this.editor.getText();
716
- const sep = cur && !/\s$/.test(cur) ? ' ' : '';
717
- this.editor.insertTextAtCursor(`${sep}[Image #${n}] `);
267
+ const current = this.editor.getText();
268
+ this.editor.insertTextAtCursor(`${current && !/\s$/.test(current) ? ' ' : ''}[Image #${n}] `);
718
269
  this.notify(note ? `Image #${n} attached (${note})` : `Image #${n} attached`);
719
270
  this.tui.requestRender();
720
271
  }
272
+ expandImagePlaceholders(text) {
273
+ return text.replace(/\[Image #(\d+)\]/g, (match, digits) => this.pastedImages.get(Number(digits)) ?? match);
274
+ }
275
+ clearPastedImages() {
276
+ this.pastedImages.clear();
277
+ this.pasteSeq = 0;
278
+ }
279
+ async openExternalEditor() {
280
+ const editorCommand = process.env.VISUAL || process.env.EDITOR;
281
+ if (!editorCommand) {
282
+ this.notify('No editor configured — set $VISUAL or $EDITOR');
283
+ return;
284
+ }
285
+ const expandedEditor = this.editor;
286
+ const temporaryFile = join(tmpdir(), `crtr-attach-editor-${Date.now()}.pi.md`);
287
+ try {
288
+ writeFileSync(temporaryFile, expandedEditor.getExpandedText?.() ?? this.editor.getText(), 'utf-8');
289
+ this.tui.stop();
290
+ const [command, ...args] = editorCommand.split(' ');
291
+ process.stdout.write(`Launching external editor: ${editorCommand}\nThe viewer resumes when it exits.\n`);
292
+ const status = await new Promise((resolve) => {
293
+ const child = spawn(command, [...args, temporaryFile], { stdio: 'inherit', shell: process.platform === 'win32' });
294
+ child.on('error', () => resolve(null));
295
+ child.on('close', (code) => resolve(code));
296
+ });
297
+ if (status === 0)
298
+ this.editor.setText(readFileSync(temporaryFile, 'utf-8').replace(/\n$/, ''));
299
+ }
300
+ catch (error) {
301
+ this.notify(`External editor failed: ${error instanceof Error ? error.message : String(error)}`);
302
+ }
303
+ finally {
304
+ try {
305
+ unlinkSync(temporaryFile);
306
+ }
307
+ catch { /* ignore temp cleanup */ }
308
+ this.tui.start();
309
+ this.tui.requestRender(true);
310
+ }
311
+ }
721
312
  notify(message) {
722
313
  this.hooks.onNotice?.(message);
723
314
  }