@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
@@ -31,6 +31,8 @@ import { join } from 'node:path';
31
31
  import { getNode, jobDir, updateNode, recordPid, setPresence } from '../core/canvas/index.js';
32
32
  import { transition } from '../core/runtime/lifecycle.js';
33
33
  import { markBusy, clearBusy } from '../core/runtime/busy.js';
34
+ import { recordFault, clearFault } from '../core/runtime/fault.js';
35
+ import { classify } from '../core/fault-classifier.js';
34
36
  import { evaluateStop } from '../core/runtime/stop-guard.js';
35
37
  import { personaDrift, commitPersonaAck } from '../core/runtime/persona.js';
36
38
  import { handleNewSession, relaunchRoot, markCleanExitDone } from '../core/runtime/reset.js';
@@ -277,8 +279,10 @@ export function registerCanvasStophook(pi) {
277
279
  if (event?.reason === 'new') {
278
280
  try {
279
281
  const m = getNode(nodeId);
282
+ // relaunchRoot is async now; the enclosing sync catch can't catch its
283
+ // rejection, so swallow it here to keep `/new` best-effort.
280
284
  if (m?.parent == null)
281
- relaunchRoot(nodeId);
285
+ void relaunchRoot(nodeId).catch(() => { });
282
286
  else
283
287
  handleNewSession(nodeId, id, sessionFile);
284
288
  }
@@ -316,6 +320,8 @@ export function registerCanvasStophook(pi) {
316
320
  // rather than reaping it. Cleared at the top of agent_end (turn over).
317
321
  // ---------------------------------------------------------------------------
318
322
  pi.on('agent_start', () => {
323
+ // A fresh turn started. Keep any active fault marker in place so the daemon's
324
+ // retry accounting survives a failed retry cycle; just mark the turn busy.
319
325
  try {
320
326
  markBusy(nodeId);
321
327
  }
@@ -334,6 +340,30 @@ export function registerCanvasStophook(pi) {
334
340
  catch { /* best-effort */ }
335
341
  });
336
342
  // ---------------------------------------------------------------------------
343
+ // tool_execution_end — IMMEDIATE refresh-yield. When a tool this turn set
344
+ // intent='refresh' (the node ran `crtr node yield`), shut the broker down NOW
345
+ // instead of letting the model keep burning its bloated context to the turn's
346
+ // natural end (agent_end branch (b') would only catch it there). The yield
347
+ // command persists the yield-message + any roadmap checkpoint BEFORE it
348
+ // returns, so by the time this fires nothing is lost. ctx.shutdown() disposes
349
+ // the session (aborting the live turn) and exits 0; the daemon then sees the
350
+ // dead pid + intent='refresh' and revives the node FRESH against its roadmap.
351
+ // intent only flips to 'refresh' via `node yield`, so this can't false-fire.
352
+ // (A node that somehow boots already carrying intent='refresh' correctly
353
+ // refreshes on its first tool — the desired outcome.)
354
+ pi.on('tool_execution_end', (_event, ctx) => {
355
+ try {
356
+ if (getNode(nodeId)?.intent === 'refresh') {
357
+ clearBusy(nodeId); // agent_end won't run to clear it; harmless if it lingers
358
+ try {
359
+ ctx?.shutdown?.();
360
+ }
361
+ catch { /* ignore */ }
362
+ }
363
+ }
364
+ catch { /* best-effort */ }
365
+ });
366
+ // ---------------------------------------------------------------------------
337
367
  // session_shutdown — clean exit → done.
338
368
  //
339
369
  // pi hands us a reason as a session tears down. Only 'quit' is a node-ending
@@ -349,6 +379,7 @@ export function registerCanvasStophook(pi) {
349
379
  pi.on('session_shutdown', (event, _ctx) => {
350
380
  try {
351
381
  clearBusy(nodeId); // turn marker is meaningless once pi is exiting
382
+ clearFault(nodeId); // ditto the active fault marker
352
383
  // Clean /quit (reason='quit') resolves the node to done; if it held the
353
384
  // user's viewport, Q1-close it (tearDownNode kills the frozen focus pane +
354
385
  // closes the focus row → returns the user to a shell, §1.5/flow (e)). pi is
@@ -453,8 +484,28 @@ export function registerCanvasStophook(pi) {
453
484
  const last = lastAssistantMessage(messages);
454
485
  const stopReason = last?.stopReason ?? '';
455
486
  // (a) Interrupted or errored — stay alive so the user can re-steer.
456
- if (stopReason !== 'stop' && stopReason !== 'length')
487
+ // When the engine EXHAUSTED its retry budget (stopReason 'error', NOT
488
+ // a human Esc 'aborted'), record an active fault BEFORE the return so
489
+ // the canvas graph views can surface the parked window.
490
+ if (stopReason !== 'stop' && stopReason !== 'length') {
491
+ if (stopReason === 'error') {
492
+ try {
493
+ const raw = last?.errorMessage ?? 'engine error (no errorMessage recorded)';
494
+ const classified = classify('pi→provider', raw);
495
+ recordFault(nodeId, {
496
+ link: 'pi→provider',
497
+ op: 'generation turn',
498
+ kind: classified.kind,
499
+ retry: classified.disposition === 'auto' ? { disposition: 'auto', by: 'daemon' } : { disposition: classified.disposition },
500
+ message: raw,
501
+ logPath: join(jobDirPath, 'broker.log'),
502
+ anchorEntryId: ctx.sessionManager.getLeafId?.() ?? undefined,
503
+ });
504
+ }
505
+ catch { /* best-effort */ }
506
+ }
457
507
  return;
508
+ }
458
509
  // (b) Already done: `crtr push --final` was called this turn, which
459
510
  // transitions node.status → 'done' synchronously. Shut down cleanly.
460
511
  const node = getNode(nodeId);
@@ -464,6 +515,10 @@ export function registerCanvasStophook(pi) {
464
515
  // this node's VIEWER pane (if any) and shut the broker down. The daemon
465
516
  // won't revive a done node. tearDownNode closes the viewer focus row +
466
517
  // pane; ctx.shutdown() exits the broker process.
518
+ try {
519
+ clearFault(nodeId);
520
+ }
521
+ catch { /* best-effort */ }
467
522
  if (focusOf(nodeId) !== null) {
468
523
  try {
469
524
  tearDownNode(nodeId);
@@ -477,12 +532,16 @@ export function registerCanvasStophook(pi) {
477
532
  catch { /* ignore */ }
478
533
  return;
479
534
  }
480
- // (b') Refresh-yield: the node ran `crtr node yield` this turn, setting
481
- // intent='refresh'. Just shut the broker down — the daemon sees the
482
- // dead pid + intent='refresh' and reviveNode's relaunches the broker
483
- // FRESH (no resume) so the node re-reads its roadmap. (The daemon
484
- // also force-recycles a stuck refresh whose engine never exits, so a
485
- // stale in-process stophook can't strand the yield see crtrd.)
535
+ // (b') Refresh-yield BACKSTOP: the node ran `crtr node yield` this turn,
536
+ // setting intent='refresh'. The primary exit is the tool_execution_end
537
+ // handler above, which shuts down the instant the yield tool returns
538
+ // (so the model never burns more context finishing the turn); this
539
+ // branch only fires if that shutdown was missed. Either way: shut the
540
+ // broker down the daemon sees the dead pid + intent='refresh' and
541
+ // reviveNode relaunches the broker FRESH (no resume) so the node
542
+ // re-reads its roadmap. (The daemon also force-recycles a stuck
543
+ // refresh whose engine never exits, so a stale in-process stophook
544
+ // can't strand the yield — see crtrd.)
486
545
  if (node?.intent === 'refresh') {
487
546
  // A yield is SILENT to subscribers: the node keeps its identity and
488
547
  // subscription edges across the revive and reports only through its own
@@ -505,11 +564,19 @@ export function registerCanvasStophook(pi) {
505
564
  // followUp: the turn just ended but pi may still be flushing, so an
506
565
  // unqualified sendUserMessage races with 'already processing'.
507
566
  pi.sendUserMessage(decision.message, { deliverAs: 'followUp' });
567
+ try {
568
+ clearFault(nodeId);
569
+ }
570
+ catch { /* best-effort */ }
508
571
  return;
509
572
  }
510
- // 'allow' — the node legitimately stopped. Nothing is pushed here; any
511
- // report it owed its subscribers was sent by an explicit `crtr push`
512
- // during the turn.
573
+ // 'allow' — the node legitimately stopped. The active-fault marker can
574
+ // clear now; any report it owed its subscribers was sent by an explicit
575
+ // `crtr push` during the turn.
576
+ try {
577
+ clearFault(nodeId);
578
+ }
579
+ catch { /* best-effort */ }
513
580
  //
514
581
  // Idle-release: a node awaiting its workers (reason 'awaiting') is holding
515
582
  // a tmux window for nothing. Free it — mark it idle-released and shut pi
package/dist/types.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare const ExitCode: {
8
8
  readonly NETWORK: 5;
9
9
  };
10
10
  export type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];
11
- export declare const SCHEMA_VERSION = 2;
11
+ export declare const SCHEMA_VERSION = 4;
12
12
  export interface OwnerRef {
13
13
  name?: string;
14
14
  email?: string;
@@ -50,10 +50,27 @@ export interface AutoUpdateConfig {
50
50
  content: AutoUpdateMode;
51
51
  interval_hours: number;
52
52
  }
53
- /** One canvas-nav action binding: a `crtr` argv string (templated with
54
- * {id|self|name|manager|subtree}) plus optional confirm gate + menu label.
55
- * `run` of the sentinel `__graph__` toggles the in-pi GRAPH modal instead of
56
- * shelling a command. */
53
+ export type CanvasAction = {
54
+ kind?: 'crtr';
55
+ run: string;
56
+ confirm?: boolean;
57
+ } | {
58
+ kind: 'keys';
59
+ keys: string;
60
+ } | {
61
+ kind: 'menu';
62
+ items: CanvasMenuItem[];
63
+ } | {
64
+ kind: 'popup';
65
+ run: string;
66
+ };
67
+ export interface CanvasMenuItem {
68
+ key: string;
69
+ desc: string;
70
+ action: CanvasAction;
71
+ confirm?: boolean;
72
+ }
73
+ /** Legacy GRAPH bind shape; retained for graphBinds only. */
57
74
  export interface CanvasBind {
58
75
  run: string;
59
76
  confirm?: boolean;
@@ -68,10 +85,21 @@ export interface CanvasNavConfig {
68
85
  /** Fallback pi shortcut for GRAPH toggle when NOT in tmux. Default 'alt+g'. */
69
86
  prefixKey?: string;
70
87
  /** chord key (after alt+c) → action; tmux-menu layer. */
71
- prefixBinds: Record<string, CanvasBind>;
88
+ prefixBinds: Record<string, CanvasAction & {
89
+ desc?: string;
90
+ }>;
72
91
  /** extra raw key in GRAPH → action; pi-extension layer (additive only). */
73
92
  graphBinds: Record<string, CanvasBind>;
74
93
  }
94
+ export type ModelStrength = 'ultra' | 'strong' | 'medium' | 'light';
95
+ export type ModelProvider = 'anthropic' | 'openai';
96
+ export interface ModelLaddersConfig {
97
+ /** Optional default provider for bare strengths. If unset, runtime falls back
98
+ * to CRTR_MODEL_PROVIDER, then anthropic. */
99
+ defaultProvider?: ModelProvider;
100
+ anthropic: Record<ModelStrength, string>;
101
+ openai: Record<ModelStrength, string>;
102
+ }
75
103
  export interface ScopeConfig {
76
104
  schema_version: number;
77
105
  marketplaces: Record<string, ConfigMarketplaceEntry>;
@@ -79,6 +107,8 @@ export interface ScopeConfig {
79
107
  auto_update: AutoUpdateConfig;
80
108
  max_panes_per_window: number;
81
109
  canvasNav: CanvasNavConfig;
110
+ modelLadders: ModelLaddersConfig;
111
+ personaStrengths: Record<string, ModelStrength>;
82
112
  }
83
113
  export interface ScopeState {
84
114
  marketplaces: Record<string, {
@@ -90,15 +120,6 @@ export interface ScopeState {
90
120
  last_self_check?: string;
91
121
  bootstrap_done?: boolean;
92
122
  }
93
- export declare const SKILL_TYPES: readonly ["playbook", "primer", "reference", "runbook", "freeform"];
94
- export type SkillType = (typeof SKILL_TYPES)[number];
95
- export declare function isSkillType(v: unknown): v is SkillType;
96
- export interface SkillFrontmatter {
97
- name: string;
98
- description?: string;
99
- keywords?: string[];
100
- type?: SkillType;
101
- }
102
123
  export interface SubagentFrontmatter {
103
124
  name: string;
104
125
  description?: string;
@@ -144,14 +165,12 @@ export declare const MARKETPLACE_MANIFEST_FILE = "marketplace.json";
144
165
  export declare const CRTR_DIR_NAME = ".crouter";
145
166
  export declare const CONFIG_FILE = "config.json";
146
167
  export declare const STATE_FILE = "state.json";
147
- export declare const SKILL_ENTRY_FILE = "SKILL.md";
148
- export declare const SKILLS_DIR = "skills";
149
168
  export declare const AGENTS_DIR = "agents";
150
169
  export declare const DEFAULT_MAX_PANES_PER_WINDOW = 3;
151
170
  export declare function defaultScopeConfig(): ScopeConfig;
152
171
  /** Built-in canvas-nav binds so an absent config still gives the intended UX.
153
- * prefixBinds: g→GRAPH toggle (sentinel), m→focus manager.
154
- * 1..9 (focus report N) are generated by the menu layer, not literal entries.
155
- * graphBinds: empty — built-in GRAPH keys are reserved, custom keys add on. */
172
+ * prefixBinds restate the current menu defaults; graphBinds stay empty.
173
+ */
156
174
  export declare function defaultCanvasNavConfig(): CanvasNavConfig;
175
+ export declare function defaultModelLaddersConfig(): ModelLaddersConfig;
157
176
  export declare function defaultScopeState(): ScopeState;
package/dist/types.js CHANGED
@@ -6,11 +6,7 @@ export const ExitCode = {
6
6
  AMBIGUOUS: 4,
7
7
  NETWORK: 5,
8
8
  };
9
- export const SCHEMA_VERSION = 2;
10
- export const SKILL_TYPES = ['playbook', 'primer', 'reference', 'runbook', 'freeform'];
11
- export function isSkillType(v) {
12
- return typeof v === 'string' && SKILL_TYPES.includes(v);
13
- }
9
+ export const SCHEMA_VERSION = 4;
14
10
  export const PLUGIN_MANIFEST_DIR = '.crouter-plugin';
15
11
  export const PLUGIN_MANIFEST_FILE = 'plugin.json';
16
12
  export const MARKETPLACE_MANIFEST_DIR = '.crouter-marketplace';
@@ -18,10 +14,8 @@ export const MARKETPLACE_MANIFEST_FILE = 'marketplace.json';
18
14
  export const CRTR_DIR_NAME = '.crouter';
19
15
  export const CONFIG_FILE = 'config.json';
20
16
  export const STATE_FILE = 'state.json';
21
- export const SKILL_ENTRY_FILE = 'SKILL.md';
22
- export const SKILLS_DIR = 'skills';
23
17
  // Subagent definitions live as flat `<name>.md` files under `<root>/agents/`,
24
- // for both scope roots and plugins. Mirrors SKILLS_DIR.
18
+ // for both scope roots and plugins.
25
19
  export const AGENTS_DIR = 'agents';
26
20
  export const DEFAULT_MAX_PANES_PER_WINDOW = 3;
27
21
  export function defaultScopeConfig() {
@@ -32,22 +26,53 @@ export function defaultScopeConfig() {
32
26
  auto_update: { crtr: 'notify', content: 'notify', interval_hours: 24 },
33
27
  max_panes_per_window: DEFAULT_MAX_PANES_PER_WINDOW,
34
28
  canvasNav: defaultCanvasNavConfig(),
29
+ modelLadders: defaultModelLaddersConfig(),
30
+ personaStrengths: {},
35
31
  };
36
32
  }
37
33
  /** Built-in canvas-nav binds so an absent config still gives the intended UX.
38
- * prefixBinds: g→GRAPH toggle (sentinel), m→focus manager.
39
- * 1..9 (focus report N) are generated by the menu layer, not literal entries.
40
- * graphBinds: empty — built-in GRAPH keys are reserved, custom keys add on. */
34
+ * prefixBinds restate the current menu defaults; graphBinds stay empty.
35
+ */
41
36
  export function defaultCanvasNavConfig() {
42
37
  return {
43
38
  prefixKey: 'alt+g',
44
39
  prefixBinds: {
45
- g: { run: '__graph__', desc: 'graph view' },
46
- m: { run: 'node focus {manager}', desc: 'focus manager' },
40
+ o: { kind: 'keys', keys: '/promote', desc: 'promote to orchestrator' },
41
+ r: { kind: 'keys', keys: '/resume-node', desc: 'resume node' },
42
+ d: { kind: 'crtr', run: 'node demote --pane {pane}', desc: 'demote to terminal' },
43
+ D: { kind: 'crtr', run: 'node demote --pane {pane} --detach', desc: 'detach to background' },
44
+ x: { kind: 'crtr', run: 'node close --pane {pane}', desc: 'close agent + subtree' },
45
+ c: { kind: 'keys', keys: '/context', desc: 'browse context dirs' },
46
+ g: { kind: 'keys', keys: '/graph', desc: 'graph view' },
47
+ m: { kind: 'crtr', run: 'node focus {manager}', desc: 'focus manager' },
48
+ i: {
49
+ kind: 'menu',
50
+ desc: 'new issue / todo',
51
+ items: [
52
+ { key: 'i', desc: 'idea', action: { kind: 'popup', run: 'canvas issue new --type idea' } },
53
+ { key: 'b', desc: 'bug', action: { kind: 'popup', run: 'canvas issue new --type bug' } },
54
+ ],
55
+ },
47
56
  },
48
57
  graphBinds: {},
49
58
  };
50
59
  }
60
+ export function defaultModelLaddersConfig() {
61
+ return {
62
+ anthropic: {
63
+ ultra: 'anthropic/claude-fable-5:high',
64
+ strong: 'anthropic/claude-opus-4-8:high',
65
+ medium: 'anthropic/claude-sonnet-4-6:high',
66
+ light: 'anthropic/claude-haiku-4-5:high',
67
+ },
68
+ openai: {
69
+ ultra: 'openai-codex/gpt-5.5:xhigh',
70
+ strong: 'openai-codex/gpt-5.5:high',
71
+ medium: 'openai-codex/gpt-5.5:medium',
72
+ light: 'openai-codex/gpt-5.3-codex-spark',
73
+ },
74
+ };
75
+ }
51
76
  export function defaultScopeState() {
52
77
  return { marketplaces: {}, plugins: {} };
53
78
  }
@@ -2,22 +2,11 @@
2
2
  // view. It is what <ViewPane>/useViewCore (ViewPane.tsx) are built from.
3
3
  //
4
4
  // createViewStore() seeds state from core.init, builds the IntentCtx
5
- // (set/resolve/execute/signal/dispatch) over the HTTP transport (→ the local
6
- // bridge), runs the dispatch loop with the busy-guarded refreshMs auto-poll, and
7
- // exposes a tiny external-store surface (subscribe/getVersion) the React layer
8
- // binds via useSyncExternalStore. The core it drives is byte-identical to the
9
- // TUI's; only the injected Transport differs.
10
- //
11
- // THE TAP: dispatch calls `onIntent(name, payload, state)` BEFORE delegating to
12
- // core.intents[name]. This is the host-level pre-dispatch hook the shell uses to
13
- // observe/intercept semantic intents (e.g. canvas `activate` → open a
14
- // conversation). When onIntent is undefined the loop is byte-identical to the
15
- // pre-shell behavior — a view in a ViewPane is unaware it is in a shell.
16
- //
17
- // THE SEAM (Wave 3): store.refresh() forces an immediate, busy-guarded refresh.
18
- // It is both the auto-poll callback AND the entry point the shell calls when an
19
- // SSE `/__crtr/events` invalidation arrives — event-driven refresh with no
20
- // contract change (the view still just runs its `refresh` intent).
5
+ // (set/resolve/execute/signal/dispatch/connect) over the HTTP transport, runs
6
+ // the dispatch loop with the busy-guarded refreshMs auto-poll, and exposes a
7
+ // tiny external-store surface (subscribe/getVersion) the React layer binds via
8
+ // useSyncExternalStore. The core it drives is byte-identical to the TUI's; only
9
+ // the injected Transport differs.
21
10
  import { initialChrome } from '../core/view/chrome.js';
22
11
  import { createHttpTransport } from './transport-http.js';
23
12
  /** Build the running store for one view (its core + the HTTP transport). The
@@ -25,7 +14,8 @@ import { createHttpTransport } from './transport-http.js';
25
14
  * and re-renders off subscribe/getVersion; this factory is React-free. */
26
15
  export function createViewStore(core, opts = {}) {
27
16
  const transport = createHttpTransport(opts.endpoint);
28
- let state = core.init(Object.freeze({ ...(opts.options ?? {}) }));
17
+ const initOptions = Object.freeze({ ...(opts.options ?? {}) });
18
+ let state = core.init(initOptions);
29
19
  let chrome = initialChrome();
30
20
  let onIntentTap = opts.onIntent;
31
21
  // External store: a version counter the React tree subscribes to. state and
@@ -45,6 +35,13 @@ export function createViewStore(core, opts = {}) {
45
35
  // Web has no pane to leave; closing the tab is up to the user. No-op.
46
36
  quit: () => { },
47
37
  };
38
+ const streams = new Set();
39
+ let generation = 0;
40
+ let started = false;
41
+ let timer = null;
42
+ let busyDepth = 0;
43
+ let drainingQueue = false;
44
+ let dispatchQueue = [];
48
45
  const makeCtx = () => ({
49
46
  get state() { return state; },
50
47
  set(next) {
@@ -59,20 +56,87 @@ export function createViewStore(core, opts = {}) {
59
56
  const raw = await transport.send(command.request(args));
60
57
  return command.parse(raw);
61
58
  },
59
+ connect(source, args) {
60
+ const capturedGeneration = generation;
61
+ let channel;
62
+ channel = transport.open(source.request(args), {
63
+ onFrame(frame) {
64
+ if (capturedGeneration !== generation)
65
+ return;
66
+ void enqueueDispatch(source.onFrame, frame);
67
+ },
68
+ onClose(reason) {
69
+ if (capturedGeneration !== generation)
70
+ return;
71
+ streams.delete(channel);
72
+ if (source.onClose)
73
+ void enqueueDispatch(source.onClose, { reason, channelId: channel.id });
74
+ },
75
+ onError(err) {
76
+ if (capturedGeneration !== generation)
77
+ return;
78
+ setChrome({ banner: { msg: err.display.headline, level: err.display.level } });
79
+ },
80
+ });
81
+ streams.add(channel);
82
+ if (source.onOpen)
83
+ void enqueueDispatch(source.onOpen, channel);
84
+ return channel;
85
+ },
62
86
  signal,
63
- dispatch,
87
+ dispatch(intent, payload) {
88
+ return runIntent(intent, payload);
89
+ },
64
90
  });
65
- // busyDepth counts in-flight async intents — it drives the "working" chip /
66
- // spinner. It is NOT itself the single-flight lane; the auto-poll guards on it
67
- // (refresh() below) so a bridge slower than refreshMs can never stack
68
- // overlapping refreshes that race on ctx.set. The first completed *refresh*
69
- // marks the view loaded (ready vs idle in deriveState) — a non-refresh async
70
- // intent before that must not flip the chip to ready.
71
- let busyDepth = 0;
72
- async function dispatch(intent, payload) {
73
- // THE TAP the shell observes (and may act on) every intent against a
74
- // snapshot of state BEFORE the view's own handler runs. Standalone (no tap)
75
- // this is a no-op and the loop is byte-identical to the pre-shell behavior.
91
+ function scheduleDrain() {
92
+ if (drainingQueue)
93
+ return;
94
+ drainingQueue = true;
95
+ void drainQueue();
96
+ }
97
+ async function drainQueue() {
98
+ try {
99
+ while (started && dispatchQueue.length > 0) {
100
+ if (busyDepth > 0)
101
+ return;
102
+ const job = dispatchQueue.shift();
103
+ try {
104
+ await runIntent(job.intent, job.payload);
105
+ job.resolve();
106
+ }
107
+ catch (err) {
108
+ job.reject(err);
109
+ }
110
+ }
111
+ }
112
+ finally {
113
+ drainingQueue = false;
114
+ if (started && dispatchQueue.length > 0 && busyDepth === 0)
115
+ scheduleDrain();
116
+ }
117
+ }
118
+ function enqueueDispatch(intent, payload) {
119
+ if (!started)
120
+ return Promise.resolve();
121
+ return new Promise((resolve, reject) => {
122
+ dispatchQueue.push({ intent, payload, resolve, reject });
123
+ scheduleDrain();
124
+ });
125
+ }
126
+ function settleAfterIntent(intentName) {
127
+ if (intentName === 'refresh') {
128
+ chrome = { ...chrome, busy: busyDepth > 0, loaded: true, lastRefresh: Date.now() };
129
+ emit();
130
+ return;
131
+ }
132
+ chrome = { ...chrome, busy: busyDepth > 0 };
133
+ emit();
134
+ }
135
+ async function runIntent(intent, payload) {
136
+ if (!started) {
137
+ // The store is stopped; ignore stray external work.
138
+ return;
139
+ }
76
140
  onIntentTap?.(intent, payload, state);
77
141
  const fn = core.intents[intent];
78
142
  if (!fn) {
@@ -80,48 +144,83 @@ export function createViewStore(core, opts = {}) {
80
144
  console.warn(`[crtr view] unknown intent: ${intent}`);
81
145
  return;
82
146
  }
83
- const result = fn(makeCtx(), payload);
147
+ let result;
148
+ try {
149
+ result = fn(makeCtx(), payload);
150
+ }
151
+ catch (e) {
152
+ // eslint-disable-next-line no-console
153
+ console.warn(`[crtr view] intent ${intent} threw:`, e);
154
+ throw e;
155
+ }
84
156
  if (result instanceof Promise) {
157
+ const wasBusy = busyDepth > 0;
85
158
  busyDepth += 1;
86
- setChrome({ busy: true });
159
+ if (!wasBusy)
160
+ setChrome({ busy: true });
87
161
  try {
88
162
  await result;
89
163
  }
90
164
  finally {
91
165
  busyDepth -= 1;
92
- const loaded = chrome.loaded || intent === 'refresh';
93
- chrome = { ...chrome, busy: busyDepth > 0, loaded, lastRefresh: Date.now() };
166
+ chrome = { ...chrome, busy: busyDepth > 0, loaded: chrome.loaded || intent === 'refresh', lastRefresh: Date.now() };
94
167
  emit();
168
+ if (busyDepth === 0 && started && dispatchQueue.length > 0)
169
+ scheduleDrain();
95
170
  }
171
+ return;
96
172
  }
173
+ settleAfterIntent(intent);
97
174
  }
98
- // Auto-poll cadence guard AND the shell's invalidate seam: skip while any
99
- // async intent is still in flight so refreshes never stack.
100
- const refresh = () => { if (busyDepth === 0)
101
- void dispatch('refresh'); };
102
- let started = false;
103
- let timer = null;
175
+ const refresh = () => {
176
+ if (busyDepth === 0)
177
+ void enqueueDispatch('refresh');
178
+ };
104
179
  function start() {
105
180
  if (started)
106
181
  return;
107
182
  started = true;
108
- // init → render (the React layer) → first refresh, then optional auto-poll.
109
- void dispatch('refresh');
183
+ generation += 1;
184
+ state = core.init(initOptions);
185
+ chrome = initialChrome();
186
+ dispatchQueue = [];
187
+ drainingQueue = false;
188
+ void enqueueDispatch('refresh');
110
189
  const cadence = core.manifest.refreshMs;
111
190
  if (typeof cadence === 'number' && cadence > 0)
112
191
  timer = setInterval(refresh, cadence);
113
192
  }
114
193
  function stop() {
194
+ if (!started)
195
+ return;
115
196
  started = false;
197
+ generation += 1;
198
+ const openChannels = Array.from(streams);
199
+ for (const channel of openChannels) {
200
+ try {
201
+ channel.close();
202
+ }
203
+ catch {
204
+ /* ignore */
205
+ }
206
+ }
207
+ streams.clear();
116
208
  if (timer) {
117
209
  clearInterval(timer);
118
210
  timer = null;
119
211
  }
212
+ if (dispatchQueue.length > 0) {
213
+ const err = new Error('view stopped');
214
+ for (const job of dispatchQueue)
215
+ job.reject(err);
216
+ dispatchQueue = [];
217
+ }
218
+ drainingQueue = false;
120
219
  }
121
220
  return {
122
221
  getState: () => state,
123
222
  getChrome: () => chrome,
124
- dispatch,
223
+ dispatch: enqueueDispatch,
125
224
  refresh,
126
225
  subscribe,
127
226
  getVersion: () => version,
@@ -1,8 +1,7 @@
1
- // transport-http.ts — the browser-side Transport. Posts a view's declarative
2
- // SourceRequest to the local bridge server (`crtr view serve`'s /__crtr/source)
3
- // and returns the RawResponse the bridge ran on its behalf. This is the ONE
4
- // seam a cloud deploy swaps: point the endpoint at a real backend and the same
5
- // browser bundle resolves sources against it, no core change.
1
+ // transport-http.ts — the browser-side Transport. send() posts declarative
2
+ // SourceRequests to the local bridge server (`crtr view serve`'s /__crtr/source)
3
+ // and open() connects browser stream requests to the same-origin WS relay.
4
+ import { openBrowserStream } from './transport-stream.js';
6
5
  /** Browser Transport → POST <endpoint> {SourceRequest} → RawResponse. Never
7
6
  * throws: a network/bridge failure becomes a transport-level RawResponse
8
7
  * (ok:false + stderr), exactly like the local transport's spawn failures. */
@@ -24,5 +23,8 @@ export function createHttpTransport(endpoint = '/__crtr/source') {
24
23
  return { ok: false, stdout: '', stderr: e instanceof Error ? e.message : String(e) };
25
24
  }
26
25
  },
26
+ open(req, handlers) {
27
+ return openBrowserStream(req, handlers);
28
+ },
27
29
  };
28
30
  }
@@ -0,0 +1,3 @@
1
+ import type { StreamRequest, StreamChannel } from '../core/view/contract.js';
2
+ import type { StreamHandlers } from '../core/view/transport.js';
3
+ export declare function openBrowserStream(req: StreamRequest, handlers: StreamHandlers): StreamChannel;