@crouton-kit/crouter 0.3.27 → 0.3.29

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 (343) hide show
  1. package/dist/build-root.js +4 -2
  2. package/dist/builtin-memory/crouter-development/marketplaces.md +7 -6
  3. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +1 -1
  4. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +1 -1
  5. package/dist/builtin-memory/crouter-development/personas.md +1 -1
  6. package/dist/builtin-memory/crouter-development/plugins.md +20 -18
  7. package/dist/builtin-memory/design.md +1 -1
  8. package/dist/builtin-memory/development.md +1 -1
  9. package/dist/builtin-memory/internal/INDEX.md +1 -1
  10. package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
  12. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  13. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  14. package/dist/builtin-memory/planning.md +1 -1
  15. package/dist/builtin-memory/spec.md +6 -4
  16. package/dist/builtin-personas/orchestration-kernel.md +4 -5
  17. package/dist/builtin-personas/runtime-base.md +8 -4
  18. package/dist/builtin-personas/spec/PERSONA.md +8 -4
  19. package/dist/builtin-personas/spec/orchestrator.md +5 -3
  20. package/dist/builtin-personas/spec/requirements/PERSONA.md +10 -0
  21. package/dist/builtin-views/canvas/core.mjs +586 -0
  22. package/dist/builtin-views/canvas/text.mjs +58 -0
  23. package/dist/builtin-views/canvas/tui.mjs +165 -0
  24. package/dist/builtin-views/canvas/web.jsx +120 -0
  25. package/dist/builtin-views/git-pr/core.mjs +673 -0
  26. package/dist/builtin-views/git-pr/text.mjs +84 -0
  27. package/dist/builtin-views/git-pr/tui.mjs +302 -0
  28. package/dist/builtin-views/git-pr/web.jsx +216 -0
  29. package/dist/builtin-views/inbox/_lib/render.mjs +1 -3
  30. package/dist/builtin-views/inbox/core.mjs +1273 -0
  31. package/dist/builtin-views/inbox/text.mjs +73 -0
  32. package/dist/builtin-views/inbox/tui.mjs +312 -0
  33. package/dist/builtin-views/inbox/web.jsx +188 -0
  34. package/dist/builtin-views/linkedin/core.mjs +906 -0
  35. package/dist/builtin-views/linkedin/text.mjs +69 -0
  36. package/dist/builtin-views/linkedin/tui.mjs +427 -0
  37. package/dist/builtin-views/linkedin/web.jsx +206 -0
  38. package/dist/builtin-views/workspace-sidebar/core.mjs +621 -0
  39. package/dist/builtin-views/workspace-sidebar/text.mjs +53 -0
  40. package/dist/builtin-views/workspace-sidebar/tui.mjs +142 -0
  41. package/dist/builtin-views/workspace-sidebar/web.jsx +109 -0
  42. package/dist/cli.js +3 -3
  43. package/dist/clients/attach/__tests__/action-parity.test.js +48 -0
  44. package/dist/clients/attach/__tests__/clipboard-image.test.js +76 -0
  45. package/dist/clients/attach/__tests__/editor-newline.test.js +57 -0
  46. package/dist/clients/attach/__tests__/git-info.test.js +31 -0
  47. package/dist/clients/attach/__tests__/onrequest-wired.test.js +20 -0
  48. package/dist/clients/attach/__tests__/reconnect-giveup.test.js +30 -0
  49. package/dist/clients/attach/__tests__/slash-quit-copy.test.js +42 -0
  50. package/dist/clients/attach/__tests__/titled-editor.test.d.ts +1 -0
  51. package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
  52. package/dist/clients/attach/attach-cmd.js +445 -41
  53. package/dist/clients/attach/auth-pickers.d.ts +9 -0
  54. package/dist/clients/attach/auth-pickers.js +194 -0
  55. package/dist/clients/attach/canvas-panels.d.ts +12 -0
  56. package/dist/clients/attach/canvas-panels.js +78 -0
  57. package/dist/clients/attach/chat-view.d.ts +27 -2
  58. package/dist/clients/attach/chat-view.js +98 -9
  59. package/dist/clients/attach/clipboard-image.d.ts +47 -8
  60. package/dist/clients/attach/clipboard-image.js +150 -26
  61. package/dist/clients/attach/clipboard-text.d.ts +3 -0
  62. package/dist/clients/attach/clipboard-text.js +39 -0
  63. package/dist/clients/attach/config-load.d.ts +54 -7
  64. package/dist/clients/attach/config-load.js +99 -3
  65. package/dist/clients/attach/context-message.d.ts +12 -0
  66. package/dist/clients/attach/context-message.js +72 -0
  67. package/dist/clients/attach/git-info.d.ts +20 -0
  68. package/dist/clients/attach/git-info.js +44 -0
  69. package/dist/clients/attach/graph-overlay.d.ts +43 -0
  70. package/dist/clients/attach/graph-overlay.js +306 -0
  71. package/dist/clients/attach/input-controller.d.ts +81 -13
  72. package/dist/clients/attach/input-controller.js +307 -67
  73. package/dist/clients/attach/pickers.d.ts +58 -0
  74. package/dist/clients/attach/pickers.js +171 -0
  75. package/dist/clients/attach/slash-commands.d.ts +44 -1
  76. package/dist/clients/attach/slash-commands.js +250 -27
  77. package/dist/clients/attach/titled-editor.d.ts +33 -0
  78. package/dist/clients/attach/titled-editor.js +84 -0
  79. package/dist/clients/attach/view-socket.d.ts +37 -2
  80. package/dist/clients/attach/view-socket.js +131 -7
  81. package/dist/clients/web/dev-server.d.ts +7 -0
  82. package/dist/clients/web/dev-server.js +59 -0
  83. package/dist/clients/web/events.d.ts +14 -0
  84. package/dist/clients/web/events.js +151 -0
  85. package/dist/clients/web/server.d.ts +18 -0
  86. package/dist/clients/web/server.js +450 -0
  87. package/dist/clients/web/web-cmd.d.ts +2 -0
  88. package/dist/clients/web/web-cmd.js +120 -0
  89. package/dist/commands/canvas.js +1 -2
  90. package/dist/commands/chord.js +1 -1
  91. package/dist/commands/dashboard.js +5 -1
  92. package/dist/commands/human/prompts.js +3 -3
  93. package/dist/commands/human/queue.d.ts +17 -0
  94. package/dist/commands/human/queue.js +111 -4
  95. package/dist/commands/memory/__tests__/lint-schema.test.js +27 -4
  96. package/dist/commands/memory/lint.d.ts +5 -4
  97. package/dist/commands/memory/lint.js +23 -7
  98. package/dist/commands/memory/read.js +2 -2
  99. package/dist/commands/memory/shared.d.ts +7 -1
  100. package/dist/commands/memory/shared.js +18 -3
  101. package/dist/commands/memory/write.js +20 -7
  102. package/dist/commands/memory.js +4 -4
  103. package/dist/commands/node.d.ts +1 -1
  104. package/dist/commands/node.js +64 -65
  105. package/dist/commands/pkg.js +1 -2
  106. package/dist/commands/revive.js +11 -3
  107. package/dist/commands/sys/feedback.d.ts +1 -0
  108. package/dist/commands/sys/feedback.js +163 -0
  109. package/dist/commands/sys/sync.d.ts +1 -0
  110. package/dist/commands/sys/sync.js +187 -0
  111. package/dist/commands/sys.js +4 -2
  112. package/dist/commands/view-cycle.js +2 -2
  113. package/dist/commands/view-list.js +8 -8
  114. package/dist/commands/view-new.js +21 -17
  115. package/dist/commands/view-pick.js +1 -1
  116. package/dist/commands/view-run.js +35 -14
  117. package/dist/commands/view.js +8 -6
  118. package/dist/commands/workspace.d.ts +2 -0
  119. package/dist/commands/workspace.js +161 -0
  120. package/dist/core/__tests__/broker-double-spawn.test.d.ts +1 -0
  121. package/dist/core/__tests__/broker-double-spawn.test.js +143 -0
  122. package/dist/core/__tests__/broker-fork-seam.test.d.ts +1 -0
  123. package/dist/core/__tests__/broker-fork-seam.test.js +102 -0
  124. package/dist/core/__tests__/broker-sdk-wiring.test.js +2 -0
  125. package/dist/core/__tests__/broker-snapshot-history.test.d.ts +1 -0
  126. package/dist/core/__tests__/broker-snapshot-history.test.js +105 -0
  127. package/dist/core/__tests__/child-death-wake.test.js +56 -111
  128. package/dist/core/__tests__/close.test.js +14 -20
  129. package/dist/core/__tests__/context-intro.test.js +19 -19
  130. package/dist/core/__tests__/daemon-boot.test.js +30 -23
  131. package/dist/core/__tests__/draw-style.test.js +1 -1
  132. package/dist/core/__tests__/error-stall-recycle.test.d.ts +1 -0
  133. package/dist/core/__tests__/error-stall-recycle.test.js +141 -0
  134. package/dist/core/__tests__/fixtures/fake-engine.d.ts +24 -1
  135. package/dist/core/__tests__/fixtures/fake-engine.js +32 -2
  136. package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
  137. package/dist/core/__tests__/focuses.test.js +25 -13
  138. package/dist/core/__tests__/fork.test.js +22 -5
  139. package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
  140. package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
  141. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +1 -0
  142. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +57 -0
  143. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +1 -0
  144. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +80 -0
  145. package/dist/core/__tests__/full/broker-pane-resolution.test.js +90 -0
  146. package/dist/core/__tests__/full/cascade-close.test.js +22 -8
  147. package/dist/core/__tests__/full/detach-focus.test.js +66 -169
  148. package/dist/core/__tests__/helpers/harness.d.ts +6 -9
  149. package/dist/core/__tests__/helpers/harness.js +19 -36
  150. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +1 -0
  151. package/dist/core/__tests__/human-node-not-supervised.test.js +83 -0
  152. package/dist/core/__tests__/human-stranded-deliver.test.d.ts +1 -0
  153. package/dist/core/__tests__/human-stranded-deliver.test.js +108 -0
  154. package/dist/core/__tests__/lifecycle.test.js +1 -19
  155. package/dist/core/__tests__/live-mutation-verbs.test.js +5 -5
  156. package/dist/core/__tests__/memory-resolver.test.js +1 -1
  157. package/dist/core/__tests__/on-read-dedup-resume.test.d.ts +1 -0
  158. package/dist/core/__tests__/on-read-dedup-resume.test.js +81 -0
  159. package/dist/core/__tests__/refresh-stall-recycle.test.d.ts +1 -0
  160. package/dist/core/__tests__/refresh-stall-recycle.test.js +88 -0
  161. package/dist/core/__tests__/relaunch-root.test.d.ts +1 -0
  162. package/dist/core/__tests__/relaunch-root.test.js +114 -0
  163. package/dist/core/__tests__/reset.test.js +19 -79
  164. package/dist/core/__tests__/steer-note.test.js +9 -7
  165. package/dist/core/__tests__/tmux-surface.test.js +13 -7
  166. package/dist/core/bootstrap.d.ts +0 -3
  167. package/dist/core/bootstrap.js +1 -143
  168. package/dist/core/canvas/browse/__tests__/model.test.js +86 -1
  169. package/dist/core/canvas/browse/__tests__/render.test.js +2 -1
  170. package/dist/core/canvas/browse/app.js +131 -8
  171. package/dist/core/canvas/browse/model.d.ts +23 -5
  172. package/dist/core/canvas/browse/model.js +68 -13
  173. package/dist/core/canvas/browse/render.d.ts +7 -1
  174. package/dist/core/canvas/browse/render.js +178 -47
  175. package/dist/core/canvas/canvas.js +1 -1
  176. package/dist/core/canvas/focuses.d.ts +4 -4
  177. package/dist/core/canvas/focuses.js +7 -7
  178. package/dist/core/canvas/nav-model.d.ts +162 -0
  179. package/dist/core/canvas/nav-model.js +487 -0
  180. package/dist/core/canvas/paths.d.ts +7 -0
  181. package/dist/core/canvas/paths.js +9 -0
  182. package/dist/core/canvas/render.d.ts +68 -9
  183. package/dist/core/canvas/render.js +195 -61
  184. package/dist/core/canvas/types.d.ts +16 -23
  185. package/dist/core/config.js +1 -4
  186. package/dist/core/memory-resolver.js +43 -5
  187. package/dist/core/runtime/bearings.d.ts +1 -1
  188. package/dist/core/runtime/bearings.js +8 -8
  189. package/dist/core/runtime/branded-host.d.ts +16 -0
  190. package/dist/core/runtime/branded-host.js +127 -0
  191. package/dist/core/runtime/broker-protocol.d.ts +212 -6
  192. package/dist/core/runtime/broker-sdk.d.ts +0 -12
  193. package/dist/core/runtime/broker-sdk.js +77 -6
  194. package/dist/core/runtime/broker.d.ts +5 -1
  195. package/dist/core/runtime/broker.js +520 -59
  196. package/dist/core/runtime/close.d.ts +12 -2
  197. package/dist/core/runtime/close.js +37 -16
  198. package/dist/core/runtime/front-door.js +14 -7
  199. package/dist/core/runtime/host.d.ts +14 -34
  200. package/dist/core/runtime/host.js +14 -50
  201. package/dist/core/runtime/launch.d.ts +12 -9
  202. package/dist/core/runtime/launch.js +27 -15
  203. package/dist/core/runtime/lifecycle.d.ts +1 -1
  204. package/dist/core/runtime/lifecycle.js +15 -19
  205. package/dist/core/runtime/nodes.d.ts +0 -55
  206. package/dist/core/runtime/nodes.js +6 -74
  207. package/dist/core/runtime/placement.d.ts +91 -334
  208. package/dist/core/runtime/placement.js +262 -828
  209. package/dist/core/runtime/promote.d.ts +2 -0
  210. package/dist/core/runtime/promote.js +24 -6
  211. package/dist/core/runtime/recap.d.ts +8 -0
  212. package/dist/core/runtime/recap.js +107 -0
  213. package/dist/core/runtime/recycle.js +25 -61
  214. package/dist/core/runtime/reset.d.ts +43 -37
  215. package/dist/core/runtime/reset.js +131 -218
  216. package/dist/core/runtime/revive.d.ts +9 -29
  217. package/dist/core/runtime/revive.js +45 -135
  218. package/dist/core/runtime/spawn.d.ts +15 -11
  219. package/dist/core/runtime/spawn.js +168 -136
  220. package/dist/core/runtime/surface-bg.d.ts +11 -0
  221. package/dist/core/runtime/surface-bg.js +68 -0
  222. package/dist/core/runtime/tmux.d.ts +15 -43
  223. package/dist/core/runtime/tmux.js +38 -80
  224. package/dist/core/scope.d.ts +1 -2
  225. package/dist/core/scope.js +1 -5
  226. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts +1 -0
  227. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +57 -0
  228. package/dist/core/skill-sync/builtins.d.ts +42 -0
  229. package/dist/core/skill-sync/builtins.js +112 -0
  230. package/dist/core/skill-sync/claude-plugins.d.ts +23 -0
  231. package/dist/core/skill-sync/claude-plugins.js +71 -0
  232. package/dist/core/skill-sync/engine.d.ts +42 -0
  233. package/dist/core/skill-sync/engine.js +633 -0
  234. package/dist/core/skill-sync/export.d.ts +23 -0
  235. package/dist/core/skill-sync/export.js +86 -0
  236. package/dist/core/skill-sync/manifest.d.ts +64 -0
  237. package/dist/core/skill-sync/manifest.js +181 -0
  238. package/dist/core/skill-sync/profile.d.ts +76 -0
  239. package/dist/core/skill-sync/profile.js +173 -0
  240. package/dist/core/skill-sync/snapshot.d.ts +57 -0
  241. package/dist/core/skill-sync/snapshot.js +120 -0
  242. package/dist/core/spawn.d.ts +14 -0
  243. package/dist/core/spawn.js +29 -9
  244. package/dist/core/substrate/index.d.ts +2 -2
  245. package/dist/core/substrate/index.js +7 -7
  246. package/dist/core/substrate/injected-store.d.ts +10 -0
  247. package/dist/core/substrate/injected-store.js +55 -0
  248. package/dist/core/substrate/on-read.js +68 -6
  249. package/dist/core/substrate/render.d.ts +8 -11
  250. package/dist/core/substrate/render.js +29 -43
  251. package/dist/core/substrate/schema.d.ts +16 -11
  252. package/dist/core/substrate/schema.js +32 -31
  253. package/dist/core/tui/host.d.ts +12 -4
  254. package/dist/core/tui/host.js +280 -149
  255. package/dist/core/view/bridge.d.ts +10 -0
  256. package/dist/core/view/bridge.js +31 -0
  257. package/dist/core/view/chrome.d.ts +9 -0
  258. package/dist/core/view/chrome.js +22 -0
  259. package/dist/core/view/contract.d.ts +171 -0
  260. package/dist/core/view/contract.js +23 -0
  261. package/dist/core/view/loader.d.ts +31 -0
  262. package/dist/core/view/loader.js +188 -0
  263. package/dist/core/view/transport-local.d.ts +7 -0
  264. package/dist/core/view/transport-local.js +70 -0
  265. package/dist/core/view/transport.d.ts +4 -0
  266. package/dist/core/view/transport.js +15 -0
  267. package/dist/daemon/crtrd.d.ts +38 -27
  268. package/dist/daemon/crtrd.js +303 -376
  269. package/dist/daemon/manage.js +6 -1
  270. package/dist/index.js +1 -1
  271. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +28 -20
  272. package/dist/pi-extensions/canvas-context-intro.js +4 -4
  273. package/dist/pi-extensions/canvas-doc-substrate.js +29 -20
  274. package/dist/pi-extensions/canvas-nav.js +35 -385
  275. package/dist/pi-extensions/canvas-recap.d.ts +37 -0
  276. package/dist/pi-extensions/canvas-recap.js +236 -0
  277. package/dist/pi-extensions/canvas-resume.js +2 -1
  278. package/dist/pi-extensions/canvas-stophook.d.ts +15 -13
  279. package/dist/pi-extensions/canvas-stophook.js +119 -103
  280. package/dist/pi-extensions/canvas-view.js +2 -1
  281. package/dist/pi-extensions/widget-order-bus.d.ts +6 -0
  282. package/dist/pi-extensions/widget-order-bus.js +34 -0
  283. package/dist/prompts/view.d.ts +2 -2
  284. package/dist/prompts/view.js +145 -73
  285. package/dist/types.d.ts +1 -6
  286. package/dist/types.js +1 -3
  287. package/dist/web/ViewChrome.d.ts +7 -0
  288. package/dist/web/ViewChrome.js +28 -0
  289. package/dist/web/ViewPane.d.ts +39 -0
  290. package/dist/web/ViewPane.js +48 -0
  291. package/dist/web/index.d.ts +6 -0
  292. package/dist/web/index.js +16 -0
  293. package/dist/web/runtime.d.ts +39 -0
  294. package/dist/web/runtime.js +133 -0
  295. package/dist/web/states.d.ts +24 -0
  296. package/dist/web/states.js +24 -0
  297. package/dist/web/transport-http.d.ts +5 -0
  298. package/dist/web/transport-http.js +28 -0
  299. package/dist/web-client/assets/index-BUvQb4hR.css +2 -0
  300. package/dist/web-client/assets/index-ClLQXYAE.js +10 -0
  301. package/dist/web-client/index.html +13 -0
  302. package/package.json +20 -6
  303. package/dist/builtin-views/canvas/client.mjs +0 -303
  304. package/dist/builtin-views/canvas/view.mjs +0 -576
  305. package/dist/builtin-views/git-pr/client.mjs +0 -440
  306. package/dist/builtin-views/git-pr/view.mjs +0 -675
  307. package/dist/builtin-views/inbox/sources/gmail.mjs +0 -965
  308. package/dist/builtin-views/inbox/sources/linkedin.mjs +0 -427
  309. package/dist/builtin-views/inbox/view.mjs +0 -889
  310. package/dist/builtin-views/linkedin/client.mjs +0 -610
  311. package/dist/builtin-views/linkedin/view.mjs +0 -1171
  312. package/dist/commands/pkg/bridge.d.ts +0 -1
  313. package/dist/commands/pkg/bridge.js +0 -137
  314. package/dist/commands/skill/author.d.ts +0 -3
  315. package/dist/commands/skill/author.js +0 -140
  316. package/dist/commands/skill/shared.d.ts +0 -3
  317. package/dist/commands/skill/shared.js +0 -19
  318. package/dist/commands/skill.d.ts +0 -2
  319. package/dist/commands/skill.js +0 -21
  320. package/dist/commands/tmux-spread.d.ts +0 -2
  321. package/dist/commands/tmux-spread.js +0 -144
  322. package/dist/core/__tests__/full/daemon-liveness-pane.full.test.js +0 -292
  323. package/dist/core/__tests__/full/placement-focus.test.js +0 -309
  324. package/dist/core/__tests__/full/placement-reconcile.test.js +0 -212
  325. package/dist/core/__tests__/full/placement-revive.test.js +0 -238
  326. package/dist/core/__tests__/full/placement-teardown.test.js +0 -221
  327. package/dist/core/__tests__/home-session.test.js +0 -186
  328. package/dist/core/__tests__/relaunch.test.js +0 -335
  329. package/dist/core/bridge-map.d.ts +0 -19
  330. package/dist/core/bridge-map.js +0 -73
  331. package/dist/core/tui/contract.d.ts +0 -83
  332. package/dist/core/tui/contract.js +0 -8
  333. package/dist/core/tui/loader.d.ts +0 -16
  334. package/dist/core/tui/loader.js +0 -94
  335. package/dist/prompts/skill.d.ts +0 -2
  336. package/dist/prompts/skill.js +0 -650
  337. /package/dist/{core/__tests__/full/daemon-liveness-pane.full.test.d.ts → clients/attach/__tests__/action-parity.test.d.ts} +0 -0
  338. /package/dist/{core/__tests__/full/placement-focus.test.d.ts → clients/attach/__tests__/clipboard-image.test.d.ts} +0 -0
  339. /package/dist/{core/__tests__/full/placement-reconcile.test.d.ts → clients/attach/__tests__/editor-newline.test.d.ts} +0 -0
  340. /package/dist/{core/__tests__/full/placement-revive.test.d.ts → clients/attach/__tests__/git-info.test.d.ts} +0 -0
  341. /package/dist/{core/__tests__/full/placement-teardown.test.d.ts → clients/attach/__tests__/onrequest-wired.test.d.ts} +0 -0
  342. /package/dist/{core/__tests__/home-session.test.d.ts → clients/attach/__tests__/reconnect-giveup.test.d.ts} +0 -0
  343. /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/slash-quit-copy.test.d.ts} +0 -0
@@ -28,21 +28,31 @@
28
28
  // crouter's own tsc build without a dep on the pi packages.
29
29
  import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';
30
30
  import { join } from 'node:path';
31
- import { getNode, jobDir, updateNode, recordPid, subscribersOf, setPresence } from '../core/canvas/index.js';
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
34
  import { evaluateStop } from '../core/runtime/stop-guard.js';
35
35
  import { personaDrift, commitPersonaAck } from '../core/runtime/persona.js';
36
- import { reviveInPlace } from '../core/runtime/revive.js';
37
- import { handleNewSession, markCleanExitDone } from '../core/runtime/reset.js';
38
- import { focusOf, handFocusToManager, tearDownNode, closeFocusToShell } from '../core/runtime/placement.js';
36
+ import { handleNewSession, relaunchRoot, markCleanExitDone } from '../core/runtime/reset.js';
37
+ import { focusOf, tearDownNode } from '../core/runtime/placement.js';
38
+ /** Condense a tool call into one short line for the activity cue. Picks the most
39
+ * identifying arg (command / path / pattern / description), collapses whitespace,
40
+ * and hard-caps the length so a giant bash command can't bloat telemetry.json. */
41
+ const ACTIVITY_CAP = 80;
42
+ function summarizeActivity(toolName, args) {
43
+ const a = (args ?? {});
44
+ const pick = (v) => (typeof v === 'string' ? v : '');
45
+ const detail = pick(a['command']) || pick(a['path']) || pick(a['file_path']) || pick(a['pattern']) || pick(a['description']) || pick(a['prompt']);
46
+ const line = (detail !== '' ? `${toolName}: ${detail}` : toolName).replace(/\s+/g, ' ').trim();
47
+ return line.length > ACTIVITY_CAP ? line.slice(0, ACTIVITY_CAP) : line;
48
+ }
39
49
  /**
40
50
  * Merge accumulated token counts into nodes/<nodeId>/job/telemetry.json.
41
51
  * Creates the directory when it doesn't yet exist. Best-effort; never throws.
42
52
  * `contextTokens` is the live window gauge for THIS turn; when null (pi can't
43
53
  * size the window yet) the last recorded value is preserved.
44
54
  */
45
- function flushTelemetry(jobDirPath, tokensIn, tokensOut, model, contextTokens) {
55
+ function flushTelemetry(jobDirPath, tokensIn, tokensOut, model, contextTokens, activity) {
46
56
  try {
47
57
  if (!existsSync(jobDirPath))
48
58
  mkdirSync(jobDirPath, { recursive: true });
@@ -62,6 +72,7 @@ function flushTelemetry(jobDirPath, tokensIn, tokensOut, model, contextTokens) {
62
72
  tokens_in: tokensIn,
63
73
  tokens_out: tokensOut,
64
74
  context_tokens: contextTokens ?? existing.context_tokens,
75
+ last_activity: activity ?? existing.last_activity,
65
76
  model: model !== '' ? model : (existing.model ?? ''),
66
77
  updated_at: new Date().toISOString(),
67
78
  };
@@ -98,18 +109,22 @@ function lastAssistantMessage(messages) {
98
109
  // 185k+ pushy. Keyed on mode, NOT lifecycle: a terminal/orchestrator
99
110
  // yields against its roadmap exactly like a resident one (the
100
111
  // daemon's refresh-revive keys on intent='refresh', not lifecycle).
101
- // resident/base (a root conversation): never promoted ⇒ NO roadmap on disk,
102
- // so it is NOT told to checkpoint/yield against one. Instead: if the
103
- // chat is outgrowing one window into a multi-phase job, promote;
104
- // otherwise wrap up or start fresh. 130k gentle 150k firm → 185k+
112
+ // resident/base (a root conversation): NO roadmap on disk yet, so it is not
113
+ // told to checkpoint one. `crtr node yield` is still its continue
114
+ // verb it auto-promotes + seeds a roadmap transparently — offered
115
+ // against wrap-up, since a root chat can simply end. Gentle → firm →
105
116
  // pushy.
106
- // terminal/base (a worker): 130k/150k suggest promote 170k suggest promote
107
- // (+ "ignore if nearly done") 185k+ pushy.
117
+ // terminal/base (a worker): steered to `crtr node yield` to continue in a
118
+ // fresh window when work remains (yield auto-promotes under the
119
+ // hood), or `push final` when close. Gentle → firm → pushy.
108
120
  //
109
- // The promote/push-final guidance only makes sense for a terminal BASE worker. A
110
- // resident node finishes by yielding or being closed, not `push final`, so it is
111
- // never told to push final; only an ORCHESTRATOR (either lifecycle) has a roadmap
112
- // to yield against, so only it is steered at the roadmap.
121
+ // Every persona's "keep going" verb is `crtr node yield` NEVER a bare `node
122
+ // promote`, which framed promotion as a heavy "become an orchestrator" step and
123
+ // bred yield-avoidance. A base node's yield auto-promotes + seeds the roadmap
124
+ // under the hood, so the agent never decides to promote — it just yields. Only a
125
+ // terminal node is ever told `push final` (a resident finishes by yielding or
126
+ // being closed); only an orchestrator already HAS a roadmap to checkpoint before
127
+ // it yields.
113
128
  // ---------------------------------------------------------------------------
114
129
  const STEER_STEP = 10_000;
115
130
  // Shared escalation schedule (both lifecycles). Tightens as it climbs:
@@ -132,20 +147,22 @@ function steerBand(tokens) {
132
147
  return chosen;
133
148
  }
134
149
  /** The nudge text for a crossed band, specialized to the node's (mode,
135
- * lifecycle) persona + how far along the escalation it is.
150
+ * lifecycle) persona + how far along the escalation it is. `crtr node yield` is
151
+ * the single "keep going past this window" verb for every persona: it refreshes
152
+ * the node into a clean window against a roadmap, and for a base node
153
+ * auto-promotes transparently — so no persona is ever steered at a bare `node
154
+ * promote` (which framed promotion as a heavy "become an orchestrator" step and
155
+ * bred yield-avoidance).
136
156
  *
137
- * - orchestrator (terminal OR resident): checkpoint its roadmap and yield
138
- * (gently firmly → pushy). It has a context/roadmap.md to yield against.
139
- * - resident/base (root conversation): never promoted, so NO roadmap exists
140
- * steer it to PROMOTE if the chat is growing into a multi-phase job (which
141
- * seeds a roadmap), else wrap up / start fresh. Never points at roadmap.md or
142
- * a bare `node yield`, which for a roadmap-less root just drops context.
143
- * - terminal/base (worker): PROMOTE itself — become an orchestrator — when
144
- * work remains, with an "ignore if nearly done, finish with push final" once
145
- * it's deeper in.
157
+ * - orchestrator (terminal OR resident): checkpoint context/roadmap.md, then
158
+ * yield (it already has a roadmap to bring current first).
159
+ * - resident/base (root conversation): yield to continue in a fresh window if
160
+ * the chat is growing into a job, else wrap up — a root can simply end.
161
+ * - terminal/base (worker): yield to continue if work remains, or `push final`
162
+ * when close.
146
163
  *
147
- * At/past 185k every persona goes PUSHY: the context is long enough that
148
- * drifting further risks an overflow. */
164
+ * The wording strengthens with depth: gentle < 150k firm < 185k → pushy at/
165
+ * past 185k, where drifting further risks an overflow. */
149
166
  export function steerNote(at, lifecycle, mode) {
150
167
  const k = Math.round(at / 1000);
151
168
  const pushy = at >= 185_000;
@@ -161,25 +178,31 @@ export function steerNote(at, lifecycle, mode) {
161
178
  return `Context ~${k}k — this is getting long. Stop taking on new work now: checkpoint context/roadmap.md, hand off anything outstanding, and \`crtr node yield\` immediately to refresh before this context overflows.`;
162
179
  }
163
180
  if (lifecycle === 'resident') {
164
- // resident/base — a root conversation. It has no roadmap (only promotion
165
- // seeds one), so steer it toward promote-or-wrap-up, never at roadmap.md.
166
- const grow = `If this is turning into a multi-phase job, \`crtr node promote\` to become a resident orchestrator (seeds a roadmap so you can delegate and \`crtr node yield\` to refresh).`;
181
+ // resident/base — a root conversation with no roadmap yet. `crtr node yield`
182
+ // is still the natural "carry this forward" verb: it refreshes into a clean
183
+ // window against a roadmap and auto-promotes transparently (no scary
184
+ // "become an orchestrator" step). Offer wrap-up as the alternative — a root
185
+ // chat can simply end.
186
+ const grow = `If this is turning into a multi-phase job, \`crtr node yield\` to continue in a fresh window against a roadmap — no need to "become" anything, yield just carries you forward.`;
167
187
  if (at < 150_000) {
168
188
  return `Context ~${k}k and growing. ${grow} Otherwise no rush — wrap up when you reach a good stopping point.`;
169
189
  }
170
190
  if (!pushy) {
171
- return `Context ~${k}k. ${grow} If you're near done, just finish here; if there's more open-ended work, start a fresh \`crtr\` rather than letting this context grow.`;
191
+ return `Context ~${k}k. ${grow} If you're near done, just finish here; if there's more open-ended work, \`crtr node yield\` rather than letting this context grow.`;
172
192
  }
173
- return `Context ~${k}k — this is getting long. Wrap up now before this context overflows: finish what's in hand, or \`crtr node promote\` immediately if substantial work remains, otherwise continue in a fresh \`crtr\`.`;
193
+ return `Context ~${k}k — this is getting long. Wrap up now before this context overflows: finish what's in hand, or \`crtr node yield\` immediately to continue fresh against a roadmap if substantial work remains.`;
194
+ }
195
+ // terminal/base — a worker. The natural "keep going past this window" verb is
196
+ // `crtr node yield`: it refreshes into a clean window against a roadmap and
197
+ // auto-promotes transparently, so there is no separate "become an orchestrator"
198
+ // decision to agonize over. Steer it to yield-or-finish, never a bare promote.
199
+ if (at < 150_000) {
200
+ return `Context ~${k}k and growing. If you're near done, finish with \`crtr push final\`. If real work remains, \`crtr node yield\` to continue in a fresh window against a roadmap — no rush yet.`;
174
201
  }
175
- // terminal — a worker.
176
- const suggest = `If much more work remains than this context can finish, consider \`crtr node promote\` to become an orchestrator (seeds a roadmap, lets you delegate and \`crtr node yield\` to refresh).`;
177
- if (at < 170_000)
178
- return `Context ~${k}k. ${suggest}`;
179
202
  if (!pushy) {
180
- return `Context ~${k}k. ${suggest} If you're nearly done, ignore this suggestion and finish with \`crtr push final\`.`;
203
+ return `Context ~${k}k. If you're close, finish with \`crtr push final\`; otherwise \`crtr node yield\` now to refresh into a clean window and keep going against a roadmap.`;
181
204
  }
182
- return `Context ~${k}k — this is getting long. Wrap up now: \`crtr push final\` if you're close, otherwise \`crtr node promote\` immediately to continue as an orchestrator instead of overflowing this context.`;
205
+ return `Context ~${k}k — this is getting long. Wrap up now before this context overflows: \`crtr push final\` if you're close, otherwise \`crtr node yield\` immediately to continue in a fresh window against a roadmap.`;
183
206
  }
184
207
  // ---------------------------------------------------------------------------
185
208
  // Extension
@@ -205,6 +228,10 @@ export function registerCanvasStophook(pi) {
205
228
  let totalIn = 0;
206
229
  let totalOut = 0;
207
230
  let model = '';
231
+ // Last live context-window gauge + last tool summary, tracked across handlers so
232
+ // a tool_execution_start flush (activity-only) preserves the latest token figure.
233
+ let lastContext = null;
234
+ let lastActivity = null;
208
235
  // Context-size steering. As input context grows we nudge the node once per
209
236
  // band on an escalating, persona-specific schedule (see steerBand/steerNote).
210
237
  // The node's (lifecycle, mode) persona is read at fire time, since a terminal
@@ -224,13 +251,12 @@ export function registerCanvasStophook(pi) {
224
251
  // Why reason, not a closure flag: pi RE-ACTIVATES extensions on every session
225
252
  // swap, so any id we stash on boot is reset to its initial value before the
226
253
  // next session_start fires and can never observe the change — a `/new` then
227
- // looks identical to a boot. (That is exactly the bug this replaced: `/new`
228
- // silently fell back to an in-place reset instead of relaunching, so the node
229
- // id + context dir never changed.) The event reason is delivered fresh on
230
- // every fire and is immune to the re-activation.
254
+ // looks identical to a boot. The event reason is delivered fresh on every fire
255
+ // and is immune to the re-activation.
231
256
  //
232
- // For a root, reason 'new' means a brand-new graph: relaunch (park the old
233
- // root + boot a fresh node in this pane) or, with no pane, an in-place reset.
257
+ // `/new` splits by who ran it: a ROOT relaunches into a genuinely new node
258
+ // (relaunchRoot — park the old root `done`, boot a fresh node, re-point this
259
+ // pane), a non-root CHILD just refreshes its session id (handleNewSession).
234
260
  // ---------------------------------------------------------------------------
235
261
  pi.on('session_start', (event, ctx) => {
236
262
  try {
@@ -242,16 +268,19 @@ export function registerCanvasStophook(pi) {
242
268
  // whereas a bare id is resolved cwd-relative and forks across projects.
243
269
  const filed = ctx?.sessionManager?.getSessionFile?.();
244
270
  const sessionFile = typeof filed === 'string' && filed !== '' ? filed : null;
245
- // `/new` — a brand-new conversation in the same process. Route it: a
246
- // non-root child refreshes its session id; a ROOT in a tmux pane RELAUNCHES
247
- // (parks the old root + boots a fresh node in this pane via respawn-pane
248
- // -k, which tears down THIS pi); a root with no pane falls back to an
249
- // in-place reset. The relaunch's detached respawn may kill this pi before
250
- // the lines after the call run — that's fine; do not rely on anything
251
- // after handleNewSession.
271
+ // `/new` — a brand-new conversation in the same process. The broker already
272
+ // drove the engine-side new_session (the viewer's /new broker
273
+ // new_session frame); the runtime side then splits on root-ness: a ROOT
274
+ // relaunches into a freshly-minted node (park the old root `done`, boot a
275
+ // new broker, re-point the viewer pane), a non-root CHILD just refreshes
276
+ // its session id on the SAME node.
252
277
  if (event?.reason === 'new') {
253
278
  try {
254
- handleNewSession(nodeId, id, process.env['TMUX_PANE'], {}, sessionFile);
279
+ const m = getNode(nodeId);
280
+ if (m?.parent == null)
281
+ relaunchRoot(nodeId);
282
+ else
283
+ handleNewSession(nodeId, id, sessionFile);
255
284
  }
256
285
  catch { /* best-effort */ }
257
286
  // Clear in-memory context-steering so the fresh conversation starts clean.
@@ -261,13 +290,12 @@ export function registerCanvasStophook(pi) {
261
290
  return;
262
291
  }
263
292
  // Boot / startup / resume / reload / fork → (re)bind this process to its
264
- // session id, record our OS pid (the daemon's liveness signal for inline
265
- // roots whose window outlives pi), and CONFIRM any pending refresh-yield.
266
- // Reaching session_start proves a fresh pi actually booted, so it is now
267
- // safe to clear intent='refresh'. reviveInPlace deliberately leaves intent
268
- // set: the detached respawn it dispatches can't confirm itself (it kills
269
- // the caller mid-flight), so a real boot is the only thing allowed to clear
270
- // it — otherwise a failed respawn would look identical to a successful one.
293
+ // session id, record our OS pid (the daemon's broker-liveness signal), and
294
+ // CONFIRM any pending refresh-yield. Reaching session_start proves a fresh
295
+ // engine actually booted, so it is now safe to clear intent='refresh' — a
296
+ // refresh shuts the broker down and the daemon relaunches it FRESH, which
297
+ // leaves intent='refresh' set until this boot clears it (the only proof the
298
+ // relaunch worked; otherwise a failed relaunch would look like a success).
271
299
  const existing = getNode(nodeId);
272
300
  // Identity (session id/file) → meta; runtime (pid, intent) → atomic row setters.
273
301
  updateNode(nodeId, {
@@ -294,6 +322,18 @@ export function registerCanvasStophook(pi) {
294
322
  catch { /* best-effort */ }
295
323
  });
296
324
  // ---------------------------------------------------------------------------
325
+ // tool_execution_start — capture the live "what is it doing" cue. Writes the
326
+ // one-line tool summary to telemetry.json so out-of-process readers (the nav
327
+ // chrome) show it inline without a second file or any extra read on their side.
328
+ // ---------------------------------------------------------------------------
329
+ pi.on('tool_execution_start', (event) => {
330
+ try {
331
+ lastActivity = summarizeActivity(String(event?.toolName ?? ''), event?.args);
332
+ flushTelemetry(jobDirPath, totalIn, totalOut, model, lastContext, lastActivity);
333
+ }
334
+ catch { /* best-effort */ }
335
+ });
336
+ // ---------------------------------------------------------------------------
297
337
  // session_shutdown — clean exit → done.
298
338
  //
299
339
  // pi hands us a reason as a session tears down. Only 'quit' is a node-ending
@@ -347,8 +387,10 @@ export function registerCanvasStophook(pi) {
347
387
  contextTokens = t;
348
388
  }
349
389
  catch { /* gauge unavailable this turn */ }
390
+ if (contextTokens !== null)
391
+ lastContext = contextTokens;
350
392
  // Fire-and-forget: flushTelemetry uses synchronous fs writes and never throws.
351
- flushTelemetry(jobDirPath, totalIn, totalOut, model, contextTokens);
393
+ flushTelemetry(jobDirPath, totalIn, totalOut, model, contextTokens, lastActivity);
352
394
  // Context-size steering: fire the current band once, with lifecycle-specific
353
395
  // guidance (lifecycle is read live — a terminal worker may have promoted to
354
396
  // resident since launch).
@@ -417,32 +459,18 @@ export function registerCanvasStophook(pi) {
417
459
  // transitions node.status → 'done' synchronously. Shut down cleanly.
418
460
  const node = getNode(nodeId);
419
461
  if (node?.status === 'done') {
420
- // TRULY-DONE (pushed `final` this turn). If this node owns the user's
421
- // viewport, its lifecycle successor takes the focus (§1.6):
422
- // handFocusToManager hands the focus row to the manager (the node up
423
- // the subscribes_to spine it reports to) and, when that manager's pi is
424
- // LIVE in the backstage, synchronously swaps it into this now-frozen
425
- // focus pane; a DORMANT manager is revived into the pane by the daemon
426
- // on the `final` it just pushed — either way no new window, no taint.
427
- // No manager (a root) or a manager already focused elsewhere → Q1-close
428
- // this focus AND flip remain-on-exit OFF on %m's window so the pane
429
- // closes when this pi exits (return-to-shell) instead of freezing into
430
- // an orphan. We CANNOT closePane(%m) from inside %m (self-saw), but the
431
- // pi is still alive mid-shutdown, so remain-on-exit-off is safe and
432
- // makes tmux reap the pane on exit. An unfocused done node just shuts
433
- // down (no pane anywhere, Invariant P). M is done → it owns no pane
434
- // (Invariant P), so null its own presence in BOTH sub-branches before
435
- // shutdown.
436
- const f = focusOf(nodeId);
437
- if (f !== null) {
438
- const managerId = node.parent ?? subscribersOf(nodeId)[0]?.node_id ?? null;
439
- if (!handFocusToManager(f.focus_id, managerId)) {
440
- // Q1 return-to-shell, self-saw-safe: close the focus row + disarm the
441
- // pane's freeze so it reaps on exit (we can't closePane our own pane).
442
- closeFocusToShell(f.focus_id, nodeId);
462
+ // TRULY-DONE (pushed `final` this turn). The engine is a detached
463
+ // broker, not a pane, so there is no engine to hand off — just close
464
+ // this node's VIEWER pane (if any) and shut the broker down. The daemon
465
+ // won't revive a done node. tearDownNode closes the viewer focus row +
466
+ // pane; ctx.shutdown() exits the broker process.
467
+ if (focusOf(nodeId) !== null) {
468
+ try {
469
+ tearDownNode(nodeId);
443
470
  }
471
+ catch { /* best-effort */ }
444
472
  }
445
- setPresence(nodeId, { pane: null, tmux_session: null, window: null }); // M done → owns no pane
473
+ setPresence(nodeId, { pane: null, tmux_session: null, window: null });
446
474
  try {
447
475
  ctx?.shutdown?.();
448
476
  }
@@ -450,27 +478,15 @@ export function registerCanvasStophook(pi) {
450
478
  return;
451
479
  }
452
480
  // (b') Refresh-yield: the node ran `crtr node yield` this turn, setting
453
- // intent='refresh'. Re-exec a FRESH pi IN PLACE in this same tmux
454
- // pane (respawn-pane -k) so the node re-reads its roadmap without
455
- // churning its window critically, an interactive/foreground root
456
- // is never dropped to a shell, and no daemon round-trip is needed
457
- // (the old window-death detection silently failed whenever pi
458
- // exited into a persistent shell pane). Falls back to a clean
459
- // shutdown (daemon revives in a new window) only when we're not in
460
- // a tmux pane.
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.)
461
486
  if (node?.intent === 'refresh') {
462
487
  // A yield is SILENT to subscribers: the node keeps its identity and
463
- // subscription edges across the revive and reports only through its
464
- // own explicit `crtr push` calls, so there is no checkpoint push here
465
- // — just re-exec a fresh pi in place against the roadmap.
466
- const pane = process.env['TMUX_PANE'];
467
- if (pane !== undefined && pane.trim() !== '') {
468
- try {
469
- reviveInPlace(nodeId, pane);
470
- return; // respawn-pane -k tears down this pi and starts the fresh one
471
- }
472
- catch { /* fall through to plain shutdown */ }
473
- }
488
+ // subscription edges across the revive and reports only through its own
489
+ // explicit `crtr push` calls, so there is no checkpoint push here.
474
490
  try {
475
491
  ctx?.shutdown?.();
476
492
  }
@@ -21,6 +21,7 @@
21
21
  // inside crouter's own tsc build without a dep on the pi packages (mirrors
22
22
  // canvas-resume.ts / canvas-nav.ts / canvas-commands.ts).
23
23
  import { execFile } from 'node:child_process';
24
+ import { surfaceTmuxStyleArgs } from '../core/runtime/surface-bg.js';
24
25
  /** Single-quote a string for safe interpolation into a `sh -c` command line —
25
26
  * tmux runs the display-popup trailing string through the shell, so a view
26
27
  * name with spaces or quotes must be escaped. */
@@ -59,7 +60,7 @@ export function registerCanvasView(pi) {
59
60
  // bare /view → the picker; /view <name> → that view directly.
60
61
  const cmd = name === '' ? 'crtr view pick' : `crtr view run ${shellQuote(name)}`;
61
62
  try {
62
- execFile('tmux', ['display-popup', '-E', '-w', '90%', '-h', '85%', cmd], () => { });
63
+ execFile('tmux', ['display-popup', '-E', '-w', '90%', '-h', '85%', ...surfaceTmuxStyleArgs(), cmd], () => { });
63
64
  }
64
65
  catch { /* best-effort */ }
65
66
  return;
@@ -0,0 +1,6 @@
1
+ /** Called by canvas-nav (once per session_start) to expose its scheduleRender. */
2
+ export declare function onNavRerender(cb: () => void): void;
3
+ /** Called by canvas-recap after it sets/clears `crtr-recap` to nudge canvas-nav
4
+ * to re-assert `crtr-managers` below it. No-op if nav has not registered (e.g.
5
+ * nav inert, or recap-only test harness). */
6
+ export declare function requestNavRerender(): void;
@@ -0,0 +1,34 @@
1
+ // widget-order-bus.ts — a tiny in-process bus that lets canvas-recap keep its
2
+ // recap widget pinned ABOVE canvas-nav's manager line.
3
+ //
4
+ // Why this exists: pi's interactive widget store (and the broker's relay) is
5
+ // insertion-ordered, and re-setting an existing widget key moves it to the END
6
+ // (bottom) — see interactive-mode.js `setExtensionWidget` (delete+set). So the
7
+ // LAST extension to call setWidget for an aboveEditor key renders closest to the
8
+ // editor. canvas-recap only paints during idle, when canvas-nav has not re-set
9
+ // `crtr-managers` for a long time, so a naive recap setWidget lands BELOW the
10
+ // manager line. The fix: after canvas-recap changes its widget, it asks
11
+ // canvas-nav to re-assert `crtr-managers`, which re-inserts that key after
12
+ // `crtr-recap` and so drops it below the recap — making the recap the topmost
13
+ // chrome.
14
+ //
15
+ // Both extensions load in the SAME pi process, so this module is a shared
16
+ // singleton via Node's module cache. No @earendil-works/* imports — compiles in
17
+ // crouter's own tsc build.
18
+ /** canvas-nav registers its re-render here on session_start; canvas-recap calls
19
+ * requestNavRerender() after it shows/clears the recap so the manager line
20
+ * re-inserts (and thus orders) below the recap widget. */
21
+ let navRerender;
22
+ /** Called by canvas-nav (once per session_start) to expose its scheduleRender. */
23
+ export function onNavRerender(cb) {
24
+ navRerender = cb;
25
+ }
26
+ /** Called by canvas-recap after it sets/clears `crtr-recap` to nudge canvas-nav
27
+ * to re-assert `crtr-managers` below it. No-op if nav has not registered (e.g.
28
+ * nav inert, or recap-only test harness). */
29
+ export function requestNavRerender() {
30
+ try {
31
+ navRerender?.();
32
+ }
33
+ catch { /* best-effort */ }
34
+ }
@@ -3,5 +3,5 @@ export interface ViewScaffoldVars {
3
3
  title: string;
4
4
  description: string;
5
5
  }
6
- /** Build the `view.mjs` source for a freshly scaffolded view. */
7
- export declare function viewScaffold(v: ViewScaffoldVars): string;
6
+ /** Build every file of a freshly scaffolded dual-target view, keyed by filename. */
7
+ export declare function viewScaffold(v: ViewScaffoldVars): Record<string, string>;