@crouton-kit/crouter 0.3.28 → 0.3.30

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 (324) hide show
  1. package/dist/build-root.d.ts +15 -7
  2. package/dist/build-root.js +48 -34
  3. package/dist/builtin-memory/crouter-development/marketplaces.md +7 -6
  4. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +1 -1
  5. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +1 -1
  6. package/dist/builtin-memory/crouter-development/personas.md +1 -1
  7. package/dist/builtin-memory/crouter-development/plugins.md +20 -18
  8. package/dist/builtin-memory/design.md +1 -1
  9. package/dist/builtin-memory/development.md +1 -1
  10. package/dist/builtin-memory/internal/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
  12. package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
  13. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  14. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  15. package/dist/builtin-memory/planning.md +1 -1
  16. package/dist/builtin-memory/spec.md +6 -4
  17. package/dist/builtin-personas/orchestration-kernel.md +4 -5
  18. package/dist/builtin-personas/runtime-base.md +5 -4
  19. package/dist/builtin-personas/spec/PERSONA.md +8 -4
  20. package/dist/builtin-personas/spec/orchestrator.md +5 -3
  21. package/dist/builtin-personas/spec/requirements/PERSONA.md +10 -0
  22. package/dist/builtin-views/canvas/core.mjs +586 -0
  23. package/dist/builtin-views/canvas/text.mjs +58 -0
  24. package/dist/builtin-views/canvas/tui.mjs +165 -0
  25. package/dist/builtin-views/canvas/web.jsx +120 -0
  26. package/dist/builtin-views/git-pr/core.mjs +673 -0
  27. package/dist/builtin-views/git-pr/text.mjs +84 -0
  28. package/dist/builtin-views/git-pr/tui.mjs +302 -0
  29. package/dist/builtin-views/git-pr/web.jsx +216 -0
  30. package/dist/builtin-views/inbox/_lib/render.mjs +1 -3
  31. package/dist/builtin-views/inbox/core.mjs +1273 -0
  32. package/dist/builtin-views/inbox/text.mjs +73 -0
  33. package/dist/builtin-views/inbox/tui.mjs +312 -0
  34. package/dist/builtin-views/inbox/web.jsx +188 -0
  35. package/dist/builtin-views/linkedin/core.mjs +906 -0
  36. package/dist/builtin-views/linkedin/text.mjs +69 -0
  37. package/dist/builtin-views/linkedin/tui.mjs +427 -0
  38. package/dist/builtin-views/linkedin/web.jsx +206 -0
  39. package/dist/builtin-views/workspace-sidebar/core.mjs +621 -0
  40. package/dist/builtin-views/workspace-sidebar/text.mjs +53 -0
  41. package/dist/builtin-views/workspace-sidebar/tui.mjs +142 -0
  42. package/dist/builtin-views/workspace-sidebar/web.jsx +109 -0
  43. package/dist/cli.js +22 -17
  44. package/dist/clients/attach/__tests__/action-parity.test.js +48 -0
  45. package/dist/clients/attach/__tests__/clipboard-image.test.js +76 -0
  46. package/dist/clients/attach/__tests__/editor-newline.test.js +57 -0
  47. package/dist/clients/attach/__tests__/git-info.test.js +31 -0
  48. package/dist/clients/attach/__tests__/onrequest-wired.test.js +20 -0
  49. package/dist/clients/attach/__tests__/slash-quit-copy.test.js +42 -0
  50. package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
  51. package/dist/clients/attach/attach-cmd.js +282 -46
  52. package/dist/clients/attach/auth-pickers.d.ts +9 -0
  53. package/dist/clients/attach/auth-pickers.js +194 -0
  54. package/dist/clients/attach/canvas-panels.d.ts +4 -2
  55. package/dist/clients/attach/canvas-panels.js +45 -17
  56. package/dist/clients/attach/chat-view.d.ts +27 -2
  57. package/dist/clients/attach/chat-view.js +98 -9
  58. package/dist/clients/attach/clipboard-image.d.ts +47 -8
  59. package/dist/clients/attach/clipboard-image.js +150 -26
  60. package/dist/clients/attach/clipboard-text.d.ts +3 -0
  61. package/dist/clients/attach/clipboard-text.js +39 -0
  62. package/dist/clients/attach/config-load.d.ts +54 -7
  63. package/dist/clients/attach/config-load.js +99 -3
  64. package/dist/clients/attach/context-message.d.ts +12 -0
  65. package/dist/clients/attach/context-message.js +72 -0
  66. package/dist/clients/attach/git-info.d.ts +20 -0
  67. package/dist/clients/attach/git-info.js +44 -0
  68. package/dist/clients/attach/graph-overlay.d.ts +13 -4
  69. package/dist/clients/attach/graph-overlay.js +59 -19
  70. package/dist/clients/attach/input-controller.d.ts +75 -13
  71. package/dist/clients/attach/input-controller.js +305 -67
  72. package/dist/clients/attach/pickers.d.ts +58 -0
  73. package/dist/clients/attach/pickers.js +171 -0
  74. package/dist/clients/attach/slash-commands.d.ts +22 -0
  75. package/dist/clients/attach/slash-commands.js +91 -25
  76. package/dist/clients/attach/titled-editor.d.ts +33 -0
  77. package/dist/clients/attach/titled-editor.js +84 -0
  78. package/dist/clients/attach/view-socket.d.ts +18 -1
  79. package/dist/clients/attach/view-socket.js +70 -1
  80. package/dist/clients/web/dev-server.d.ts +7 -0
  81. package/dist/clients/web/dev-server.js +59 -0
  82. package/dist/clients/web/events.d.ts +14 -0
  83. package/dist/clients/web/events.js +151 -0
  84. package/dist/clients/web/server.d.ts +18 -0
  85. package/dist/clients/web/server.js +450 -0
  86. package/dist/clients/web/web-cmd.d.ts +2 -0
  87. package/dist/clients/web/web-cmd.js +120 -0
  88. package/dist/commands/canvas.js +1 -2
  89. package/dist/commands/chord.js +4 -25
  90. package/dist/commands/dashboard.js +5 -1
  91. package/dist/commands/memory/__tests__/lint-schema.test.js +6 -6
  92. package/dist/commands/memory/lint.js +14 -2
  93. package/dist/commands/memory/read.js +2 -2
  94. package/dist/commands/memory/shared.d.ts +7 -1
  95. package/dist/commands/memory/shared.js +18 -3
  96. package/dist/commands/memory/write.js +8 -4
  97. package/dist/commands/memory.js +4 -4
  98. package/dist/commands/node.d.ts +1 -1
  99. package/dist/commands/node.js +64 -65
  100. package/dist/commands/pkg.js +1 -2
  101. package/dist/commands/revive.js +11 -3
  102. package/dist/commands/sys/sync.d.ts +1 -0
  103. package/dist/commands/sys/sync.js +187 -0
  104. package/dist/commands/sys.js +3 -2
  105. package/dist/commands/view-cycle.js +2 -2
  106. package/dist/commands/view-list.js +8 -8
  107. package/dist/commands/view-new.js +21 -17
  108. package/dist/commands/view-pick.js +1 -1
  109. package/dist/commands/view-run.js +35 -14
  110. package/dist/commands/view.js +8 -6
  111. package/dist/commands/workspace.d.ts +2 -0
  112. package/dist/commands/workspace.js +161 -0
  113. package/dist/core/__tests__/broker-double-spawn.test.d.ts +1 -0
  114. package/dist/core/__tests__/broker-double-spawn.test.js +143 -0
  115. package/dist/core/__tests__/broker-fork-seam.test.d.ts +1 -0
  116. package/dist/core/__tests__/broker-fork-seam.test.js +102 -0
  117. package/dist/core/__tests__/broker-sdk-wiring.test.js +2 -0
  118. package/dist/core/__tests__/child-death-wake.test.js +56 -111
  119. package/dist/core/__tests__/close.test.js +14 -20
  120. package/dist/core/__tests__/context-intro.test.js +19 -19
  121. package/dist/core/__tests__/daemon-boot.test.js +30 -23
  122. package/dist/core/__tests__/draw-style.test.js +1 -1
  123. package/dist/core/__tests__/error-stall-recycle.test.d.ts +1 -0
  124. package/dist/core/__tests__/error-stall-recycle.test.js +141 -0
  125. package/dist/core/__tests__/fixtures/fake-engine.d.ts +17 -1
  126. package/dist/core/__tests__/fixtures/fake-engine.js +22 -2
  127. package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
  128. package/dist/core/__tests__/focuses.test.js +25 -13
  129. package/dist/core/__tests__/fork.test.js +22 -5
  130. package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
  131. package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
  132. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +1 -0
  133. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +57 -0
  134. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +1 -0
  135. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +80 -0
  136. package/dist/core/__tests__/full/broker-pane-resolution.test.js +90 -0
  137. package/dist/core/__tests__/full/cascade-close.test.js +22 -8
  138. package/dist/core/__tests__/full/detach-focus.test.js +66 -169
  139. package/dist/core/__tests__/helpers/harness.d.ts +6 -9
  140. package/dist/core/__tests__/helpers/harness.js +19 -36
  141. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +1 -0
  142. package/dist/core/__tests__/human-node-not-supervised.test.js +83 -0
  143. package/dist/core/__tests__/lifecycle.test.js +1 -19
  144. package/dist/core/__tests__/listing-completeness.test.js +2 -2
  145. package/dist/core/__tests__/live-mutation-verbs.test.js +5 -5
  146. package/dist/core/__tests__/memory-resolver.test.js +1 -1
  147. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -2
  148. package/dist/core/__tests__/refresh-stall-recycle.test.d.ts +1 -0
  149. package/dist/core/__tests__/refresh-stall-recycle.test.js +88 -0
  150. package/dist/core/__tests__/relaunch-root.test.d.ts +1 -0
  151. package/dist/core/__tests__/relaunch-root.test.js +114 -0
  152. package/dist/core/__tests__/reset.test.js +19 -79
  153. package/dist/core/__tests__/steer-note.test.js +9 -7
  154. package/dist/core/__tests__/tmux-surface.test.js +13 -7
  155. package/dist/core/bootstrap.d.ts +0 -3
  156. package/dist/core/bootstrap.js +1 -143
  157. package/dist/core/canvas/browse/__tests__/model.test.js +86 -1
  158. package/dist/core/canvas/browse/__tests__/render.test.js +2 -1
  159. package/dist/core/canvas/browse/app.js +131 -8
  160. package/dist/core/canvas/browse/model.d.ts +23 -5
  161. package/dist/core/canvas/browse/model.js +68 -13
  162. package/dist/core/canvas/browse/render.d.ts +7 -1
  163. package/dist/core/canvas/browse/render.js +178 -47
  164. package/dist/core/canvas/canvas.js +1 -1
  165. package/dist/core/canvas/focuses.d.ts +4 -4
  166. package/dist/core/canvas/focuses.js +7 -7
  167. package/dist/core/canvas/nav-model.js +8 -7
  168. package/dist/core/canvas/render.d.ts +68 -9
  169. package/dist/core/canvas/render.js +195 -61
  170. package/dist/core/canvas/types.d.ts +16 -23
  171. package/dist/core/config.js +1 -4
  172. package/dist/core/memory-resolver.js +43 -5
  173. package/dist/core/runtime/bearings.d.ts +1 -1
  174. package/dist/core/runtime/bearings.js +8 -8
  175. package/dist/core/runtime/branded-host.d.ts +16 -0
  176. package/dist/core/runtime/branded-host.js +127 -0
  177. package/dist/core/runtime/broker-protocol.d.ts +212 -6
  178. package/dist/core/runtime/broker.d.ts +4 -1
  179. package/dist/core/runtime/broker.js +494 -58
  180. package/dist/core/runtime/close.d.ts +12 -2
  181. package/dist/core/runtime/close.js +37 -16
  182. package/dist/core/runtime/front-door.js +6 -14
  183. package/dist/core/runtime/host.d.ts +14 -34
  184. package/dist/core/runtime/host.js +14 -50
  185. package/dist/core/runtime/launch.d.ts +12 -9
  186. package/dist/core/runtime/launch.js +27 -15
  187. package/dist/core/runtime/lifecycle.d.ts +1 -1
  188. package/dist/core/runtime/lifecycle.js +15 -19
  189. package/dist/core/runtime/nodes.d.ts +0 -55
  190. package/dist/core/runtime/nodes.js +6 -74
  191. package/dist/core/runtime/placement.d.ts +91 -335
  192. package/dist/core/runtime/placement.js +291 -858
  193. package/dist/core/runtime/promote.d.ts +2 -0
  194. package/dist/core/runtime/promote.js +24 -6
  195. package/dist/core/runtime/recap.d.ts +8 -0
  196. package/dist/core/runtime/recap.js +107 -0
  197. package/dist/core/runtime/recycle.js +25 -61
  198. package/dist/core/runtime/reset.d.ts +43 -37
  199. package/dist/core/runtime/reset.js +131 -218
  200. package/dist/core/runtime/revive.d.ts +9 -29
  201. package/dist/core/runtime/revive.js +40 -139
  202. package/dist/core/runtime/spawn.d.ts +15 -16
  203. package/dist/core/runtime/spawn.js +165 -194
  204. package/dist/core/runtime/surface-bg.d.ts +11 -0
  205. package/dist/core/runtime/surface-bg.js +68 -0
  206. package/dist/core/runtime/tmux.d.ts +15 -52
  207. package/dist/core/runtime/tmux.js +35 -99
  208. package/dist/core/scope.d.ts +1 -2
  209. package/dist/core/scope.js +1 -5
  210. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts +1 -0
  211. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +57 -0
  212. package/dist/core/skill-sync/builtins.d.ts +42 -0
  213. package/dist/core/skill-sync/builtins.js +112 -0
  214. package/dist/core/skill-sync/claude-plugins.d.ts +23 -0
  215. package/dist/core/skill-sync/claude-plugins.js +71 -0
  216. package/dist/core/skill-sync/engine.d.ts +42 -0
  217. package/dist/core/skill-sync/engine.js +633 -0
  218. package/dist/core/skill-sync/export.d.ts +23 -0
  219. package/dist/core/skill-sync/export.js +86 -0
  220. package/dist/core/skill-sync/manifest.d.ts +64 -0
  221. package/dist/core/skill-sync/manifest.js +181 -0
  222. package/dist/core/skill-sync/profile.d.ts +76 -0
  223. package/dist/core/skill-sync/profile.js +173 -0
  224. package/dist/core/skill-sync/snapshot.d.ts +57 -0
  225. package/dist/core/skill-sync/snapshot.js +120 -0
  226. package/dist/core/substrate/index.d.ts +1 -1
  227. package/dist/core/substrate/index.js +1 -1
  228. package/dist/core/substrate/injected-store.js +3 -3
  229. package/dist/core/substrate/on-read.js +68 -6
  230. package/dist/core/substrate/render.d.ts +8 -11
  231. package/dist/core/substrate/render.js +29 -43
  232. package/dist/core/substrate/schema.d.ts +10 -3
  233. package/dist/core/substrate/schema.js +6 -3
  234. package/dist/core/tui/host.d.ts +12 -4
  235. package/dist/core/tui/host.js +280 -149
  236. package/dist/core/view/bridge.d.ts +10 -0
  237. package/dist/core/view/bridge.js +31 -0
  238. package/dist/core/view/chrome.d.ts +9 -0
  239. package/dist/core/view/chrome.js +22 -0
  240. package/dist/core/view/contract.d.ts +171 -0
  241. package/dist/core/view/contract.js +23 -0
  242. package/dist/core/view/loader.d.ts +31 -0
  243. package/dist/core/view/loader.js +188 -0
  244. package/dist/core/view/transport-local.d.ts +7 -0
  245. package/dist/core/view/transport-local.js +70 -0
  246. package/dist/core/view/transport.d.ts +4 -0
  247. package/dist/core/view/transport.js +15 -0
  248. package/dist/daemon/crtrd.d.ts +38 -27
  249. package/dist/daemon/crtrd.js +303 -376
  250. package/dist/daemon/manage.js +6 -1
  251. package/dist/index.js +1 -1
  252. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +28 -20
  253. package/dist/pi-extensions/canvas-context-intro.js +4 -4
  254. package/dist/pi-extensions/canvas-doc-substrate.js +16 -16
  255. package/dist/pi-extensions/canvas-nav.js +5 -0
  256. package/dist/pi-extensions/canvas-recap.d.ts +37 -0
  257. package/dist/pi-extensions/canvas-recap.js +236 -0
  258. package/dist/pi-extensions/canvas-resume.js +2 -1
  259. package/dist/pi-extensions/canvas-stophook.d.ts +14 -12
  260. package/dist/pi-extensions/canvas-stophook.js +87 -101
  261. package/dist/pi-extensions/canvas-view.js +2 -1
  262. package/dist/pi-extensions/widget-order-bus.d.ts +6 -0
  263. package/dist/pi-extensions/widget-order-bus.js +34 -0
  264. package/dist/prompts/view.d.ts +2 -2
  265. package/dist/prompts/view.js +145 -73
  266. package/dist/types.d.ts +1 -6
  267. package/dist/types.js +1 -3
  268. package/dist/web/ViewChrome.d.ts +7 -0
  269. package/dist/web/ViewChrome.js +28 -0
  270. package/dist/web/ViewPane.d.ts +39 -0
  271. package/dist/web/ViewPane.js +48 -0
  272. package/dist/web/index.d.ts +6 -0
  273. package/dist/web/index.js +16 -0
  274. package/dist/web/runtime.d.ts +39 -0
  275. package/dist/web/runtime.js +133 -0
  276. package/dist/web/states.d.ts +24 -0
  277. package/dist/web/states.js +24 -0
  278. package/dist/web/transport-http.d.ts +5 -0
  279. package/dist/web/transport-http.js +28 -0
  280. package/dist/web-client/assets/index-BUvQb4hR.css +2 -0
  281. package/dist/web-client/assets/index-ClLQXYAE.js +10 -0
  282. package/dist/web-client/index.html +13 -0
  283. package/package.json +20 -6
  284. package/dist/builtin-views/canvas/client.mjs +0 -303
  285. package/dist/builtin-views/canvas/view.mjs +0 -576
  286. package/dist/builtin-views/git-pr/client.mjs +0 -440
  287. package/dist/builtin-views/git-pr/view.mjs +0 -675
  288. package/dist/builtin-views/inbox/sources/gmail.mjs +0 -965
  289. package/dist/builtin-views/inbox/sources/linkedin.mjs +0 -427
  290. package/dist/builtin-views/inbox/view.mjs +0 -889
  291. package/dist/builtin-views/linkedin/client.mjs +0 -610
  292. package/dist/builtin-views/linkedin/view.mjs +0 -1171
  293. package/dist/commands/pkg/bridge.d.ts +0 -1
  294. package/dist/commands/pkg/bridge.js +0 -137
  295. package/dist/commands/skill/author.d.ts +0 -3
  296. package/dist/commands/skill/author.js +0 -140
  297. package/dist/commands/skill/shared.d.ts +0 -3
  298. package/dist/commands/skill/shared.js +0 -19
  299. package/dist/commands/skill.d.ts +0 -2
  300. package/dist/commands/skill.js +0 -21
  301. package/dist/commands/tmux-spread.d.ts +0 -2
  302. package/dist/commands/tmux-spread.js +0 -144
  303. package/dist/core/__tests__/full/daemon-liveness-pane.full.test.js +0 -292
  304. package/dist/core/__tests__/full/placement-focus.test.js +0 -309
  305. package/dist/core/__tests__/full/placement-reconcile.test.js +0 -212
  306. package/dist/core/__tests__/full/placement-revive.test.js +0 -238
  307. package/dist/core/__tests__/full/placement-teardown.test.js +0 -297
  308. package/dist/core/__tests__/home-session.test.js +0 -186
  309. package/dist/core/__tests__/relaunch.test.js +0 -335
  310. package/dist/core/bridge-map.d.ts +0 -19
  311. package/dist/core/bridge-map.js +0 -73
  312. package/dist/core/tui/contract.d.ts +0 -83
  313. package/dist/core/tui/contract.js +0 -8
  314. package/dist/core/tui/loader.d.ts +0 -16
  315. package/dist/core/tui/loader.js +0 -94
  316. package/dist/prompts/skill.d.ts +0 -2
  317. package/dist/prompts/skill.js +0 -650
  318. /package/dist/{core/__tests__/full/daemon-liveness-pane.full.test.d.ts → clients/attach/__tests__/action-parity.test.d.ts} +0 -0
  319. /package/dist/{core/__tests__/full/placement-focus.test.d.ts → clients/attach/__tests__/clipboard-image.test.d.ts} +0 -0
  320. /package/dist/{core/__tests__/full/placement-reconcile.test.d.ts → clients/attach/__tests__/editor-newline.test.d.ts} +0 -0
  321. /package/dist/{core/__tests__/full/placement-revive.test.d.ts → clients/attach/__tests__/git-info.test.d.ts} +0 -0
  322. /package/dist/{core/__tests__/full/placement-teardown.test.d.ts → clients/attach/__tests__/onrequest-wired.test.d.ts} +0 -0
  323. /package/dist/{core/__tests__/home-session.test.d.ts → clients/attach/__tests__/slash-quit-copy.test.d.ts} +0 -0
  324. /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/titled-editor.test.d.ts} +0 -0
@@ -9,10 +9,14 @@
9
9
  // `pi --session` directly (see canvas-resume.ts header for the desync hazard).
10
10
  import { execFileSync } from 'node:child_process';
11
11
  import { resolve } from 'node:path';
12
- import { dashboardRowsAll, renderForest } from '../render.js';
13
- import { listNodes, subscriptionsOf } from '../canvas.js';
12
+ import { dashboardRowsAll, enrichRows, loadPreview, renderForest } from '../render.js';
13
+ import { listNodes, subscriptionsOf, getNode } from '../canvas.js';
14
+ import { closeNode } from '../../runtime/close.js';
14
15
  import { setupTerminal, restoreTerminal, getTerminalSize, parseKeypress, } from '../../tui/terminal.js';
15
- import { buildTree, flatten, TABS, SORTS } from './model.js';
16
+ import { buildTree, flatten, TABS } from './model.js';
17
+ // Sort cycle for the `s` key. Starts on the default `attention` ordering; one `s`
18
+ // press restores the structural `tree` view, then relevance/recency, then back.
19
+ const SORT_CYCLE = ['attention', 'tree', 'relevance', 'recency'];
16
20
  import { renderFrame, detectColorCaps, headerHeight, PREVIEW_HEIGHT } from './render.js';
17
21
  /** Viewport (body) height = total rows minus the header renderFrame draws (see
18
22
  * render.ts headerHeight), the footer, and the preview panel when shown. Kept
@@ -50,11 +54,16 @@ export async function runBrowse(opts = {}) {
50
54
  search: false,
51
55
  scrollOffset: 0,
52
56
  cwdScope: launchCwd, // default: this dir
53
- sort: 'tree',
57
+ sort: 'attention', // default: attention ordering (attached/streaming/live first)
54
58
  preview: true, // default ON (decision)
55
59
  residentsOnly: true, // default ON: hide one-shot workers (decision)
60
+ pendingClose: null,
56
61
  };
57
62
  let visible = [];
63
+ // The DashboardRow ref the tree holds for a visible id. Enrichment mutates this
64
+ // object in place, so a later renderFrame (reading off the same tree) re-renders
65
+ // the upgraded label/ctx/preview without a re-snapshot.
66
+ const rowOf = (id) => tree.nodes.get(id)?.row;
58
67
  // Color capability is fixed for the session (it's a property of the tty/env).
59
68
  const caps = detectColorCaps();
60
69
  // Restore the terminal exactly once, however we leave (quit, resume, crash).
@@ -114,11 +123,29 @@ export async function runBrowse(opts = {}) {
114
123
  state.scrollOffset = state.cursor - viewport + 1;
115
124
  if (state.scrollOffset < 0)
116
125
  state.scrollOffset = 0;
126
+ // Lazy paint: enrich only the rows about to be drawn (full label + ctx + asks),
127
+ // and load the selected row's preview. Both are idempotent (guard-flagged), so
128
+ // calling them every flush is cheap once a row is warm.
129
+ const top = state.scrollOffset;
130
+ const bottom = Math.min(visible.length, top + viewport);
131
+ const slice = [];
132
+ for (let i = top; i < bottom; i++) {
133
+ const r = rowOf(visible[i].id);
134
+ if (r !== undefined)
135
+ slice.push(r);
136
+ }
137
+ enrichRows(slice);
138
+ const cur = curRow();
139
+ if (cur !== undefined) {
140
+ const r = rowOf(cur.id);
141
+ if (r !== undefined)
142
+ loadPreview(r);
143
+ }
117
144
  const frame = renderFrame({
118
145
  tree, visible, tab: state.tab, cursor: state.cursor, scrollOffset: state.scrollOffset,
119
146
  query: state.query, search: state.search, totalNodes,
120
147
  cwdScope: state.cwdScope, sort: state.sort, preview: state.preview,
121
- residentsOnly: state.residentsOnly,
148
+ residentsOnly: state.residentsOnly, pendingClose: state.pendingClose,
122
149
  }, size, caps);
123
150
  process.stdout.write(frame);
124
151
  };
@@ -128,6 +155,50 @@ export async function runBrowse(opts = {}) {
128
155
  };
129
156
  const curRow = () => visible[state.cursor];
130
157
  const isExpanded = (id) => !state.collapsed.has(id);
158
+ // Every node in the snapshot subtree rooted at `id` (the node + all its tree
159
+ // descendants), used by the close-out streaming check.
160
+ const subtreeIds = (id) => {
161
+ const out = [];
162
+ const stack = [id];
163
+ const seen = new Set();
164
+ while (stack.length > 0) {
165
+ const cur = stack.pop();
166
+ if (seen.has(cur))
167
+ continue;
168
+ seen.add(cur);
169
+ out.push(cur);
170
+ for (const c of tree.nodes.get(cur)?.childIds ?? [])
171
+ stack.push(c);
172
+ }
173
+ return out;
174
+ };
175
+ // Is the node, or any descendant, GENUINELY mid-turn right now (from the snapshot)?
176
+ const anyStreaming = (id) => subtreeIds(id).some((x) => tree.nodes.get(x)?.row.streaming === true);
177
+ // Close-out (`x`): finalize the node to `done`, cascade-cancel its exclusive
178
+ // subtree, tear down each engine, and detach every attached viewer. Then reflect
179
+ // the new terminal statuses back into the live snapshot so the row updates in
180
+ // place (no re-snapshot). Best-effort: a teardown failure never wedges the picker.
181
+ const doClose = (id) => {
182
+ state.pendingClose = null;
183
+ let closed = [];
184
+ try {
185
+ closed = closeNode(id, { rootEvent: 'finalize' }).closed;
186
+ }
187
+ catch {
188
+ return; // unknown/already-gone node — nothing to reflect
189
+ }
190
+ for (const cid of closed) {
191
+ const n = tree.nodes.get(cid);
192
+ if (n === undefined)
193
+ continue;
194
+ const m = getNode(cid);
195
+ if (m !== null)
196
+ n.row.status = m.status; // real post-close status (done / canceled)
197
+ n.row.streaming = false;
198
+ n.row.viewed = false;
199
+ }
200
+ recompute(id);
201
+ };
131
202
  const cycleTab = (dir) => {
132
203
  const i = TABS.indexOf(state.tab);
133
204
  state.tab = TABS[(i + dir + TABS.length) % TABS.length];
@@ -135,11 +206,12 @@ export async function runBrowse(opts = {}) {
135
206
  state.scrollOffset = 0;
136
207
  recompute();
137
208
  };
138
- // Cycle sort (tree → relevance → recency), keeping the selected node put.
209
+ // Cycle sort (attention → tree → relevance → recency → attention), keeping the
210
+ // selected node put. One `s` from the default `attention` lands on `tree`.
139
211
  const cycleSort = () => {
140
212
  const keep = curRow()?.id;
141
- const i = SORTS.indexOf(state.sort);
142
- state.sort = SORTS[(i + 1) % SORTS.length];
213
+ const i = SORT_CYCLE.indexOf(state.sort);
214
+ state.sort = SORT_CYCLE[(i + 1) % SORT_CYCLE.length];
143
215
  recompute(keep);
144
216
  };
145
217
  // Toggle cwd scope between the launch dir and All dirs (no-op if launch dir
@@ -204,6 +276,19 @@ export async function runBrowse(opts = {}) {
204
276
  quit();
205
277
  return;
206
278
  }
279
+ // Close-out confirm sub-state: a streaming node is awaiting y/n. Swallow every
280
+ // other key so the confirm is modal.
281
+ if (state.pendingClose !== null) {
282
+ if (input === 'y' || input === 'Y') {
283
+ doClose(state.pendingClose);
284
+ flush();
285
+ return;
286
+ }
287
+ // n / Esc / anything else cancels the close.
288
+ state.pendingClose = null;
289
+ flush();
290
+ return;
291
+ }
207
292
  const row = curRow();
208
293
  // Quit.
209
294
  if (input === 'q' || key.escape)
@@ -294,6 +379,18 @@ export async function runBrowse(opts = {}) {
294
379
  flush();
295
380
  return;
296
381
  }
382
+ // Close-out the selected node (+ its exclusive subtree). If anything in that
383
+ // subtree is actively streaming, confirm first (y/n); otherwise close at once.
384
+ if (input === 'x') {
385
+ if (row !== undefined) {
386
+ if (anyStreaming(row.id))
387
+ state.pendingClose = row.id;
388
+ else
389
+ doClose(row.id);
390
+ }
391
+ flush();
392
+ return;
393
+ }
297
394
  // Sort / scope / residents / preview.
298
395
  if (input === 's') {
299
396
  cycleSort();
@@ -350,6 +447,32 @@ export async function runBrowse(opts = {}) {
350
447
  }
351
448
  setupTerminal();
352
449
  flush();
450
+ // Background corpus warmer: after the instant first paint, progressively enrich
451
+ // + load-preview every row (in small chunks, off the event loop) so the prompt
452
+ // super-search corpus lights up shortly after the instant name/kind/id search.
453
+ // Mutates row objects only; re-flushes solely while a live search is open so new
454
+ // matches surface. Stops as soon as the terminal is restored (quit/resume/crash).
455
+ const warmRows = [...tree.nodes.values()].map((n) => n.row);
456
+ let warmIdx = 0;
457
+ const WARM_CHUNK = 30;
458
+ const warmChunk = () => {
459
+ if (restored)
460
+ return;
461
+ const end = Math.min(warmIdx + WARM_CHUNK, warmRows.length);
462
+ const slice = warmRows.slice(warmIdx, end);
463
+ enrichRows(slice);
464
+ for (const r of slice)
465
+ loadPreview(r);
466
+ warmIdx = end;
467
+ // A live search may match the freshly-warmed prompts → recompute + repaint.
468
+ if (state.search && state.query !== '') {
469
+ recompute(curRow()?.id);
470
+ flush();
471
+ }
472
+ if (warmIdx < warmRows.length)
473
+ setImmediate(warmChunk);
474
+ };
475
+ setImmediate(warmChunk);
353
476
  await new Promise(() => {
354
477
  const onData = (data) => {
355
478
  try {
@@ -4,9 +4,11 @@ export type Tab = 'All' | 'Live' | 'Dormant' | 'Flagged';
4
4
  export declare const TABS: readonly Tab[];
5
5
  /** How the visible rows are ordered.
6
6
  * tree — spanning-tree order, ancestors shown for context (the default).
7
+ * attention — FLAT list, tiered by where your attention belongs (attached/
8
+ * streaming/live), newest-message-first within each tier.
7
9
  * relevance — FLAT list, best query match first (super-search).
8
10
  * recency — FLAT list, newest `created` first. */
9
- export type SortMode = 'tree' | 'relevance' | 'recency';
11
+ export type SortMode = 'tree' | 'attention' | 'relevance' | 'recency';
10
12
  export declare const SORTS: readonly SortMode[];
11
13
  /** Does a node belong to this tab's slice?
12
14
  * All — every node.
@@ -88,6 +90,22 @@ export declare function fieldScore(query: string, text: string): number;
88
90
  /** Weighted relevance of a row to the query across all searched fields. 0 means
89
91
  * no field matched (excluded from relevance results, same as `queryMatch`). */
90
92
  export declare function scoreRow(query: string, row: DashboardRow): number;
93
+ /** The "most recent message" signal for the attention sort: the pi session-file
94
+ * mtime (ms) the snapshot stamps on each row, falling back to the `created`
95
+ * birth timestamp when no session file exists. Larger = more recent.
96
+ *
97
+ * NOTE (phase-3 reconcile): phase 1 owns the field name on DashboardRow. We read
98
+ * `mtimeMs` here as the most likely name; if phase 1 lands a different name,
99
+ * update this single accessor. `created` is ISO 8601, so its epoch-ms is a
100
+ * monotonic stand-in within the same tier. */
101
+ export declare function attentionMtime(row: DashboardRow): number;
102
+ /** Attention tier (lower = higher priority, shown first):
103
+ * T0 — attached AND streaming (`viewed && streaming`)
104
+ * T1 — attached, not streaming (`viewed`)
105
+ * T2 — streaming, not attached
106
+ * T3 — live but neither (`status active|idle`)
107
+ * T4 — everything else (dormant: done/dead/canceled). */
108
+ export declare function attentionTier(row: DashboardRow): number;
91
109
  export interface VisibleRow {
92
110
  id: string;
93
111
  depth: number;
@@ -103,10 +121,10 @@ export interface FlattenOpts {
103
121
  * undefined = All dirs (no cwd filter). Like the tab predicate, it gates the
104
122
  * matched set — ancestors from other dirs still render dimmed for tree context. */
105
123
  cwdScope?: string | null;
106
- /** Lifecycle filter: when true, hide `terminal` (one-shot worker) nodes so only
107
- * the persistent `resident` agents you come back to show. The resume picker
108
- * defaults this ON. Undefined/false = every lifecycle. Gates the matched set
109
- * like the tab + cwd predicates (ancestors of a match still render for context). */
124
+ /** Lifecycle filter: when true, `terminal` (one-shot worker) nodes are kept out
125
+ * of the TOP-LEVEL rows and the flat search results but, in tree mode, still
126
+ * appear when you manually expand their parent fold (drilling into a resident).
127
+ * The resume picker defaults this ON. Undefined/false = every lifecycle. */
110
128
  residentsOnly?: boolean;
111
129
  /** Ordering. `tree` keeps the spanning tree + ancestor context; `relevance` /
112
130
  * `recency` produce a FLAT ranked list of directly-matched rows. */
@@ -5,7 +5,7 @@
5
5
  // access (dashboardRowsAll / listNodes / subscriptionsOf) into buildTree, then
6
6
  // drives flatten() on each keystroke.
7
7
  export const TABS = ['All', 'Live', 'Dormant', 'Flagged'];
8
- export const SORTS = ['tree', 'relevance', 'recency'];
8
+ export const SORTS = ['tree', 'attention', 'relevance', 'recency'];
9
9
  /** Does a node belong to this tab's slice?
10
10
  * All — every node.
11
11
  * Live — active | idle.
@@ -287,6 +287,43 @@ export function scoreRow(query, row) {
287
287
  FIELD_WEIGHTS.id * fieldScore(query, shortId(row.node_id)) +
288
288
  FIELD_WEIGHTS.prompt * (prompt !== '' ? fieldScore(query, prompt) : 0));
289
289
  }
290
+ // ---------------------------------------------------------------------------
291
+ // Attention tiering (default sort)
292
+ // ---------------------------------------------------------------------------
293
+ /** The "most recent message" signal for the attention sort: the pi session-file
294
+ * mtime (ms) the snapshot stamps on each row, falling back to the `created`
295
+ * birth timestamp when no session file exists. Larger = more recent.
296
+ *
297
+ * NOTE (phase-3 reconcile): phase 1 owns the field name on DashboardRow. We read
298
+ * `mtimeMs` here as the most likely name; if phase 1 lands a different name,
299
+ * update this single accessor. `created` is ISO 8601, so its epoch-ms is a
300
+ * monotonic stand-in within the same tier. */
301
+ export function attentionMtime(row) {
302
+ const m = row.mtimeMs;
303
+ if (typeof m === 'number' && Number.isFinite(m))
304
+ return m;
305
+ const t = Date.parse(row.created);
306
+ return Number.isFinite(t) ? t : 0;
307
+ }
308
+ /** Attention tier (lower = higher priority, shown first):
309
+ * T0 — attached AND streaming (`viewed && streaming`)
310
+ * T1 — attached, not streaming (`viewed`)
311
+ * T2 — streaming, not attached
312
+ * T3 — live but neither (`status active|idle`)
313
+ * T4 — everything else (dormant: done/dead/canceled). */
314
+ export function attentionTier(row) {
315
+ const viewed = row.viewed === true;
316
+ const streaming = row.streaming === true;
317
+ if (viewed && streaming)
318
+ return 0;
319
+ if (viewed)
320
+ return 1;
321
+ if (streaming)
322
+ return 2;
323
+ if (row.status === 'active' || row.status === 'idle')
324
+ return 3;
325
+ return 4;
326
+ }
290
327
  /** Is this row inside the active cwd scope? No scope (null/undefined) = All dirs. */
291
328
  export function cwdMatch(scope, row) {
292
329
  return scope === null || scope === undefined || row.cwd === scope;
@@ -312,13 +349,19 @@ export function lifecycleMatch(residentsOnly, row) {
312
349
  export function flatten(tree, opts) {
313
350
  const { collapsed, tab, query, cwdScope, residentsOnly, sort = 'tree' } = opts;
314
351
  // 1. Directly-matched nodes: tab predicate AND cwd scope AND lifecycle AND query.
352
+ // `matched` honors the residents-only filter (drives top-level rows, ancestor
353
+ // context, force-expand, and the flat search results below). `matchedAll` is
354
+ // the same set WITHOUT the lifecycle gate — it's how a terminal node becomes
355
+ // visible once you expand the resident parent that owns it (the fold-reveal
356
+ // below), so residents-only de-clutters the top level + search yet still lets
357
+ // you drill into a fold and see everything under it.
315
358
  const matched = new Set();
359
+ const matchedAll = new Set();
316
360
  for (const [id, node] of tree.nodes) {
317
- if (tabPredicate(tab, node.row) &&
318
- cwdMatch(cwdScope, node.row) &&
319
- lifecycleMatch(residentsOnly, node.row) &&
320
- queryMatch(query, node.row)) {
321
- matched.add(id);
361
+ if (tabPredicate(tab, node.row) && cwdMatch(cwdScope, node.row) && queryMatch(query, node.row)) {
362
+ matchedAll.add(id);
363
+ if (lifecycleMatch(residentsOnly, node.row))
364
+ matched.add(id);
322
365
  }
323
366
  }
324
367
  // FLAT ranked modes (relevance / recency): no tree, no ancestors — just the
@@ -328,7 +371,14 @@ export function flatten(tree, opts) {
328
371
  const ids = [...matched];
329
372
  const createdOf = (id) => tree.nodes.get(id)?.row.created ?? '';
330
373
  const byRecency = (a, b) => createdOf(b).localeCompare(createdOf(a));
331
- if (sort === 'recency' || query === '') {
374
+ if (sort === 'attention') {
375
+ // Tiered (attached/streaming/live first), newest-message-first within tier.
376
+ const rowOf = (id) => tree.nodes.get(id).row;
377
+ ids.sort((a, b) => (attentionTier(rowOf(a)) - attentionTier(rowOf(b))) ||
378
+ (attentionMtime(rowOf(b)) - attentionMtime(rowOf(a))) ||
379
+ byRecency(a, b));
380
+ }
381
+ else if (sort === 'recency' || query === '') {
332
382
  ids.sort(byRecency);
333
383
  }
334
384
  else {
@@ -354,14 +404,19 @@ export function flatten(tree, opts) {
354
404
  return true; // force-expand to reveal matches
355
405
  return !collapsed.has(id);
356
406
  };
357
- // 3. Walk the tree in order, emitting included nodes and descending only into
358
- // expanded ones.
407
+ // 3. Walk the tree in order, emitting shown nodes and descending only into
408
+ // expanded ones. A node is shown when it is `included` (matched or ancestor),
409
+ // OR — under residents-only — when its already-shown parent is expanded and the
410
+ // node matches everything but the lifecycle gate (`matchedAll`). That is the
411
+ // fold-reveal: a terminal worker hidden from the top level reappears the moment
412
+ // you open the resident fold that owns it. `parentShown` threads that down.
359
413
  const out = [];
360
- const walk = (id) => {
414
+ const walk = (id, parentShown) => {
361
415
  const node = tree.nodes.get(id);
362
416
  if (node === undefined)
363
417
  return;
364
- if (included(id)) {
418
+ const show = included(id) || (residentsOnly === true && parentShown && matchedAll.has(id));
419
+ if (show) {
365
420
  const hasChildren = node.childIds.length > 0;
366
421
  out.push({
367
422
  id,
@@ -373,10 +428,10 @@ export function flatten(tree, opts) {
373
428
  }
374
429
  if (isExpanded(id)) {
375
430
  for (const c of node.childIds)
376
- walk(c);
431
+ walk(c, show);
377
432
  }
378
433
  };
379
434
  for (const r of tree.roots)
380
- walk(r);
435
+ walk(r, false);
381
436
  return out;
382
437
  }
@@ -2,7 +2,9 @@ import type { Tab, Tree, VisibleRow, SortMode } from './model.js';
2
2
  import { detectColorCaps, type ColorCaps } from '../../tui/draw.js';
3
3
  export { detectColorCaps };
4
4
  export type { ColorCaps };
5
- export declare const PREVIEW_BODY = 5;
5
+ export declare const PROMPT_LINES = 3;
6
+ export declare const REPLY_LINES = 3;
7
+ export declare const PREVIEW_BODY: number;
6
8
  export declare const PREVIEW_HEIGHT: number;
7
9
  export declare function headerHeight(search: boolean): number;
8
10
  export interface RenderState {
@@ -23,6 +25,10 @@ export interface RenderState {
23
25
  /** Lifecycle filter: when true, `terminal` (one-shot worker) nodes are hidden
24
26
  * — surfaced as a status-line cue. */
25
27
  residentsOnly: boolean;
28
+ /** When set, a close-out (`x`) is awaiting y/n confirmation because the node (or
29
+ * a descendant) is actively streaming. Holds the node id being confirmed; the
30
+ * footer becomes the y/n prompt. null/undefined = no pending confirm. */
31
+ pendingClose?: string | null;
26
32
  }
27
33
  /**
28
34
  * Render the whole frame. Returns a single string that, written as-is, repaints