@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
@@ -0,0 +1,682 @@
1
+ // tmux bindings — crouter-owned keybinding manifest persistence and reconciliation.
2
+ //
3
+ // Raw tmux process invocation stays in tmux-driver.ts. This module owns the
4
+ // persistent binding manifest, key parsing, and installation.
5
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
6
+ import { tmpdir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ import { ATTACH_CONTROL_BINDINGS, BINDING_CATALOG, BINDING_IDS, encodeAttachControlInput, formatGesture, isAttachPaneBinding, resolveUserKeybindingSettings, } from '../keybindings/index.js';
9
+ import { normalizeTmuxPassthrough } from '../config.js';
10
+ import { withExclusiveDirectoryLock } from '../human/convention.js';
11
+ import { surfaceTmuxStyleArgs } from './surface-bg.js';
12
+ import { runTmux as tmux, shellQuote } from './tmux-driver.js';
13
+ // ---------------------------------------------------------------------------
14
+ // Crouter-owned tmux bindings
15
+ // ---------------------------------------------------------------------------
16
+ const TMUX_BINDING_MANIFEST = '@crtr_keybindings_manifest';
17
+ const ROOT_TABLE = 'root';
18
+ const LEAF_MENU_ACTIONS = {
19
+ 'crtr.tmux.menu.promote': { description: 'promote to orchestrator', action: { kind: 'keys', keys: '/promote' } },
20
+ 'crtr.tmux.menu.resume': { description: 'find node', action: { kind: 'keys', keys: '/resume-node' } },
21
+ 'crtr.tmux.menu.fork': { description: 'fork into idle root', action: { kind: 'direct', run: 'node fork --pane {pane}' } },
22
+ 'crtr.tmux.menu.demote': { description: 'demote (finish when done)', action: { kind: 'command', run: 'node lifecycle demote --pane {pane}' } },
23
+ 'crtr.tmux.menu.detach': { description: 'demote + close viewer', action: { kind: 'command', run: 'node lifecycle demote --pane {pane} --detach' } },
24
+ 'crtr.tmux.menu.close-subtree': { description: 'close agent + subtree', action: { kind: 'command', run: 'node lifecycle close --pane {pane}' } },
25
+ 'crtr.tmux.menu.background-bash': { description: 'background running bash', action: { kind: 'direct', run: 'node bash background --pane {pane}' } },
26
+ // M-S-r, not M-R: with extended-keys csi-u tmux encodes M-R as alt+uppercase-R
27
+ // WITHOUT the shift modifier bit, which pi-tui's `alt+shift+r` matcher rejects;
28
+ // M-S-r carries shift+alt and lands as the same chord the keyboard produces.
29
+ 'crtr.tmux.menu.file-review': { description: 'review a file', action: { kind: 'raw-keys', keys: 'M-S-r' } },
30
+ 'crtr.tmux.menu.metadata': { description: 'metadata', action: { kind: 'keys', keys: '/node-metadata' } },
31
+ 'crtr.tmux.menu.fold-tools': { description: 'fold finished tool calls', action: { kind: 'keys', keys: '/fold-tools' } },
32
+ 'crtr.tmux.menu.context': { description: 'context + reports', action: { kind: 'keys', keys: '/context' } },
33
+ // The settings page lays out a grouped rail beside a content panel; the
34
+ // default dialog-sized popup clipped both the rail and the models matrix.
35
+ 'crtr.tmux.menu.providers': { description: 'settings', action: { kind: 'popup', run: 'sys settings', size: { w: '90%', h: '85%' } } },
36
+ 'crtr.tmux.menu.graph': { description: 'canvas graph', action: { kind: 'keys', keys: '/graph' } },
37
+ 'crtr.tmux.menu.focus-manager': { description: 'go to manager', action: { kind: 'command', run: 'surface node focus {manager}' } },
38
+ 'crtr.tmux.menu.copy-id': { description: 'copy node id', action: { kind: 'command', run: 'surface node id --pane {pane}' } },
39
+ 'crtr.tmux.menu.reload-viewer': { description: 'reload viewer', action: { kind: 'direct', run: 'surface reload --pane {pane}' } },
40
+ 'crtr.tmux.menu.recap': { description: 'generate recap', action: { kind: 'keys', keys: '/recap' } },
41
+ 'crtr.tmux.menu.pin': { description: 'pin conversation', action: { kind: 'direct', run: 'surface node pin --pane {pane}' } },
42
+ 'crtr.tmux.menu.issue.idea': { description: 'idea', action: { kind: 'popup', run: 'canvas issue new --type idea' } },
43
+ 'crtr.tmux.menu.issue.bug': { description: 'bug', action: { kind: 'popup', run: 'canvas issue new --type bug' } },
44
+ };
45
+ function escapeTmuxFormats(value) {
46
+ return value.replace(/#/g, '##');
47
+ }
48
+ function tmuxCommand(args) {
49
+ return args.map(shellQuote).join(' ');
50
+ }
51
+ /** Popups default to a small dialog. A surface that has to lay out its own
52
+ * navigation and a content panel side by side asks for the larger size. */
53
+ const DEFAULT_POPUP_SIZE = { w: '60%', h: '50%' };
54
+ function chordCommand(run, interactive = false, size = DEFAULT_POPUP_SIZE) {
55
+ const command = `crtr canvas chord --pane '#{pane_id}' --run ${shellQuote(escapeTmuxFormats(run))}${interactive ? ' --interactive' : ''}`;
56
+ return interactive
57
+ ? {
58
+ argv: ['display-popup', '-E', '-w', size.w, '-h', size.h, '-d', '#{pane_current_path}', command],
59
+ menu: `display-popup -E -w ${size.w} -h ${size.h} -d "#{pane_current_path}" ${shellQuote(command)}`,
60
+ }
61
+ : {
62
+ argv: ['run-shell', `${command} >/dev/null 2>&1`],
63
+ menu: `run-shell ${shellQuote(`${command} >/dev/null 2>&1`)}`,
64
+ };
65
+ }
66
+ /** Dispatch an action straight to a `crtr` invocation, skipping the
67
+ * `canvas chord` hop entirely. Only sound for an action whose sole
68
+ * interpolation need is `{pane}` — tmux expands it natively as `#{pane_id}` at
69
+ * keypress time (a real tmux format, substituted AFTER escaping the static
70
+ * run text so the `#` here is never doubled), so chord's node-resolution
71
+ * round trip buys nothing. `-b` keeps dispatch non-blocking. */
72
+ function directCommand(run) {
73
+ const argv = escapeTmuxFormats(run).replace('{pane}', '#{pane_id}');
74
+ const command = `crtr ${argv} >/dev/null 2>&1`;
75
+ return {
76
+ argv: ['run-shell', '-b', command],
77
+ menu: `run-shell -b ${shellQuote(command)}`,
78
+ };
79
+ }
80
+ /** One command constructor serves both display-menu selections and direct root
81
+ * shortcuts. Each result carries the argv bind-key needs plus the stable menu
82
+ * serialization existing Alt+C installs already use. */
83
+ function menuActionCommand(action) {
84
+ switch (action.kind) {
85
+ case 'disabled': return null;
86
+ case 'separator': return null;
87
+ // Choosing any menu item closes it; refresh-client is the deliberately
88
+ // inert command that lets the prefix shortcut choose this close row.
89
+ case 'dismiss': return { argv: ['refresh-client'], menu: 'refresh-client' };
90
+ case 'keys': {
91
+ const keys = escapeTmuxFormats(action.keys);
92
+ return {
93
+ // A root binding already runs in the pressed key's pane context. Its
94
+ // target must stay implicit: bind-key does not expand `#{pane_id}` in
95
+ // send-keys' target argument. A menu command is serialized later and
96
+ // does need the pane captured when the menu opened.
97
+ argv: ['send-keys', keys, 'Enter'],
98
+ menu: `send-keys -t '#{pane_id}' ${shellQuote(keys)} Enter`,
99
+ };
100
+ }
101
+ case 'raw-keys': {
102
+ const keys = escapeTmuxFormats(action.keys);
103
+ return {
104
+ argv: ['send-keys', keys],
105
+ menu: `send-keys -t '#{pane_id}' ${shellQuote(keys)}`,
106
+ };
107
+ }
108
+ case 'literal-input': {
109
+ const input = escapeTmuxFormats(action.input);
110
+ return {
111
+ argv: ['send-keys', '-l', input],
112
+ menu: `send-keys -l -t '#{pane_id}' ${shellQuote(input)}`,
113
+ };
114
+ }
115
+ case 'command': return chordCommand(action.run);
116
+ case 'direct': return directCommand(action.run);
117
+ case 'popup': return chordCommand(action.run, true, action.size);
118
+ case 'submenu': {
119
+ const argv = buildMenuArgs(action.title, action.items);
120
+ return { argv, menu: tmuxCommand(argv) };
121
+ }
122
+ }
123
+ }
124
+ function renderMenuAction(action) {
125
+ return menuActionCommand(action)?.menu ?? '';
126
+ }
127
+ /** Visible width of a menu item name: style markup (`#[...]`) takes no cells. */
128
+ function menuItemWidth(description) {
129
+ return description.replace(/#\[[^\]]*\]/g, '').length;
130
+ }
131
+ function buildMenuArgs(title, items) {
132
+ const safeTitle = escapeTmuxFormats(title);
133
+ const widestItem = items.reduce((width, item) => Math.max(width, menuItemWidth(item.description)), 0);
134
+ const args = ['display-menu', '-T', `#[align=centre]${safeTitle}`, ...surfaceTmuxStyleArgs()];
135
+ args.push('-x', `#{e|-:#{pane_right},${Math.max(safeTitle.length, widestItem) + 7}}`, '-y', '#{e|+:#{pane_top},3}', '--');
136
+ for (const item of items) {
137
+ // A separator is a single empty name with key and command omitted; tmux
138
+ // draws it as a horizontal rule. A header (disabled) name is a crouter-owned
139
+ // literal carrying `#[...]` style markup, so it skips format escaping.
140
+ if (item.action.kind === 'separator') {
141
+ args.push('');
142
+ continue;
143
+ }
144
+ const name = item.action.kind === 'disabled' ? item.description : escapeTmuxFormats(item.description);
145
+ args.push(name, item.selector, renderMenuAction(item.action));
146
+ }
147
+ args.push('', '-esc / q to close', '', '');
148
+ return args;
149
+ }
150
+ const TMUX_NAMED_KEYS = {
151
+ enter: 'Enter', escape: 'Escape', tab: 'Tab', backspace: 'BSpace', delete: 'DC',
152
+ space: 'Space', up: 'Up', down: 'Down', left: 'Left', right: 'Right',
153
+ pageup: 'PPage', pagedown: 'NPage', home: 'Home', end: 'End',
154
+ };
155
+ function normalizedStrokeParts(stroke) {
156
+ if (stroke === '+')
157
+ return { key: '+', modifiers: [] };
158
+ if (stroke.endsWith('++'))
159
+ return { key: '+', modifiers: stroke.slice(0, -2).split('+') };
160
+ const parts = stroke.split('+');
161
+ return { key: parts.pop(), modifiers: parts };
162
+ }
163
+ function tmuxKey(stroke) {
164
+ let { key, modifiers: modifierParts } = normalizedStrokeParts(stroke);
165
+ const modifiers = new Set(modifierParts);
166
+ if (modifiers.has('shift') && key.length === 1 && /[a-z]/.test(key)) {
167
+ // tmux names a shifted printable with its uppercase character even when an
168
+ // extended-key terminal also preserves Shift as a modifier: Alt+Shift+F is
169
+ // `M-S-F`. Shift by itself needs only the uppercase character (`F`).
170
+ key = key.toUpperCase();
171
+ if (!modifiers.has('ctrl') && !modifiers.has('alt'))
172
+ modifiers.delete('shift');
173
+ }
174
+ key = TMUX_NAMED_KEYS[key] ?? key;
175
+ const prefix = [
176
+ modifiers.has('ctrl') ? 'C-' : '',
177
+ modifiers.has('alt') ? 'M-' : '',
178
+ modifiers.has('shift') ? 'S-' : '',
179
+ ].join('');
180
+ return `${prefix}${key}`;
181
+ }
182
+ function menuSelector(stroke) {
183
+ const { key, modifiers } = normalizedStrokeParts(stroke);
184
+ return modifiers.includes('shift') && /[a-z]/.test(key) ? key.toUpperCase() : key === 'space' ? ' ' : key;
185
+ }
186
+ function boundMenuItems(ids, entries, bindings) {
187
+ const items = [];
188
+ for (const id of ids) {
189
+ const entry = entries.get(id);
190
+ if (entry === undefined)
191
+ continue;
192
+ for (const gesture of bindings.gestures(id)) {
193
+ items.push({ description: entry.description, selector: menuSelector(gesture), action: entry.action });
194
+ }
195
+ }
196
+ return items;
197
+ }
198
+ /** Resolve the complete action descriptor set once. Submenu openers point at
199
+ * the same child MenuItems the prefix menu renders, so their direct global
200
+ * shortcuts cannot drift from the menu path. */
201
+ function resolvedMenuActions(bindings) {
202
+ const entries = new Map();
203
+ for (const [id, entry] of Object.entries(LEAF_MENU_ACTIONS)) {
204
+ if (entry !== undefined)
205
+ entries.set(id, entry);
206
+ }
207
+ for (const { menuId, actionId } of ATTACH_CONTROL_BINDINGS) {
208
+ const definition = BINDING_CATALOG.find((candidate) => candidate.id === actionId);
209
+ if (definition === undefined)
210
+ throw new Error(`missing attach binding definition ${actionId}`);
211
+ entries.set(menuId, {
212
+ description: definition.name.toLowerCase(),
213
+ action: { kind: 'literal-input', input: encodeAttachControlInput(actionId) },
214
+ });
215
+ }
216
+ const attachItems = boundMenuItems(ATTACH_CONTROL_BINDINGS.map(({ menuId }) => menuId), entries, bindings);
217
+ if (attachItems.length > 0) {
218
+ entries.set('crtr.tmux.menu.attach', {
219
+ description: 'attach viewer ›',
220
+ action: { kind: 'submenu', title: ' attach viewer ', items: attachItems },
221
+ });
222
+ }
223
+ const showItems = boundMenuItems([
224
+ 'crtr.tmux.menu.metadata',
225
+ 'crtr.tmux.menu.context',
226
+ ], entries, bindings);
227
+ entries.set('crtr.tmux.menu.show', {
228
+ description: 'inspect ›',
229
+ action: { kind: 'submenu', title: ' inspect node ', items: showItems },
230
+ });
231
+ // How this viewer PAINTS the transcript, as opposed to what it shows. Each item
232
+ // is a toggle the viewer applies to its own render state (a `/`-command sent to
233
+ // the pane); a tmux menu is built once and cannot show live state, so the item
234
+ // reads as the action and the viewer answers with a notice.
235
+ const viewSettingsItems = boundMenuItems(['crtr.tmux.menu.fold-tools'], entries, bindings);
236
+ entries.set('crtr.tmux.menu.view-settings', {
237
+ description: 'display ›',
238
+ action: { kind: 'submenu', title: ' display ', items: viewSettingsItems },
239
+ });
240
+ const issueItems = boundMenuItems([
241
+ 'crtr.tmux.menu.issue.idea',
242
+ 'crtr.tmux.menu.issue.bug',
243
+ ], entries, bindings);
244
+ entries.set('crtr.tmux.menu.issues', {
245
+ description: 'create ›',
246
+ action: { kind: 'submenu', title: ' create ', items: issueItems },
247
+ });
248
+ return entries;
249
+ }
250
+ function menuItems(bindings, entries) {
251
+ const boundItems = (ids) => boundMenuItems(ids, entries, bindings);
252
+ const items = [];
253
+ const addGroup = (heading, groupItems) => {
254
+ if (groupItems.length === 0)
255
+ return;
256
+ if (items.length > 0)
257
+ items.push({ description: '', selector: '', action: { kind: 'separator' } });
258
+ items.push({ description: `-#[bold]${heading}`, selector: '', action: { kind: 'disabled' } }, ...groupItems);
259
+ };
260
+ // Explore changes what is in view or opens a canvas-level browser. Current
261
+ // node acts on the agent in this pane without changing its lifecycle.
262
+ addGroup('Explore', boundItems([
263
+ 'crtr.tmux.menu.resume',
264
+ 'crtr.tmux.menu.graph',
265
+ 'crtr.tmux.menu.focus-manager',
266
+ 'crtr.tmux.menu.show',
267
+ 'crtr.tmux.menu.view-settings',
268
+ ]));
269
+ addGroup('Current node', boundItems([
270
+ 'crtr.tmux.menu.attach',
271
+ 'crtr.tmux.menu.background-bash',
272
+ 'crtr.tmux.menu.file-review',
273
+ 'crtr.tmux.menu.pin',
274
+ 'crtr.tmux.menu.copy-id',
275
+ 'crtr.tmux.menu.reload-viewer',
276
+ 'crtr.tmux.menu.recap',
277
+ ]));
278
+ addGroup('Lifecycle', boundItems([
279
+ 'crtr.tmux.menu.fork',
280
+ 'crtr.tmux.menu.promote',
281
+ 'crtr.tmux.menu.demote',
282
+ 'crtr.tmux.menu.detach',
283
+ 'crtr.tmux.menu.close-subtree',
284
+ ]));
285
+ addGroup('System', boundItems([
286
+ 'crtr.tmux.menu.providers',
287
+ 'crtr.tmux.menu.issues',
288
+ ]));
289
+ const prefixGestures = bindings.gestures('crtr.tmux.menu.open');
290
+ if (prefixGestures.length > 0 && items.length > 0) {
291
+ items.push({ description: '', selector: '', action: { kind: 'separator' } });
292
+ for (const gesture of prefixGestures) {
293
+ items.push({
294
+ description: `${formatGesture(gesture)} to close`,
295
+ // Menu selectors accept tmux key notation, including modifiers. This
296
+ // is intentionally the exact binding that opened the menu.
297
+ selector: tmuxKey(gesture),
298
+ action: { kind: 'dismiss' },
299
+ });
300
+ }
301
+ }
302
+ return items;
303
+ }
304
+ /** Read one token from tmux's sourceable `list-keys` output. Key names that
305
+ * contain punctuation are quoted, so splitting on whitespace would make their
306
+ * displayed token differ from the key name accepted by bind/unbind commands. */
307
+ function readTmuxToken(line, from) {
308
+ let index = from;
309
+ while (index < line.length && /\s/.test(line[index]))
310
+ index += 1;
311
+ if (index >= line.length)
312
+ return null;
313
+ let quote = null;
314
+ let value = '';
315
+ while (index < line.length) {
316
+ const char = line[index];
317
+ if (quote === null && /\s/.test(char))
318
+ break;
319
+ if (quote === null && (char === "'" || char === '"')) {
320
+ quote = char;
321
+ index += 1;
322
+ continue;
323
+ }
324
+ if (quote === char) {
325
+ quote = null;
326
+ index += 1;
327
+ continue;
328
+ }
329
+ if (char === '\\' && quote !== "'" && index + 1 < line.length) {
330
+ value += line[index + 1];
331
+ index += 2;
332
+ continue;
333
+ }
334
+ value += char;
335
+ index += 1;
336
+ }
337
+ return { value, end: index };
338
+ }
339
+ function parseTmuxBinding(line) {
340
+ const commandName = readTmuxToken(line, 0);
341
+ if (commandName?.value !== 'bind-key')
342
+ return null;
343
+ let cursor = commandName.end;
344
+ let repeat = false;
345
+ while (true) {
346
+ const token = readTmuxToken(line, cursor);
347
+ if (token === null)
348
+ return null;
349
+ cursor = token.end;
350
+ if (token.value === '-r') {
351
+ repeat = true;
352
+ continue;
353
+ }
354
+ if (token.value !== '-T')
355
+ continue;
356
+ const table = readTmuxToken(line, cursor);
357
+ if (table === null)
358
+ return null;
359
+ const key = readTmuxToken(line, table.end);
360
+ if (key === null)
361
+ return null;
362
+ return {
363
+ table: table.value,
364
+ key: key.value,
365
+ command: line.slice(key.end).trimStart(),
366
+ repeat,
367
+ };
368
+ }
369
+ }
370
+ function bindingAt(table, key) {
371
+ const result = tmux(['list-keys', '-T', table]);
372
+ if (!result.ok)
373
+ return null;
374
+ return result.stdout.split('\n').find((line) => {
375
+ const parsed = parseTmuxBinding(line);
376
+ return parsed?.table === table && parsed.key === key;
377
+ }) ?? null;
378
+ }
379
+ /** Effective crouter gestures that tmux's root table currently consumes before
380
+ * pane input can see them. One list-keys snapshot serves the whole query. */
381
+ export function occupiedTmuxRootGestures(gestures) {
382
+ const result = tmux(['list-keys', '-T', ROOT_TABLE]);
383
+ if (!result.ok)
384
+ return [];
385
+ const occupied = new Set(result.stdout.split('\n').flatMap((line) => {
386
+ const parsed = parseTmuxBinding(line);
387
+ return parsed?.table === ROOT_TABLE ? [parsed.key] : [];
388
+ }));
389
+ return gestures.filter((gesture) => !gesture.includes(' > ') && occupied.has(tmuxKey(gesture)));
390
+ }
391
+ function isInstalledPair(value) {
392
+ return typeof value === 'object' && value !== null
393
+ && typeof value.table === 'string'
394
+ && typeof value.key === 'string';
395
+ }
396
+ function isWrappedRootBinding(value) {
397
+ if (typeof value !== 'object' || value === null)
398
+ return false;
399
+ const wrapper = value;
400
+ return typeof wrapper.bindingId === 'string'
401
+ && BINDING_IDS.has(wrapper.bindingId)
402
+ && typeof wrapper.gesture === 'string'
403
+ && typeof wrapper.key === 'string'
404
+ && (typeof wrapper.original === 'string' || wrapper.original === null)
405
+ && typeof wrapper.wrapper === 'string'
406
+ && (wrapper.state === 'owned' || wrapper.state === 'relinquished');
407
+ }
408
+ function readManifest() {
409
+ const option = tmux(['show-options', '-s', '-q', TMUX_BINDING_MANIFEST]);
410
+ if (!option.ok)
411
+ return null;
412
+ if (option.stdout === '')
413
+ return { pairs: [], wrappers: [] };
414
+ const result = tmux(['show-options', '-s', '-qv', TMUX_BINDING_MANIFEST]);
415
+ if (!result.ok)
416
+ return null;
417
+ try {
418
+ const value = JSON.parse(result.stdout);
419
+ // Before passthrough wrappers, the manifest was the pair array itself.
420
+ if (Array.isArray(value))
421
+ return value.every(isInstalledPair) ? { pairs: value, wrappers: [] } : null;
422
+ if (typeof value !== 'object' || value === null)
423
+ return null;
424
+ const manifest = value;
425
+ if (!Array.isArray(manifest.pairs) || !manifest.pairs.every(isInstalledPair)
426
+ || !Array.isArray(manifest.wrappers) || !manifest.wrappers.every(isWrappedRootBinding))
427
+ return null;
428
+ return { pairs: manifest.pairs, wrappers: manifest.wrappers };
429
+ }
430
+ catch {
431
+ return null;
432
+ }
433
+ }
434
+ function writeManifest(manifest) {
435
+ return tmux(['set-option', '-s', TMUX_BINDING_MANIFEST, JSON.stringify(manifest)]);
436
+ }
437
+ /** Tmux parses semicolons once more even when argv already isolated them. */
438
+ function tmuxKeyArgument(key) {
439
+ return key.replace(/\\/g, '\\\\').replace(/;/g, '\\;');
440
+ }
441
+ function tmuxCommandToken(value) {
442
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/;/g, '\\;')}"`;
443
+ }
444
+ /** A top-level separator is printed as `\;` in a bind-key line, but the same
445
+ * sequence is literal after the command tail becomes one if-shell branch. */
446
+ function bindingCommandForBranch(command) {
447
+ let quote = null;
448
+ let out = '';
449
+ for (let index = 0; index < command.length; index += 1) {
450
+ const char = command[index];
451
+ if (quote === null && (char === "'" || char === '"')) {
452
+ quote = char;
453
+ out += char;
454
+ continue;
455
+ }
456
+ if (quote === char) {
457
+ quote = null;
458
+ out += char;
459
+ continue;
460
+ }
461
+ if (char === '\\' && index + 1 < command.length) {
462
+ const next = command[index + 1];
463
+ if (quote === null && next === ';') {
464
+ out += ';';
465
+ }
466
+ else {
467
+ out += char + next;
468
+ }
469
+ index += 1;
470
+ continue;
471
+ }
472
+ out += char;
473
+ }
474
+ return out;
475
+ }
476
+ function sourceBindingLine(line) {
477
+ const dir = mkdtempSync(join(tmpdir(), 'crtr-tmux-binding-'));
478
+ const file = join(dir, 'binding.conf');
479
+ try {
480
+ writeFileSync(file, `${line}\n`, 'utf8');
481
+ return tmux(['source-file', file]);
482
+ }
483
+ finally {
484
+ rmSync(dir, { recursive: true, force: true });
485
+ }
486
+ }
487
+ function restoreWrappedBinding(wrapper) {
488
+ return wrapper.original === null
489
+ ? tmux(['unbind-key', '-T', ROOT_TABLE, tmuxKeyArgument(wrapper.key)])
490
+ : sourceBindingLine(wrapper.original);
491
+ }
492
+ // --quiet keeps the background `run-shell -b` binding from printing its result JSON, which
493
+ // tmux would otherwise surface as a view-mode overlay on the active pane. The popup opening
494
+ // is the feedback; a genuine failure still prints through the CLI toggle action. This deliberately
495
+ // invokes bare `crtr` from PATH: it is installed once and must resolve to the selected generation,
496
+ // not the sealed generation that installed it. The neighboring `crtr surface node cycle` builders
497
+ // share that property.
498
+ export function inboxToggleTmuxCommand() {
499
+ return ['run-shell', '-b', 'crtr human inbox toggle --quiet --tmux-socket "#{socket_path}" --tmux-client "#{client_name}" --target-pane "#{pane_id}"'];
500
+ }
501
+ function actionCommand(id) {
502
+ switch (id) {
503
+ case 'crtr.tmux.node.next': return ['run-shell', `crtr surface node cycle --dir next --pane '#{pane_id}' >/dev/null 2>&1`];
504
+ case 'crtr.tmux.node.previous': return ['run-shell', `crtr surface node cycle --dir prev --pane '#{pane_id}' >/dev/null 2>&1`];
505
+ case 'crtr.tmux.inbox.toggle': return inboxToggleTmuxCommand();
506
+ default: throw new Error(`no tmux command for ${id}`);
507
+ }
508
+ }
509
+ /** A directory beside the server socket serializes every process driving that
510
+ * exact tmux server. Outside tmux, ask the default server for its socket first;
511
+ * an unavailable server uses one per-user fallback lock for the error path. */
512
+ function tmuxBindingLockPath() {
513
+ const envSocket = process.env['TMUX']?.split(',')[0]?.trim();
514
+ if (envSocket !== undefined && envSocket !== '')
515
+ return `${envSocket}.crtr-keybindings.lock`;
516
+ const result = tmux(['display-message', '-p', '#{socket_path}']);
517
+ if (result.ok && result.stdout !== '')
518
+ return `${result.stdout}.crtr-keybindings.lock`;
519
+ const user = typeof process.getuid === 'function' ? process.getuid() : 'user';
520
+ return join(tmpdir(), `crtr-tmux-keybindings-${user}.lock`);
521
+ }
522
+ /** Reconcile the tmux server with one immutable crouter settings snapshot.
523
+ * Passing snapshots is hermetic; the no-argument front door reads both user
524
+ * settings from the same config read. The server lock covers the entire
525
+ * manifest read, restore, install, fingerprint, and write transition. */
526
+ export function installTmuxBindings(bindingSnapshot, passthroughSnapshot = []) {
527
+ return withExclusiveDirectoryLock(tmuxBindingLockPath(), () => reconcileTmuxBindings(bindingSnapshot, passthroughSnapshot));
528
+ }
529
+ function reconcileTmuxBindings(bindingSnapshot, passthroughSnapshot = []) {
530
+ const userSettings = bindingSnapshot === undefined ? resolveUserKeybindingSettings() : undefined;
531
+ const bindings = bindingSnapshot ?? userSettings.bindings;
532
+ const passthrough = bindingSnapshot === undefined
533
+ ? userSettings.tmuxPassthrough
534
+ : normalizeTmuxPassthrough(passthroughSnapshot);
535
+ const diagnostics = [];
536
+ const installed = [];
537
+ const manifest = readManifest();
538
+ if (manifest === null) {
539
+ diagnostics.push({ code: 'tmux-error', message: `tmux is unavailable or ${TMUX_BINDING_MANIFEST} is malformed` });
540
+ return { ok: false, installed, diagnostics };
541
+ }
542
+ const desiredWrappers = new Map();
543
+ for (const bindingId of passthrough) {
544
+ const definition = BINDING_CATALOG.find((candidate) => candidate.id === bindingId);
545
+ if (definition === undefined || !isAttachPaneBinding(definition))
546
+ continue;
547
+ for (const gesture of bindings.gestures(bindingId)) {
548
+ if (gesture.includes(' > '))
549
+ continue;
550
+ const key = tmuxKey(gesture);
551
+ if (!desiredWrappers.has(key))
552
+ desiredWrappers.set(key, { bindingId, gesture });
553
+ }
554
+ }
555
+ // Restore every wrapper crouter still owns before the ordinary binding sweep.
556
+ // A live line that differs from the fingerprint is a user's later rebind: it
557
+ // is protected from this and every later reconciliation until they toggle the
558
+ // passthrough preference off and on again.
559
+ const protectedRootKeys = new Set();
560
+ const wrappers = [];
561
+ for (const prior of manifest.wrappers) {
562
+ const desired = desiredWrappers.get(prior.key);
563
+ const live = bindingAt(ROOT_TABLE, prior.key);
564
+ if (prior.state === 'relinquished' || live !== prior.wrapper) {
565
+ protectedRootKeys.add(prior.key);
566
+ if (desired !== undefined) {
567
+ wrappers.push({ ...prior, ...desired, state: 'relinquished' });
568
+ }
569
+ continue;
570
+ }
571
+ const restored = restoreWrappedBinding(prior);
572
+ if (!restored.ok) {
573
+ protectedRootKeys.add(prior.key);
574
+ wrappers.push(prior);
575
+ diagnostics.push({
576
+ code: 'tmux-error',
577
+ bindingId: prior.bindingId,
578
+ gesture: prior.gesture,
579
+ message: `could not restore tmux binding ${ROOT_TABLE}:${prior.key}: ${restored.stderr}`,
580
+ });
581
+ }
582
+ }
583
+ for (const pair of manifest.pairs) {
584
+ if (pair.table === ROOT_TABLE && protectedRootKeys.has(pair.key))
585
+ continue;
586
+ const result = tmux(['unbind-key', '-T', pair.table, tmuxKeyArgument(pair.key)]);
587
+ if (!result.ok && bindingAt(pair.table, pair.key) !== null) {
588
+ diagnostics.push({ code: 'tmux-error', message: `could not remove crouter binding ${pair.table}:${pair.key}: ${result.stderr}` });
589
+ }
590
+ }
591
+ if (diagnostics.some((diagnostic) => diagnostic.code === 'tmux-error')) {
592
+ const save = writeManifest({ pairs: manifest.pairs, wrappers });
593
+ if (!save.ok)
594
+ diagnostics.push({ code: 'tmux-error', message: `could not save crouter tmux ownership manifest: ${save.stderr}` });
595
+ return { ok: false, installed, diagnostics };
596
+ }
597
+ const install = (table, key, command, id, gesture) => {
598
+ if (bindingAt(table, key) !== null) {
599
+ diagnostics.push({ code: 'occupied', bindingId: id, gesture, message: `${table}:${key} is already bound by tmux; ${id} was not installed` });
600
+ return false;
601
+ }
602
+ const result = tmux(['bind-key', '-T', table, tmuxKeyArgument(key), ...command]);
603
+ if (!result.ok) {
604
+ diagnostics.push({ code: 'tmux-error', bindingId: id, gesture, message: `could not install ${id}: ${result.stderr}` });
605
+ return false;
606
+ }
607
+ installed.push({ table, key });
608
+ return true;
609
+ };
610
+ const menuActions = resolvedMenuActions(bindings);
611
+ const menuCommand = buildMenuArgs(' crtr ', menuItems(bindings, menuActions));
612
+ for (const gesture of bindings.gestures('crtr.tmux.menu.open')) {
613
+ install(ROOT_TABLE, tmuxKey(gesture), menuCommand, 'crtr.tmux.menu.open', gesture);
614
+ }
615
+ for (const definition of BINDING_CATALOG) {
616
+ if (definition.pair?.field !== 'menu')
617
+ continue;
618
+ const paired = BINDING_CATALOG.find((candidate) => candidate.id === definition.pair?.pairedId);
619
+ if (paired?.pair?.field !== 'global')
620
+ continue;
621
+ const menuId = definition.id;
622
+ const globalId = paired.id;
623
+ const entry = menuActions.get(menuId);
624
+ const command = entry === undefined ? null : menuActionCommand(entry.action);
625
+ if (command === null)
626
+ throw new Error(`no direct tmux action for paired menu binding ${menuId}`);
627
+ for (const gesture of bindings.gestures(globalId)) {
628
+ install(ROOT_TABLE, tmuxKey(gesture), command.argv, globalId, gesture);
629
+ }
630
+ }
631
+ for (const id of ['crtr.tmux.node.next', 'crtr.tmux.node.previous', 'crtr.tmux.inbox.toggle']) {
632
+ for (const gesture of bindings.gestures(id))
633
+ install(ROOT_TABLE, tmuxKey(gesture), actionCommand(id), id, gesture);
634
+ }
635
+ // Wrap only occupied roots. Unoccupied gestures already reach the pane and do
636
+ // not need crouter ownership. Re-read the installed line as the fingerprint;
637
+ // tmux canonicalizes quoting and braces in ways hand serialization cannot.
638
+ const newlyWrapped = [];
639
+ for (const [key, desired] of desiredWrappers) {
640
+ if (protectedRootKeys.has(key))
641
+ continue;
642
+ const original = bindingAt(ROOT_TABLE, key);
643
+ if (original === null)
644
+ continue;
645
+ const parsed = parseTmuxBinding(original);
646
+ if (parsed === null || parsed.command === '') {
647
+ diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not parse occupied tmux binding ${ROOT_TABLE}:${key}` });
648
+ continue;
649
+ }
650
+ const result = tmux([
651
+ 'bind-key',
652
+ ...(parsed.repeat ? ['-r'] : []),
653
+ '-T', ROOT_TABLE, tmuxKeyArgument(key),
654
+ 'if-shell', '-F', '#{!=:#{@crtr_attach},}',
655
+ `send-keys ${tmuxCommandToken(key)}`,
656
+ bindingCommandForBranch(parsed.command),
657
+ ]);
658
+ if (!result.ok) {
659
+ diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not wrap ${ROOT_TABLE}:${key}: ${result.stderr}` });
660
+ continue;
661
+ }
662
+ const wrapper = bindingAt(ROOT_TABLE, key);
663
+ if (wrapper === null || !wrapper.includes('@crtr_attach')) {
664
+ diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not fingerprint wrapped tmux binding ${ROOT_TABLE}:${key}` });
665
+ continue;
666
+ }
667
+ const record = { ...desired, key, original, wrapper, state: 'owned' };
668
+ wrappers.push(record);
669
+ newlyWrapped.push(record);
670
+ }
671
+ const save = writeManifest({ pairs: installed, wrappers });
672
+ if (!save.ok) {
673
+ diagnostics.push({ code: 'tmux-error', message: `could not save crouter tmux ownership manifest: ${save.stderr}` });
674
+ // A wrapper without restoration data is unsafe. Put each newly wrapped key
675
+ // back immediately when manifest persistence fails.
676
+ for (const wrapper of newlyWrapped) {
677
+ if (bindingAt(ROOT_TABLE, wrapper.key) === wrapper.wrapper)
678
+ restoreWrappedBinding(wrapper);
679
+ }
680
+ }
681
+ return { ok: diagnostics.every((diagnostic) => diagnostic.code !== 'tmux-error'), installed, diagnostics };
682
+ }