@north-light/crouter 0.3.173 → 0.3.175

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 (674) 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/memory/write.js +13 -12
  138. package/dist/commands/node/bash.d.ts +1 -0
  139. package/dist/commands/node/bash.js +198 -0
  140. package/dist/commands/node/create.d.ts +23 -0
  141. package/dist/commands/node/create.js +207 -0
  142. package/dist/commands/node/inspect.d.ts +2 -0
  143. package/dist/commands/node/inspect.js +302 -0
  144. package/dist/commands/node/lifecycle.d.ts +4 -0
  145. package/dist/commands/node/lifecycle.js +415 -0
  146. package/dist/commands/node/message.d.ts +1 -0
  147. package/dist/commands/node/message.js +164 -0
  148. package/dist/commands/node/pyramid.d.ts +2 -0
  149. package/dist/commands/node/pyramid.js +79 -0
  150. package/dist/commands/node/subscription.d.ts +1 -0
  151. package/dist/commands/node/subscription.js +82 -0
  152. package/dist/commands/node/wait.d.ts +1 -0
  153. package/dist/commands/node/wait.js +159 -0
  154. package/dist/commands/node-lifecycle-revive.js +10 -57
  155. package/dist/commands/node.d.ts +0 -42
  156. package/dist/commands/node.js +11 -2065
  157. package/dist/commands/search.js +1 -1
  158. package/dist/commands/surface/node/cycle.d.ts +1 -0
  159. package/dist/commands/surface/node/cycle.js +83 -0
  160. package/dist/commands/surface/node/focus.d.ts +1 -0
  161. package/dist/commands/surface/node/focus.js +67 -0
  162. package/dist/commands/surface/node/id.d.ts +1 -0
  163. package/dist/commands/surface/node/id.js +38 -0
  164. package/dist/commands/surface/node/pin.d.ts +1 -0
  165. package/dist/commands/surface/node/pin.js +42 -0
  166. package/dist/commands/surface/node/placement.d.ts +46 -0
  167. package/dist/commands/surface/node/placement.js +124 -0
  168. package/dist/commands/surface-node.js +4 -1
  169. package/dist/commands/surface-reload.js +1 -2
  170. package/dist/commands/surface-tmux-spread.js +1 -1
  171. package/dist/commands/surface.js +5 -20
  172. package/dist/commands/sys/__tests__/setup-core.test.js +18 -271
  173. package/dist/commands/sys/config.js +6 -6
  174. package/dist/commands/sys/logs.js +1 -1
  175. package/dist/commands/sys/panels/broker-limits-panel.d.ts +17 -0
  176. package/dist/commands/sys/panels/broker-limits-panel.js +104 -0
  177. package/dist/commands/sys/panels/dependencies-panel.d.ts +14 -0
  178. package/dist/commands/sys/panels/dependencies-panel.js +80 -0
  179. package/dist/commands/sys/panels/keybindings-panel.d.ts +56 -0
  180. package/dist/commands/sys/panels/keybindings-panel.js +703 -0
  181. package/dist/commands/sys/panels/keys-panel.d.ts +16 -0
  182. package/dist/commands/sys/panels/keys-panel.js +60 -0
  183. package/dist/commands/sys/panels/models-panel.d.ts +77 -0
  184. package/dist/commands/sys/panels/models-panel.js +647 -0
  185. package/dist/commands/sys/panels/panel.d.ts +45 -0
  186. package/dist/commands/sys/panels/panel.js +137 -0
  187. package/dist/commands/sys/panels/plugins-panel.d.ts +16 -0
  188. package/dist/commands/sys/panels/plugins-panel.js +80 -0
  189. package/dist/commands/sys/panels/prefs-panel.d.ts +21 -0
  190. package/dist/commands/sys/panels/prefs-panel.js +85 -0
  191. package/dist/commands/sys/panels/provider-panel.d.ts +62 -0
  192. package/dist/commands/sys/panels/provider-panel.js +538 -0
  193. package/dist/commands/sys/settings-shell.d.ts +55 -0
  194. package/dist/commands/sys/settings-shell.js +400 -0
  195. package/dist/commands/sys/settings.d.ts +2 -0
  196. package/dist/commands/sys/settings.js +31 -0
  197. package/dist/commands/sys/setup-core.d.ts +3 -33
  198. package/dist/commands/sys/setup-core.js +27 -78
  199. package/dist/commands/sys/setup-settings-runner.d.ts +24 -0
  200. package/dist/commands/sys/setup-settings-runner.js +63 -0
  201. package/dist/commands/sys/setup-wizard.d.ts +26 -125
  202. package/dist/commands/sys/setup-wizard.js +209 -2473
  203. package/dist/commands/sys/setup.js +5 -7
  204. package/dist/commands/sys.js +3 -2
  205. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +50 -0
  206. package/dist/core/__tests__/broker-launch-candidates.test.js +20 -9
  207. package/dist/core/__tests__/broker-preflight.test.js +1 -1
  208. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +108 -27
  209. package/dist/core/__tests__/canvas-inbox-watcher.test.js +14 -0
  210. package/dist/core/__tests__/canvas.test.js +2 -36
  211. package/dist/core/__tests__/close.test.js +14 -3
  212. package/dist/core/__tests__/context-intro.test.js +32 -143
  213. package/dist/core/__tests__/cron-broker-capacity.test.js +22 -3
  214. package/dist/core/__tests__/daemon-boot.test.js +2 -20
  215. package/dist/core/__tests__/daemon-wedge.test.js +1 -1
  216. package/dist/core/__tests__/dead-node-policy-table.test.js +1 -18
  217. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  218. package/dist/core/__tests__/fixtures/fake-engine.js +2 -2
  219. package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
  220. package/dist/core/__tests__/focuses.test.js +4 -98
  221. package/dist/core/__tests__/helpers/broker-clients.d.ts +0 -3
  222. package/dist/core/__tests__/helpers/broker-clients.js +0 -14
  223. package/dist/core/__tests__/helpers/harness.d.ts +0 -8
  224. package/dist/core/__tests__/helpers/harness.js +7 -87
  225. package/dist/core/__tests__/helpers/viewer-panes.js +1 -1
  226. package/dist/core/__tests__/inline-memory-refs.test.js +5 -134
  227. package/dist/core/__tests__/kickoff.test.js +1 -1
  228. package/dist/core/__tests__/memory-resolver-precedence.test.js +29 -1
  229. package/dist/core/__tests__/migration.test.js +35 -48
  230. package/dist/core/__tests__/on-read-dedup-resume.test.js +22 -12
  231. package/dist/core/__tests__/passive-subscription.test.js +26 -49
  232. package/dist/core/__tests__/preview-registry-sync.test.js +1 -2
  233. package/dist/core/__tests__/prune.test.js +94 -1
  234. package/dist/core/__tests__/review-model-floor.test.js +0 -32
  235. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +255 -0
  236. package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.js +39 -7
  237. package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.js +30 -29
  238. package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.js +54 -5
  239. package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.js +134 -82
  240. package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.js +15 -4
  241. package/dist/core/__tests__/{full/idle-release-settlement.test.js → seam/dormancy-release.test.js} +69 -17
  242. package/dist/core/__tests__/{full/yield-message-transaction.test.js → seam/yield-refresh-transaction.test.js} +31 -9
  243. package/dist/core/__tests__/secrets.test.js +1 -7
  244. package/dist/core/__tests__/{broker-fork-seam.test.js → serial/broker-fork-seam.test.js} +58 -7
  245. package/dist/core/__tests__/{broker-sdk-wiring.test.js → serial/broker-sdk-wiring.test.js} +18 -162
  246. package/dist/core/__tests__/{broker-snapshot-history.test.js → serial/broker-snapshot-history.test.js} +9 -9
  247. package/dist/core/__tests__/{command-plugins.test.js → serial/command-plugins.test.js} +105 -15
  248. package/dist/core/__tests__/{deferred-no-wake.test.js → serial/deferred-no-wake.test.js} +23 -20
  249. package/dist/core/__tests__/{flagship-lifecycle.test.js → serial/flagship-lifecycle.test.js} +7 -7
  250. package/dist/core/__tests__/{host-teardown-process-group.test.js → serial/host-teardown-process-group.test.js} +6 -6
  251. package/dist/core/__tests__/{human-deliver-e2e.test.js → serial/human-deliver-e2e.test.js} +12 -12
  252. package/dist/core/__tests__/{live-mutation.test.js → serial/live-mutation.test.js} +5 -5
  253. package/dist/core/__tests__/{refresh-stall-recycle.test.js → serial/refresh-stall-recycle.test.js} +5 -14
  254. package/dist/core/__tests__/{revive.test.js → serial/revive.test.js} +15 -100
  255. package/dist/core/__tests__/{spawn-root.test.js → serial/spawn-root.test.js} +32 -15
  256. package/dist/core/__tests__/serial/subscription-delivery.test.js +126 -0
  257. package/dist/core/__tests__/serial/tmux-surface.test.js +256 -0
  258. package/dist/core/__tests__/{worktree.test.js → serial/worktree.test.js} +15 -15
  259. package/dist/core/__tests__/session-model.test.js +0 -18
  260. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +9 -0
  261. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +50 -0
  262. package/dist/core/broker-client/__tests__/transport-relay.test.js +15 -16
  263. package/dist/core/broker-client/__tests__/transport-socket.test.js +28 -0
  264. package/dist/core/broker-client/client.d.ts +2 -2
  265. package/dist/core/broker-client/client.js +4 -7
  266. package/dist/core/broker-client/transport-relay.d.ts +13 -19
  267. package/dist/core/broker-client/transport-relay.js +19 -34
  268. package/dist/core/broker-client/transport-socket.d.ts +2 -2
  269. package/dist/core/broker-client/transport-socket.js +9 -2
  270. package/dist/core/broker-client/transport.d.ts +7 -3
  271. package/dist/core/broker-client/transport.js +4 -4
  272. package/dist/core/canvas/__tests__/attention.test.js +1 -18
  273. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +7 -64
  274. package/dist/core/canvas/__tests__/remote-transport.test.js +2 -3
  275. package/dist/core/canvas/__tests__/render-remote.test.js +7 -52
  276. package/dist/core/canvas/browse/app.d.ts +2 -17
  277. package/dist/core/canvas/browse/app.js +16 -88
  278. package/dist/core/canvas/browse/model.d.ts +0 -4
  279. package/dist/core/canvas/browse/model.js +1 -12
  280. package/dist/core/canvas/browse/render.js +6 -3
  281. package/dist/core/canvas/crons.d.ts +8 -5
  282. package/dist/core/canvas/crons.js +7 -6
  283. package/dist/core/canvas/extensions.d.ts +7 -3
  284. package/dist/core/canvas/extensions.js +16 -4
  285. package/dist/core/canvas/nav-model.d.ts +0 -4
  286. package/dist/core/canvas/nav-model.js +2 -14
  287. package/dist/core/canvas/node-order.d.ts +3 -0
  288. package/dist/core/canvas/node-order.js +12 -0
  289. package/dist/core/canvas/remote-canvas-source.d.ts +3 -6
  290. package/dist/core/canvas/remote-canvas-source.js +8 -15
  291. package/dist/core/command.js +11 -4
  292. package/dist/core/config.js +3 -1
  293. package/dist/core/events/envelope.js +1 -1
  294. package/dist/core/events/read.js +7 -21
  295. package/dist/core/events/source.d.ts +0 -2
  296. package/dist/core/events/source.js +0 -16
  297. package/dist/core/events/types.d.ts +1 -1
  298. package/dist/core/fault-classifier.d.ts +1 -1
  299. package/dist/core/feed/passive.d.ts +0 -11
  300. package/dist/core/feed/passive.js +1 -62
  301. package/dist/core/human/__tests__/{inbox-core.test.js → serial/inbox-core.test.js} +6 -6
  302. package/dist/core/keybindings/__tests__/resolve.test.js +1 -96
  303. package/dist/core/keybindings/background-bash.js +3 -4
  304. package/dist/core/keybindings/catalog.js +1 -1
  305. package/dist/core/memory/inline-ref-grammar.js +3 -3
  306. package/dist/core/memory-resolver.d.ts +11 -25
  307. package/dist/core/memory-resolver.js +51 -33
  308. package/dist/core/model-routes.d.ts +15 -0
  309. package/dist/core/model-routes.js +12 -0
  310. package/dist/core/pool-credential-store.js +1 -1
  311. package/dist/core/preview-registry.js +8 -9
  312. package/dist/core/profiles/__tests__/root-profile.test.js +4 -14
  313. package/dist/core/review/realize.d.ts +0 -13
  314. package/dist/core/review/realize.js +0 -40
  315. package/dist/core/review/types.d.ts +1 -1
  316. package/dist/core/runtime/__tests__/engine-route.test.js +2 -2
  317. package/dist/core/runtime/bearings.js +1 -11
  318. package/dist/core/runtime/broker/client-registry.d.ts +33 -0
  319. package/dist/core/runtime/broker/client-registry.js +164 -0
  320. package/dist/core/runtime/broker/daemon-ops.d.ts +19 -0
  321. package/dist/core/runtime/broker/daemon-ops.js +49 -0
  322. package/dist/core/runtime/broker/event-projection.d.ts +42 -0
  323. package/dist/core/runtime/broker/event-projection.js +178 -0
  324. package/dist/core/runtime/broker/fault-retry.d.ts +39 -0
  325. package/dist/core/runtime/broker/fault-retry.js +277 -0
  326. package/dist/core/runtime/broker/frame-dispatch.d.ts +238 -0
  327. package/dist/core/runtime/broker/frame-dispatch.js +1537 -0
  328. package/dist/core/runtime/broker/inbox.d.ts +22 -0
  329. package/dist/core/runtime/broker/inbox.js +167 -0
  330. package/dist/core/runtime/broker/passive.d.ts +16 -0
  331. package/dist/core/runtime/broker/passive.js +44 -0
  332. package/dist/core/runtime/broker/read-ops.d.ts +23 -0
  333. package/dist/core/runtime/broker/read-ops.js +150 -0
  334. package/dist/core/runtime/broker/rebind.d.ts +74 -0
  335. package/dist/core/runtime/broker/rebind.js +211 -0
  336. package/dist/core/runtime/broker/tool-groups.d.ts +39 -0
  337. package/dist/core/runtime/broker/tool-groups.js +244 -0
  338. package/dist/core/runtime/broker-extension-render.d.ts +11 -0
  339. package/dist/core/runtime/broker-extension-render.js +181 -0
  340. package/dist/core/runtime/broker-persona-guidance.d.ts +13 -0
  341. package/dist/core/runtime/broker-persona-guidance.js +58 -0
  342. package/dist/core/runtime/broker-protocol.d.ts +1 -1
  343. package/dist/core/runtime/broker-protocol.js +1 -1
  344. package/dist/core/runtime/broker.d.ts +5 -196
  345. package/dist/core/runtime/broker.js +276 -3059
  346. package/dist/core/runtime/canvas-extensions.js +11 -2
  347. package/dist/core/runtime/fault.js +1 -1
  348. package/dist/core/runtime/model-selection.d.ts +9 -0
  349. package/dist/core/runtime/model-selection.js +12 -0
  350. package/dist/core/runtime/model-swap.d.ts +3 -3
  351. package/dist/core/runtime/model-swap.js +33 -25
  352. package/dist/core/runtime/placement-tmux.d.ts +3 -14
  353. package/dist/core/runtime/placement-tmux.js +11 -80
  354. package/dist/core/runtime/placement.d.ts +4 -2
  355. package/dist/core/runtime/placement.js +7 -12
  356. package/dist/core/runtime/promote.d.ts +14 -1
  357. package/dist/core/runtime/promote.js +1 -1
  358. package/dist/core/runtime/reset.d.ts +1 -1
  359. package/dist/core/runtime/roadmap.js +1 -1
  360. package/dist/core/runtime/spawn.d.ts +8 -0
  361. package/dist/core/runtime/spawn.js +36 -6
  362. package/dist/core/runtime/stop-guard.d.ts +4 -3
  363. package/dist/core/runtime/stop-guard.js +36 -18
  364. package/dist/core/runtime/stop-signals.d.ts +4 -1
  365. package/dist/core/runtime/stream-watchdog.js +4 -5
  366. package/dist/core/runtime/structured-output.d.ts +15 -2
  367. package/dist/core/runtime/structured-output.js +35 -12
  368. package/dist/core/runtime/tmux-bindings.d.ts +25 -0
  369. package/dist/core/runtime/tmux-bindings.js +682 -0
  370. package/dist/core/runtime/tmux-chrome.d.ts +2 -1
  371. package/dist/core/runtime/tmux-chrome.js +3 -2
  372. package/dist/core/runtime/{tmux.d.ts → tmux-driver.d.ts} +18 -26
  373. package/dist/core/runtime/tmux-driver.js +598 -0
  374. package/dist/core/runtime/view-socket.d.ts +14 -0
  375. package/dist/core/runtime/view-socket.js +83 -0
  376. package/dist/core/session-model/session-state.d.ts +1 -2
  377. package/dist/core/session-model/session-state.js +5 -8
  378. package/dist/core/spawn.d.ts +1 -1
  379. package/dist/core/spawn.js +2 -2
  380. package/dist/core/substrate/gate.d.ts +1 -1
  381. package/dist/core/substrate/on-read-pure.d.ts +7 -0
  382. package/dist/core/substrate/on-read-pure.js +272 -0
  383. package/dist/core/termrender/termrender.js +32 -2
  384. package/dist/core/user-settings.d.ts +23 -1
  385. package/dist/core/user-settings.js +18 -1
  386. package/dist/daemon/api/__tests__/node-model-validation.test.js +134 -0
  387. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +98 -0
  388. package/dist/daemon/api/__tests__/{full → seam}/api-server.test.js +6 -48
  389. package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.js +17 -44
  390. package/dist/daemon/api/__tests__/{full/push-parity.test.js → seam/leaf-api-parity.test.js} +61 -42
  391. package/dist/daemon/api/bridge.js +40 -81
  392. package/dist/daemon/api/handlers/broker-ops.d.ts +2 -0
  393. package/dist/daemon/api/handlers/broker-ops.js +383 -0
  394. package/dist/daemon/api/handlers/crons.js +9 -4
  395. package/dist/daemon/api/handlers/files.js +6 -7
  396. package/dist/daemon/api/handlers/inbox.js +2 -340
  397. package/dist/daemon/api/handlers/nodes.d.ts +10 -1
  398. package/dist/daemon/api/handlers/nodes.js +74 -39
  399. package/dist/daemon/api/handlers/review-comments.js +5 -1
  400. package/dist/daemon/api/handlers/reviews.js +0 -19
  401. package/dist/daemon/api/map.js +25 -4
  402. package/dist/daemon/api/router.js +2 -3
  403. package/dist/daemon/api/server.js +5 -7
  404. package/dist/daemon/cron-run.d.ts +4 -0
  405. package/dist/daemon/cron-run.js +30 -13
  406. package/dist/daemon/crtrd.d.ts +3 -58
  407. package/dist/daemon/crtrd.js +28 -1244
  408. package/dist/daemon/manage.js +0 -1
  409. package/dist/daemon/reconcilers/broker-supervision.d.ts +50 -0
  410. package/dist/daemon/reconcilers/broker-supervision.js +549 -0
  411. package/dist/daemon/reconcilers/cron-lane.d.ts +10 -0
  412. package/dist/daemon/reconcilers/cron-lane.js +32 -0
  413. package/dist/daemon/reconcilers/dormant-inbox.d.ts +18 -0
  414. package/dist/daemon/reconcilers/dormant-inbox.js +143 -0
  415. package/dist/daemon/reconcilers/storage-maintenance.d.ts +22 -0
  416. package/dist/daemon/reconcilers/storage-maintenance.js +213 -0
  417. package/dist/daemon/review/finish.js +2 -5
  418. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +34 -53
  419. package/dist/pi-extensions/__tests__/canvas-recap.test.js +16 -113
  420. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +72 -99
  421. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +29 -12
  422. package/dist/pi-extensions/broker-local.d.ts +20 -0
  423. package/dist/pi-extensions/broker-local.js +89 -0
  424. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -3
  425. package/dist/pi-extensions/canvas-context-intro.js +22 -11
  426. package/dist/pi-extensions/canvas-doc-substrate.js +17 -34
  427. package/dist/pi-extensions/canvas-goal-capture.js +7 -12
  428. package/dist/pi-extensions/canvas-inbox-watcher.js +124 -139
  429. package/dist/pi-extensions/canvas-passive-context.d.ts +3 -3
  430. package/dist/pi-extensions/canvas-passive-context.js +22 -18
  431. package/dist/pi-extensions/canvas-recap.d.ts +2 -2
  432. package/dist/pi-extensions/canvas-recap.js +48 -21
  433. package/dist/pi-extensions/canvas-review-boundary.js +10 -4
  434. package/dist/pi-extensions/canvas-stophook.d.ts +0 -5
  435. package/dist/pi-extensions/canvas-stophook.js +149 -431
  436. package/dist/pi-extensions/canvas-structured-output.js +52 -30
  437. package/dist/pi-extensions/envelope-merge.d.ts +13 -0
  438. package/dist/pi-extensions/envelope-merge.js +29 -0
  439. package/dist/shared/working-activity.d.ts +3 -2
  440. package/dist/shared/working-activity.js +3 -2
  441. package/dist/types.d.ts +6 -5
  442. package/dist/types.js +2 -2
  443. package/docs/compat/hearth-crtr-v1.md +4 -12
  444. package/docs/compat/hearth-crtr-v2.md +3 -11
  445. package/docs/compat/hearth-crtr-v3.md +4 -12
  446. package/docs/compat/hearth-crtr-v4.md +3 -11
  447. package/docs/compat/hearth-crtr-v5.md +3 -11
  448. package/docs/public-api.md +3 -7
  449. package/package.json +7 -49
  450. package/runtime.lock.json +2766 -14533
  451. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/claude-plugin-commands.test.ts +0 -49
  452. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/memory-slash-commands.test.ts +0 -194
  453. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/statusline.test.ts +0 -68
  454. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/test.ts +0 -119
  455. package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +0 -439
  456. package/dist/clients/attach/__tests__/attach-keybindings.test.js +0 -170
  457. package/dist/clients/attach/__tests__/crtr-output-coverage.test.js +0 -57
  458. package/dist/clients/attach/__tests__/editor-frame-title.test.js +0 -19
  459. package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +0 -32
  460. package/dist/clients/attach/__tests__/markdown-headings.test.js +0 -35
  461. package/dist/clients/attach/__tests__/session-identity-refresh.test.js +0 -32
  462. package/dist/clients/conversation/conversation.d.ts +0 -65
  463. package/dist/clients/conversation/conversation.js +0 -318
  464. package/dist/clients/conversation/frames.d.ts +0 -30
  465. package/dist/clients/conversation/frames.js +0 -52
  466. package/dist/clients/conversation/region.d.ts +0 -51
  467. package/dist/clients/conversation/region.js +0 -265
  468. package/dist/clients/inbox/__tests__/inbox-controller.test.js +0 -247
  469. package/dist/clients/inbox/__tests__/mount-panel.test.js +0 -776
  470. package/dist/clients/inbox/review/__tests__/remap.test.js +0 -196
  471. package/dist/clients/inbox/review/columns.d.ts +0 -22
  472. package/dist/clients/inbox/review/columns.js +0 -40
  473. package/dist/clients/inbox/review/composition.d.ts +0 -60
  474. package/dist/clients/inbox/review/composition.js +0 -278
  475. package/dist/clients/inbox/review/document-region.d.ts +0 -120
  476. package/dist/clients/surfaces/region.d.ts +0 -16
  477. package/dist/clients/web/__tests__/push-engine.test.js +0 -158
  478. package/dist/clients/web/__tests__/push-registry.test.d.ts +0 -1
  479. package/dist/clients/web/__tests__/push-registry.test.js +0 -177
  480. package/dist/clients/web/dev-server.d.ts +0 -7
  481. package/dist/clients/web/dev-server.js +0 -46
  482. package/dist/clients/web/events.d.ts +0 -19
  483. package/dist/clients/web/events.js +0 -163
  484. package/dist/clients/web/push-engine.d.ts +0 -44
  485. package/dist/clients/web/push-engine.js +0 -125
  486. package/dist/clients/web/push-registry.d.ts +0 -85
  487. package/dist/clients/web/push-registry.js +0 -373
  488. package/dist/clients/web/server.d.ts +0 -19
  489. package/dist/clients/web/server.js +0 -661
  490. package/dist/clients/web/web-client/shared/protocol.d.ts +0 -214
  491. package/dist/clients/web/web-client/shared/protocol.js +0 -8
  492. package/dist/clients/web/web-cmd.d.ts +0 -2
  493. package/dist/clients/web/web-cmd.js +0 -141
  494. package/dist/commands/__tests__/api-canvas-source.test.d.ts +0 -1
  495. package/dist/commands/__tests__/api-canvas-source.test.js +0 -37
  496. package/dist/commands/__tests__/canvas-config.test.d.ts +0 -1
  497. package/dist/commands/__tests__/canvas-config.test.js +0 -107
  498. package/dist/commands/__tests__/node-no-kickoff-flag.test.d.ts +0 -1
  499. package/dist/commands/__tests__/node-no-kickoff-flag.test.js +0 -73
  500. package/dist/commands/__tests__/node-worktree-flag.test.d.ts +0 -1
  501. package/dist/commands/__tests__/node-worktree-flag.test.js +0 -49
  502. package/dist/commands/__tests__/search-contents.test.d.ts +0 -1
  503. package/dist/commands/__tests__/search-contents.test.js +0 -52
  504. package/dist/commands/memory/__tests__/lint.test.d.ts +0 -1
  505. package/dist/commands/memory/__tests__/lint.test.js +0 -15
  506. package/dist/commands/sys/__tests__/config-broker-thresholds.test.d.ts +0 -1
  507. package/dist/commands/sys/__tests__/config-broker-thresholds.test.js +0 -95
  508. package/dist/commands/sys/__tests__/config-keybindings.test.d.ts +0 -1
  509. package/dist/commands/sys/__tests__/config-keybindings.test.js +0 -55
  510. package/dist/commands/sys/__tests__/helpers/setup-runtime.d.ts +0 -5
  511. package/dist/commands/sys/__tests__/helpers/setup-runtime.js +0 -19
  512. package/dist/commands/sys/__tests__/setup-front-door.test.d.ts +0 -1
  513. package/dist/commands/sys/__tests__/setup-front-door.test.js +0 -102
  514. package/dist/commands/sys/__tests__/setup-models.test.d.ts +0 -1
  515. package/dist/commands/sys/__tests__/setup-models.test.js +0 -205
  516. package/dist/commands/sys/__tests__/setup-providers-viewport.test.d.ts +0 -1
  517. package/dist/commands/sys/__tests__/setup-providers-viewport.test.js +0 -155
  518. package/dist/commands/sys/__tests__/sync-deps.test.d.ts +0 -1
  519. package/dist/commands/sys/__tests__/sync-deps.test.js +0 -179
  520. package/dist/commands/sys/__tests__/sync-import.test.d.ts +0 -1
  521. package/dist/commands/sys/__tests__/sync-import.test.js +0 -315
  522. package/dist/core/__tests__/base-worker-prompt.test.d.ts +0 -1
  523. package/dist/core/__tests__/base-worker-prompt.test.js +0 -57
  524. package/dist/core/__tests__/command-plugins-surfaces.test.d.ts +0 -1
  525. package/dist/core/__tests__/command-plugins-surfaces.test.js +0 -359
  526. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +0 -1
  527. package/dist/core/__tests__/connection-reconnect-resume.test.js +0 -36
  528. package/dist/core/__tests__/finalization-migration.test.d.ts +0 -1
  529. package/dist/core/__tests__/finalization-migration.test.js +0 -38
  530. package/dist/core/__tests__/fixtures/extra-extension.d.ts +0 -1
  531. package/dist/core/__tests__/fixtures/extra-extension.js +0 -3
  532. package/dist/core/__tests__/fixtures/spawn-proof-target.test.d.ts +0 -1
  533. package/dist/core/__tests__/fixtures/spawn-proof-target.test.js +0 -15
  534. package/dist/core/__tests__/fork.test.d.ts +0 -1
  535. package/dist/core/__tests__/fork.test.js +0 -108
  536. package/dist/core/__tests__/full/broker-attach-limits.test.d.ts +0 -1
  537. package/dist/core/__tests__/full/broker-attach-limits.test.js +0 -147
  538. package/dist/core/__tests__/full/broker-deliver-ack.test.d.ts +0 -1
  539. package/dist/core/__tests__/full/broker-deliver-ack.test.js +0 -73
  540. package/dist/core/__tests__/full/broker-dialogs.test.d.ts +0 -1
  541. package/dist/core/__tests__/full/broker-dialogs.test.js +0 -180
  542. package/dist/core/__tests__/full/broker-display-snapshot.test.d.ts +0 -1
  543. package/dist/core/__tests__/full/broker-display-snapshot.test.js +0 -75
  544. package/dist/core/__tests__/full/broker-dormant-wake.test.d.ts +0 -1
  545. package/dist/core/__tests__/full/broker-dormant-wake.test.js +0 -130
  546. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +0 -1
  547. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +0 -57
  548. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +0 -1
  549. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +0 -80
  550. package/dist/core/__tests__/full/cascade-close.test.d.ts +0 -1
  551. package/dist/core/__tests__/full/cascade-close.test.js +0 -240
  552. package/dist/core/__tests__/full/detach-focus.test.d.ts +0 -1
  553. package/dist/core/__tests__/full/detach-focus.test.js +0 -126
  554. package/dist/core/__tests__/full/idle-release-settlement.test.d.ts +0 -1
  555. package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.d.ts +0 -1
  556. package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.js +0 -107
  557. package/dist/core/__tests__/full/park-clock-release.test.d.ts +0 -1
  558. package/dist/core/__tests__/full/park-clock-release.test.js +0 -56
  559. package/dist/core/__tests__/full/yield-message-transaction.test.d.ts +0 -1
  560. package/dist/core/__tests__/memory-index-render.test.d.ts +0 -1
  561. package/dist/core/__tests__/memory-index-render.test.js +0 -60
  562. package/dist/core/__tests__/model-routes.test.d.ts +0 -1
  563. package/dist/core/__tests__/model-routes.test.js +0 -41
  564. package/dist/core/__tests__/on-read-identity.test.d.ts +0 -1
  565. package/dist/core/__tests__/on-read-identity.test.js +0 -61
  566. package/dist/core/__tests__/phase4-review-migration.test.d.ts +0 -1
  567. package/dist/core/__tests__/phase4-review-migration.test.js +0 -100
  568. package/dist/core/__tests__/prune-to-limit.test.d.ts +0 -1
  569. package/dist/core/__tests__/prune-to-limit.test.js +0 -126
  570. package/dist/core/__tests__/reset.test.d.ts +0 -1
  571. package/dist/core/__tests__/reset.test.js +0 -69
  572. package/dist/core/__tests__/spawn-no-kickoff.test.d.ts +0 -1
  573. package/dist/core/__tests__/spawn-no-kickoff.test.js +0 -115
  574. package/dist/core/__tests__/steer-note.test.d.ts +0 -1
  575. package/dist/core/__tests__/steer-note.test.js +0 -41
  576. package/dist/core/__tests__/subcommand-tier.test.d.ts +0 -1
  577. package/dist/core/__tests__/subcommand-tier.test.js +0 -99
  578. package/dist/core/__tests__/subscription-delivery.test.js +0 -211
  579. package/dist/core/__tests__/test-runner-cheap-sharding.test.d.ts +0 -1
  580. package/dist/core/__tests__/test-runner-cheap-sharding.test.js +0 -97
  581. package/dist/core/__tests__/tmux-surface.test.js +0 -484
  582. package/dist/core/__tests__/wake-bearings.test.d.ts +0 -1
  583. package/dist/core/__tests__/wake-bearings.test.js +0 -102
  584. package/dist/core/canvas/__tests__/remote-event-stream.test.d.ts +0 -1
  585. package/dist/core/canvas/__tests__/remote-event-stream.test.js +0 -146
  586. package/dist/core/canvas/__tests__/source-resolve.test.d.ts +0 -1
  587. package/dist/core/canvas/__tests__/source-resolve.test.js +0 -77
  588. package/dist/core/canvas/browse/__tests__/model.test.d.ts +0 -1
  589. package/dist/core/canvas/browse/__tests__/model.test.js +0 -290
  590. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.d.ts +0 -1
  591. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +0 -134
  592. package/dist/core/canvas/browse/__tests__/render.test.d.ts +0 -1
  593. package/dist/core/canvas/browse/__tests__/render.test.js +0 -111
  594. package/dist/core/canvas/browse/rebuild-coalescer.d.ts +0 -7
  595. package/dist/core/canvas/browse/rebuild-coalescer.js +0 -61
  596. package/dist/core/canvas/remote-event-stream.d.ts +0 -24
  597. package/dist/core/canvas/remote-event-stream.js +0 -94
  598. package/dist/core/feed/__tests__/coalesce-human.test.d.ts +0 -1
  599. package/dist/core/feed/__tests__/coalesce-human.test.js +0 -36
  600. package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +0 -1
  601. package/dist/core/feed/__tests__/empty-node-guard.test.js +0 -45
  602. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +0 -1
  603. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +0 -84
  604. package/dist/core/human/__tests__/visible-paths.test.d.ts +0 -1
  605. package/dist/core/human/__tests__/visible-paths.test.js +0 -28
  606. package/dist/core/keybindings/__tests__/inbox-affordance.test.d.ts +0 -1
  607. package/dist/core/keybindings/__tests__/inbox-affordance.test.js +0 -38
  608. package/dist/core/review/__tests__/project.test.d.ts +0 -1
  609. package/dist/core/review/__tests__/project.test.js +0 -71
  610. package/dist/core/runtime/__tests__/command-expansion.test.d.ts +0 -1
  611. package/dist/core/runtime/__tests__/command-expansion.test.js +0 -25
  612. package/dist/core/runtime/__tests__/session-visibility.test.d.ts +0 -1
  613. package/dist/core/runtime/__tests__/session-visibility.test.js +0 -119
  614. package/dist/core/runtime/__tests__/spawn-at-given-id.test.d.ts +0 -1
  615. package/dist/core/runtime/__tests__/spawn-at-given-id.test.js +0 -34
  616. package/dist/core/runtime/tmux.js +0 -1314
  617. package/dist/daemon/api/__tests__/full/push-parity.test.d.ts +0 -1
  618. package/dist/daemon/api/__tests__/full/worktree-parity.test.d.ts +0 -1
  619. package/dist/daemon/api/__tests__/full/worktree-parity.test.js +0 -160
  620. package/dist/daemon/api/__tests__/inbox.test.d.ts +0 -1
  621. package/dist/daemon/api/__tests__/inbox.test.js +0 -489
  622. package/dist/web-client/apple-touch-icon.png +0 -0
  623. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  624. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  625. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  626. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  627. package/dist/web-client/assets/index-CCPaLeW7.css +0 -2
  628. package/dist/web-client/assets/index-kyOWsnZK.js +0 -86
  629. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  630. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  631. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  632. package/dist/web-client/assets/workbox-window.prod.es5-Bd17z0YL.js +0 -2
  633. package/dist/web-client/icon-192.png +0 -0
  634. package/dist/web-client/icon-512.png +0 -0
  635. package/dist/web-client/icon-maskable-512.png +0 -0
  636. package/dist/web-client/index.html +0 -17
  637. package/dist/web-client/manifest.webmanifest +0 -1
  638. package/dist/web-client/sw.js +0 -1
  639. /package/dist/api/__tests__/{client.test.d.ts → serial/client.test.d.ts} +0 -0
  640. /package/dist/clients/inbox/__tests__/{inbox-controller.test.d.ts → serial/inbox-controller.test.d.ts} +0 -0
  641. /package/dist/clients/inbox/__tests__/{mount-panel.test.d.ts → serial/mount-panel.test.d.ts} +0 -0
  642. /package/dist/clients/{attach/__tests__/attach-chrome-remote.test.d.ts → inbox/review/__tests__/teardown-notice.test.d.ts} +0 -0
  643. /package/dist/{clients/attach/__tests__/attach-keybindings.test.d.ts → core/__tests__/broker-extension-canvas-db-boundary.test.d.ts} +0 -0
  644. /package/dist/{clients/attach/__tests__/crtr-output-coverage.test.d.ts → core/__tests__/seam/broker-attach-multiclient.test.d.ts} +0 -0
  645. /package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.d.ts +0 -0
  646. /package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.d.ts +0 -0
  647. /package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.d.ts +0 -0
  648. /package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.d.ts +0 -0
  649. /package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.d.ts +0 -0
  650. /package/dist/{clients/attach/__tests__/editor-frame-title.test.d.ts → core/__tests__/seam/dormancy-release.test.d.ts} +0 -0
  651. /package/dist/{clients/attach/__tests__/input-controller-extension-command.test.d.ts → core/__tests__/seam/yield-refresh-transaction.test.d.ts} +0 -0
  652. /package/dist/core/__tests__/{broker-fork-seam.test.d.ts → serial/broker-fork-seam.test.d.ts} +0 -0
  653. /package/dist/core/__tests__/{broker-sdk-wiring.test.d.ts → serial/broker-sdk-wiring.test.d.ts} +0 -0
  654. /package/dist/core/__tests__/{broker-snapshot-history.test.d.ts → serial/broker-snapshot-history.test.d.ts} +0 -0
  655. /package/dist/core/__tests__/{command-plugins.test.d.ts → serial/command-plugins.test.d.ts} +0 -0
  656. /package/dist/core/__tests__/{deferred-no-wake.test.d.ts → serial/deferred-no-wake.test.d.ts} +0 -0
  657. /package/dist/core/__tests__/{flagship-lifecycle.test.d.ts → serial/flagship-lifecycle.test.d.ts} +0 -0
  658. /package/dist/core/__tests__/{host-teardown-process-group.test.d.ts → serial/host-teardown-process-group.test.d.ts} +0 -0
  659. /package/dist/core/__tests__/{human-deliver-e2e.test.d.ts → serial/human-deliver-e2e.test.d.ts} +0 -0
  660. /package/dist/core/__tests__/{live-mutation.test.d.ts → serial/live-mutation.test.d.ts} +0 -0
  661. /package/dist/core/__tests__/{refresh-stall-recycle.test.d.ts → serial/refresh-stall-recycle.test.d.ts} +0 -0
  662. /package/dist/core/__tests__/{revive.test.d.ts → serial/revive.test.d.ts} +0 -0
  663. /package/dist/core/__tests__/{spawn-root.test.d.ts → serial/spawn-root.test.d.ts} +0 -0
  664. /package/dist/core/__tests__/{subscription-delivery.test.d.ts → serial/subscription-delivery.test.d.ts} +0 -0
  665. /package/dist/core/__tests__/{tmux-surface.test.d.ts → serial/tmux-surface.test.d.ts} +0 -0
  666. /package/dist/core/__tests__/{worktree.test.d.ts → serial/worktree.test.d.ts} +0 -0
  667. /package/dist/{clients/attach/__tests__/markdown-headings.test.d.ts → core/__tests__/watchdog-abort-arms-retry.test.d.ts} +0 -0
  668. /package/dist/{clients/attach/__tests__/session-identity-refresh.test.d.ts → core/broker-client/__tests__/transport-socket.test.d.ts} +0 -0
  669. /package/dist/core/human/__tests__/{inbox-core.test.d.ts → serial/inbox-core.test.d.ts} +0 -0
  670. /package/dist/{clients/inbox/review/__tests__/remap.test.d.ts → daemon/api/__tests__/node-model-validation.test.d.ts} +0 -0
  671. /package/dist/{clients/surfaces/region.js → daemon/api/__tests__/review-terminal-conflict.test.d.ts} +0 -0
  672. /package/dist/daemon/api/__tests__/{full → seam}/api-server.test.d.ts +0 -0
  673. /package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.d.ts +0 -0
  674. /package/dist/{clients/web/__tests__/push-engine.test.d.ts → daemon/api/__tests__/seam/leaf-api-parity.test.d.ts} +0 -0
@@ -1,661 +0,0 @@
1
- // server.ts — the `surface web serve` dev shell: a thin reverse proxy in front
2
- // of crtrd (spec §7.5, C-1). It hosts NO canvas writer of its own — crtrd is the
3
- // sole writer. This process opens NO `canvas.db`, spawns NO subprocess bridge,
4
- // and terminates NO WebSocket. It serves, on one origin:
5
- // • /v1/* → REVERSE PROXY to crtrd's unix socket. The SPA (and
6
- // any /v1 client) reaches the REST API THROUGH this
7
- // shell; the shell owns the auth boundary (crtrd
8
- // applies none), then streams the request/response
9
- // verbatim to/from the socket.
10
- // • WS /node/<id> → TRANSPARENT TUNNEL to crtrd's
11
- // `GET /v1/nodes/{id}/attach`. The shell does NOT
12
- // terminate the WebSocket — it forwards the upgrade
13
- // rewritten to the attach path and pipes bytes both
14
- // directions. crtrd owns the handshake and
15
- // attach-implies-revive; the browser is the SAME
16
- // protocol peer as `crtr surface attach`.
17
- // • GET /* → the shell SPA (static dist/web-client/, or Vite
18
- // middleware in --dev) + SPA fallback.
19
- // • GET /__crtr/events → the SSE change-invalidation lane (events.ts).
20
- // canvas.db-free; explicitly out of P1 scope, kept.
21
- // • Web push (§push) → GET /__crtr/push/vapid-public-key, POST/DELETE
22
- // /__crtr/push/subscribe. A server-side diff engine
23
- // (push-engine.ts) watches the SAME SSE invalidations
24
- // above and sends web-push to a persisted, auth-scoped
25
- // registry (push-registry.ts). Its canvas reads go
26
- // through the crtrd API (via `cliClient()` command
27
- // leaves), never `canvas.db`. Out of P1 scope, kept.
28
- //
29
- // §0 ONE-WRITER INVARIANT: this process never mutates the canvas. Every write
30
- // and every canvas read flows to crtrd — the `/v1/*` proxy and the `/node/<id>`
31
- // attach tunnel both terminate at crtrd's unix socket, and the push engine's
32
- // reads route through the crtrd-backed command leaves. There is no local
33
- // transport, no `canvas.db` handle, and no direct broker socket here.
34
- import { createServer as createHttpServer, request as httpRequest } from 'node:http';
35
- import { createConnection, isIP } from 'node:net';
36
- import { createHash } from 'node:crypto';
37
- import { createReadStream, existsSync, statSync } from 'node:fs';
38
- import { fileURLToPath } from 'node:url';
39
- import { dirname, extname, join, normalize, resolve } from 'node:path';
40
- import webpush from 'web-push';
41
- import { apiSocketPath, isSafeNodeId } from '../../core/canvas/paths.js';
42
- import { emitEvent } from '../../core/events/emit.js';
43
- import { bindWebEventSource } from '../../core/events/source.js';
44
- import { canvasRosterLeaf } from '../../commands/canvas-roster.js';
45
- import { startEventHub } from './events.js';
46
- import { createDevServer } from './dev-server.js';
47
- import { PushLastSeenStore, PushRegistryStore, PushRequestError, loadOrCreateVapidKeypair, parsePushDeleteBody, parsePushSubscriptionBody, pushBodyMaxBytes, } from './push-registry.js';
48
- import { PushDiffEngine } from './push-engine.js';
49
- const HERE = dirname(fileURLToPath(import.meta.url));
50
- /** Candidate dirs to serve the shell SPA bundle from, in priority order: an
51
- * explicit override, the compiled-module-relative `dist/web-client/` (what
52
- * `vite build` emits and what we serve in production), and the placeholder
53
- * copied from source. The first that exists wins. (--dev bypasses this entirely
54
- * — Vite middleware owns asset serving in that mode.) */
55
- function resolveClientDir() {
56
- const candidates = [
57
- process.env['CRTR_WEB_CLIENT_DIR'],
58
- join(HERE, '../../web-client'),
59
- resolve(HERE, '../../../dist/web-client'),
60
- ].filter((p) => p !== undefined && p !== '');
61
- for (const dir of candidates) {
62
- if (existsSync(dir))
63
- return dir;
64
- }
65
- // Fall back to the module-relative path even if absent; missing-file handling
66
- // below returns a clear 503 so a not-yet-built shell is diagnosable.
67
- return join(HERE, '../../web-client');
68
- }
69
- const CONTENT_TYPES = {
70
- '.html': 'text/html; charset=utf-8',
71
- '.js': 'text/javascript; charset=utf-8',
72
- '.mjs': 'text/javascript; charset=utf-8',
73
- '.css': 'text/css; charset=utf-8',
74
- '.json': 'application/json; charset=utf-8',
75
- '.webmanifest': 'application/manifest+json',
76
- '.svg': 'image/svg+xml',
77
- '.png': 'image/png',
78
- '.jpg': 'image/jpeg',
79
- '.jpeg': 'image/jpeg',
80
- '.gif': 'image/gif',
81
- '.webp': 'image/webp',
82
- '.ico': 'image/x-icon',
83
- '.woff': 'font/woff',
84
- '.woff2': 'font/woff2',
85
- '.map': 'application/json; charset=utf-8',
86
- };
87
- function contentTypeFor(path) {
88
- return CONTENT_TYPES[extname(path).toLowerCase()] ?? 'application/octet-stream';
89
- }
90
- /** Match a WS/GET path of the shape `/node/<id>` (with any number of leading
91
- * segments, future-proofing a mount prefix). Returns the decoded node id, or
92
- * null if the path doesn't match or the id is undecodable (malformed `%`). The
93
- * caller must still validate the decoded id with the shared persisted-node-ID
94
- * predicate before building a filesystem path — see {@link isSafeNodeId}. */
95
- function nodeIdFromPath(pathname) {
96
- const m = /\/node\/([^/?#]+)\/?$/.exec(pathname);
97
- if (m === null)
98
- return null;
99
- try {
100
- return decodeURIComponent(m[1]);
101
- }
102
- catch {
103
- return null; // malformed percent-encoding — treat as no match
104
- }
105
- }
106
- function isLoopbackHost(host) {
107
- const lower = host.toLowerCase();
108
- const ipVersion = isIP(host);
109
- if (lower === 'localhost' || lower === '::1')
110
- return true;
111
- if (ipVersion === 4 && host.startsWith('127.'))
112
- return true;
113
- if (lower === '::ffff:127.0.0.1')
114
- return true;
115
- if (ipVersion === 6 && lower.startsWith('::ffff:127.'))
116
- return true;
117
- return false;
118
- }
119
- function hostForUrl(host) {
120
- return host.includes(':') ? `[${host}]` : host;
121
- }
122
- function requestTokens(req) {
123
- const url = new URL(req.url ?? '/', 'http://127.0.0.1');
124
- const tokens = [];
125
- const queryToken = url.searchParams.get('token');
126
- if (queryToken !== null && queryToken !== '')
127
- tokens.push(queryToken);
128
- const authorization = req.headers.authorization;
129
- if (typeof authorization === 'string') {
130
- const match = /^Bearer\s+(.+)$/.exec(authorization);
131
- if (match !== null && match[1] !== '')
132
- tokens.push(match[1]);
133
- }
134
- return tokens;
135
- }
136
- function requestTokenMatches(req, secret) {
137
- if (secret === undefined)
138
- return false;
139
- return requestTokens(req).some((token) => token === secret);
140
- }
141
- /** Read a request body into a string, aborting the moment the accumulated body exceeds
142
- * `maxBytes` — so a push endpoint never buffers arbitrary attacker memory
143
- * before validation rejects it. Rejects with PushRequestError(413), which the
144
- * push error handler maps to a 413 response. */
145
- function readBodyCapped(req, maxBytes) {
146
- return new Promise((resolve, reject) => {
147
- const chunks = [];
148
- let total = 0;
149
- req.on('data', (c) => {
150
- total += c.length;
151
- if (total > maxBytes) {
152
- reject(new PushRequestError(413, 'push subscription body is too large'));
153
- req.destroy();
154
- return;
155
- }
156
- chunks.push(c);
157
- });
158
- req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
159
- req.on('error', reject);
160
- });
161
- }
162
- /** Derive the persisted auth scope from a bearer token: a short, stable hash so
163
- * the raw secret never touches disk. */
164
- function scopeForToken(secret) {
165
- return `token:${createHash('sha256').update(secret).digest('base64url').slice(0, 16)}`;
166
- }
167
- /** Serve a static file from the client dir, with SPA fallback to index.html for
168
- * any unmatched (non-asset) GET — so `/` and `/node/<id>` both boot the app
169
- * shell. Returns true if it wrote a response. */
170
- function serveStatic(clientDir, req, res) {
171
- const url = new URL(req.url ?? '/', 'http://127.0.0.1');
172
- let pathname;
173
- try {
174
- pathname = decodeURIComponent(url.pathname);
175
- }
176
- catch {
177
- // Malformed percent-encoding (e.g. `GET /%`) — never let it throw out of
178
- // the request listener and crash the whole multiplexing server (C1).
179
- res.writeHead(400, { 'content-type': 'text/plain; charset=utf-8' });
180
- res.end('bad request\n');
181
- return;
182
- }
183
- // Resolve within clientDir; reject traversal.
184
- const rel = normalize(pathname).replace(/^(\.\.(\/|\\|$))+/, '');
185
- let filePath = join(clientDir, rel);
186
- if (!filePath.startsWith(clientDir)) {
187
- res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
188
- res.end('forbidden\n');
189
- return;
190
- }
191
- // Directory or root → index.html. A path with no file extension that does not
192
- // exist → SPA fallback to index.html (so deep links like /node/<id> boot).
193
- const isFile = existsSync(filePath) && statSync(filePath).isFile();
194
- if (!isFile) {
195
- filePath = join(clientDir, 'index.html');
196
- }
197
- if (!existsSync(filePath)) {
198
- res.writeHead(503, { 'content-type': 'text/plain; charset=utf-8' });
199
- res.end('crtr surface web shell bundle not found — run `npm run build` to emit it into ' +
200
- `${clientDir} (or pass --dev for Vite middleware)\n`);
201
- return;
202
- }
203
- res.writeHead(200, { 'content-type': contentTypeFor(filePath), 'cache-control': 'no-cache' });
204
- if (req.method === 'HEAD') {
205
- res.end();
206
- return;
207
- }
208
- const stream = createReadStream(filePath);
209
- stream.on('error', () => {
210
- if (!res.headersSent)
211
- res.writeHead(500);
212
- res.end();
213
- });
214
- stream.pipe(res);
215
- }
216
- /** RFC 7230 §6.1 hop-by-hop headers: meaningful only for a single transport hop,
217
- * never forwarded through a proxy. Stripping them keeps this shell's browser↔shell
218
- * and shell↔crtrd connection lifetimes independent instead of leaning on Node's
219
- * re-framing to paper over a forwarded `Transfer-Encoding`/`Connection` (m5). */
220
- const HOP_BY_HOP_HEADERS = new Set([
221
- 'connection',
222
- 'keep-alive',
223
- 'proxy-authenticate',
224
- 'proxy-authorization',
225
- 'te',
226
- 'trailer',
227
- 'transfer-encoding',
228
- 'upgrade',
229
- ]);
230
- /** Copy incoming headers minus the hop-by-hop set. Node lowercases incoming
231
- * header names, so a lowercase-keyed delete is exhaustive. */
232
- function withoutHopByHop(headers) {
233
- const out = { ...headers };
234
- for (const key of HOP_BY_HOP_HEADERS)
235
- delete out[key];
236
- return out;
237
- }
238
- /** Parse a request URL and strip the `token` auth param — it authorizes THIS
239
- * shell hop only; forwarding it into crtrd's request path would leak the secret
240
- * into crtrd's (loggable) request line, and crtrd doesn't consume it (m4). Any
241
- * OTHER query survives (e.g. crtrd's browser-only `?revive=0` opt-out). */
242
- function stripTokenParam(rawUrl) {
243
- const url = new URL(rawUrl, 'http://127.0.0.1');
244
- url.searchParams.delete('token');
245
- return url;
246
- }
247
- /** Proxy a `/v1/*` REST request to crtrd's unix socket. The dev shell adds only
248
- * its auth boundary (crtrd applies none) then streams the request body up and
249
- * the response back verbatim. No `canvas.db`, no subprocess: pure reverse proxy. */
250
- function proxyToCrtrd(req, res) {
251
- const target = stripTokenParam(req.url ?? '/');
252
- const upstream = httpRequest({
253
- socketPath: apiSocketPath(),
254
- method: req.method,
255
- path: `${target.pathname}${target.search}`,
256
- headers: withoutHopByHop(req.headers),
257
- }, (upstreamRes) => {
258
- res.writeHead(upstreamRes.statusCode ?? 502, withoutHopByHop(upstreamRes.headers));
259
- upstreamRes.pipe(res);
260
- });
261
- upstream.on('error', () => {
262
- // Once headers are on the wire we can no longer signal an error in-band —
263
- // appending a JSON error to a partially-streamed body would corrupt it, so
264
- // truncate the response instead (m1). Before headersSent, a clean 502.
265
- if (res.headersSent) {
266
- res.destroy();
267
- return;
268
- }
269
- res.writeHead(502, { 'content-type': 'application/json; charset=utf-8' });
270
- res.end(JSON.stringify({ error: { code: 'daemon_unavailable', message: 'crtrd is not reachable' } }));
271
- });
272
- // Propagate a browser abort/response-close upstream: without this an aborted
273
- // request leaves the crtrd response paused and its in-flight work pinned until
274
- // upstream completes on its own (M1). `destroy` on a finished exchange no-ops.
275
- res.on('close', () => upstream.destroy());
276
- req.pipe(upstream);
277
- }
278
- /** Transparently tunnel a browser `/node/<id>` WS upgrade to crtrd's
279
- * `GET /v1/nodes/{id}/attach`. The dev shell does NOT terminate the WebSocket:
280
- * it opens a raw connection to crtrd's unix socket, forwards the upgrade request
281
- * rewritten to the attach path (headers verbatim), and pipes bytes both
282
- * directions. crtrd performs the 101 handshake (which flows back through the
283
- * tunnel) and owns attach-implies-revive — so the browser is the SAME protocol
284
- * peer as `crtr surface attach` and this process only relays. Idempotent
285
- * teardown; the tunneled socket is tracked so shutdown can drop it. */
286
- function tunnelAttachUpgrade(req, socket, head, nodeId, track) {
287
- if (!isSafeNodeId(nodeId)) {
288
- try {
289
- socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
290
- socket.destroy();
291
- }
292
- catch {
293
- /* ignore */
294
- }
295
- return;
296
- }
297
- const upstream = createConnection(apiSocketPath());
298
- track.add(socket);
299
- let tornDown = false;
300
- let relayStarted = false;
301
- const teardown = () => {
302
- if (tornDown)
303
- return;
304
- tornDown = true;
305
- track.delete(socket);
306
- if (!socket.destroyed)
307
- socket.destroy();
308
- if (!upstream.destroyed)
309
- upstream.destroy();
310
- };
311
- upstream.on('connect', () => {
312
- // Preserve any non-token query (crtrd honors a browser-only `?revive=0`
313
- // opt-out here — a WS handshake can't set the equivalent request header, so
314
- // this query is the browser's ONLY channel for it); the token is stripped
315
- // for the same reason as the REST proxy (m2/m4).
316
- const path = `/v1/nodes/${encodeURIComponent(nodeId)}/attach${stripTokenParam(req.url ?? '/').search}`;
317
- const lines = [`GET ${path} HTTP/1.1`];
318
- for (let i = 0; i + 1 < req.rawHeaders.length; i += 2) {
319
- lines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`);
320
- }
321
- try {
322
- upstream.write(lines.join('\r\n') + '\r\n\r\n');
323
- if (head.length > 0)
324
- upstream.write(head);
325
- }
326
- catch {
327
- teardown();
328
- return;
329
- }
330
- relayStarted = true;
331
- socket.pipe(upstream);
332
- upstream.pipe(socket);
333
- });
334
- upstream.on('error', () => {
335
- // A pre-handshake upstream failure (crtrd down) leaves the browser with a
336
- // bare TCP reset, indistinguishable from any other failure. Before any byte
337
- // has been relayed, answer a 502 so the browser sees a distinct gateway
338
- // error; once relaying is underway a bare reset is the correct WS signal (m3).
339
- if (!relayStarted && !tornDown && !socket.destroyed) {
340
- try {
341
- socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n');
342
- }
343
- catch {
344
- /* ignore */
345
- }
346
- }
347
- teardown();
348
- });
349
- socket.on('error', teardown);
350
- upstream.on('close', teardown);
351
- socket.on('close', teardown);
352
- }
353
- /** Build + start the unified web server. Binds the requested host/port and
354
- * resolves once the server is listening. Async because --dev awaits a Vite
355
- * middleware server before listening. */
356
- export async function startWebServer(opts) {
357
- const host = opts.host ?? '127.0.0.1';
358
- const token = opts.token !== undefined && opts.token !== '' ? opts.token : undefined;
359
- const dev = opts.dev ?? false;
360
- const tokenRequired = !isLoopbackHost(host);
361
- if (tokenRequired && (token === undefined || token === '')) {
362
- throw new Error(`crtr surface web serve --host ${host} requires --token for remote-capable endpoints`);
363
- }
364
- const clientDir = resolveClientDir();
365
- // The SSE change lane — one hub, fanned to every connected EventSource.
366
- const eventHub = startEventHub();
367
- // Live `/node/<id>` attach tunnels to crtrd — tracked so shutdown drops them.
368
- const activeTunnels = new Set();
369
- // Same-origin allowlist: tokenless localhost pages are implicitly allowed,
370
- // while a valid relay token can authorize remote/guest route origins that would
371
- // otherwise be rejected by originAllowed().
372
- let allowedOrigins = new Set();
373
- const originAllowed = (origin) => {
374
- if (origin === undefined || origin === '')
375
- return true;
376
- return allowedOrigins.has(origin);
377
- };
378
- const authorizeRequest = (req) => {
379
- if (token !== undefined && requestTokenMatches(req, token)) {
380
- return { allowed: true, scope: scopeForToken(token) };
381
- }
382
- if (tokenRequired)
383
- return { allowed: false };
384
- if (!originAllowed(req.headers.origin))
385
- return { allowed: false };
386
- return { allowed: true, scope: 'local' };
387
- };
388
- // The set of auth scopes this server actually authorizes — the push diff
389
- // engine sends ONLY to subscriptions whose authScope is in this set (the same
390
- // auth boundary that gates the /v1 proxy, SSE, and attach tunnel). Mirrors authorizeRequest:
391
- // a token authorizes scopeForToken(token); a tokenless request is authorized
392
- // only when !tokenRequired, as scope 'local'. A record left by a rotated/old
393
- // token has a different digest and is therefore excluded.
394
- const targetScopes = new Set();
395
- if (token !== undefined)
396
- targetScopes.add(scopeForToken(token));
397
- if (!tokenRequired)
398
- targetScopes.add('local');
399
- // §push — the server-side web-push registry + diff engine (design D8). One
400
- // persisted, auth-scoped subscription registry; one locally-generated VAPID
401
- // identity signing every send; one diff engine that re-reads the canvas on
402
- // node invalidations received through eventHub.subscribe (no HTTP self-loop)
403
- // and pushes done-transition notifications.
404
- const pushRegistry = new PushRegistryStore();
405
- const pushLastSeen = new PushLastSeenStore();
406
- const vapid = loadOrCreateVapidKeypair();
407
- webpush.setVapidDetails(vapid.subject, vapid.publicKey, vapid.privateKey);
408
- const pushEngine = new PushDiffEngine({
409
- registry: pushRegistry,
410
- lastSeen: pushLastSeen,
411
- // The lean roster (node_id/parent/status/attention_count is exactly the
412
- // CanvasNodeRow subset this diff engine needs) instead of the enriched
413
- // snapshot — this fires on every 'nodes' invalidation, not on a bounded
414
- // interval, so it must never force the enrichRows/per-row getNode pass.
415
- readCanvasNodes: async () => {
416
- const snap = (await canvasRosterLeaf.run({}));
417
- return snap.nodes;
418
- },
419
- isTargetScope: (s) => targetScopes.has(s),
420
- sendPush: async (record, payload) => {
421
- try {
422
- await webpush.sendNotification({ endpoint: record.endpoint, keys: record.keys }, JSON.stringify(payload));
423
- return 'sent';
424
- }
425
- catch (err) {
426
- const statusCode = err.statusCode;
427
- return statusCode === 404 || statusCode === 410 ? 'gone' : 'error';
428
- }
429
- },
430
- });
431
- // A restart is not an attention event — record current state, send nothing.
432
- await pushEngine.seed();
433
- const unsubscribePush = eventHub.subscribe((kind) => {
434
- if (kind === 'nodes')
435
- void pushEngine.onNodesInvalidation();
436
- });
437
- // Assigned before listen() when --dev; the request handler closes over it.
438
- let vite;
439
- const httpServer = createHttpServer((req, res) => {
440
- try {
441
- const url = new URL(req.url ?? '/', 'http://127.0.0.1');
442
- const pathname = url.pathname;
443
- // The crtrd REST proxy: /v1/* → reverse-proxy to crtrd's unix socket. The
444
- // SPA (and any /v1 client) reaches the API THROUGH this dev shell. The
445
- // shell owns the auth boundary (crtrd applies none); token-or-origin gated
446
- // like the SSE lane and the attach tunnel. No canvas.db, no subprocess.
447
- if (pathname === '/v1' || pathname.startsWith('/v1/')) {
448
- if (!authorizeRequest(req).allowed) {
449
- res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
450
- res.end('forbidden\n');
451
- return;
452
- }
453
- proxyToCrtrd(req, res);
454
- return;
455
- }
456
- // The SSE change-invalidation lane: GET /__crtr/events. Token-or-origin
457
- // gated too — a foreign page should not even learn the user's graph is
458
- // mutating.
459
- if (pathname === '/__crtr/events') {
460
- if (req.method !== 'GET') {
461
- res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'GET' });
462
- res.end('method not allowed\n');
463
- return;
464
- }
465
- if (!authorizeRequest(req).allowed) {
466
- res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
467
- res.end('forbidden\n');
468
- return;
469
- }
470
- eventHub.addClient(res);
471
- return;
472
- }
473
- // §push — the VAPID public key a browser needs to subscribe. Read-only,
474
- // non-secret, but behind the same auth boundary as everything else so a
475
- // foreign page can't even enumerate the surface.
476
- if (pathname === '/__crtr/push/vapid-public-key') {
477
- if (req.method !== 'GET') {
478
- res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'GET' });
479
- res.end('method not allowed\n');
480
- return;
481
- }
482
- if (!authorizeRequest(req).allowed) {
483
- res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
484
- res.end('forbidden\n');
485
- return;
486
- }
487
- res.writeHead(200, { 'content-type': 'application/json' });
488
- res.end(JSON.stringify({ publicKey: vapid.publicKey }));
489
- return;
490
- }
491
- // §push — subscribe (POST) / unsubscribe (DELETE). Validation lives in
492
- // parsePush*Body, which rejects oversized/malformed/private/http/file
493
- // shapes BEFORE any persistence, throwing PushRequestError with a status.
494
- if (pathname === '/__crtr/push/subscribe') {
495
- const auth = authorizeRequest(req);
496
- if (!auth.allowed) {
497
- res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
498
- res.end('forbidden\n');
499
- return;
500
- }
501
- const handlePushError = (e) => {
502
- if (e instanceof PushRequestError) {
503
- res.writeHead(e.statusCode, { 'content-type': 'application/json' });
504
- res.end(JSON.stringify({ ok: false, error: e.message }));
505
- return;
506
- }
507
- res.writeHead(500, { 'content-type': 'application/json' });
508
- res.end(JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }));
509
- };
510
- if (req.method === 'POST') {
511
- void readBodyCapped(req, pushBodyMaxBytes())
512
- .then((body) => {
513
- const record = parsePushSubscriptionBody(body, auth.scope);
514
- const stored = pushRegistry.upsert(record);
515
- res.writeHead(200, { 'content-type': 'application/json' });
516
- res.end(JSON.stringify({ ok: true, id: stored.id, authScope: stored.authScope }));
517
- })
518
- .catch(handlePushError);
519
- return;
520
- }
521
- if (req.method === 'DELETE') {
522
- void readBodyCapped(req, pushBodyMaxBytes())
523
- .then((body) => {
524
- const { endpoint } = parsePushDeleteBody(body);
525
- // Scoped: a caller may only delete a subscription it owns.
526
- const deleted = pushRegistry.deleteByEndpoint(endpoint, auth.scope);
527
- res.writeHead(200, { 'content-type': 'application/json' });
528
- res.end(JSON.stringify({ ok: true, deleted }));
529
- })
530
- .catch(handlePushError);
531
- return;
532
- }
533
- res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'POST, DELETE' });
534
- res.end('method not allowed\n');
535
- return;
536
- }
537
- // --dev: Vite middleware owns all remaining asset/HTML serving (incl. its
538
- // own SPA fallback). It is mounted AFTER the bridge + SSE checks above, so
539
- // those never fall through to Vite.
540
- if (vite !== undefined) {
541
- vite.middlewares(req, res);
542
- return;
543
- }
544
- // Shipped: static shell bundle + SPA fallback.
545
- if (req.method !== 'GET' && req.method !== 'HEAD') {
546
- res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'GET, HEAD' });
547
- res.end('method not allowed\n');
548
- return;
549
- }
550
- serveStatic(clientDir, req, res);
551
- }
552
- catch {
553
- // Belt-and-suspenders: nothing in the handler should throw now (C1), but a
554
- // request listener that throws crashes the whole daemon — never allow it.
555
- if (!res.headersSent)
556
- res.writeHead(500, { 'content-type': 'text/plain; charset=utf-8' });
557
- res.end('internal error\n');
558
- }
559
- });
560
- httpServer.on('upgrade', (req, socket, head) => {
561
- // A raw upgrade socket with no 'error' listener throws on a write to a reset
562
- // peer — attach one before we ever write/destroy it (n8).
563
- socket.on('error', () => {
564
- /* peer reset mid-handshake — destroy below or already destroyed */
565
- });
566
- try {
567
- const url = new URL(req.url ?? '/', 'http://127.0.0.1');
568
- const nodeId = nodeIdFromPath(url.pathname);
569
- if (nodeId === null) {
570
- // --dev: Vite's HMR WebSocket shares this HTTP server. Vite registers its
571
- // OWN 'upgrade' listener (via server.hmr.server); leave every non-`/node/`
572
- // upgrade for it instead of destroying the socket.
573
- if (vite !== undefined)
574
- return;
575
- socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
576
- socket.destroy();
577
- return;
578
- }
579
- if (!authorizeRequest(req).allowed) {
580
- socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
581
- socket.destroy();
582
- return;
583
- }
584
- // Transparent tunnel to crtrd's attach endpoint — the shell never
585
- // terminates the WS; crtrd owns the handshake and attach-implies-revive.
586
- tunnelAttachUpgrade(req, socket, head, nodeId, activeTunnels);
587
- }
588
- catch {
589
- // Never let an upgrade-handler throw escape and crash the daemon (C1).
590
- try {
591
- socket.destroy();
592
- }
593
- catch {
594
- /* ignore */
595
- }
596
- }
597
- });
598
- // --dev: create the Vite middleware server BEFORE listen so the request
599
- // handler's `vite` is set and Vite's HMR upgrade listener is attached (ours
600
- // was added first, so a vite-hmr upgrade hits ours → nodeId null → returns →
601
- // Vite's listener handles it).
602
- if (dev) {
603
- vite = await createDevServer(httpServer);
604
- }
605
- return new Promise((resolveListening, rejectListening) => {
606
- httpServer.once('error', rejectListening);
607
- httpServer.listen(opts.port, host, () => {
608
- let port;
609
- try {
610
- const addr = httpServer.address();
611
- if (typeof addr !== 'object' || addr === null) {
612
- throw new Error('web server address is unavailable after listen');
613
- }
614
- port = addr.port;
615
- bindWebEventSource(host, port);
616
- }
617
- catch (error) {
618
- httpServer.close(() => {
619
- httpServer.off('error', rejectListening);
620
- rejectListening(error);
621
- });
622
- httpServer.closeAllConnections();
623
- return;
624
- }
625
- httpServer.off('error', rejectListening);
626
- // A server error AFTER listen (e.g. an accept failure) must not be an
627
- // unhandled 'error' that crashes the process (n9) — emit + carry on.
628
- httpServer.on('error', (err) => {
629
- emitEvent({ event: 'web.server.failed', level: 'error', error: err });
630
- });
631
- const hostUrl = hostForUrl(host);
632
- const url = `http://${hostUrl}:${port}`;
633
- allowedOrigins = new Set([
634
- `http://${hostUrl}:${port}`,
635
- `http://127.0.0.1:${port}`,
636
- `http://localhost:${port}`,
637
- ]);
638
- const close = () => new Promise((done) => {
639
- unsubscribePush();
640
- eventHub.close();
641
- void vite?.close().catch(() => {
642
- /* best-effort */
643
- });
644
- for (const s of activeTunnels) {
645
- try {
646
- s.destroy();
647
- }
648
- catch {
649
- /* ignore */
650
- }
651
- }
652
- activeTunnels.clear();
653
- httpServer.close(() => done());
654
- // Idle keep-alive HTTP connections would otherwise keep close()'s
655
- // callback from ever firing — drop them so shutdown completes (m4).
656
- httpServer.closeAllConnections();
657
- });
658
- resolveListening({ url, port, clientDir, close });
659
- });
660
- });
661
- }