@crouton-kit/crouter 0.3.31 → 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 (443) 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 +9 -3
  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 +37 -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/__tests__/empty-node-guard.test.d.ts +1 -0
  230. package/dist/core/feed/__tests__/empty-node-guard.test.js +45 -0
  231. package/dist/core/feed/feed.js +8 -0
  232. package/dist/core/feed/inbox.d.ts +15 -0
  233. package/dist/core/feed/inbox.js +40 -14
  234. package/dist/core/frontmatter.d.ts +1 -11
  235. package/dist/core/frontmatter.js +1 -55
  236. package/dist/core/hearth/config.d.ts +2 -0
  237. package/dist/core/hearth/config.js +75 -0
  238. package/dist/core/hearth/guest-env.d.ts +2 -0
  239. package/dist/core/hearth/guest-env.js +11 -0
  240. package/dist/core/hearth/index.d.ts +5 -0
  241. package/dist/core/hearth/index.js +5 -0
  242. package/dist/core/hearth/provider.d.ts +21 -0
  243. package/dist/core/hearth/provider.js +10 -0
  244. package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
  245. package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
  246. package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
  247. package/dist/core/hearth/providers/blaxel-home.js +126 -0
  248. package/dist/core/hearth/providers/blaxel.d.ts +26 -0
  249. package/dist/core/hearth/providers/blaxel.js +208 -0
  250. package/dist/core/hearth/providers/types.d.ts +72 -0
  251. package/dist/core/hearth/providers/types.js +1 -0
  252. package/dist/core/hearth/registry.d.ts +15 -0
  253. package/dist/core/hearth/registry.js +179 -0
  254. package/dist/core/hearth/types.d.ts +121 -0
  255. package/dist/core/hearth/types.js +1 -0
  256. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
  257. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
  258. package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
  259. package/dist/core/host-exports/builtins.js +68 -0
  260. package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
  261. package/dist/core/{skill-sync → host-exports}/export.js +37 -14
  262. package/dist/core/io.js +1 -1
  263. package/dist/core/log.d.ts +9 -0
  264. package/dist/core/log.js +113 -0
  265. package/dist/core/memory-resolver.d.ts +8 -8
  266. package/dist/core/memory-resolver.js +15 -28
  267. package/dist/core/personas/index.d.ts +4 -4
  268. package/dist/core/personas/index.js +2 -2
  269. package/dist/core/personas/loader.d.ts +51 -0
  270. package/dist/core/personas/loader.js +54 -50
  271. package/dist/core/personas/resolve.d.ts +43 -27
  272. package/dist/core/personas/resolve.js +336 -94
  273. package/dist/core/runtime/auth-reload.d.ts +7 -0
  274. package/dist/core/runtime/auth-reload.js +69 -0
  275. package/dist/core/runtime/bearings.d.ts +35 -15
  276. package/dist/core/runtime/bearings.js +305 -44
  277. package/dist/core/runtime/branded-host.d.ts +4 -2
  278. package/dist/core/runtime/branded-host.js +66 -4
  279. package/dist/core/runtime/broker-protocol.d.ts +38 -3
  280. package/dist/core/runtime/broker.js +343 -18
  281. package/dist/core/runtime/close.js +37 -29
  282. package/dist/core/runtime/connectivity.d.ts +12 -0
  283. package/dist/core/runtime/connectivity.js +73 -0
  284. package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
  285. package/dist/core/runtime/fault-recovery-nudge.js +2 -0
  286. package/dist/core/runtime/fault-recovery.d.ts +23 -0
  287. package/dist/core/runtime/fault-recovery.js +74 -0
  288. package/dist/core/runtime/fault.d.ts +25 -0
  289. package/dist/core/runtime/fault.js +176 -0
  290. package/dist/core/runtime/front-door.d.ts +1 -1
  291. package/dist/core/runtime/front-door.js +2 -2
  292. package/dist/core/runtime/host.d.ts +10 -0
  293. package/dist/core/runtime/host.js +71 -9
  294. package/dist/core/runtime/kickoff.js +31 -45
  295. package/dist/core/runtime/launch.d.ts +32 -18
  296. package/dist/core/runtime/launch.js +148 -43
  297. package/dist/core/runtime/model-swap.d.ts +18 -0
  298. package/dist/core/runtime/model-swap.js +95 -0
  299. package/dist/core/runtime/naming.d.ts +7 -0
  300. package/dist/core/runtime/naming.js +61 -9
  301. package/dist/core/runtime/nodes.js +6 -1
  302. package/dist/core/runtime/persona.js +16 -17
  303. package/dist/core/runtime/placement.d.ts +36 -31
  304. package/dist/core/runtime/placement.js +149 -64
  305. package/dist/core/runtime/promote.d.ts +2 -0
  306. package/dist/core/runtime/promote.js +23 -3
  307. package/dist/core/runtime/recycle.js +6 -4
  308. package/dist/core/runtime/reset.d.ts +5 -2
  309. package/dist/core/runtime/reset.js +2 -2
  310. package/dist/core/runtime/revive.d.ts +4 -0
  311. package/dist/core/runtime/revive.js +27 -9
  312. package/dist/core/runtime/roadmap.d.ts +1 -1
  313. package/dist/core/runtime/roadmap.js +1 -1
  314. package/dist/core/runtime/spawn.d.ts +2 -2
  315. package/dist/core/runtime/spawn.js +30 -6
  316. package/dist/core/runtime/stop-guard.js +6 -1
  317. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  318. package/dist/core/runtime/tmux-chrome.js +1 -1
  319. package/dist/core/runtime/tmux.d.ts +29 -6
  320. package/dist/core/runtime/tmux.js +134 -80
  321. package/dist/core/runtime/view-socket-client.d.ts +46 -0
  322. package/dist/core/runtime/view-socket-client.js +203 -0
  323. package/dist/core/substrate/index.d.ts +1 -1
  324. package/dist/core/substrate/index.js +1 -1
  325. package/dist/core/substrate/on-read.js +16 -4
  326. package/dist/core/substrate/render.d.ts +10 -13
  327. package/dist/core/substrate/render.js +56 -51
  328. package/dist/core/tui/host.js +83 -18
  329. package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
  330. package/dist/core/view/__tests__/transport-cache.test.js +62 -0
  331. package/dist/core/view/contract.d.ts +20 -0
  332. package/dist/core/view/stream-local.d.ts +3 -0
  333. package/dist/core/view/stream-local.js +184 -0
  334. package/dist/core/view/transport-cache.d.ts +8 -0
  335. package/dist/core/view/transport-cache.js +38 -0
  336. package/dist/core/view/transport-local.js +4 -0
  337. package/dist/core/view/transport.d.ts +7 -1
  338. package/dist/daemon/crtrd.d.ts +3 -19
  339. package/dist/daemon/crtrd.js +139 -178
  340. package/dist/daemon/manage.d.ts +18 -1
  341. package/dist/daemon/manage.js +54 -9
  342. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
  343. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
  344. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
  345. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
  346. package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
  347. package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
  348. package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
  349. package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
  350. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
  351. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
  352. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
  353. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
  354. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
  355. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
  356. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
  357. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
  358. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
  359. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
  360. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
  361. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
  362. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
  363. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
  364. package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
  365. package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
  366. package/dist/hearth/wake-proxy/auth.d.ts +22 -0
  367. package/dist/hearth/wake-proxy/auth.js +128 -0
  368. package/dist/hearth/wake-proxy/config.d.ts +2 -0
  369. package/dist/hearth/wake-proxy/config.js +151 -0
  370. package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
  371. package/dist/hearth/wake-proxy/guest-source.js +130 -0
  372. package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
  373. package/dist/hearth/wake-proxy/hearth-status.js +267 -0
  374. package/dist/hearth/wake-proxy/home.d.ts +67 -0
  375. package/dist/hearth/wake-proxy/home.js +297 -0
  376. package/dist/hearth/wake-proxy/main.d.ts +1 -0
  377. package/dist/hearth/wake-proxy/main.js +134 -0
  378. package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
  379. package/dist/hearth/wake-proxy/model-auth.js +345 -0
  380. package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
  381. package/dist/hearth/wake-proxy/proxy.js +716 -0
  382. package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
  383. package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
  384. package/dist/hearth/wake-proxy/redact.d.ts +1 -0
  385. package/dist/hearth/wake-proxy/redact.js +17 -0
  386. package/dist/hearth/wake-proxy/server.d.ts +11 -0
  387. package/dist/hearth/wake-proxy/server.js +142 -0
  388. package/dist/hearth/wake-proxy/state.d.ts +18 -0
  389. package/dist/hearth/wake-proxy/state.js +342 -0
  390. package/dist/hearth/wake-proxy/types.d.ts +76 -0
  391. package/dist/hearth/wake-proxy/types.js +1 -0
  392. package/dist/index.js +3 -2
  393. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
  394. package/dist/pi-extensions/canvas-context-intro.js +7 -7
  395. package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
  396. package/dist/pi-extensions/canvas-nav.js +15 -8
  397. package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
  398. package/dist/pi-extensions/canvas-stophook.js +78 -11
  399. package/dist/types.d.ts +39 -20
  400. package/dist/types.js +38 -13
  401. package/dist/web/runtime.js +141 -42
  402. package/dist/web/transport-http.js +7 -5
  403. package/dist/web/transport-stream.d.ts +3 -0
  404. package/dist/web/transport-stream.js +204 -0
  405. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  406. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  407. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  408. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  409. package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  410. package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  411. package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  412. package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
  413. package/dist/web-client/assets/index-D36PNBj4.js +80 -0
  414. package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
  415. package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
  416. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  417. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  418. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  419. package/dist/web-client/index.html +3 -2
  420. package/package.json +32 -10
  421. package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
  422. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
  423. package/dist/core/skill-sync/builtins.js +0 -112
  424. package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
  425. package/dist/core/skill-sync/claude-plugins.js +0 -71
  426. package/dist/core/skill-sync/engine.d.ts +0 -42
  427. package/dist/core/skill-sync/engine.js +0 -633
  428. package/dist/core/skill-sync/manifest.d.ts +0 -64
  429. package/dist/core/skill-sync/manifest.js +0 -181
  430. package/dist/core/skill-sync/profile.d.ts +0 -76
  431. package/dist/core/skill-sync/profile.js +0 -173
  432. package/dist/core/skill-sync/snapshot.d.ts +0 -57
  433. package/dist/core/skill-sync/snapshot.js +0 -120
  434. package/dist/pi-extensions/canvas-commands.d.ts +0 -37
  435. package/dist/pi-extensions/canvas-commands.js +0 -113
  436. package/dist/pi-extensions/canvas-resume.d.ts +0 -21
  437. package/dist/pi-extensions/canvas-resume.js +0 -83
  438. package/dist/pi-extensions/canvas-view.d.ts +0 -21
  439. package/dist/pi-extensions/canvas-view.js +0 -76
  440. package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
  441. package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
  442. /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
  443. /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
@@ -13,12 +13,17 @@ import { reviveNode } from '../core/runtime/revive.js';
13
13
  import { newNodeId } from '../core/runtime/nodes.js';
14
14
  import { readRoadmap } from '../core/runtime/roadmap.js';
15
15
  import { parseWhen, parseCadence, cadenceDisplay } from '../core/wake.js';
16
+ import { nodeSnapshotLeaf } from './node-snapshot.js';
17
+ import { nodeContextLeaf } from './node-context.js';
16
18
  import { recycleNode } from '../core/runtime/recycle.js';
19
+ import { readFaultAsync } from '../core/runtime/fault.js';
20
+ import { activeFaultForDisplay } from '../core/canvas/render.js';
17
21
  import { detachToBackground, focus as placementFocus, windowAlive, windowOfPane, currentTmux, getPaneOption } from '../core/runtime/placement.js';
18
- import { buildLaunchSpec } from '../core/runtime/launch.js';
22
+ import { buildLaunchSpec, normalizeModel } from '../core/runtime/launch.js';
19
23
  import { closeNode } from '../core/runtime/close.js';
24
+ import { isBrokerLive, setModelLive, persistDormantModel } from '../core/runtime/model-swap.js';
20
25
  import { appendInbox } from '../core/feed/inbox.js';
21
- import { availableKinds, kindWhenToUse, subPersonasFor } from '../core/personas/index.js';
26
+ import { availableKinds, kindWhenToUse } from '../core/personas/index.js';
22
27
  import { stateBlock } from '../core/help.js';
23
28
  import { getNode, updateNode, listNodes, subscribe, unsubscribe, subscriptionsOf, subscribersOf, readContextTokens, view, armWake, listWakes, cancelWake, WakeArmError, } from '../core/canvas/index.js';
24
29
  // Past this much context, an ORCHESTRATOR that spawns a managed child is better
@@ -56,11 +61,8 @@ function assertKind(kind) {
56
61
  * it reflects the caller's cwd/project scope. Appended to `node new -h` and
57
62
  * `node promote -h` so custom kinds appear in help. Soft-fails via the renderer.
58
63
  *
59
- * CONTEXT-AWARE: when the CALLER is a live node (CRTR_NODE_ID set) whose kind
60
- * has sub-personas available to it, a SECOND `<sub-personas for=K count=M>`
61
- * block is appended right after — the specialist sub-personas THAT kind may
62
- * spawn (full kind string + whenToUse). Everything but the top-level `<kinds>`
63
- * block soft-fails to omission (caller block wrapped in its own try/catch). */
64
+ * Keep this block cheap and caller-agnostic: help should not depend on the
65
+ * running node's current persona env, which can lag after polymorph. */
64
66
  function kindsStateBlock() {
65
67
  const kinds = availableKinds();
66
68
  const lines = kinds
@@ -69,33 +71,7 @@ function kindsStateBlock() {
69
71
  return w ? `${k} — ${w}` : k;
70
72
  })
71
73
  .join('\n');
72
- const block = stateBlock('kinds', { count: kinds.length }, lines);
73
- const sub = callerSubPersonasBlock();
74
- return sub ? `${block}\n${sub}` : block;
75
- }
76
- /** When the caller is a live node whose kind has sub-personas available to it,
77
- * render a `<sub-personas for="<kind>" count=M>` block — one
78
- * `<full-kind-string> — <whenToUse>` line per sub-persona spawnable BY that
79
- * kind (e.g. `plan/reviewers/security`). Returns '' (so the second block is
80
- * omitted) when CRTR_NODE_ID is unset, getNode returns null, the caller kind
81
- * has no sub-personas, or anything throws — this is help output, never error. */
82
- function callerSubPersonasBlock() {
83
- try {
84
- const id = process.env['CRTR_NODE_ID'];
85
- if (id === undefined || id === '')
86
- return '';
87
- const node = getNode(id);
88
- if (node === null)
89
- return '';
90
- const subs = subPersonasFor(node.kind);
91
- if (subs.length === 0)
92
- return '';
93
- const lines = subs.map((s) => (s.whenToUse ? `${s.kind} — ${s.whenToUse}` : s.kind)).join('\n');
94
- return stateBlock('sub-personas', { for: node.kind, count: subs.length }, lines);
95
- }
96
- catch {
97
- return '';
98
- }
74
+ return stateBlock('kinds', { count: kinds.length }, lines);
99
75
  }
100
76
  // ---------------------------------------------------------------------------
101
77
  // node new — spawn a terminal worker as a detached headless broker under the root
@@ -110,14 +86,14 @@ const nodeNew = defineLeaf({
110
86
  summary: 'spawn a terminal worker onto the canvas as a detached broker — returns its node id',
111
87
  params: [
112
88
  { kind: 'stdin', name: 'prompt', required: true, constraint: 'First user message for the spawned node. Deliver it on stdin from a quoted heredoc (`<<\'EOF\'`) or a file (`< prompt.md`).' },
113
- { kind: 'flag', name: 'kind', type: 'string', required: false, default: 'general', constraint: 'Persona kind — match the work to the kind. The <kinds> list below names every installable kind and when to use each; the <sub-personas> block (when present) names the specialist sub-personas available to YOU, each spawnable by its full kind string (e.g. plan/reviewers/security).' },
89
+ { kind: 'flag', name: 'kind', type: 'string', required: false, default: 'general', constraint: 'Persona kind — match the work to the kind. The <kinds> list below names every installable kind and when to use each. For an exact composed persona list, use `crtr sys prompt-review --list --kind <kind>`.' },
114
90
  { kind: 'flag', name: 'mode', type: 'enum', choices: ['base', 'orchestrator'], required: false, default: 'base', constraint: 'Persona mode. base for a worker that finishes in one window; orchestrator to create the child directly as a sub-orchestrator (it boots with the orchestrator persona + a seeded roadmap and fans its scope out) — use it when the unit is too large for one window, e.g. a big review, instead of spawning a base worker and counting on it to promote itself.' },
115
91
  { kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Dir the node is pinned to. Defaults to the caller cwd.' },
116
92
  { kind: 'flag', name: 'name', type: 'string', required: false, constraint: 'Display name (tmux window + resume picker). Defaults to the kind.' },
117
93
  { kind: 'flag', name: 'parent', type: 'string', required: false, constraint: 'Parent node id. Defaults to the calling node (CRTR_NODE_ID).' },
118
94
  { kind: 'flag', name: 'root', type: 'bool', required: false, constraint: 'Spawn an INDEPENDENT root instead of a managed child: no parent (top-level on the canvas), NO subscription back to you (you are NOT woken by it), resident lifecycle. It records spawned_by=you for provenance and is brought forefront so it can be driven directly. Use for a node you hand off and do not manage (e.g. a sub-orchestrator a human will discuss with).' },
119
95
  { kind: 'flag', name: 'fork-from', type: 'string', required: false, constraint: 'FORK the new node from an existing pi conversation instead of starting it fresh: pass a node id (forks from that node\'s session), an absolute session `.jsonl` path, or a partial pi session uuid. pi copies that whole history into a NEW session for the child (the source is untouched), then the prompt is delivered as the next message — i.e. the child wakes up as a continuation of that conversation. Use to branch exploratory work off a node that already built up the context you need, instead of re-deriving it. One-shot at birth: the fork resumes its own session thereafter.' },
120
- { kind: 'flag', name: 'model', type: 'enum', choices: ['ultra', 'strong', 'medium', 'light'], required: false, constraint: 'Override the model the node runs on, by capability TIER: ultra (frontier), strong (opus), medium (sonnet), light (haiku). Omit to use the kind\'s persona default (explore=light, every other kind=strong). The override is durable — it survives revives and polymorphs (promote/demote).' },
96
+ { kind: 'flag', name: 'model', type: 'enum', choices: ['ultra', 'strong', 'medium', 'light'], required: false, constraint: 'Override the model the node runs on, by capability TIER: ultra (frontier), strong (opus), medium (sonnet), light (haiku). Omit to use the kind\'s persona default (advisor=ultra, explore=light, most other builtins=strong). The override is durable — it survives revives and polymorphs (promote/demote).' },
121
97
  ],
122
98
  output: [
123
99
  { name: 'node_id', type: 'string', required: true, constraint: 'The new node id.' },
@@ -148,7 +124,7 @@ const nodeNew = defineLeaf({
148
124
  const root = input['root'] === true;
149
125
  const forkFrom = input['forkFrom'];
150
126
  const model = input['model'];
151
- const res = spawnChild({ kind, mode, cwd, name, prompt, parent, root, forkFrom, model });
127
+ const res = await spawnChild({ kind, mode, cwd, name, prompt, parent, root, forkFrom, model });
152
128
  return {
153
129
  node_id: res.node.node_id,
154
130
  name: res.node.name,
@@ -175,24 +151,67 @@ const nodeNew = defineLeaf({
175
151
  const nodeList = defineLeaf({
176
152
  name: 'list',
177
153
  description: 'list nodes on the canvas',
178
- whenToUse: 'you want a flat roster of the nodes on the canvas, optionally sliced by status: a quick read of what exists and what is still running. Use `node inspect show` instead to drill into one node and its spine neighbors, `canvas dashboard` for the tree SHAPE, and `canvas attention` to find which nodes are blocked on a human',
154
+ whenToUse: 'you want a flat roster of the nodes on the canvas, sliced by any combination of attribute filters (status, kind, mode, lifecycle, cwd, hanging) and graph scope (--under a node, for that node plus its subscription descendants). The filters AND together, so "every human node in my subtree" is `--kind human --under <me>` and "every parked orchestrator" is `--mode orchestrator --hanging`. Use `node inspect show` instead to drill into one node and its spine neighbors, `canvas dashboard` for the tree SHAPE, and `canvas attention` to find which nodes are blocked on a human',
179
155
  help: {
180
156
  name: 'node inspect list',
181
- summary: 'list nodes on the canvas, optionally by status',
157
+ summary: 'list nodes on the canvas, filtered by any combination of attribute slices and a graph-scope (--under) that AND together',
182
158
  params: [
183
159
  { kind: 'flag', name: 'status', type: 'string', required: false, constraint: 'Filter: active | idle | done | dead | canceled. Comma-separated for several. NOTE: `active` means the engine process is live (never closed), NOT that the node is generating right now — an active node is usually dormant between turns. To tell working-vs-idle, check the pi session-file mtime or CPU, not status.' },
160
+ { kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Filter by persona kind (general, explore, developer, design, plan, review, spec, human, …). Comma-separated for several (OR within the flag).' },
161
+ { kind: 'flag', name: 'mode', type: 'string', required: false, constraint: 'Filter by mode: base | orchestrator. Comma-separated for several.' },
162
+ { kind: 'flag', name: 'lifecycle', type: 'string', required: false, constraint: 'Filter by lifecycle: terminal | resident. Comma-separated for several.' },
163
+ { kind: 'flag', name: 'cwd', type: 'string', required: false, constraint: 'Filter to nodes pinned to this exact cwd (absolute path). Roster is canvas-wide (all cwds) by default.' },
164
+ { kind: 'flag', name: 'under', type: 'string', required: false, constraint: 'Graph scope: restrict to this node and its subscription descendants (the sub-DAG reachable downward — its reports, theirs, …). This is "one initiative / my subtree". Combine with attribute filters, e.g. `--kind human --under <me>`.' },
165
+ { kind: 'flag', name: 'hanging', type: 'bool', required: false, constraint: 'Only nodes PARKED on an active fault (live broker + marker when provider-owned). Use to find stuck/rate-limited nodes awaiting auto-revive.' },
184
166
  ],
185
167
  output: [
186
- { name: 'nodes', type: 'object[]', required: true, constraint: 'Rows: {node_id, name, kind, mode, lifecycle, status, cwd, parent, created}.' },
168
+ { name: 'nodes', type: 'object[]', required: true, constraint: 'Rows: {node_id, name, kind, mode, lifecycle, status, cwd, parent, created, hanging}. `hanging` is null normally, or {kind, message, since} when the node is PARKED on an active fault (rate-limit/overloaded/connection/other); the canvas-graph views render it with ⚠ + a countdown or state cue.' },
187
169
  ],
188
170
  outputKind: 'object',
189
- effects: ['Read-only: queries canvas.db.'],
171
+ effects: ['Read-only: queries canvas.db (+ an active-fault marker read).'],
190
172
  },
191
173
  run: async (input) => {
192
- const raw = input['status'];
193
- const status = raw !== undefined && raw !== '' ? raw.split(',').map((s) => s.trim()) : undefined;
194
- const nodes = listNodes(status !== undefined ? { status } : undefined);
195
- return { nodes };
174
+ const csv = (k) => {
175
+ const raw = input[k];
176
+ return raw !== undefined && raw !== '' ? raw.split(',').map((s) => s.trim()).filter((s) => s !== '') : undefined;
177
+ };
178
+ const status = csv('status');
179
+ const kinds = csv('kind');
180
+ const modes = csv('mode');
181
+ const lifecycles = csv('lifecycle');
182
+ const cwd = input['cwd'];
183
+ const under = input['under'];
184
+ const hangingOnly = input['hanging'] === true;
185
+ // Graph scope (--under): self + subscription descendants. Validate the anchor
186
+ // so a typo'd id is a structured error, not a silently-empty roster.
187
+ let scope;
188
+ if (under !== undefined && under !== '') {
189
+ if (getNode(under) === null) {
190
+ throw new InputError({ error: 'not_found', message: `no node: ${under}`, next: 'List nodes with `crtr node inspect list`, then pass a real id to --under.' });
191
+ }
192
+ scope = new Set([under, ...view(under)]);
193
+ }
194
+ const rows = listNodes(status !== undefined ? { status } : undefined).filter((row) => {
195
+ if (scope !== undefined && !scope.has(row.node_id))
196
+ return false;
197
+ if (kinds !== undefined && !kinds.includes(row.kind))
198
+ return false;
199
+ if (modes !== undefined && !modes.includes(row.mode))
200
+ return false;
201
+ if (lifecycles !== undefined && !lifecycles.includes(row.lifecycle))
202
+ return false;
203
+ if (cwd !== undefined && cwd !== '' && row.cwd !== cwd)
204
+ return false;
205
+ return true;
206
+ });
207
+ // Enrich each row with its active-fault state. Daemon-owned provider faults
208
+ // are suppressed when the broker pid is gone; manual/client faults stay
209
+ // visible until cleared.
210
+ const nodes = await Promise.all(rows.map(async (row) => {
211
+ const hanging = activeFaultForDisplay(row, await readFaultAsync(row.node_id));
212
+ return { ...row, hanging };
213
+ }));
214
+ return { nodes: nodes.filter((n) => !hangingOnly || n.hanging !== null) };
196
215
  },
197
216
  });
198
217
  // ---------------------------------------------------------------------------
@@ -282,10 +301,10 @@ const nodeFocus = defineLeaf({
282
301
  // (revive it if dormant), then open/move its one viewer pane beside the
283
302
  // caller (or split a fresh viewer with --new-pane). The reviver is injected
284
303
  // so placement need not import revive.ts.
285
- const res = placementFocus(id, {
304
+ const res = await placementFocus(id, {
286
305
  pane: input['pane'],
287
306
  newPane: input['newPane'] === true,
288
- revive: (nid) => { reviveNode(nid, { resume: true }); },
307
+ revive: (nid) => reviveNode(nid, { resume: true }),
289
308
  });
290
309
  return { focused: res.focused, session: res.session, revived: res.revived, in_place: res.inPlace };
291
310
  },
@@ -439,6 +458,74 @@ const nodeClose = defineLeaf({
439
458
  },
440
459
  });
441
460
  // ---------------------------------------------------------------------------
461
+ // node model — headless live model/provider swap for a node (no viewer attach)
462
+ // ---------------------------------------------------------------------------
463
+ const nodeModel = defineLeaf({
464
+ name: 'model',
465
+ description: 'swap a node\'s model/provider headlessly (durable, survives revives)',
466
+ whenToUse: 'you want to change which model (and thus provider) a node runs on WITHOUT opening its viewer — flip a running node to a stronger/cheaper model or a different provider, or repin a dormant node\'s model before its next revive. The choice is durable: it survives revives and polymorphs. This is the headless twin of the in-viewer `/model` slash command; reach for `node promote --model` instead when you are also changing a node\'s role',
467
+ help: {
468
+ name: 'node model',
469
+ summary: 'swap a node\'s model headlessly — if its broker is live, drive the switch over view.sock (no viewer attached); if dormant, repin its durable recipe for the next revive. The choice persists across revives either way.',
470
+ params: [
471
+ { kind: 'positional', name: 'spec', required: true, constraint: 'Model query: an exact `provider/id` (e.g. `openai-codex/gpt-5.5`), a capability tier (`ultra|strong|medium|light`), a bare family alias (`opus|sonnet|haiku`), or a free-text substring matched against the live registry. A free-text substring only resolves against a LIVE broker; a dormant node needs a concrete `provider/id` (or a tier/alias).' },
472
+ { kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node to repoint. Defaults to the node occupying --pane (or your current pane).' },
473
+ { kind: 'flag', name: 'pane', type: 'string', required: false, constraint: 'tmux pane id whose node to repoint. Defaults to $TMUX_PANE / your current pane.' },
474
+ ],
475
+ output: [
476
+ { name: 'node_id', type: 'string', required: true, constraint: 'The node whose model was swapped.' },
477
+ { name: 'model', type: 'string', required: true, constraint: 'The resolved model — a concrete `provider/id` for a live broker (registry-resolved) or the normalized recipe value for a dormant node.' },
478
+ { name: 'applied', type: 'string', required: true, constraint: '"live" (the running broker switched immediately and persisted it) or "next-revive" (the dormant node\'s recipe was repinned; takes effect when it next revives).' },
479
+ ],
480
+ outputKind: 'object',
481
+ effects: [
482
+ 'LIVE broker: connects to the node\'s view.sock, claims controller, and drives `set_model` on the running engine; the broker persists the choice (model_override + launch.model) itself.',
483
+ 'DORMANT node: writes model_override + launch.model directly into the node\'s durable recipe — no engine runs until its next revive.',
484
+ ],
485
+ },
486
+ run: async (input) => {
487
+ const pane = input['pane'] ?? process.env['TMUX_PANE'];
488
+ let id = input['node'];
489
+ if (id === undefined || id === '')
490
+ id = nodeInPane(pane);
491
+ if (id === undefined || id === '') {
492
+ throw new InputError({ error: 'no_node', message: 'no node found in this pane to repoint', next: 'Pass --node <id>, or run from inside the agent\'s pane.' });
493
+ }
494
+ const meta = getNode(id);
495
+ if (meta === null) {
496
+ throw new InputError({ error: 'not_found', message: `no node: ${id}`, next: 'List nodes with `crtr node inspect list`.' });
497
+ }
498
+ const spec = (input['spec'] ?? '').trim();
499
+ if (spec === '') {
500
+ throw new InputError({ error: 'empty_spec', message: 'a model spec is required', field: 'spec', next: 'Pass a `provider/id`, a tier (ultra|strong|medium|light), an alias (opus|sonnet|haiku), or a substring.' });
501
+ }
502
+ if (isBrokerLive(meta)) {
503
+ // Live: drive the running engine over its socket. The broker resolves the
504
+ // query against the live registry (substrings included) and persists.
505
+ let model;
506
+ try {
507
+ model = await setModelLive(id, spec);
508
+ }
509
+ catch (err) {
510
+ throw new InputError({ error: 'switch_failed', message: `live model switch failed: ${err instanceof Error ? err.message : String(err)}`, next: 'Check the spec resolves (`provider/id`, tier, alias, or a substring of a registered model).' });
511
+ }
512
+ return { node_id: id, model, applied: 'live' };
513
+ }
514
+ // Dormant: no live registry to search, so normalizeModel only maps
515
+ // tiers/aliases. A bare substring that isn't a tier/alias can't resolve to a
516
+ // concrete id offline — reject rather than write a meaningless override.
517
+ const normalized = normalizeModel(spec);
518
+ if (!normalized.includes('/')) {
519
+ throw new InputError({ error: 'unresolvable_dormant', message: `'${spec}' does not resolve to a concrete model for a dormant node`, field: 'spec', next: 'Pass an exact `provider/id` (e.g. openai-codex/gpt-5.5) or a tier/alias — the live registry search is only available while the node\'s broker is up.' });
520
+ }
521
+ persistDormantModel(id, normalized);
522
+ return { node_id: id, model: normalized, applied: 'next-revive' };
523
+ },
524
+ render: (r) => r['applied'] === 'live'
525
+ ? `Switched ${r['node_id']} to ${r['model']} (live — applied now and persisted).`
526
+ : `Repinned ${r['node_id']} to ${r['model']} (applies on next revive).`,
527
+ });
528
+ // ---------------------------------------------------------------------------
442
529
  // node cycle — DFS-walk the canvas one window at a time (Alt+] / Alt+[)
443
530
  // ---------------------------------------------------------------------------
444
531
  /** Every live node in DFS pre-order across the whole forest. The spawn tree is
@@ -513,9 +600,9 @@ const nodeCycle = defineLeaf({
513
600
  return { focused: false, from: fromId };
514
601
  // Placement brings the neighbor's viewer forefront beside the caller,
515
602
  // relaunching its broker engine first if it was dormant.
516
- const res = placementFocus(targetId, {
603
+ const res = await placementFocus(targetId, {
517
604
  pane,
518
- revive: (nid) => { reviveNode(nid, { resume: true }); },
605
+ revive: (nid) => reviveNode(nid, { resume: true }),
519
606
  });
520
607
  return { focused: res.focused, node_id: targetId, name: target.name, from: fromId };
521
608
  },
@@ -556,13 +643,15 @@ const nodeMsg = defineLeaf({
556
643
  throw new InputError({ error: 'empty_body', message: 'a message body is required', field: 'body', next: 'Pass the message after the node id or on stdin.' });
557
644
  const tier = (input['tier'] ?? 'normal');
558
645
  const from = process.env['CRTR_NODE_ID'] ?? 'human';
646
+ // A long body clips in the receiver's digest; appendInbox mints a short `id`
647
+ // for it so the receiver can read the full text back via `feed message <id>`.
559
648
  appendInbox(id, { from, tier, kind: 'message', label: body.split('\n')[0].slice(0, 120), data: { body } });
560
649
  // A direct message wakes any node: if the target has no live window
561
650
  // (done/dead/idle-released), revive it so its inbox-watcher delivers this.
562
651
  let woke = false;
563
652
  if (!windowAlive(target.tmux_session, target.window)) {
564
653
  try {
565
- reviveNode(id, { resume: true });
654
+ void reviveNode(id, { resume: true });
566
655
  woke = true;
567
656
  }
568
657
  catch { /* best-effort wake */ }
@@ -657,7 +746,7 @@ const nodePromote = defineLeaf({
657
746
  name: 'node promote',
658
747
  summary: 'promote yourself to an orchestrator — do this when your task outgrows one context window (many phases to delegate and persist across refreshes); not for work that fits one window, and not merely because you spawned a child. Mode only — lifecycle stays as-is unless you pass --resident',
659
748
  params: [
660
- { kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Specialize as this kind of orchestrator. The <kinds> list below names every installable kind and when to use each; the <sub-personas> block (when present) names the specialist sub-personas available to YOU, spawnable by full kind string. Defaults to your current kind. Promoting from a generic kind? CHOOSE a concrete one — it sets the orchestrator persona you revive into.' },
749
+ { kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Specialize as this kind of orchestrator. The <kinds> list below names every installable kind and when to use each. For an exact composed persona list, use `crtr sys prompt-review --list --kind <kind>` with the target kind. Defaults to your current kind. Promoting from a generic kind? CHOOSE a concrete one — it sets the orchestrator persona you revive into.' },
661
750
  { kind: 'flag', name: 'resident', type: 'bool', required: false, constraint: 'ALSO flip lifecycle→resident: make the node interactable — it stays dormant, woken by inbox/human, and is never forced to submit a final. Omit to stay terminal/orchestrator (delegates + holds a roadmap, but still owes a final up the spine and reaps when done).' },
662
751
  { kind: 'flag', name: 'model', type: 'enum', choices: ['ultra', 'strong'], required: false, constraint: 'Change the model you run on, by capability tier: `ultra` (frontier — reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus — regular dev work). Omit to keep your current model. An orchestrator steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives.' },
663
752
  { kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node to promote. Defaults to the caller (CRTR_NODE_ID).' },
@@ -1335,9 +1424,96 @@ const nodeWake = defineBranch({
1335
1424
  },
1336
1425
  children: [nodeWakeAt, nodeWakeUntil, nodeWakeSpawn, nodeWakeList, nodeWakeCancel],
1337
1426
  });
1427
+ // ── /pyramid slash command ───────────────────────────────────────────────────
1428
+ // Rides on the `node` subtree, not because pyramid is a `node` verb, but because
1429
+ // (1) the workflow IS node orchestration — it fans out explore agents — and
1430
+ // (2) provisionExports lazy-loads only the invoked subtree (build-root.ts), so a
1431
+ // slash spec only gets (re)written to the host's command dirs when its host
1432
+ // subtree runs; `node` is the most-invoked subtree, so `/pyramid` provisions
1433
+ // reliably. The body is a self-contained agent workflow prompt (pi prompt
1434
+ // template / Claude Code command), exported one-way by host-exports.
1435
+ const pyramidSlash = {
1436
+ name: 'pyramid',
1437
+ description: 'Explain a topic as a live-building Minto pyramid diagram, backed by parallel explore agents.',
1438
+ argumentHint: '<topic to explain>',
1439
+ body: [
1440
+ '# Pyramid explanation',
1441
+ '',
1442
+ 'Explain **$ARGUMENTS** as a *pyramid*: lead with the answer, support it with a few key pillars, ground each pillar in evidence. You build this as a LIVE termrender diagram that fills in as parallel research lands — the user watches the skeleton grow into a complete picture.',
1443
+ '',
1444
+ '## The pyramid (Minto principle)',
1445
+ '- **Apex** — ONE crisp sentence: the single governing answer/claim, the thing worth remembering.',
1446
+ '- **Pillars** — 2–4 MECE supporting arguments that together prove the apex.',
1447
+ '- **Base** — concrete evidence under each pillar (mechanisms, `file:line` refs, numbers, sources).',
1448
+ '',
1449
+ 'Top-down: the reader gets the conclusion first, then descends for as much support as they want.',
1450
+ '',
1451
+ '## Workflow',
1452
+ '',
1453
+ '1. **Frame it.** Restate the topic in one line and commit to a *working* apex hypothesis (you will sharpen it). Name the 2–4 pillars you expect to need.',
1454
+ '',
1455
+ '2. **Scaffold the live diagram FIRST.** Write the termrender skeleton below to `./pyramid-<slug>.md` (apex = your hypothesis, pillars = titles, every base entry marked `[?]` pending). Then open it live so the user watches it build:',
1456
+ ' ```bash',
1457
+ ' termrender pane open ./pyramid-<slug>.md --watch # detached tmux side-pane; re-renders on every save',
1458
+ ' ```',
1459
+ ' Not in tmux? Tell the user to run `termrender doc watch ./pyramid-<slug>.md` in another terminal, then continue regardless.',
1460
+ '',
1461
+ '3. **Investigate in parallel — do not block.** Fan out ONE explore agent per pillar/unknown AND dig into what you can answer yourself, concurrently:',
1462
+ ' ```bash',
1463
+ ' crtr node new "Investigate: <focused question for this pillar>. Report findings with sources / file:line refs." --kind explore',
1464
+ ' ```',
1465
+ ' You auto-subscribe; each finish wakes you. Never poll or wait — kick them all off, then start filling in what you already know.',
1466
+ '',
1467
+ '4. **Live-update as findings land.** Every time you learn something — your own digging or an explore report — EDIT the diagram file: flip a `[?]` to `[x]` (or `[!]` for a caveat/risk), add evidence under the right pillar, sharpen or rewrite the apex, drop a pillar that did not hold. Each save re-renders the pane. The diagram visibly grows skeleton → complete.',
1468
+ '',
1469
+ '5. **Land it.** When the apex is one crisp sentence, the pillars are MECE, and the base is concrete, do a final pass for accuracy, then give the user a 3–4 sentence spoken summary that mirrors apex → pillars. Tell them the diagram file path.',
1470
+ '',
1471
+ '## termrender skeleton (write this verbatim, then keep editing it)',
1472
+ '',
1473
+ 'Apex panel on top, a divider, then the pillars as a row of boxes below — a literal pyramid: 1 claim over N supports.',
1474
+ '```markdown',
1475
+ ':::panel{title="<TOPIC>" color="cyan"}',
1476
+ '**<one-line apex answer — your governing claim>**',
1477
+ ':::',
1478
+ '',
1479
+ ':::divider{label="why"}',
1480
+ ':::',
1481
+ '',
1482
+ '::::::columns',
1483
+ ':::::col{width="33%"}',
1484
+ '::::panel{title="Pillar 1 — <claim>" color="green"}',
1485
+ ':::tree',
1486
+ 'evidence A [x]',
1487
+ 'evidence B [?]',
1488
+ ':::',
1489
+ '::::',
1490
+ ':::::',
1491
+ ':::::col{width="33%"}',
1492
+ '::::panel{title="Pillar 2 — <claim>" color="green"}',
1493
+ ':::tree',
1494
+ 'evidence C [?]',
1495
+ ':::',
1496
+ '::::',
1497
+ ':::::',
1498
+ ':::::col{width="34%"}',
1499
+ '::::panel{title="Pillar 3 — <claim>" color="green"}',
1500
+ ':::tree',
1501
+ 'evidence D [?]',
1502
+ ':::',
1503
+ '::::',
1504
+ ':::::',
1505
+ '::::::',
1506
+ '```',
1507
+ '',
1508
+ '**Nesting rule (termrender will reject the file otherwise): an OUTER directive needs strictly MORE colons than the directive nested inside it.** Hence `::::::columns` (6) wraps `:::::col` (5) wraps `::::panel` (4) wraps `:::tree` (3). Every block closes with a bare run of its OWN colon count on its own line. Run `termrender doc check ./pyramid-<slug>.md` after each edit — if it errors, fix the colon counts before re-saving.',
1509
+ '',
1510
+ 'Markers inside a tree: `[x]` → ✔ confirmed, `[!]` → ⚠ caveat/risk; `[?]` is not a marker and stays literal — use it for still-pending. Write structure only — termrender draws every box, border, and guide line; never hand-draw ASCII. Keep the apex to one sentence; push all detail into the base.',
1511
+ ].join('\n'),
1512
+ };
1338
1513
  export function registerNode() {
1339
1514
  return defineBranch({
1340
1515
  name: 'node',
1516
+ slash: pyramidSlash,
1341
1517
  rootEntry: {
1342
1518
  concept: 'a unit of the agent runtime — an agent on the canvas with its own context dir and pi vehicle. Spawning nodes is how work gets done here, not an optimization you reach for late',
1343
1519
  desc: 'spawn, inspect, and navigate nodes on the canvas',
@@ -1351,6 +1527,6 @@ export function registerNode() {
1351
1527
  'HOW: `crtr node new "<task>" --kind <kind>` returns a node id immediately and runs the worker as a detached headless broker (no window opens; open a viewer on demand with `crtr node focus`). Match the kind to the work (see `node new -h`). You are woken when a child finishes — the wake message ALREADY IS the coalesced digest (the watcher drains your inbox to wake you), so don\'t re-run `crtr feed read` to "open" it (it would read empty, the cursor already advanced); instead dereference the report paths in that digest that matter, don\'t act on a one-line label. (`crtr feed read` is for proactively polling before a wake, or inspecting a child\'s inbox via `--node`; `--all` re-reads history with full message bodies.) Integrate, then either delegate the next units or finish.\n\n' +
1352
1528
  'FINISH: a worker ends its own work with `crtr push final "<result>"` (writes the canonical result, marks done, closes the window) — stopping without it is not finishing. For a job too big for one context window, `node promote` to an orchestrator (holds a roadmap, delegates phases); when context fills, `node yield` to refresh against that roadmap.',
1353
1529
  },
1354
- children: [nodeNew, nodeInspect, nodeFocus, nodeCycle, nodeRecycle, nodeClose, nodeMsg, nodeSubscribe, nodeUnsubscribe, nodePromote, nodeDemote, nodeLifecycle, nodeYield, nodeWake],
1530
+ children: [nodeNew, nodeInspect, nodeSnapshotLeaf, nodeContextLeaf, nodeFocus, nodeCycle, nodeRecycle, nodeClose, nodeMsg, nodeModel, nodeSubscribe, nodeUnsubscribe, nodePromote, nodeDemote, nodeLifecycle, nodeYield, nodeWake],
1355
1531
  });
1356
1532
  }
@@ -94,7 +94,7 @@ const pluginShow = defineLeaf({
94
94
  { name: 'path', type: 'string', required: true, constraint: 'Absolute path to the plugin directory.' },
95
95
  { name: 'enabled', type: 'boolean', required: true, constraint: 'Whether the plugin is active.' },
96
96
  { name: 'manifest', type: 'object', required: true, constraint: 'Full plugin.json contents.' },
97
- { name: 'skills', type: 'object[]', required: true, constraint: 'Each: {name, path}. Substrate docs provided by the plugin (its `<pluginName>/` memory subtree).' },
97
+ { name: 'docs', type: 'object[]', required: true, constraint: 'Each: {name, path}. Memory docs provided by the plugin (its `<pluginName>/` memory subtree).' },
98
98
  ],
99
99
  outputKind: 'object',
100
100
  effects: ['None. Read-only.'],
@@ -119,16 +119,16 @@ const pluginShow = defineLeaf({
119
119
  throw notFound(`plugin not found: ${name}`);
120
120
  }
121
121
  const prefix = `${found.name}/`;
122
- const skills = listAllMemoryDocs().filter((d) => d.name.startsWith(prefix));
122
+ const docs = listAllMemoryDocs().filter((d) => d.name.startsWith(prefix));
123
123
  return {
124
124
  name: found.name,
125
125
  scope: found.scope,
126
126
  path: found.root,
127
127
  enabled: found.enabled,
128
128
  manifest: found.manifest,
129
- skills: skills.map((s) => ({
130
- name: s.name,
131
- path: s.path,
129
+ docs: docs.map((d) => ({
130
+ name: d.name,
131
+ path: d.path,
132
132
  })),
133
133
  };
134
134
  },
@@ -136,7 +136,7 @@ const pluginShow = defineLeaf({
136
136
  export const pluginInspectBranch = defineBranch({
137
137
  name: 'inspect',
138
138
  description: 'list or show installed plugins',
139
- whenToUse: 'reading installed plugin metadata to decide before you change anything — list what is installed, or show the manifest and skills of one plugin. Read-only; switch to `pkg plugin manage` to actually install, enable, disable, or remove',
139
+ whenToUse: 'reading installed plugin metadata to decide before you change anything — list what is installed, or show the manifest and memory docs of one plugin. Read-only; switch to `pkg plugin manage` to actually install, enable, disable, or remove',
140
140
  help: {
141
141
  name: 'pkg plugin inspect',
142
142
  summary: 'read plugin metadata without modifying state',
@@ -146,7 +146,7 @@ const pluginRemove = defineLeaf({
146
146
  const pluginEnable = defineLeaf({
147
147
  name: 'enable',
148
148
  description: 'enable a plugin',
149
- whenToUse: 'reactivating a plugin that was previously disabled so its skills resolve again',
149
+ whenToUse: 'reactivating a plugin that was previously disabled so its memory docs resolve again',
150
150
  help: {
151
151
  name: 'pkg plugin manage enable',
152
152
  summary: 'enable a plugin in the given scope',
@@ -7,8 +7,8 @@ export const pluginBranch = defineBranch({
7
7
  whenToUse: 'working with individual plugins directly — installing one from a git URL, inspecting or showing what a plugin provides, or enabling, disabling, removing, and updating an installed plugin. Use `pkg market` instead when you want a curated collection: browsing a marketplace index and installing plugins by name from it rather than handling raw git URLs yourself',
8
8
  help: {
9
9
  name: 'pkg plugin',
10
- summary: 'install and manage plugins that extend crtr with skills',
11
- model: 'Plugins are git repos or local directories containing a .crouter-plugin/plugin.json manifest and a skills/ directory.',
10
+ summary: 'install and manage plugins that extend crtr with memory docs',
11
+ model: 'Plugins are git repos or local directories containing a .crouter-plugin/plugin.json manifest and a memory/ directory.',
12
12
  },
13
13
  children: [pluginManageBranch, pluginInspectBranch],
14
14
  });
@@ -8,9 +8,9 @@ export function registerPkg() {
8
8
  return defineBranch({
9
9
  name: 'pkg',
10
10
  rootEntry: {
11
- concept: 'plugins and marketplaces that supply skills',
11
+ concept: 'plugins and marketplaces that supply memory docs',
12
12
  desc: 'manage plugins and marketplaces',
13
- useWhen: 'installing or browsing skill collections',
13
+ useWhen: 'installing or browsing memory-doc collections',
14
14
  },
15
15
  help: {
16
16
  name: 'pkg',
@@ -1,3 +1,5 @@
1
1
  import type { BranchDef } from '../core/command.js';
2
+ import { InputError } from '../core/io.js';
3
+ export declare function mapFinalizeTransitionRaceError(nodeStatus: string | null | undefined, err: unknown): InputError | null;
2
4
  export declare function registerPush(): BranchDef;
3
5
  export declare function registerFeed(): BranchDef;