@crouton-kit/crouter 0.3.32 → 0.3.33

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 (440) hide show
  1. package/README.md +8 -0
  2. package/dist/build-root.js +1 -0
  3. package/dist/builtin-memory/crouter-development/marketplaces.md +2 -2
  4. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +2 -2
  5. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +5 -5
  6. package/dist/builtin-memory/crouter-development/personas.md +3 -4
  7. package/dist/builtin-memory/crouter-development/plugins.md +18 -18
  8. package/dist/builtin-memory/design.md +4 -1
  9. package/dist/builtin-memory/development.md +4 -1
  10. package/dist/builtin-memory/internal/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/nodes-and-canvas.md +6 -6
  12. package/dist/builtin-memory/internal/storage-tiers.md +5 -5
  13. package/dist/builtin-memory/planning.md +4 -1
  14. package/dist/builtin-memory/product.md +80 -0
  15. package/dist/builtin-memory/spec.md +4 -1
  16. package/dist/builtin-personas/advisor/PERSONA.md +10 -0
  17. package/dist/builtin-personas/design/PERSONA.md +2 -2
  18. package/dist/builtin-personas/design/orchestrator.md +3 -3
  19. package/dist/builtin-personas/developer/PERSONA.md +1 -1
  20. package/dist/builtin-personas/developer/orchestrator.md +2 -2
  21. package/dist/builtin-personas/explore/PERSONA.md +3 -3
  22. package/dist/builtin-personas/explore/orchestrator.md +4 -2
  23. package/dist/builtin-personas/general/PERSONA.md +1 -1
  24. package/dist/builtin-personas/general/orchestrator.md +1 -1
  25. package/dist/builtin-personas/orchestration-kernel.md +7 -14
  26. package/dist/builtin-personas/plan/PERSONA.md +1 -1
  27. package/dist/builtin-personas/plan/orchestrator.md +2 -2
  28. package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -1
  29. package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -1
  30. package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -1
  31. package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -1
  32. package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -1
  33. package/dist/builtin-personas/product/PERSONA.md +18 -0
  34. package/dist/builtin-personas/product/orchestrator.md +14 -0
  35. package/dist/builtin-personas/product/teardown/PERSONA.md +13 -0
  36. package/dist/builtin-personas/review/PERSONA.md +1 -1
  37. package/dist/builtin-personas/review/orchestrator.md +1 -1
  38. package/dist/builtin-personas/runtime-base.md +5 -0
  39. package/dist/builtin-personas/spec/PERSONA.md +1 -1
  40. package/dist/builtin-personas/spec/orchestrator.md +2 -2
  41. package/dist/builtin-personas/spec/requirements/PERSONA.md +1 -1
  42. package/dist/builtin-views/canvas/core.mjs +82 -1
  43. package/dist/builtin-views/canvas/tui.mjs +9 -5
  44. package/dist/builtin-views/canvas/web.jsx +3 -2
  45. package/dist/builtin-views/chat/core.mjs +725 -0
  46. package/dist/builtin-views/chat/text.mjs +101 -0
  47. package/dist/builtin-views/chat/tui.mjs +368 -0
  48. package/dist/builtin-views/chat/web.jsx +367 -0
  49. package/dist/builtin-views/prompt-review/core.mjs +863 -0
  50. package/dist/builtin-views/prompt-review/text.mjs +15 -0
  51. package/dist/builtin-views/prompt-review/tui.mjs +196 -0
  52. package/dist/builtin-views/prompt-review/web.jsx +484 -0
  53. package/dist/builtin-views/settings/core.mjs +397 -0
  54. package/dist/builtin-views/settings/text.mjs +40 -0
  55. package/dist/builtin-views/settings/tui.mjs +95 -0
  56. package/dist/builtin-views/settings/web.jsx +167 -0
  57. package/dist/cli.js +16 -3
  58. package/dist/clients/attach/__tests__/autocomplete-and-bash-mode.test.js +96 -0
  59. package/dist/clients/attach/__tests__/bash-bang-routing.test.js +58 -0
  60. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.d.ts +1 -0
  61. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.js +56 -0
  62. package/dist/clients/attach/__tests__/titled-editor.test.js +22 -1
  63. package/dist/clients/attach/attach-cmd.d.ts +18 -0
  64. package/dist/clients/attach/attach-cmd.js +1723 -795
  65. package/dist/clients/attach/canvas-panels.js +2 -2
  66. package/dist/clients/attach/chat-view.d.ts +10 -0
  67. package/dist/clients/attach/chat-view.js +23 -0
  68. package/dist/clients/attach/config-load.d.ts +8 -4
  69. package/dist/clients/attach/config-load.js +2 -0
  70. package/dist/clients/attach/extension-dialogs.d.ts +2 -3
  71. package/dist/clients/attach/extension-dialogs.js +7 -9
  72. package/dist/clients/attach/graph-overlay.js +3 -2
  73. package/dist/clients/attach/input-controller.d.ts +4 -3
  74. package/dist/clients/attach/input-controller.js +24 -3
  75. package/dist/clients/attach/slash-commands.d.ts +7 -7
  76. package/dist/clients/attach/slash-commands.js +38 -14
  77. package/dist/clients/attach/titled-editor.js +18 -14
  78. package/dist/clients/attach/view-socket.d.ts +2 -1
  79. package/dist/clients/attach/view-socket.js +27 -8
  80. package/dist/clients/web/__tests__/source-cache.test.d.ts +1 -0
  81. package/dist/clients/web/__tests__/source-cache.test.js +32 -0
  82. package/dist/clients/web/dev-server.js +1 -0
  83. package/dist/clients/web/events.js +9 -11
  84. package/dist/clients/web/server.d.ts +4 -3
  85. package/dist/clients/web/server.js +138 -31
  86. package/dist/clients/web/source-cache.d.ts +10 -0
  87. package/dist/clients/web/source-cache.js +57 -0
  88. package/dist/clients/web/web-cmd.js +28 -9
  89. package/dist/commands/__tests__/human.test.js +30 -28
  90. package/dist/commands/canvas-history/search.js +1 -1
  91. package/dist/commands/canvas-history.js +5 -8
  92. package/dist/commands/canvas-issue.d.ts +2 -0
  93. package/dist/commands/canvas-issue.js +148 -0
  94. package/dist/commands/canvas-prune.js +19 -0
  95. package/dist/commands/canvas-rebuild-index.d.ts +2 -0
  96. package/dist/commands/canvas-rebuild-index.js +57 -0
  97. package/dist/commands/canvas-snapshot.d.ts +2 -0
  98. package/dist/commands/canvas-snapshot.js +48 -0
  99. package/dist/commands/canvas-tmux-spread.d.ts +2 -0
  100. package/dist/commands/canvas-tmux-spread.js +188 -0
  101. package/dist/commands/canvas.d.ts +1 -0
  102. package/dist/commands/canvas.js +16 -1
  103. package/dist/commands/chord.js +143 -48
  104. package/dist/commands/daemon.js +3 -3
  105. package/dist/commands/human/prompts.d.ts +0 -1
  106. package/dist/commands/human/prompts.js +39 -54
  107. package/dist/commands/human/queue.d.ts +12 -1
  108. package/dist/commands/human/queue.js +468 -73
  109. package/dist/commands/human/shared.d.ts +3 -4
  110. package/dist/commands/human/shared.js +3 -3
  111. package/dist/commands/human.js +11 -10
  112. package/dist/commands/memory/find.js +3 -2
  113. package/dist/commands/memory/list.js +4 -3
  114. package/dist/commands/memory/read.js +3 -3
  115. package/dist/commands/memory/shared.d.ts +1 -4
  116. package/dist/commands/memory/shared.js +6 -9
  117. package/dist/commands/memory/write.js +3 -3
  118. package/dist/commands/memory.js +4 -7
  119. package/dist/commands/node-context.d.ts +2 -0
  120. package/dist/commands/node-context.js +172 -0
  121. package/dist/commands/node-snapshot.d.ts +2 -0
  122. package/dist/commands/node-snapshot.js +123 -0
  123. package/dist/commands/node.js +228 -52
  124. package/dist/commands/pkg/plugin-inspect.js +6 -6
  125. package/dist/commands/pkg/plugin-manage.js +1 -1
  126. package/dist/commands/pkg/plugin.js +2 -2
  127. package/dist/commands/pkg.js +2 -2
  128. package/dist/commands/push.d.ts +2 -0
  129. package/dist/commands/push.js +79 -11
  130. package/dist/commands/revive.js +74 -1
  131. package/dist/commands/search/answer.d.ts +1 -0
  132. package/dist/commands/search/answer.js +50 -0
  133. package/dist/commands/search/contents.d.ts +1 -0
  134. package/dist/commands/search/contents.js +96 -0
  135. package/dist/commands/search/exa.d.ts +53 -0
  136. package/dist/commands/search/exa.js +155 -0
  137. package/dist/commands/search/puremd.d.ts +11 -0
  138. package/dist/commands/search/puremd.js +53 -0
  139. package/dist/commands/search/web.d.ts +1 -0
  140. package/dist/commands/search/web.js +65 -0
  141. package/dist/commands/search.d.ts +2 -0
  142. package/dist/commands/search.js +24 -0
  143. package/dist/commands/sys/__tests__/sync-import.test.d.ts +1 -0
  144. package/dist/commands/sys/__tests__/sync-import.test.js +72 -0
  145. package/dist/commands/sys/config.js +59 -10
  146. package/dist/commands/sys/doctor.js +56 -4
  147. package/dist/commands/sys/prompt-review.d.ts +1 -0
  148. package/dist/commands/sys/prompt-review.js +178 -0
  149. package/dist/commands/sys/promptstudio.d.ts +2 -0
  150. package/dist/commands/sys/promptstudio.js +65 -0
  151. package/dist/commands/sys/settings.d.ts +2 -0
  152. package/dist/commands/sys/settings.js +16 -0
  153. package/dist/commands/sys/sync.js +311 -159
  154. package/dist/commands/sys/sysprompt.d.ts +1 -0
  155. package/dist/commands/sys/sysprompt.js +86 -0
  156. package/dist/commands/sys.js +9 -5
  157. package/dist/commands/view-new.js +2 -2
  158. package/dist/core/__tests__/artifact-paths.test.d.ts +1 -0
  159. package/dist/core/__tests__/artifact-paths.test.js +44 -0
  160. package/dist/core/__tests__/broker-preflight.test.d.ts +1 -0
  161. package/dist/core/__tests__/broker-preflight.test.js +85 -0
  162. package/dist/core/__tests__/broker-sdk-wiring.test.js +15 -0
  163. package/dist/core/__tests__/canvas.test.js +37 -1
  164. package/dist/core/__tests__/chat-view-reconnect.test.d.ts +1 -0
  165. package/dist/core/__tests__/chat-view-reconnect.test.js +70 -0
  166. package/dist/core/__tests__/child-death-wake.test.js +11 -2
  167. package/dist/core/__tests__/close.test.js +39 -1
  168. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +1 -0
  169. package/dist/core/__tests__/connection-reconnect-resume.test.js +37 -0
  170. package/dist/core/__tests__/context-intro.test.js +48 -14
  171. package/dist/core/__tests__/daemon-boot.test.js +182 -9
  172. package/dist/core/__tests__/editor-label.test.d.ts +1 -0
  173. package/dist/core/__tests__/editor-label.test.js +26 -0
  174. package/dist/core/__tests__/fault-marker.test.d.ts +1 -0
  175. package/dist/core/__tests__/fault-marker.test.js +112 -0
  176. package/dist/core/__tests__/fault-retry-rewind.test.d.ts +1 -0
  177. package/dist/core/__tests__/fault-retry-rewind.test.js +123 -0
  178. package/dist/core/__tests__/fixtures/fake-engine.d.ts +30 -4
  179. package/dist/core/__tests__/fixtures/fake-engine.js +156 -17
  180. package/dist/core/__tests__/fixtures/fake-pi-host.js +10 -1
  181. package/dist/core/__tests__/full/human-new-window-regression.test.js +1 -1
  182. package/dist/core/__tests__/helpers/harness.d.ts +1 -1
  183. package/dist/core/__tests__/helpers/harness.js +2 -2
  184. package/dist/core/__tests__/human-stranded-deliver.test.js +38 -1
  185. package/dist/core/__tests__/human-surface-target.test.js +1 -1
  186. package/dist/core/__tests__/kickoff.test.js +18 -19
  187. package/dist/core/__tests__/memory-resolver.test.js +12 -1
  188. package/dist/core/__tests__/migration.test.js +49 -14
  189. package/dist/core/__tests__/model-ladders.test.d.ts +1 -0
  190. package/dist/core/__tests__/model-ladders.test.js +160 -0
  191. package/dist/core/__tests__/node-env.test.d.ts +1 -0
  192. package/dist/core/__tests__/node-env.test.js +26 -0
  193. package/dist/core/__tests__/push-final-guard.test.js +7 -1
  194. package/dist/core/__tests__/relaunch-root.test.js +9 -9
  195. package/dist/core/__tests__/revive.test.js +36 -6
  196. package/dist/core/__tests__/spawn-root.test.js +24 -1
  197. package/dist/core/__tests__/stop-guard.test.js +10 -0
  198. package/dist/core/__tests__/stranded-relaunch.test.d.ts +1 -0
  199. package/dist/core/__tests__/stranded-relaunch.test.js +72 -0
  200. package/dist/core/__tests__/tmux-surface.test.js +5 -1
  201. package/dist/core/__tests__/unknown-path.test.js +19 -19
  202. package/dist/core/__tests__/yield-ensures-daemon.test.d.ts +1 -0
  203. package/dist/core/__tests__/yield-ensures-daemon.test.js +54 -0
  204. package/dist/core/artifact.d.ts +2 -33
  205. package/dist/core/artifact.js +27 -87
  206. package/dist/core/bootstrap.js +2 -0
  207. package/dist/core/canvas/browse/__tests__/model.test.js +35 -1
  208. package/dist/core/canvas/browse/app.js +87 -17
  209. package/dist/core/canvas/browse/model.d.ts +10 -1
  210. package/dist/core/canvas/browse/model.js +37 -1
  211. package/dist/core/canvas/browse/render.js +35 -33
  212. package/dist/core/canvas/canvas.d.ts +7 -0
  213. package/dist/core/canvas/canvas.js +54 -5
  214. package/dist/core/canvas/db.js +35 -8
  215. package/dist/core/canvas/labels.d.ts +6 -8
  216. package/dist/core/canvas/labels.js +8 -11
  217. package/dist/core/canvas/nav-model.d.ts +5 -4
  218. package/dist/core/canvas/nav-model.js +31 -17
  219. package/dist/core/canvas/paths.d.ts +18 -1
  220. package/dist/core/canvas/paths.js +31 -2
  221. package/dist/core/canvas/render.d.ts +25 -0
  222. package/dist/core/canvas/render.js +96 -13
  223. package/dist/core/canvas/status-glyph.d.ts +35 -0
  224. package/dist/core/canvas/status-glyph.js +104 -0
  225. package/dist/core/canvas/types.d.ts +2 -2
  226. package/dist/core/config.js +114 -16
  227. package/dist/core/fault-classifier.d.ts +41 -0
  228. package/dist/core/fault-classifier.js +110 -0
  229. package/dist/core/feed/inbox.d.ts +15 -0
  230. package/dist/core/feed/inbox.js +40 -14
  231. package/dist/core/frontmatter.d.ts +1 -11
  232. package/dist/core/frontmatter.js +1 -55
  233. package/dist/core/hearth/config.d.ts +2 -0
  234. package/dist/core/hearth/config.js +75 -0
  235. package/dist/core/hearth/guest-env.d.ts +2 -0
  236. package/dist/core/hearth/guest-env.js +11 -0
  237. package/dist/core/hearth/index.d.ts +5 -0
  238. package/dist/core/hearth/index.js +5 -0
  239. package/dist/core/hearth/provider.d.ts +21 -0
  240. package/dist/core/hearth/provider.js +10 -0
  241. package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
  242. package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
  243. package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
  244. package/dist/core/hearth/providers/blaxel-home.js +126 -0
  245. package/dist/core/hearth/providers/blaxel.d.ts +26 -0
  246. package/dist/core/hearth/providers/blaxel.js +208 -0
  247. package/dist/core/hearth/providers/types.d.ts +72 -0
  248. package/dist/core/hearth/providers/types.js +1 -0
  249. package/dist/core/hearth/registry.d.ts +15 -0
  250. package/dist/core/hearth/registry.js +179 -0
  251. package/dist/core/hearth/types.d.ts +121 -0
  252. package/dist/core/hearth/types.js +1 -0
  253. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
  254. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
  255. package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
  256. package/dist/core/host-exports/builtins.js +68 -0
  257. package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
  258. package/dist/core/{skill-sync → host-exports}/export.js +37 -14
  259. package/dist/core/io.js +1 -1
  260. package/dist/core/log.d.ts +9 -0
  261. package/dist/core/log.js +113 -0
  262. package/dist/core/memory-resolver.d.ts +8 -8
  263. package/dist/core/memory-resolver.js +15 -28
  264. package/dist/core/personas/index.d.ts +4 -4
  265. package/dist/core/personas/index.js +2 -2
  266. package/dist/core/personas/loader.d.ts +51 -0
  267. package/dist/core/personas/loader.js +54 -50
  268. package/dist/core/personas/resolve.d.ts +43 -27
  269. package/dist/core/personas/resolve.js +336 -94
  270. package/dist/core/runtime/auth-reload.d.ts +7 -0
  271. package/dist/core/runtime/auth-reload.js +69 -0
  272. package/dist/core/runtime/bearings.d.ts +35 -15
  273. package/dist/core/runtime/bearings.js +305 -44
  274. package/dist/core/runtime/branded-host.d.ts +4 -2
  275. package/dist/core/runtime/branded-host.js +66 -4
  276. package/dist/core/runtime/broker-protocol.d.ts +38 -3
  277. package/dist/core/runtime/broker.js +343 -18
  278. package/dist/core/runtime/close.js +37 -29
  279. package/dist/core/runtime/connectivity.d.ts +12 -0
  280. package/dist/core/runtime/connectivity.js +73 -0
  281. package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
  282. package/dist/core/runtime/fault-recovery-nudge.js +2 -0
  283. package/dist/core/runtime/fault-recovery.d.ts +23 -0
  284. package/dist/core/runtime/fault-recovery.js +74 -0
  285. package/dist/core/runtime/fault.d.ts +25 -0
  286. package/dist/core/runtime/fault.js +176 -0
  287. package/dist/core/runtime/front-door.d.ts +1 -1
  288. package/dist/core/runtime/front-door.js +2 -2
  289. package/dist/core/runtime/host.d.ts +10 -0
  290. package/dist/core/runtime/host.js +71 -9
  291. package/dist/core/runtime/kickoff.js +31 -45
  292. package/dist/core/runtime/launch.d.ts +32 -18
  293. package/dist/core/runtime/launch.js +148 -43
  294. package/dist/core/runtime/model-swap.d.ts +18 -0
  295. package/dist/core/runtime/model-swap.js +95 -0
  296. package/dist/core/runtime/naming.d.ts +7 -0
  297. package/dist/core/runtime/naming.js +61 -9
  298. package/dist/core/runtime/nodes.js +6 -1
  299. package/dist/core/runtime/persona.js +16 -17
  300. package/dist/core/runtime/placement.d.ts +36 -31
  301. package/dist/core/runtime/placement.js +149 -64
  302. package/dist/core/runtime/promote.d.ts +2 -0
  303. package/dist/core/runtime/promote.js +23 -3
  304. package/dist/core/runtime/recycle.js +6 -4
  305. package/dist/core/runtime/reset.d.ts +5 -2
  306. package/dist/core/runtime/reset.js +2 -2
  307. package/dist/core/runtime/revive.d.ts +4 -0
  308. package/dist/core/runtime/revive.js +27 -9
  309. package/dist/core/runtime/roadmap.d.ts +1 -1
  310. package/dist/core/runtime/roadmap.js +1 -1
  311. package/dist/core/runtime/spawn.d.ts +2 -2
  312. package/dist/core/runtime/spawn.js +30 -6
  313. package/dist/core/runtime/stop-guard.js +6 -1
  314. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  315. package/dist/core/runtime/tmux-chrome.js +1 -1
  316. package/dist/core/runtime/tmux.d.ts +29 -6
  317. package/dist/core/runtime/tmux.js +134 -80
  318. package/dist/core/runtime/view-socket-client.d.ts +46 -0
  319. package/dist/core/runtime/view-socket-client.js +203 -0
  320. package/dist/core/substrate/index.d.ts +1 -1
  321. package/dist/core/substrate/index.js +1 -1
  322. package/dist/core/substrate/on-read.js +16 -4
  323. package/dist/core/substrate/render.d.ts +10 -13
  324. package/dist/core/substrate/render.js +56 -51
  325. package/dist/core/tui/host.js +83 -18
  326. package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
  327. package/dist/core/view/__tests__/transport-cache.test.js +62 -0
  328. package/dist/core/view/contract.d.ts +20 -0
  329. package/dist/core/view/stream-local.d.ts +3 -0
  330. package/dist/core/view/stream-local.js +184 -0
  331. package/dist/core/view/transport-cache.d.ts +8 -0
  332. package/dist/core/view/transport-cache.js +38 -0
  333. package/dist/core/view/transport-local.js +4 -0
  334. package/dist/core/view/transport.d.ts +7 -1
  335. package/dist/daemon/crtrd.d.ts +3 -19
  336. package/dist/daemon/crtrd.js +139 -178
  337. package/dist/daemon/manage.d.ts +18 -1
  338. package/dist/daemon/manage.js +54 -9
  339. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
  340. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
  341. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
  342. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
  343. package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
  344. package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
  345. package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
  346. package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
  347. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
  348. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
  349. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
  350. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
  351. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
  352. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
  353. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
  354. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
  355. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
  356. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
  357. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
  358. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
  359. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
  360. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
  361. package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
  362. package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
  363. package/dist/hearth/wake-proxy/auth.d.ts +22 -0
  364. package/dist/hearth/wake-proxy/auth.js +128 -0
  365. package/dist/hearth/wake-proxy/config.d.ts +2 -0
  366. package/dist/hearth/wake-proxy/config.js +151 -0
  367. package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
  368. package/dist/hearth/wake-proxy/guest-source.js +130 -0
  369. package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
  370. package/dist/hearth/wake-proxy/hearth-status.js +267 -0
  371. package/dist/hearth/wake-proxy/home.d.ts +67 -0
  372. package/dist/hearth/wake-proxy/home.js +297 -0
  373. package/dist/hearth/wake-proxy/main.d.ts +1 -0
  374. package/dist/hearth/wake-proxy/main.js +134 -0
  375. package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
  376. package/dist/hearth/wake-proxy/model-auth.js +345 -0
  377. package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
  378. package/dist/hearth/wake-proxy/proxy.js +716 -0
  379. package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
  380. package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
  381. package/dist/hearth/wake-proxy/redact.d.ts +1 -0
  382. package/dist/hearth/wake-proxy/redact.js +17 -0
  383. package/dist/hearth/wake-proxy/server.d.ts +11 -0
  384. package/dist/hearth/wake-proxy/server.js +142 -0
  385. package/dist/hearth/wake-proxy/state.d.ts +18 -0
  386. package/dist/hearth/wake-proxy/state.js +342 -0
  387. package/dist/hearth/wake-proxy/types.d.ts +76 -0
  388. package/dist/hearth/wake-proxy/types.js +1 -0
  389. package/dist/index.js +3 -2
  390. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
  391. package/dist/pi-extensions/canvas-context-intro.js +7 -7
  392. package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
  393. package/dist/pi-extensions/canvas-nav.js +15 -8
  394. package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
  395. package/dist/pi-extensions/canvas-stophook.js +78 -11
  396. package/dist/types.d.ts +39 -20
  397. package/dist/types.js +38 -13
  398. package/dist/web/runtime.js +141 -42
  399. package/dist/web/transport-http.js +7 -5
  400. package/dist/web/transport-stream.d.ts +3 -0
  401. package/dist/web/transport-stream.js +204 -0
  402. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  403. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  404. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  405. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  406. package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  407. package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  408. package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  409. package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
  410. package/dist/web-client/assets/index-D36PNBj4.js +80 -0
  411. package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
  412. package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
  413. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  414. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  415. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  416. package/dist/web-client/index.html +3 -2
  417. package/package.json +32 -10
  418. package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
  419. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
  420. package/dist/core/skill-sync/builtins.js +0 -112
  421. package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
  422. package/dist/core/skill-sync/claude-plugins.js +0 -71
  423. package/dist/core/skill-sync/engine.d.ts +0 -42
  424. package/dist/core/skill-sync/engine.js +0 -633
  425. package/dist/core/skill-sync/manifest.d.ts +0 -64
  426. package/dist/core/skill-sync/manifest.js +0 -181
  427. package/dist/core/skill-sync/profile.d.ts +0 -76
  428. package/dist/core/skill-sync/profile.js +0 -173
  429. package/dist/core/skill-sync/snapshot.d.ts +0 -57
  430. package/dist/core/skill-sync/snapshot.js +0 -120
  431. package/dist/pi-extensions/canvas-commands.d.ts +0 -37
  432. package/dist/pi-extensions/canvas-commands.js +0 -113
  433. package/dist/pi-extensions/canvas-resume.d.ts +0 -21
  434. package/dist/pi-extensions/canvas-resume.js +0 -83
  435. package/dist/pi-extensions/canvas-view.d.ts +0 -21
  436. package/dist/pi-extensions/canvas-view.js +0 -76
  437. package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
  438. package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
  439. /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
  440. /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
@@ -9,6 +9,7 @@
9
9
  // their window; reviving opens a fresh one.
10
10
  import { spawnSync } from 'node:child_process';
11
11
  import { readConfig } from '../config.js';
12
+ import { defaultCanvasNavConfig } from '../../types.js';
12
13
  import { surfaceTmuxStyleArgs } from './surface-bg.js';
13
14
  // ---------------------------------------------------------------------------
14
15
  // Shell quoting + tmux invocation
@@ -48,12 +49,32 @@ export function currentTmux() {
48
49
  export function sessionExists(name) {
49
50
  return tmux(['has-session', '-t', name]).ok;
50
51
  }
52
+ /** Force bracketed-paste capabilities on for every terminal on this tmux server.
53
+ *
54
+ * Many terminfo entries lack the Enbp/Dsbp (enable/disable bracketed paste)
55
+ * capabilities. Without them tmux never asks the outer terminal to wrap pastes,
56
+ * so a multi-line paste arrives as raw `\r` and the inner app (pi, zsh, …)
57
+ * submits on the first newline — i.e. one paste lands as several messages.
58
+ * Declaring the caps for `*` restores bracketed paste so pastes stay atomic.
59
+ *
60
+ * `terminal-overrides` is a server option, so this is set once per server and
61
+ * is idempotent: we only append our entry when the Enbp marker isn't already
62
+ * present (e.g. the user's own tmux.conf already declares it). Must run only
63
+ * once a server exists — set-option errors out with no server running — so
64
+ * callers invoke it AFTER ensuring a session. */
65
+ function ensureBracketedPaste() {
66
+ const current = tmux(['show-options', '-s', 'terminal-overrides']).stdout;
67
+ if (current.includes('Enbp='))
68
+ return;
69
+ tmux(['set-option', '-sga', 'terminal-overrides', '*:Enbp=\E[?2004h:Dsbp=\E[?2004l']);
70
+ }
51
71
  /** Create a detached session rooted at `cwd` if it doesn't exist. The session
52
72
  * name is a root's tmux home; every node under that root is a window in it. */
53
73
  export function ensureSession(name, cwd) {
54
- if (sessionExists(name))
55
- return;
56
- tmux(['new-session', '-d', '-s', name, '-c', cwd]);
74
+ if (!sessionExists(name)) {
75
+ tmux(['new-session', '-d', '-s', name, '-c', cwd]);
76
+ }
77
+ ensureBracketedPaste();
57
78
  }
58
79
  function envFlags(env) {
59
80
  const out = [];
@@ -149,6 +170,31 @@ export function closeWindow(window) {
149
170
  export function renameWindow(window, name) {
150
171
  return tmux(['rename-window', '-t', window, name]).ok;
151
172
  }
173
+ /** Break a single PANE out into a BRAND-NEW window of its own (`tmux break-pane
174
+ * -d`). `-d` keeps the new window in the background (does not switch the client
175
+ * to it); `-s <pane>` names the source pane — the §2.2 HARD DRIVER INVARIANT, so
176
+ * the break never falls back to tmux's global current pane. The pane keeps its
177
+ * durable `%id` across the move (only its window changes), so callers can keep
178
+ * using the same pane handle. Returns the new `{window, pane}` location, or null
179
+ * if tmux fails. Used by `canvas tmux-spread` to lift the caller's viewer into a
180
+ * fresh window before tiling sibling viewers beside it. */
181
+ export function breakPane(pane) {
182
+ const r = tmux([
183
+ 'break-pane',
184
+ '-d',
185
+ '-P',
186
+ '-F',
187
+ '#{window_id}\t#{pane_id}',
188
+ '-s',
189
+ pane,
190
+ ]);
191
+ if (!r.ok)
192
+ return null;
193
+ const [window, p] = r.stdout.split('\t');
194
+ if (window === undefined || window === '' || p === undefined || p === '')
195
+ return null;
196
+ return { window, pane: p };
197
+ }
152
198
  /** Close a single PANE. Its window closes automatically once this was the last
153
199
  * pane, but sibling panes survive — so co-located nodes (several agents sharing
154
200
  * one window via swap-pane focus) are torn down one at a time instead of all
@@ -226,6 +272,17 @@ export function paneCurrentPath(pane) {
226
272
  const r = tmux(['display-message', '-p', '-t', pane, '#{pane_current_path}']);
227
273
  return r.ok && r.stdout !== '' ? r.stdout : null;
228
274
  }
275
+ /** Flash a transient status-line toast on the client showing `pane` (or the
276
+ * current client when `pane` is omitted). `ms` sets the display duration. Used
277
+ * by `crtr canvas issue new` to confirm a capture from inside its popup.
278
+ * Best-effort; never throws. */
279
+ export function displayMessage(message, pane, ms = 4000) {
280
+ const args = ['display-message', '-d', String(ms)];
281
+ if (pane !== undefined && pane !== '')
282
+ args.push('-t', pane);
283
+ args.push(message);
284
+ return tmux(args).ok;
285
+ }
229
286
  /** Set a PANE-scoped tmux option (`tmux set-option -p -t <pane> <name> <value>`).
230
287
  * Used to tag a pane with the view id it currently hosts (`@crtr_view`) so the
231
288
  * view-nav cycle can read it back and switch to the next/prev view in place.
@@ -311,6 +368,21 @@ export function windowAlive(session, window) {
311
368
  export function selectWindow(session, window) {
312
369
  return tmux(['select-window', '-t', `${session}:${window}`]).ok;
313
370
  }
371
+ /** Apply a named tmux layout to a window (`tmux select-layout -t <window>
372
+ * <layout>`). `canvas tmux-spread` calls it with `tiled` to evenly grid every
373
+ * viewer pane in the spread window (and between splits, to redistribute space so
374
+ * the next split has room). Best-effort; false if tmux fails. */
375
+ export function selectLayout(window, layout) {
376
+ return tmux(['select-layout', '-t', window, layout]).ok;
377
+ }
378
+ /** Make a pane the ACTIVE pane in its window (`tmux select-pane -t <pane>`). A
379
+ * `split-window -d` keeps the CALLER active, so a freshly-opened viewer pane is
380
+ * not focused until this runs — `focus` calls it so picking a node (e.g. from the
381
+ * alt+g graph) lands the keyboard on the new viewer. Best-effort; false if tmux
382
+ * fails. */
383
+ export function selectPane(pane) {
384
+ return tmux(['select-pane', '-t', pane]).ok;
385
+ }
314
386
  /** Switch the tmux client to a different session (cross-session focus). Runs
315
387
  * `tmux switch-client -t <session>`. Best-effort; never throws. The caller is
316
388
  * responsible for following up with selectWindow to land on the right window. */
@@ -329,91 +401,73 @@ export function sendKeysEnter(pane, text) {
329
401
  // ---------------------------------------------------------------------------
330
402
  // Prefix menu — Alt+C opens a which-key-style tmux display-menu of crouter
331
403
  // actions. Installed on the running server at root boot; idempotent (a re-bind
332
- // overwrites the previous one). Items shell out to `crtr`, passing the active
333
- // pane so an action targets the agent currently in front of you.
404
+ // overwrites the previous one). Menu entries are data-driven: tmux renders the
405
+ // action tree, and every `crtr` / `popup` action routes through `crtr canvas
406
+ // chord` so node-relative vars resolve in one safe place.
334
407
  // ---------------------------------------------------------------------------
335
- /** Reserved mnemonic keys owned by the built-in menu items below — a custom
336
- * `prefixBind` may not claim these (the built-in item wins). */
337
- const RESERVED_MENU_KEYS = new Set(['o', 'r', 'd', 'D', 'x']);
338
- /** Bind Alt+C to the crouter action menu. Best-effort; false if tmux fails.
339
- * The built-in items (promote/resume/demote/detach/close) are static; the canvas-nav
340
- * chords (default g→graph, m→manager + any custom prefixBind) are appended
341
- * from `canvasNav.prefixBinds`, each routed through `crtr canvas chord` (or, for
342
- * the `__graph__` sentinel, a `send-keys '/graph'`) so the menu stays static
343
- * while behaviour is config-driven. */
408
+ function escapeTmuxFormats(value) {
409
+ return value.replace(/#/g, '##');
410
+ }
411
+ function runShellCmd(shellCmd) {
412
+ return `run-shell ${shellQuote(shellCmd)}`;
413
+ }
414
+ function tmuxCmd(args) {
415
+ return args.map(shellQuote).join(' ');
416
+ }
417
+ function chordShellCmd(run) {
418
+ return runShellCmd(`crtr canvas chord --pane '#{pane_id}' --run ${shellQuote(escapeTmuxFormats(run))} >/dev/null 2>&1`);
419
+ }
420
+ function popupCmd(run) {
421
+ return `display-popup -E -w 60% -h 50% -d "#{pane_current_path}" ${shellQuote(`crtr canvas chord --pane '#{pane_id}' --run ${shellQuote(escapeTmuxFormats(run))} --interactive`)}`;
422
+ }
423
+ function keysCmd(keys) {
424
+ return `send-keys -t '#{pane_id}' ${shellQuote(escapeTmuxFormats(keys))} Enter`;
425
+ }
426
+ function buildMenuArgs(title, items) {
427
+ const safeTitle = escapeTmuxFormats(title);
428
+ const args = ['display-menu', '-T', `#[align=centre]${safeTitle}`, ...surfaceTmuxStyleArgs()];
429
+ const boxW = Math.max(safeTitle.length, ...items.map((i) => escapeTmuxFormats(i.desc).length)) + 6;
430
+ const nudgeX = 1;
431
+ const nudgeY = 3;
432
+ args.push('-x', `#{e|-:#{pane_right},${boxW + nudgeX}}`, '-y', `#{e|+:#{pane_top},${nudgeY}}`);
433
+ for (const item of items) {
434
+ args.push(escapeTmuxFormats(item.desc), escapeTmuxFormats(item.key), renderAction(item.action, item.desc));
435
+ }
436
+ args.push('-esc / q to close', '', '');
437
+ return args;
438
+ }
439
+ function renderAction(action, label) {
440
+ const kind = action.kind ?? 'crtr';
441
+ switch (kind) {
442
+ case 'keys':
443
+ return keysCmd(action.keys);
444
+ case 'popup':
445
+ return popupCmd(action.run);
446
+ case 'menu':
447
+ return tmuxCmd(buildMenuArgs(label, action.items));
448
+ case 'crtr':
449
+ return chordShellCmd(action.run);
450
+ default:
451
+ throw new Error(`unknown canvas action kind: ${kind}`);
452
+ }
453
+ }
454
+ /** Bind Alt+C to the crouter action menu. Best-effort; false if tmux fails. */
344
455
  export function installMenuBinding() {
345
- const title = ' crtr ';
346
- const items = [
347
- // Promote types `/promote` into the agent's pane rather than shelling out:
348
- // the slash command delivers the orchestration guidance into the node's
349
- // context, which a bare `run-shell` (output discarded) could not.
350
- { name: 'promote to orchestrator', key: 'o', cmd: `send-keys -t '#{pane_id}' '/promote' Enter` },
351
- // Resume types `/resume-node` into the agent's pane: the slash command opens
352
- // a whole-canvas picker (incl. dormant nodes) and revives the choice via
353
- // `crtr node focus` — the only sync-safe open (routes through reviveNode).
354
- { name: 'resume node', key: 'r', cmd: `send-keys -t '#{pane_id}' '/resume-node' Enter` },
355
- // `d` runs `node demote`: flip the agent to TERMINAL in place — no finalize,
356
- // no kill — it keeps running where it is, and because it is now terminal it
357
- // is forced to push a final up the spine when it finishes. `D` runs `node
358
- // demote --detach`, which ALSO closes the agent's viewer pane (frees the
359
- // pane; the detached broker keeps generating). Neither ends it.
360
- { name: 'demote to terminal', key: 'd', cmd: `run-shell "crtr node demote --pane '#{pane_id}' >/dev/null 2>&1"` },
361
- { name: 'detach to background', key: 'D', cmd: `run-shell "crtr node demote --pane '#{pane_id}' --detach >/dev/null 2>&1"` },
362
- // Close cascades down the subscribes_to spine (kills the subtree's windows,
363
- // marks them canceled); revivable. Output discarded — the keypress just acts.
364
- { name: 'close agent + subtree', key: 'x', cmd: `run-shell "crtr node close --pane '#{pane_id}' >/dev/null 2>&1"` },
365
- ];
366
- // Canvas-nav chords from config (default: g→graph, m→manager). The
367
- // `__graph__` sentinel toggles the in-pi GRAPH modal via send-keys; every
368
- // other bind shells the chord dispatcher, which resolves the pane's node and
369
- // interpolates the bind at popup time. Keys colliding with the built-ins are
370
- // skipped (the built-in wins).
371
- let prefixBinds = {};
456
+ let prefixBinds;
372
457
  try {
373
458
  prefixBinds = readConfig('user').canvasNav.prefixBinds;
374
459
  }
375
- catch { /* defaults below */ }
460
+ catch {
461
+ prefixBinds = defaultCanvasNavConfig().prefixBinds;
462
+ }
463
+ const items = [];
376
464
  for (const [key, bind] of Object.entries(prefixBinds)) {
377
- if (key.length !== 1 || RESERVED_MENU_KEYS.has(key))
465
+ if (key.length !== 1)
378
466
  continue;
379
- const name = bind.desc !== undefined && bind.desc !== '' ? bind.desc : `chord ${key}`;
380
- const cmd = bind.run === '__graph__'
381
- ? `send-keys -t '#{pane_id}' '/graph' Enter`
382
- : `run-shell "crtr canvas chord --pane '#{pane_id}' --key ${key} >/dev/null 2>&1"`;
383
- items.push({ name, key, cmd });
467
+ const desc = bind.desc ?? `chord ${key}`;
468
+ items.push({ key, desc, action: bind });
384
469
  }
385
- // Dismiss hint. A tmux display-menu always closes on its native cancel keys
386
- // (Escape / q / C-c), encoding-independent. We do NOT try to catch a re-pressed
387
- // Alt+C: under `extended-keys on` (common, and what pi negotiates) the second
388
- // Alt+C reaches the overlay as a CSI-u key (`\033[99;3u`) that tmux's menu does
389
- // NOT match against an `M-c` mnemonic item, so a "close menu" row keyed M-c
390
- // never fires and the menu just sits open (verified in tmux 3.6b: legacy Esc+c
391
- // closes, CSI-u does not). Instead, a disabled (`-` prefix → dim, unselectable)
392
- // last row tells the user the close keys that always work. Placed last so it
393
- // reads as chrome below the actions.
394
- items.push({ name: '-esc / q to close', key: '', cmd: '' });
395
- // tmux's -x sets the menu's LEFT edge. To sit the box INSIDE the pane's
396
- // top-right corner, shift x left by the box width (longest line + tmux chrome:
397
- // borders + padding + the right-aligned mnemonic-key column) via format math.
398
- const boxW = Math.max(title.length, ...items.map((i) => i.name.length)) + 6;
399
- // Fine-tune nudges off the pane's top-right corner: a hair further left and
400
- // one row down so the box doesn't kiss the pane border.
401
- const nudgeX = 1; // extra columns left
402
- const nudgeY = 3; // rows down
403
- const args = [
404
- 'bind-key', '-n', 'M-c', 'display-menu',
405
- '-T', `#[align=centre]${title}`,
406
- // Frame the menu on the theme's distinct-surface background so it doesn't
407
- // blend into the pane behind it (CTO ruling). Empty in the front door (theme
408
- // not loaded there); the attach viewer re-installs this with the style once
409
- // it has themed — see attach-cmd.ts.
410
- ...surfaceTmuxStyleArgs(),
411
- '-x', `#{e|-:#{pane_right},${boxW + nudgeX}}`,
412
- '-y', `#{e|+:#{pane_top},${nudgeY}}`,
413
- ];
414
- for (const it of items)
415
- args.push(it.name, it.key, it.cmd);
416
- return tmux(args).ok;
470
+ return tmux(['bind-key', '-n', 'M-c', ...buildMenuArgs(' crtr ', items)]).ok;
417
471
  }
418
472
  // ---------------------------------------------------------------------------
419
473
  // Nav bindings — Alt+] / Alt+[ DFS-walk the canvas one window at a time. Each
@@ -0,0 +1,46 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { type NodeRow } from '../canvas/index.js';
3
+ import { type BrokerDataFrame, type BrokerToClient, type ClientToBroker, type DequeueFrame, type GetSettingsFrame, type GetTreeFrame, type ListModelsFrame, type ListScopedModelsFrame, type ListSessionsFrame } from './broker-protocol.js';
4
+ export type ReadOpRequest = Omit<ListModelsFrame, 'id'> | Omit<ListSessionsFrame, 'id'> | Omit<GetTreeFrame, 'id'> | Omit<GetSettingsFrame, 'id'> | Omit<ListScopedModelsFrame, 'id'> | Omit<DequeueFrame, 'id'>;
5
+ export declare function reconnectShouldGiveUp(row: NodeRow | null): boolean;
6
+ export declare class BrokerUnavailableError extends Error {
7
+ readonly nodeId: string;
8
+ constructor(nodeId: string);
9
+ }
10
+ export interface ViewSocketClientOptions {
11
+ emitUnmatchedDataFrames?: boolean;
12
+ }
13
+ export interface ViewSocketClient {
14
+ on(event: 'connect', listener: () => void): this;
15
+ on(event: 'frame', listener: (frame: BrokerToClient) => void): this;
16
+ on(event: 'close', listener: () => void): this;
17
+ on(event: 'error', listener: (err: Error) => void): this;
18
+ once(event: 'connect', listener: () => void): this;
19
+ once(event: 'frame', listener: (frame: BrokerToClient) => void): this;
20
+ once(event: 'close', listener: () => void): this;
21
+ once(event: 'error', listener: (err: Error) => void): this;
22
+ off(event: 'connect', listener: () => void): this;
23
+ off(event: 'error', listener: (err: Error) => void): this;
24
+ }
25
+ export declare class ViewSocketClient extends EventEmitter {
26
+ private readonly nodeId;
27
+ private socket;
28
+ private decoder;
29
+ private closeEmitted;
30
+ private readonly emitUnmatchedDataFrames;
31
+ private pending;
32
+ constructor(nodeId: string, options?: ViewSocketClientOptions);
33
+ private recordSocketFault;
34
+ request(frame: ReadOpRequest): Promise<BrokerDataFrame>;
35
+ private rejectAllPending;
36
+ get socketPath(): string;
37
+ connect(): void;
38
+ redial(): Promise<void>;
39
+ send(frame: ClientToBroker): void;
40
+ close(): void;
41
+ private onData;
42
+ private onError;
43
+ private onClose;
44
+ private destroy;
45
+ private emitError;
46
+ }
@@ -0,0 +1,203 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { createConnection } from 'node:net';
4
+ import { classify } from '../fault-classifier.js';
5
+ import { viewSocketPath } from '../canvas/paths.js';
6
+ import { clearFault, recordFault } from './fault.js';
7
+ import { CLIENT_READ_CAPS, encodeFrame, FrameDecoder, FrameOverflowError, } from './broker-protocol.js';
8
+ const REQUEST_TIMEOUT_MS = 10_000;
9
+ export function reconnectShouldGiveUp(row) {
10
+ if (row === null)
11
+ return true;
12
+ return row.status === 'done' || row.status === 'dead' || row.status === 'canceled';
13
+ }
14
+ export class BrokerUnavailableError extends Error {
15
+ nodeId;
16
+ constructor(nodeId) {
17
+ super(`node ${nodeId} has no running broker — focus or revive it first`);
18
+ this.nodeId = nodeId;
19
+ this.name = 'BrokerUnavailableError';
20
+ }
21
+ }
22
+ export class ViewSocketClient extends EventEmitter {
23
+ nodeId;
24
+ socket;
25
+ decoder = new FrameDecoder(CLIENT_READ_CAPS);
26
+ closeEmitted = false;
27
+ emitUnmatchedDataFrames;
28
+ pending = new Map();
29
+ constructor(nodeId, options = {}) {
30
+ super();
31
+ this.nodeId = nodeId;
32
+ this.emitUnmatchedDataFrames = options.emitUnmatchedDataFrames ?? false;
33
+ }
34
+ recordSocketFault(op, err) {
35
+ const classified = classify('viewer↔broker', err);
36
+ recordFault(this.nodeId, {
37
+ link: 'viewer↔broker',
38
+ op,
39
+ kind: classified.kind,
40
+ retry: op === 'redial'
41
+ ? { disposition: 'auto', by: 'client' }
42
+ : { disposition: 'manual' },
43
+ message: err instanceof Error ? err.message : String(err),
44
+ });
45
+ }
46
+ request(frame) {
47
+ const id = randomUUID();
48
+ return new Promise((resolve, reject) => {
49
+ const sock = this.socket;
50
+ if (sock === undefined || sock.destroyed) {
51
+ reject(new Error(`cannot issue '${frame.type}': no live broker connection`));
52
+ return;
53
+ }
54
+ const timer = setTimeout(() => {
55
+ this.pending.delete(id);
56
+ reject(new Error(`request '${frame.type}' timed out after ${REQUEST_TIMEOUT_MS}ms`));
57
+ }, REQUEST_TIMEOUT_MS);
58
+ if (typeof timer.unref === 'function')
59
+ timer.unref();
60
+ this.pending.set(id, { resolve, reject, timer });
61
+ this.send({ ...frame, id });
62
+ });
63
+ }
64
+ rejectAllPending(reason) {
65
+ for (const { reject, timer } of this.pending.values()) {
66
+ clearTimeout(timer);
67
+ reject(new Error(reason));
68
+ }
69
+ this.pending.clear();
70
+ }
71
+ get socketPath() {
72
+ return viewSocketPath(this.nodeId);
73
+ }
74
+ connect() {
75
+ const socket = createConnection(this.socketPath);
76
+ this.socket = socket;
77
+ socket.on('connect', () => {
78
+ clearFault(this.nodeId, { link: 'viewer↔broker' });
79
+ this.emit('connect');
80
+ });
81
+ socket.on('data', (chunk) => this.onData(chunk));
82
+ socket.on('error', (err) => this.onError(err, 'connect'));
83
+ socket.on('close', () => this.onClose());
84
+ }
85
+ redial() {
86
+ this.destroy();
87
+ this.decoder = new FrameDecoder(CLIENT_READ_CAPS);
88
+ this.closeEmitted = false;
89
+ return new Promise((resolve, reject) => {
90
+ const socket = createConnection(this.socketPath);
91
+ this.socket = socket;
92
+ let settled = false;
93
+ socket.on('connect', () => {
94
+ if (settled)
95
+ return;
96
+ settled = true;
97
+ clearFault(this.nodeId, { link: 'viewer↔broker' });
98
+ this.emit('connect');
99
+ resolve();
100
+ });
101
+ socket.on('data', (chunk) => this.onData(chunk));
102
+ socket.on('error', (err) => {
103
+ if (settled) {
104
+ this.onError(err, 'redial');
105
+ return;
106
+ }
107
+ settled = true;
108
+ this.recordSocketFault('redial', err);
109
+ try {
110
+ socket.destroy();
111
+ }
112
+ catch {
113
+ /* ignore */
114
+ }
115
+ reject(err);
116
+ });
117
+ socket.on('close', () => this.onClose());
118
+ });
119
+ }
120
+ send(frame) {
121
+ const sock = this.socket;
122
+ if (sock === undefined || sock.destroyed)
123
+ return;
124
+ try {
125
+ sock.write(encodeFrame(frame));
126
+ }
127
+ catch {
128
+ /* dead socket — 'close' will fire and drive teardown */
129
+ }
130
+ }
131
+ close() {
132
+ this.destroy();
133
+ }
134
+ onData(chunk) {
135
+ let frames;
136
+ try {
137
+ frames = this.decoder.push(chunk);
138
+ }
139
+ catch (err) {
140
+ const msg = err instanceof FrameOverflowError
141
+ ? `broker sent an oversized frame (${err.message}) — disconnecting`
142
+ : `failed to decode a broker frame: ${String(err)}`;
143
+ this.rejectAllPending(msg);
144
+ this.emitError(new Error(msg));
145
+ this.destroy();
146
+ return;
147
+ }
148
+ for (const raw of frames) {
149
+ const frame = raw;
150
+ if (frame.type === 'data') {
151
+ const entry = typeof frame.id === 'string' ? this.pending.get(frame.id) : undefined;
152
+ if (entry) {
153
+ this.pending.delete(frame.id);
154
+ clearTimeout(entry.timer);
155
+ entry.resolve(frame);
156
+ }
157
+ else if (this.emitUnmatchedDataFrames) {
158
+ this.emit('frame', frame);
159
+ }
160
+ continue;
161
+ }
162
+ if (frame.type === 'error' && typeof frame.id === 'string' && this.pending.has(frame.id)) {
163
+ const entry = this.pending.get(frame.id);
164
+ this.pending.delete(frame.id);
165
+ clearTimeout(entry.timer);
166
+ entry.reject(new Error(frame.message || `request failed: ${frame.code}`));
167
+ continue;
168
+ }
169
+ this.emit('frame', frame);
170
+ }
171
+ }
172
+ onError(err, op) {
173
+ this.recordSocketFault(op, err);
174
+ if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT') {
175
+ this.emitError(new BrokerUnavailableError(this.nodeId));
176
+ }
177
+ else {
178
+ this.emitError(new Error(`view socket error: ${err.message}`));
179
+ }
180
+ }
181
+ onClose() {
182
+ if (this.closeEmitted)
183
+ return;
184
+ this.closeEmitted = true;
185
+ this.rejectAllPending('broker connection closed');
186
+ this.emit('close');
187
+ }
188
+ destroy() {
189
+ const sock = this.socket;
190
+ if (sock !== undefined && !sock.destroyed) {
191
+ try {
192
+ sock.destroy();
193
+ }
194
+ catch {
195
+ /* ignore */
196
+ }
197
+ }
198
+ }
199
+ emitError(err) {
200
+ if (this.listenerCount('error') > 0)
201
+ this.emit('error', err);
202
+ }
203
+ }
@@ -5,5 +5,5 @@ export type { NodeConfigSubject } from './subject.js';
5
5
  export { gatePasses } from './gate.js';
6
6
  export { INDEX_NAME, isIndexName, indexDirOf, displayName, buildCeilingIndex, effectiveRung, } from './ceiling.js';
7
7
  export type { Surface } from './ceiling.js';
8
- export { renderPreferencesSection, renderKnowledgeBlock, renderMemoryGuidance, } from './render.js';
8
+ export { renderPreferencesSection, renderKnowledgeBlock, } from './render.js';
9
9
  export { renderOnReadDocs } from './on-read.js';
@@ -15,5 +15,5 @@ parseSubstrateFrontmatter, parseSubstrateDoc, previewLine, } from './schema.js';
15
15
  export { scopeForCwd, spineDepth, assembleNodeSubject } from './subject.js';
16
16
  export { gatePasses } from './gate.js';
17
17
  export { INDEX_NAME, isIndexName, indexDirOf, displayName, buildCeilingIndex, effectiveRung, } from './ceiling.js';
18
- export { renderPreferencesSection, renderKnowledgeBlock, renderMemoryGuidance, } from './render.js';
18
+ export { renderPreferencesSection, renderKnowledgeBlock, } from './render.js';
19
19
  export { renderOnReadDocs } from './on-read.js';
@@ -23,6 +23,14 @@
23
23
  // the doc fires only on its trigger, never positionally on every read under its
24
24
  // scope. The two explicit triggers compose by OR.
25
25
  //
26
+ // D6 — an explicitly-triggered doc also PIERCES the INDEX ceiling: it surfaces at
27
+ // its OWN fileReadVisibility, not min(own, ancestor INDEX rungs). The ceiling is
28
+ // the mechanism for collapsing a subtree's BOOT + POSITIONAL on-read surface; a
29
+ // cross-cutting applies-to/read-when trigger is the author deliberately opting
30
+ // ONE doc into surfacing when a specific file is read, so a `none`-capped
31
+ // ancestor INDEX (the default for a reference plugin pulled by name) must not
32
+ // veto it. Same stance as D5 — the explicit trigger is the more specific signal.
33
+ //
26
34
  // Each candidate runs the substrate pipeline at its fileReadVisibility rung:
27
35
  // parse → gatePasses(doc, assembleNodeSubject(nodeId)) → render
28
36
  // (content → body, preview → previewLine, name → bare tag, none → skip).
@@ -157,7 +165,7 @@ function positionalCandidates(absReadFile) {
157
165
  // D5: a doc with an explicit read-trigger (applies-to/read-when) does
158
166
  // NOT fire positionally — it surfaces only via that trigger.
159
167
  if (doc && !hasExplicitTrigger(doc))
160
- out.push({ doc, realpath: real, order: depth });
168
+ out.push({ doc, realpath: real, order: depth, explicit: false });
161
169
  }
162
170
  }
163
171
  }
@@ -218,7 +226,7 @@ function appliesToCandidates(absReadFile, taken) {
218
226
  if (!globs.some((g) => globMatches(g, absReadFile, owningRootOf(doc))))
219
227
  continue;
220
228
  taken.add(real);
221
- out.push({ doc, realpath: real, order: -1 });
229
+ out.push({ doc, realpath: real, order: -1, explicit: true });
222
230
  }
223
231
  return out;
224
232
  }
@@ -261,7 +269,7 @@ function readWhenCandidates(readFileFm, taken) {
261
269
  if (!evalCondition(doc.readWhen, readFileFm))
262
270
  continue;
263
271
  taken.add(real);
264
- out.push({ doc, realpath: real, order: -1 });
272
+ out.push({ doc, realpath: real, order: -1, explicit: true });
265
273
  }
266
274
  return out;
267
275
  }
@@ -350,7 +358,11 @@ export function renderOnReadDocs(nodeId, readFilePath, seen = new Set()) {
350
358
  try {
351
359
  if (!gatePasses(c.doc, subject))
352
360
  continue; // gated out for this node
353
- const rung = effectiveRung(c.doc, ceil, 'fileReadVisibility');
361
+ // D6: an explicit applies-to/read-when trigger pierces the INDEX ceiling
362
+ // and renders at the doc's own rung; positional docs honor the ceiling.
363
+ const rung = c.explicit
364
+ ? c.doc.fileReadVisibility
365
+ : effectiveRung(c.doc, ceil, 'fileReadVisibility');
354
366
  const doc = rung === c.doc.fileReadVisibility && displayName(c.doc.name) === c.doc.name
355
367
  ? c.doc
356
368
  : { ...c.doc, fileReadVisibility: rung, name: displayName(c.doc.name) };
@@ -1,19 +1,16 @@
1
- /** The `<preferences>` system-prompt block: every eligible `kind: preference`
2
- * doc in one tree at its effective rung (the preference default rung is
3
- * `preview` → the routing line). Returns '' when nothing is eligible. */
1
+ /** The system-prompt memory block: the always-present memory-usage guidance,
2
+ * then (when any are eligible) the `kind: preference` docs in one tree at their
3
+ * effective rung (the preference default rung is `preview` → the routing line).
4
+ * ALWAYS returns a non-empty `<memory kind="preference">` block — the guidance
5
+ * frame is unconditional, so the system-prompt splice is never empty even when
6
+ * no preference is eligible. */
4
7
  export declare function renderPreferencesSection(nodeId: string): string;
5
- /** The `<knowledge>` block embedded INSIDE the `<crtr-context>` session_start
6
- * message (bearings.ts pushes the returned string into the block, or drops it
8
+ /** The `<memory kind="knowledge">` block embedded in the session_start bearings
9
+ * message (bearings.ts pushes it as a SIBLING of `<crtr-bearings>`, or drops it
7
10
  * when ''). The consultable catalog: every eligible `kind: knowledge` resolver
8
11
  * doc at its effective rung (most surface only as `[+N more]` counts unless
9
12
  * author-promoted) PLUS the node-local memory docs (any kind), the latter
10
13
  * floored to `name` so a `none`-rung node-local doc still shows its name rather
11
- * than collapsing into a count. Procedural skills and factual references both
12
- * live here now — they merged into the one `knowledge` kind. Returns '' when
13
- * nothing is eligible. */
14
+ * than collapsing into a count. Procedural guidance and factual references both
15
+ * live here as `knowledge`. Returns '' when nothing is eligible. */
14
16
  export declare function renderKnowledgeBlock(nodeId: string): string;
15
- /** The memory-hygiene directive spliced into the system prompt after the
16
- * preferences block. ALWAYS present for a canvas node (the memory system always
17
- * exists), so the system-prompt splice stays non-empty even when both trees are
18
- * empty. Guidance about USING the memory system, not a per-doc surface. */
19
- export declare function renderMemoryGuidance(): string;