@pellux/goodvibes-tui 2.0.21 → 2.1.0

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 (388) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +16 -16
  3. package/docs/README.md +4 -4
  4. package/docs/foundation-artifacts/operator-contract.json +1 -1
  5. package/package.json +16 -6
  6. package/src/cli/management.ts +5 -2
  7. package/src/cli/tui-startup.ts +34 -39
  8. package/src/core/agent-view-render.ts +157 -0
  9. package/src/core/approval-alert.ts +49 -11
  10. package/src/core/bookmark-navigation.ts +7 -1
  11. package/src/core/budget-breach-notifier.ts +27 -8
  12. package/src/core/compaction-receipt.ts +1 -1
  13. package/src/core/context-usage.ts +1 -1
  14. package/src/core/conversation-compaction-render.ts +3 -3
  15. package/src/core/conversation-display-only.ts +86 -0
  16. package/src/core/conversation-event-navigation.ts +27 -0
  17. package/src/core/conversation-line-cache.ts +216 -447
  18. package/src/core/conversation-render-context.ts +20 -27
  19. package/src/core/conversation-rendering.ts +157 -575
  20. package/src/core/conversation-splash-state.ts +6 -1
  21. package/src/core/conversation-types.ts +35 -0
  22. package/src/core/conversation-usage.ts +12 -0
  23. package/src/core/conversation.ts +130 -131
  24. package/src/core/long-task-notifier.ts +100 -31
  25. package/src/core/notices.ts +75 -0
  26. package/src/core/pairing-tailscale-gateway.ts +1 -1
  27. package/src/core/process-output-log.ts +143 -0
  28. package/src/core/rewind-receipt.ts +1 -1
  29. package/src/core/session-focus.ts +190 -0
  30. package/src/core/session-resume-core.ts +23 -81
  31. package/src/core/stream-event-wiring.ts +31 -5
  32. package/src/core/system-message-noise.ts +2 -2
  33. package/src/core/system-message-router.ts +16 -18
  34. package/src/core/turn-event-wiring.ts +272 -33
  35. package/src/core/work-tree-focus.ts +290 -0
  36. package/src/core/work-tree-fold-store.ts +143 -0
  37. package/src/core/work-tree-model.ts +799 -0
  38. package/src/core/work-tree-render.ts +384 -0
  39. package/src/core/work-tree-sources.ts +184 -0
  40. package/src/core/work-tree-wiring.ts +176 -0
  41. package/src/core/workstream-notification.ts +36 -6
  42. package/src/core/wrfc-notice-titles.ts +37 -0
  43. package/src/input/agents-modal-text.ts +119 -0
  44. package/src/input/agents-modal.ts +699 -0
  45. package/src/input/bookmark-modal.ts +33 -9
  46. package/src/input/changes-git.ts +251 -0
  47. package/src/input/changes-modal.ts +629 -0
  48. package/src/input/changes-model.ts +125 -0
  49. package/src/input/command-palette-types.ts +41 -0
  50. package/src/input/command-palette.ts +272 -0
  51. package/src/input/command-registry.ts +77 -35
  52. package/src/input/commands/acp-runtime.ts +19 -9
  53. package/src/input/commands/channel-runtime.ts +2 -2
  54. package/src/input/commands/ci-runtime.ts +1 -1
  55. package/src/input/commands/codebase-runtime.ts +1 -1
  56. package/src/input/commands/config.ts +7 -1
  57. package/src/input/commands/context-window.ts +25 -19
  58. package/src/input/commands/control-room-runtime.ts +18 -18
  59. package/src/input/commands/conversation-runtime.ts +0 -1
  60. package/src/input/commands/cost-runtime.ts +29 -20
  61. package/src/input/commands/diff-runtime.ts +35 -181
  62. package/src/input/commands/eval.ts +1 -1
  63. package/src/input/commands/experience-runtime.ts +3 -7
  64. package/src/input/commands/fresh-conversation.ts +86 -0
  65. package/src/input/commands/git-runtime.ts +5 -22
  66. package/src/input/commands/graph-runtime.ts +1 -1
  67. package/src/input/commands/guidance-runtime.ts +1 -1
  68. package/src/input/commands/health-runtime.ts +8 -9
  69. package/src/input/commands/hooks-runtime.ts +2 -2
  70. package/src/input/commands/hosted-runtime.ts +3 -3
  71. package/src/input/commands/incident-runtime.ts +3 -3
  72. package/src/input/commands/intelligence-runtime.ts +6 -9
  73. package/src/input/commands/knowledge.ts +2 -2
  74. package/src/input/commands/legacy-panel-command.ts +57 -0
  75. package/src/input/commands/local-auth-runtime.ts +8 -9
  76. package/src/input/commands/local-provider-runtime.ts +7 -4
  77. package/src/input/commands/local-runtime.ts +12 -15
  78. package/src/input/commands/local-setup-review.ts +1 -1
  79. package/src/input/commands/local-setup.ts +13 -19
  80. package/src/input/commands/memory.ts +1 -1
  81. package/src/input/commands/notify-runtime.ts +9 -0
  82. package/src/input/commands/operator-runtime.ts +10 -12
  83. package/src/input/commands/permissions-provenance.ts +3 -3
  84. package/src/input/commands/planning-runtime.ts +7 -7
  85. package/src/input/commands/platform-sandbox-runtime.ts +2 -2
  86. package/src/input/commands/platform-services-runtime.ts +1 -1
  87. package/src/input/commands/policy-dispatch.ts +2 -2
  88. package/src/input/commands/policy.ts +3 -3
  89. package/src/input/commands/provider.ts +1 -1
  90. package/src/input/commands/remote-runtime.ts +1 -1
  91. package/src/input/commands/review-runtime.ts +39 -94
  92. package/src/input/commands/rewind-runtime.ts +81 -108
  93. package/src/input/commands/runtime-services.ts +15 -17
  94. package/src/input/commands/services-runtime.ts +2 -2
  95. package/src/input/commands/session-content.ts +21 -5
  96. package/src/input/commands/session-workflow.ts +13 -38
  97. package/src/input/commands/settings-sync-runtime.ts +2 -2
  98. package/src/input/commands/shell-core.ts +19 -4
  99. package/src/input/commands/skills-runtime.ts +2 -2
  100. package/src/input/commands/status-runtime.ts +13 -0
  101. package/src/input/commands/subscription-runtime.ts +1 -1
  102. package/src/input/commands/tasks-runtime.ts +2 -8
  103. package/src/input/commands/work-plan-runtime.ts +4 -4
  104. package/src/input/commands/workstream-attempts.ts +24 -47
  105. package/src/input/commands/workstream-runtime.ts +2 -2
  106. package/src/input/commands/worktree-runtime.ts +3 -3
  107. package/src/input/commands.ts +3 -1
  108. package/src/input/composer-editor.ts +44 -0
  109. package/src/input/concealed-input.ts +1 -1
  110. package/src/input/config-modal-types.ts +20 -8
  111. package/src/input/config-modal.ts +106 -105
  112. package/src/input/confirm-dialog-types.ts +28 -0
  113. package/src/input/confirm-dialog.ts +70 -0
  114. package/src/input/delete-key-policy.ts +3 -3
  115. package/src/input/feed-context-factory.ts +15 -16
  116. package/src/input/handler-command-route.ts +7 -42
  117. package/src/input/handler-content-actions.ts +16 -15
  118. package/src/input/handler-feed-routes.ts +26 -211
  119. package/src/input/handler-feed.ts +68 -95
  120. package/src/input/handler-interactions.ts +5 -3
  121. package/src/input/handler-modal-routes.ts +215 -216
  122. package/src/input/handler-modal-stack.ts +81 -11
  123. package/src/input/handler-modal-token-routes.ts +4 -0
  124. package/src/input/handler-onboarding.ts +0 -1
  125. package/src/input/handler-picker-routes.ts +65 -107
  126. package/src/input/handler-session-view-route.ts +149 -0
  127. package/src/input/handler-shortcuts.ts +16 -154
  128. package/src/input/handler-types.ts +4 -2
  129. package/src/input/handler-ui-state.ts +45 -15
  130. package/src/input/handler-work-tree-route.ts +123 -0
  131. package/src/input/handler.ts +25 -70
  132. package/src/input/keybindings.ts +16 -71
  133. package/src/input/masked-entry-modal.ts +117 -0
  134. package/src/input/mcp-workspace.ts +48 -17
  135. package/src/input/model-picker-filter.ts +8 -0
  136. package/src/input/model-picker-open.ts +99 -0
  137. package/src/input/model-picker-types.ts +1 -1
  138. package/src/input/model-picker.ts +92 -9
  139. package/src/input/notifications-modal.ts +123 -0
  140. package/src/input/overlay-filter.ts +26 -0
  141. package/src/input/profile-picker-modal.ts +37 -8
  142. package/src/input/search.ts +3 -3
  143. package/src/input/selection-modal-queue.ts +2 -0
  144. package/src/input/selection-modal.ts +28 -16
  145. package/src/input/session-picker-modal.ts +33 -8
  146. package/src/input/settings-modal-activation.ts +7 -2
  147. package/src/input/settings-modal-data.ts +11 -6
  148. package/src/input/settings-modal-types.ts +1 -1
  149. package/src/input/settings-modal.ts +20 -20
  150. package/src/input/submission-intent.ts +1 -1
  151. package/src/input/submission-router.ts +4 -9
  152. package/src/input/surface-modal-host.ts +110 -0
  153. package/src/input/theme-settings-actions.ts +82 -0
  154. package/src/input/usage-modal.ts +183 -0
  155. package/src/input/views.ts +97 -0
  156. package/src/main.ts +175 -165
  157. package/src/permissions/hunk-selection.ts +1 -1
  158. package/src/permissions/prompt-card.ts +511 -0
  159. package/src/permissions/prompt.ts +59 -622
  160. package/src/renderer/agents-modal.ts +310 -0
  161. package/src/renderer/autocomplete-overlay.ts +42 -220
  162. package/src/renderer/back-to-bottom.ts +80 -0
  163. package/src/renderer/block-actions-overlay.ts +30 -48
  164. package/src/renderer/bookmark-modal.ts +48 -79
  165. package/src/renderer/changes-modal.ts +325 -0
  166. package/src/renderer/code-block.ts +184 -106
  167. package/src/renderer/command-palette.ts +93 -0
  168. package/src/renderer/composer-fragments.ts +4 -4
  169. package/src/renderer/composer.ts +146 -0
  170. package/src/renderer/compositor.ts +26 -160
  171. package/src/renderer/config-modal.ts +185 -81
  172. package/src/renderer/confirm-dialog.ts +50 -0
  173. package/src/renderer/context-inspector.ts +116 -154
  174. package/src/renderer/context-status-hint.ts +4 -4
  175. package/src/renderer/conversation-layout.ts +20 -0
  176. package/src/renderer/conversation-overlays.ts +94 -84
  177. package/src/renderer/conversation-surface.ts +19 -38
  178. package/src/renderer/diff-tint.ts +98 -0
  179. package/src/renderer/file-picker-overlay.ts +63 -90
  180. package/src/renderer/footer-targets.ts +38 -0
  181. package/src/renderer/git-status.ts +3 -4
  182. package/src/renderer/header-line.ts +140 -117
  183. package/src/renderer/help-overlay.ts +133 -219
  184. package/src/renderer/history-search-overlay.ts +15 -12
  185. package/src/renderer/lane-graph/bead.ts +800 -0
  186. package/src/renderer/lane-graph/glyphs.ts +134 -0
  187. package/src/renderer/lane-graph/layout.ts +248 -0
  188. package/src/renderer/lane-graph/paint.ts +537 -0
  189. package/src/renderer/lane-graph/semantic-memo.ts +77 -0
  190. package/src/renderer/layout-engine.ts +2 -57
  191. package/src/renderer/layout.ts +7 -13
  192. package/src/renderer/markdown-inline.ts +2 -1
  193. package/src/renderer/markdown-table.ts +17 -12
  194. package/src/renderer/markdown.ts +92 -34
  195. package/src/renderer/masked-entry-modal.ts +78 -0
  196. package/src/renderer/mcp-workspace.ts +166 -203
  197. package/src/renderer/model-workspace.ts +381 -436
  198. package/src/renderer/notifications-modal.ts +116 -0
  199. package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
  200. package/src/renderer/process-view.ts +192 -0
  201. package/src/renderer/profile-picker-modal.ts +58 -110
  202. package/src/renderer/search-overlay.ts +6 -3
  203. package/src/renderer/selection-modal-overlay.ts +87 -327
  204. package/src/renderer/semantic-diff.ts +2 -2
  205. package/src/renderer/session-chips.ts +102 -0
  206. package/src/renderer/session-picker-modal.ts +177 -297
  207. package/src/renderer/settings-modal-connections.ts +14 -23
  208. package/src/renderer/settings-modal-context.ts +367 -0
  209. package/src/renderer/settings-modal-helpers.ts +6 -11
  210. package/src/renderer/settings-modal.ts +322 -726
  211. package/src/renderer/shell-surface.ts +277 -175
  212. package/src/renderer/shortcuts-overlay.ts +217 -0
  213. package/src/renderer/status-line.ts +475 -0
  214. package/src/renderer/surface-compose.ts +174 -0
  215. package/src/renderer/surface-kit-extra.ts +303 -0
  216. package/src/renderer/surface-kit-list.ts +366 -0
  217. package/src/renderer/surface-kit-parts.ts +360 -0
  218. package/src/renderer/surface-kit.ts +568 -0
  219. package/src/renderer/syntax-highlighter.ts +280 -197
  220. package/src/renderer/syntax-theme.ts +51 -0
  221. package/src/renderer/system-message.ts +0 -1
  222. package/src/renderer/term-caps.ts +42 -1
  223. package/src/renderer/terminal-bg-probe.ts +116 -19
  224. package/src/renderer/terminal-palette-probe.ts +322 -0
  225. package/src/renderer/terminal-palette-reader.ts +183 -0
  226. package/src/renderer/terminal-palette.ts +58 -0
  227. package/src/renderer/theme-mode-config.ts +16 -1
  228. package/src/renderer/theme.ts +234 -207
  229. package/src/renderer/thinking.ts +0 -1
  230. package/src/renderer/throbber.ts +188 -0
  231. package/src/renderer/toast-center.ts +119 -0
  232. package/src/renderer/ui-factory.ts +39 -602
  233. package/src/renderer/ui-primitives.ts +8 -25
  234. package/src/renderer/usage-modal.ts +353 -0
  235. package/src/renderer/user-message.ts +58 -0
  236. package/src/renderer/voice-capture-chip.ts +53 -0
  237. package/src/runtime/bootstrap-command-context.ts +0 -5
  238. package/src/runtime/bootstrap-command-parts.ts +38 -89
  239. package/src/runtime/bootstrap-core.ts +13 -15
  240. package/src/runtime/bootstrap-hook-bridge.ts +12 -21
  241. package/src/runtime/bootstrap-notifier-sync.ts +17 -1
  242. package/src/runtime/bootstrap-shell.ts +16 -50
  243. package/src/runtime/bootstrap.ts +14 -14
  244. package/src/runtime/client/fleet-union.ts +5 -5
  245. package/src/runtime/client/hosted-exit.ts +2 -2
  246. package/src/runtime/client/hosted-sessions.ts +1 -1
  247. package/src/runtime/client/spine-adoption.ts +1 -1
  248. package/src/runtime/context-accounting-source.ts +5 -4
  249. package/src/runtime/index.ts +1 -0
  250. package/src/runtime/notification-dispatch.ts +102 -29
  251. package/src/runtime/recovery-offer-wiring.ts +4 -1
  252. package/src/runtime/recovery-prompt.ts +31 -15
  253. package/src/runtime/runtime-services-types.ts +10 -6
  254. package/src/runtime/services.ts +17 -12
  255. package/src/runtime/session-continuity-hints.ts +2 -5
  256. package/src/runtime/session-inbound-inputs.ts +1 -1
  257. package/src/runtime/session-resume-liveness-confirm.ts +14 -2
  258. package/src/runtime/startup-typeahead-gate.ts +103 -0
  259. package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
  260. package/src/runtime/store/domains/index.ts +0 -8
  261. package/src/runtime/store/selectors/index.ts +0 -28
  262. package/src/runtime/store/state.ts +0 -8
  263. package/src/runtime/ui-services.ts +4 -6
  264. package/src/runtime/usage-tracker.ts +348 -0
  265. package/src/runtime/wrfc-persistence.ts +4 -4
  266. package/src/shell/blocking-input.ts +92 -2
  267. package/src/shell/recovery-input-helpers.ts +1 -1
  268. package/src/shell/session-views.ts +539 -0
  269. package/src/shell/status-report.ts +86 -0
  270. package/src/shell/transcript-scroll.ts +68 -0
  271. package/src/shell/ui-openers.ts +84 -172
  272. package/src/shell/view-openers.ts +205 -0
  273. package/src/tools/wrfc-agent-guard.ts +22 -2
  274. package/src/utils/format-duration.ts +4 -8
  275. package/src/utils/splash-lines.ts +9 -1
  276. package/src/utils/terminal-width.ts +56 -0
  277. package/src/verification/verification-ledger.ts +18 -16
  278. package/src/{panels → views}/agent-inspector-shared.ts +2 -2
  279. package/src/{panels → views}/builtin-modals.ts +22 -21
  280. package/src/views/builtin-views.ts +135 -0
  281. package/src/{panels → views}/confirm-state.ts +9 -9
  282. package/src/{panels → views}/diff-review-model.ts +10 -3
  283. package/src/{panels → views}/eval-registry.ts +1 -1
  284. package/src/{panels → views}/fleet-acts.ts +49 -85
  285. package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
  286. package/src/{panels → views}/fleet-gateway.ts +5 -5
  287. package/src/{panels → views}/fleet-observed-render.ts +16 -16
  288. package/src/{panels → views}/fleet-read-model.ts +9 -9
  289. package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
  290. package/src/{panels → views}/fleet-spawn.ts +33 -49
  291. package/src/{panels → views}/fleet-steer.ts +12 -12
  292. package/src/{panels → views}/fleet-stop.ts +4 -4
  293. package/src/{panels → views}/fleet-transcript.ts +26 -27
  294. package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
  295. package/src/{panels → views}/hosted-session-feed.ts +6 -6
  296. package/src/views/modal-surface-registry.ts +41 -0
  297. package/src/{panels → views}/modals/devices-modal.ts +2 -2
  298. package/src/{panels → views}/modals/hooks-modal.ts +13 -13
  299. package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
  300. package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
  301. package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
  302. package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
  303. package/src/{panels → views}/modals/memory-modal.ts +8 -8
  304. package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
  305. package/src/{panels → views}/modals/pairing-modal.ts +9 -9
  306. package/src/{panels → views}/modals/planning-modal.ts +15 -20
  307. package/src/{panels → views}/modals/plugins-modal.ts +6 -6
  308. package/src/{panels → views}/modals/policy-modal.ts +13 -5
  309. package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
  310. package/src/{panels → views}/modals/remote-modal.ts +3 -3
  311. package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
  312. package/src/{panels → views}/modals/security-modal.ts +22 -16
  313. package/src/{panels → views}/modals/services-modal.ts +3 -3
  314. package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
  315. package/src/{panels → views}/modals/skills-modal.ts +9 -9
  316. package/src/{panels → views}/modals/subscription-modal.ts +3 -3
  317. package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
  318. package/src/views/notifications-feed.ts +314 -0
  319. package/src/{panels → views}/polish-core.ts +38 -36
  320. package/src/{panels → views}/polish-tables.ts +35 -26
  321. package/src/{panels → views}/polish.ts +101 -95
  322. package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
  323. package/src/{panels → views}/provider-health-routes.ts +14 -14
  324. package/src/{panels → views}/search-focus.ts +8 -8
  325. package/src/{panels → views}/session-maintenance.ts +11 -11
  326. package/src/views/skills-discovery.ts +159 -0
  327. package/src/views/view-deps.ts +80 -0
  328. package/src/views/view-panel-adapter.ts +53 -0
  329. package/src/{panels → views}/workstream-graph-render.ts +1 -1
  330. package/src/core/conversation-fold.ts +0 -101
  331. package/src/core/conversation-turn-structure.ts +0 -464
  332. package/src/core/conversation-user-receipts.ts +0 -42
  333. package/src/input/commands/operator-panel-runtime.ts +0 -186
  334. package/src/input/panel-integration-actions.ts +0 -33
  335. package/src/input/panel-mouse-geometry.ts +0 -97
  336. package/src/input/panel-paste-flood-guard.ts +0 -86
  337. package/src/panels/base-panel.ts +0 -301
  338. package/src/panels/builtin/agent.ts +0 -27
  339. package/src/panels/builtin/development.ts +0 -92
  340. package/src/panels/builtin/knowledge.ts +0 -12
  341. package/src/panels/builtin/operations.ts +0 -177
  342. package/src/panels/builtin/session.ts +0 -81
  343. package/src/panels/builtin/shared.ts +0 -357
  344. package/src/panels/builtin-panels.ts +0 -28
  345. package/src/panels/cost-tracker-panel.ts +0 -746
  346. package/src/panels/diff-panel.ts +0 -797
  347. package/src/panels/diff-review-panel.ts +0 -395
  348. package/src/panels/expandable-list-panel.ts +0 -189
  349. package/src/panels/fleet-deep-link.ts +0 -31
  350. package/src/panels/fleet-panel.ts +0 -799
  351. package/src/panels/git-panel.ts +0 -800
  352. package/src/panels/hosted-session-panel.ts +0 -221
  353. package/src/panels/index.ts +0 -21
  354. package/src/panels/local-auth-panel.ts +0 -478
  355. package/src/panels/notifications-feed.ts +0 -127
  356. package/src/panels/notifications-panel.ts +0 -160
  357. package/src/panels/panel-confirm-overlay.ts +0 -72
  358. package/src/panels/panel-manager.ts +0 -791
  359. package/src/panels/plugins-panel.ts +0 -371
  360. package/src/panels/scrollable-list-panel.ts +0 -498
  361. package/src/panels/skills-panel.ts +0 -454
  362. package/src/panels/token-budget-panel.ts +0 -688
  363. package/src/panels/types.ts +0 -191
  364. package/src/panels/wrfc-panel-format.ts +0 -133
  365. package/src/renderer/diff-view.ts +0 -134
  366. package/src/renderer/fleet-tab-strip.ts +0 -56
  367. package/src/renderer/footer-tips.ts +0 -53
  368. package/src/renderer/fullscreen-primitives.ts +0 -146
  369. package/src/renderer/fullscreen-workspace.ts +0 -194
  370. package/src/renderer/hint-grammar.ts +0 -52
  371. package/src/renderer/modal-factory.ts +0 -480
  372. package/src/renderer/overlay-box.ts +0 -154
  373. package/src/renderer/overlay-viewport.ts +0 -104
  374. package/src/renderer/panel-composite.ts +0 -145
  375. package/src/renderer/panel-workspace-bar.ts +0 -54
  376. package/src/renderer/process-indicator.ts +0 -163
  377. package/src/renderer/tab-strip.ts +0 -222
  378. package/src/renderer/text-layout.ts +0 -31
  379. package/src/renderer/tool-call.ts +0 -276
  380. package/src/renderer/tool-result-expanded-lines.ts +0 -48
  381. package/src/renderer/tool-result-summary.ts +0 -219
  382. package/src/runtime/diagnostics/panels/index.ts +0 -21
  383. package/src/runtime/diagnostics/panels/ops.ts +0 -156
  384. package/src/runtime/perf/panel-contracts.ts +0 -32
  385. package/src/runtime/store/domains/panels.ts +0 -117
  386. /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
  387. /package/src/{panels → views}/modals/modal-theme.ts +0 -0
  388. /package/src/{panels → views}/provider-health-domains.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,64 @@ All notable changes to GoodVibes TUI.
4
4
 
5
5
  ---
6
6
 
7
+ ## [2.1.0] - 2026-09-30
8
+
9
+ ### Added
10
+
11
+ - **Themes.** display.theme picks goodvibes (the default), goodvibes-neon,
12
+ catppuccin, tokyonight, dracula, nord, gruvbox, one-dark, rosepine,
13
+ solarized, github, or system, which follows the terminal's own palette,
14
+ under tmux and over ssh too. Settings previews themes live. A theme you
15
+ already chose is kept.
16
+ - **Modals replace panes.** Usage, Changes, Agents and Notifications open as
17
+ large dimmed modals on one surface kit with a live search row and one-level
18
+ Esc; ctrl+p opens the command palette.
19
+ - **A calmer base screen.** A one-row header, an empty row under it,
20
+ full-width user messages, an input area that holds only input, and a status
21
+ line with the context bar and its compaction tick.
22
+ - **The throbber is back** on its own row above the input area, naming what
23
+ the session is doing with elapsed time, with half-row gaps between output,
24
+ throbber, input area and status line.
25
+ - **A lane-graph work tree** with status on each bead, agents as lanes,
26
+ folding, and a display.treeGlyphs choice of rounded, square or ascii.
27
+ - **Full-screen agent and process views** with session chips. Esc never
28
+ stops them and ctrl+x asks before stopping.
29
+ - **A back-to-bottom pill** shows while the output is scrolled back. Esc
30
+ returns to the live output and never interrupts the turn.
31
+ - **Notifications name the work** on the desktop, in the terminal, on
32
+ webhooks and for approvals, budgets, agents and workstreams. The
33
+ behavior.notificationsMetadataOnly setting sends metadata only and is off
34
+ by default.
35
+ - **System notices are toasts** kept in a /notifications history with plain
36
+ names, one entry per event.
37
+ - /context window and /status say where the context window came from.
38
+
39
+ ### Changed
40
+
41
+ - **WRFC chains run in their own git worktree.** A passed chain commits only
42
+ its own changes plus edits GoodVibes' own tools made, runs your git hooks,
43
+ and leaves your other uncommitted edits in place. A failed chain stops its
44
+ remaining fix tasks.
45
+ - A remote model with no real window takes it from the models.dev catalog,
46
+ so abacusai route-llm reads 128.0k instead of a guessed 8.2k.
47
+ - The pins move to sdk 2.1.0 and daemon 1.29.0.
48
+ - Every open dependency advisory is closed: undici, brace-expansion, the
49
+ Anthropic client (0.92), qs, body-parser, ip-address, fflate, fast-uri,
50
+ js-yaml and sharp move to patched versions.
51
+
52
+ ### Fixed
53
+
54
+ - Read and command bodies keep their indentation; wrapped prose no longer
55
+ starts one column right; long code lines wrap.
56
+ - A finished turn stops saying working; ended processes stop counting as
57
+ running.
58
+ - The recovery offer opens before the first frame and never takes keys typed
59
+ before it appeared.
60
+ - Instructions meant for the model no longer show as notices, and notices
61
+ keep their full text.
62
+ - Printed command output survives resizes.
63
+
64
+
7
65
  ## [2.0.21] - 2026-08-23
8
66
 
9
67
  ### Changes
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Version](https://img.shields.io/badge/version-2.0.21-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
5
+ [![Version](https://img.shields.io/badge/version-2.1.0-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
6
6
 
7
7
  GoodVibes is a terminal console for coding and operations work with an AI model. You run `goodvibes` in a project directory and get a full-screen terminal app where you talk to a model that can read and edit your files, run shell commands, search the web, and hand work off to background agents, asking your permission before anything that writes or executes.
8
8
 
9
- It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that `/scan` finds on your machine and LAN), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn. Alongside the conversation, panels turn background work into live control rooms: running agents, git state, diffs, tokens, cost.
9
+ It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that `/scan` finds on your machine and LAN), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn. Background work opens in large modals over the conversation: running agents, changed files and diffs, tokens and cost.
10
10
 
11
11
  <img src="docs/assets/splash.png" alt="GoodVibes starting up in a terminal: a glitch-art GOODVIBES wordmark in cyan-to-magenta gradient, the version and tagline beneath it, the active model and tool count, the working directory, a line offering to resume the last session, and a hint line reading Ctrl+P panels / ? help / F2 fleet. A status footer shows mode, token usage, context usage, and the daemon and web listener addresses." width="900">
12
12
 
@@ -80,11 +80,11 @@ Models come from a live catalog, so the picker lists far more than a hardcoded s
80
80
 
81
81
  The `synthetic` provider groups the same model across every backend that serves it into one entry. Pick it and requests route to whichever backend is healthy, failing over on rate limits and transient errors without changing the model you chose, and without ever crossing the free, paid, and subscription boundaries. Any OpenAI-compatible API can be added as a custom provider by dropping a JSON file in `~/.goodvibes/tui/providers/`. It is hot-reloaded. See [docs/providers-and-routing.md](docs/providers-and-routing.md).
82
82
 
83
- ### The Fleet control room
83
+ ### The Agents control room
84
84
 
85
85
  <img src="docs/assets/fleet-panel.png" alt="The Fleet panel open beside the conversation. It lists background work as rows with a kind, a name, elapsed time, token count, and cost: a CI watch poller and a runtime heartbeat watcher at 4 minutes 23 seconds each, and an idle code indexer. The detail region below shows the selected watcher's id, state idle, elapsed time, model, tokens, cost, a headline reading CI watch poller, healthy, an activity phase, and an approvals line. The action hints read j/k navigate, Enter attach, K kill, A archive finished, v archived, n host agent." width="900">
86
86
 
87
- Work that is not conversation goes into panels instead of scrolling past in the transcript. Fleet, reached with `F2`, is the live control room for agents, workstreams, watchers, and scheduled jobs: what is running, for how long, at what token cost, and what it is waiting on. You can attach to a running agent to watch or steer it, detach again without killing it, pause and resume, and archive finished work. Git, diff, cost, token, and local-auth panels sit alongside it. Heavier operator surfaces open as review modals.
87
+ Work that is not conversation opens in modals instead of scrolling past in the transcript. Agents, reached with `F2`, is the live control room for agents, WRFC chains, workstreams, watchers, scheduled jobs and hosted sessions: what is running, for how long, at what token cost, and what it is waiting on. You can open a running agent to watch its live tail or steer it, back out with `Esc` without stopping it, stop it with `Ctrl+X` (it asks first), pause and resume, and archive finished work. Usage (`/usage`) shows context pressure, tokens and cost; Changes (`/changes`) shows changed files with a tinted diff, a semantic summary, hunk staging and review comments; Notifications (`/notifications`) keeps the history. The screenshot above predates the modal redesign.
88
88
 
89
89
  ### Everything else is a command
90
90
 
@@ -99,14 +99,14 @@ Press `?` for a searchable, categorized list of every slash command with its arg
99
99
  | `Enter` / `Shift+Enter` | Send the message / insert a newline |
100
100
  | `?` | Help and command picker (on an empty prompt) |
101
101
  | `@` | File picker: insert a path into the prompt |
102
- | `Tab` | Toggle collapse on the nearest block, or complete a path |
102
+ | `Tab` | Complete a path, or toggle collapse on the nearest block. With the session chips showing and an empty composer, switch to the next session |
103
103
  | `Ctrl+F` | Search the conversation |
104
104
  | `Ctrl+Y` / `Ctrl+S` | Copy / save the nearest block |
105
- | `Ctrl+P` | Toggle the panel sidebar |
106
- | `F2` | Toggle Fleet: open and focus it, bring it to front, or close it |
107
- | `Ctrl+K` | Command palette: search and run any command |
108
- | `Shift+Tab` | Cycle the session permission mode |
109
- | `Esc` | Leave the current mode: search, command, or modal |
105
+ | `Ctrl+P` / `Ctrl+K` | Command palette: search and run any command, view or setting |
106
+ | `F2` / `Ctrl+O` | Open or close Agents |
107
+ | `Shift+Tab` | Cycle the session permission mode. With the session chips showing and an empty composer, switch to the previous session |
108
+ | `Esc` | Close the top modal (one level), clear the composer, leave an agent or process view (never stops it), or interrupt main's turn |
109
+ | `Ctrl+X` | Inside an agent or process view: stop it (press twice to confirm) |
110
110
  | `Ctrl+C` | Clear input, cancel a running turn. Press twice to quit |
111
111
 
112
112
  Most bindings are customizable in `~/.goodvibes/tui/keybindings.json`, and `/keybindings` shows what is currently bound. Five keys are fixed and stay out of that file: `F2` (Fleet), `Shift+Tab` (permission-mode cycle), `Esc` (leave the current mode), `?` (help), and `@` (file picker). The full reference is in [docs/tools-and-commands.md](docs/tools-and-commands.md).
@@ -129,7 +129,7 @@ Each row links to the page that documents it. The product's own `?` overlay and
129
129
  | CLI flags | Session lifecycle (`--continue`, `--resume`, `--fork`), non-interactive mode, output formats, host selection | [cli-flags.md](docs/cli-flags.md) |
130
130
  | Knowledge and memory | Session and durable memory, a structured knowledge store with connectors and extractors, embeddings and retrieval, artifacts, multimodal analysis | [knowledge-artifacts-and-multimodal.md](docs/knowledge-artifacts-and-multimodal.md) |
131
131
  | Session durability | Post-turn snapshots plus an fsync-per-record transcript journal replayed at every resume | [session-durability.md](docs/session-durability.md) |
132
- | Planning | Conversational planning loop, project-scoped knowledge spaces, readiness evaluation, the Planning panel | [project-planning.md](docs/project-planning.md) |
132
+ | Planning | Conversational planning loop, project-scoped knowledge spaces, readiness evaluation, the Planning modal | [project-planning.md](docs/project-planning.md) |
133
133
  | Sharing and export | `/share` to HTML, JSON, or Markdown with redaction, upload, and clipboard options | [share-command.md](docs/share-command.md) |
134
134
  | Daemon and services | Connecting to the standalone GoodVibes daemon, browser operator surface, background service and autostart, inbound TLS, outbound trust | [deployment-and-services.md](docs/deployment-and-services.md) |
135
135
  | Remote access | A worked home-server setup: always-on daemon, browser access, TUI over SSH, reachability and TLS | [remote-access.md](docs/remote-access.md) |
@@ -137,7 +137,7 @@ Each row links to the page that documents it. The product's own `?` overlay and
137
137
  | Voice | Live `/tts` playback, TTS and STT providers, streaming voice API | [voice-and-live-tts.md](docs/voice-and-live-tts.md) |
138
138
  | Sandboxing | Bounded eval and isolated MCP execution, with a QEMU-backed VM path | [qemu-sandbox.md](docs/qemu-sandbox.md) |
139
139
  | Integrations | Home Assistant surface, Cloudflare Workers/Queues batch, GitHub Action | [homeassistant-surface.md](docs/homeassistant-surface.md) · [cloudflare-batch.md](docs/cloudflare-batch.md) · [github-action.md](docs/github-action.md) |
140
- | Contributing surfaces | Writing a new TUI panel; the checked-in operator/peer contracts and knowledge schemas | [panel-authoring.md](docs/panel-authoring.md) · [foundation-artifacts](docs/foundation-artifacts/README.md) |
140
+ | Contributing surfaces | The checked-in operator/peer contracts and knowledge schemas | [foundation-artifacts](docs/foundation-artifacts/README.md) |
141
141
 
142
142
  Full index: [docs/README.md](docs/README.md).
143
143
 
@@ -218,12 +218,12 @@ bun run dev
218
218
 
219
219
  The compiled binary is the TUI entrypoint. With `daemon.enabled` on (the default) it adopts a running standalone GoodVibes daemon over loopback, and when a daemon is installed as a service but stopped, it starts that service once and waits for it to come online. It never embeds or constructs a daemon of its own. The control plane, HTTP listener (`danger.httpListener`), and web surface are all hosted by the daemon; the TUI configures them and reports their bindings.
220
220
 
221
- Tests live under `src/test/`, mirroring the source tree, and cover contract, security, release-gate, runtime, renderer, panel, integration, and anti-regression cases. Several gates run alongside them in CI: byte-exact golden renderer frames, performance budgets for startup and frame composition and line production (`scripts/perf-baseline.json`), and architecture rules for import cycles, layer boundaries, source-file size, and unused renderer exports (`scripts/check-architecture.ts`).
221
+ Tests live under `src/test/`, mirroring the source tree, and cover contract, security, release-gate, runtime, renderer, view, integration, and anti-regression cases. Several gates run alongside them in CI: byte-exact golden renderer frames, performance budgets for startup and frame composition and line production (`scripts/perf-baseline.json`), and architecture rules for import cycles, layer boundaries, source-file size, and unused renderer exports (`scripts/check-architecture.ts`).
222
222
 
223
223
  Some decisions worth knowing before you read the source:
224
224
 
225
225
  - **Bun runtime.** Native TypeScript execution, fast startup, built-in test runner.
226
- - **Raw ANSI renderer.** It writes the UI straight to the alternate screen buffer, giving direct control over every byte sent to the terminal. Conversation, panels, modals, overlays, and the footer all share that one renderer.
226
+ - **Raw ANSI renderer.** It writes the UI straight to the alternate screen buffer, giving direct control over every byte sent to the terminal. Conversation, modals, overlays, and the footer all share that one renderer.
227
227
  - **In-process agents.** Agents run in the same process rather than over IPC, staying isolated through scoped tool registries and namespaced state.
228
228
  - **Typed runtime store.** A plain `zustand/vanilla` store with typed selectors and dispatch paths, reachable from agents, tools, renderer, hooks, channels, and daemon surfaces alike.
229
229
  - **Tree-sitter and bundled language servers.** Grammars for structural analysis, outlines, and AST-level edits, several embedded as WASM for instant startup. TypeScript, Python, Bash, CSS, HTML, and JSON language servers ship as dependencies, while `rust-analyzer` and `gopls` are fetched on first use with checksum verification.
@@ -233,13 +233,13 @@ Some decisions worth knowing before you read the source:
233
233
 
234
234
  The TUI consumes the published `@pellux/goodvibes-sdk` platform layer for shared contracts, daemon routes, and transports, and keeps the terminal UI, host wiring, and product composition here. The dependency is pinned in `package.json`. Reference consumers of those surfaces live under [`examples/`](examples/reference-operator-client/README.md).
235
235
 
236
- Source layout, in brief:
236
+ Source layout, in brief (the layer rules and the operator API bridge are in [docs/architecture.md](docs/architecture.md)):
237
237
 
238
238
  ```text
239
239
  src/
240
240
  ├── main.ts, core/ terminal entrypoint, orchestrator, conversation and transcript state
241
241
  ├── renderer/ raw ANSI compositor, overlays, modals, fullscreen workspaces
242
- ├── panels/ panel manager, the Fleet control room, git/diff/cost/token consoles
242
+ ├── views/ modal surfaces, the fleet read model and acts behind Agents, view wiring
243
243
  ├── input/ slash commands, keybindings, composer, pickers, settings modals
244
244
  ├── runtime/ bootstrap wiring, typed store, service composition, session recovery
245
245
  ├── shell/ shell-level modal openers, blocking input, retry affordances
package/docs/README.md CHANGED
@@ -32,7 +32,7 @@ This directory contains the current product documentation for `goodvibes-tui`.
32
32
  Session memory, durable memory, structured knowledge, connectors, extractors, embeddings, artifacts, and multimodal analysis.
33
33
 
34
34
  - [Project planning](project-planning.md)
35
- TUI-owned conversational planning loop, passive SDK planning artifacts, project-scoped knowledge spaces, readiness evaluation, and the Planning panel.
35
+ TUI-owned conversational planning loop, passive SDK planning artifacts, project-scoped knowledge spaces, readiness evaluation, and the Planning modal.
36
36
 
37
37
  - [Channels, remote runtime, and API](channels-remote-and-api.md)
38
38
  Omnichannel surfaces, reply routing, remote peers, node-host contracts, and the daemon/control-plane HTTP + streaming surfaces.
@@ -46,6 +46,9 @@ This directory contains the current product documentation for `goodvibes-tui`.
46
46
  - [CLI flags](cli-flags.md)
47
47
  Global flags reference covering session lifecycle (`--continue`, `--resume`, `--fork`), the `-y`/`--non-interactive` commit gate for plugin bundle installs, output format consolidation, the `--host` alias, and all other startup and command-specific flags.
48
48
 
49
+ - [Architecture](architecture.md)
50
+ Source layout, the import-direction rules `bun run architecture:check` enforces, and the operator API bridge that answers `panels.list` / `panels.open` with the modal views.
51
+
49
52
  - [Commands reference](commands-reference.md)
50
53
  The generated, categorized list of every slash command, its arguments, and its description. It is the same list `?` renders in the product.
51
54
 
@@ -64,9 +67,6 @@ This directory contains the current product documentation for `goodvibes-tui`.
64
67
  - [Local verification](verification/local-verification.md)
65
68
  Inventory coverage, GoodVibes home audits, compiled CLI probes, authenticated daemon probes, and release-oriented local gates.
66
69
 
67
- - [Panel authoring](panel-authoring.md)
68
- Class hierarchy, canonical example, palette convention, rendering utilities, input handling, performance instrumentation, and contract test registration for contributors building new TUI panels.
69
-
70
70
  - [Changelog](../CHANGELOG.md)
71
71
  Current release history. Older per-version release-note files remain under `docs/releases/` for historical releases, but `CHANGELOG.md` is the canonical current stream.
72
72
 
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "2.0.23"
6
+ "version": "2.1.0"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "2.0.21",
3
+ "version": "2.1.0",
4
4
  "description": "Terminal-native GoodVibes client for coding, operations, knowledge and channel workflows; a pure client of the GoodVibes daemon.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
@@ -96,11 +96,11 @@
96
96
  "@anthropic-ai/bedrock-sdk": "^0.29.1",
97
97
  "@anthropic-ai/sdk": "^0.92.0",
98
98
  "@ast-grep/napi": "^0.42.0",
99
- "@pellux/goodvibes-sdk": "2.0.23",
100
- "@pellux/goodvibes-terminal-shell": "2.0.23",
99
+ "@pellux/goodvibes-sdk": "2.1.0",
100
+ "@pellux/goodvibes-terminal-shell": "2.1.0",
101
101
  "bash-language-server": "^5.6.0",
102
102
  "fuse.js": "^7.1.0",
103
- "@pellux/goodvibes-daemon": "1.28.25",
103
+ "@pellux/goodvibes-daemon": "1.29.0",
104
104
  "graphql": "^16.13.2",
105
105
  "jszip": "^3.10.1",
106
106
  "node-edge-tts": "^1.2.10",
@@ -121,7 +121,7 @@
121
121
  "zustand": "^5.0.12"
122
122
  },
123
123
  "devDependencies": {
124
- "@pellux/goodvibes-toolchain": "2.0.23",
124
+ "@pellux/goodvibes-toolchain": "2.1.0",
125
125
  "@types/bun": "^1.3.10",
126
126
  "typescript": "^5.9.3"
127
127
  },
@@ -133,6 +133,16 @@
133
133
  },
134
134
  "trustedDependencies": [],
135
135
  "overrides": {
136
- "minimatch": "^10.2.5"
136
+ "minimatch": "^10.2.5",
137
+ "undici": "^7.29.1",
138
+ "brace-expansion": "^5.0.11",
139
+ "@anthropic-ai/sdk": "^0.92.0",
140
+ "qs": "^6.16.0",
141
+ "body-parser": "^1.20.8",
142
+ "ip-address": "^10.7.2",
143
+ "fflate": "^0.8.3",
144
+ "fast-uri": "^3.1.6",
145
+ "js-yaml": "^4.3.2",
146
+ "sharp": "^0.35.4"
137
147
  }
138
148
  }
@@ -232,6 +232,8 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
232
232
  id: model.id,
233
233
  displayName: model.displayName,
234
234
  contextWindow: services.providerRegistry.getContextWindowForModel(model),
235
+ // False when nothing states the window (a guess, or disproven by a larger accepted request).
236
+ contextWindowKnown: services.providerRegistry.getKnownContextWindowForModel(model) !== null,
235
237
  providerConfigured: providerSnapshot?.runtime.auth?.configured ?? true,
236
238
  setup,
237
239
  };
@@ -241,7 +243,7 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
241
243
  ` provider: ${model.provider}`,
242
244
  ` setup: ${setup.setupLabel}`,
243
245
  ` provider configured: ${yesNo(value.providerConfigured)}`,
244
- ` context: ${value.contextWindow.toLocaleString()}`,
246
+ ` context: ${value.contextWindowKnown ? value.contextWindow.toLocaleString() : 'unknown'}`,
245
247
  ].join('\n'));
246
248
  }
247
249
  if (subOrFilter === 'use' || subOrFilter === 'set') {
@@ -316,6 +318,7 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
316
318
  id: model.id,
317
319
  displayName: model.displayName,
318
320
  contextWindow: services.providerRegistry.getContextWindowForModel(model),
321
+ contextWindowKnown: services.providerRegistry.getKnownContextWindowForModel(model) !== null,
319
322
  current: model.registryKey === current,
320
323
  ...(synthInfo !== null ? {
321
324
  isSynthetic: true,
@@ -331,7 +334,7 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
331
334
  const synthLabel = model.isSynthetic
332
335
  ? ` [synthetic ${model.syntheticConfiguredBackends}/${model.syntheticBackends}p]`
333
336
  : '';
334
- return ` ${model.current ? '*' : ' '} ${model.registryKey.padEnd(42)} setup=${model.setupClass} ctx=${model.contextWindow.toLocaleString()}${synthLabel} ${model.displayName}`;
337
+ return ` ${model.current ? '*' : ' '} ${model.registryKey.padEnd(42)} setup=${model.setupClass} ctx=${model.contextWindowKnown ? model.contextWindow.toLocaleString() : 'unknown'}${synthLabel} ${model.displayName}`;
335
338
  }),
336
339
  ].join('\n'));
337
340
  });
@@ -7,7 +7,7 @@ import { startOnboardingFastPath } from '../runtime/onboarding/fast-path.ts';
7
7
  import { checkRecoveryForSession, readLastSessionPointer, type SessionSurface } from '@/runtime/index.ts';
8
8
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
9
9
  import { writeFatalLine } from '@pellux/goodvibes-sdk/platform/daemon';
10
- import { offerRecoverySnapshot } from '../runtime/recovery-prompt.ts';
10
+ import { offerRecoverySnapshot, type RecoveryTypeaheadHooks } from '../runtime/recovery-prompt.ts';
11
11
  import { buildRecoveryOfferWiring } from '../runtime/recovery-offer-wiring.ts';
12
12
  import type { ConversationManager } from '../core/conversation.ts';
13
13
  import type { SessionManager } from '@pellux/goodvibes-sdk/platform/sessions';
@@ -34,6 +34,8 @@ export interface TuiStartupRecoveryDeps {
34
34
  readonly conversation: ConversationManager;
35
35
  readonly writeLastSessionPointer: (sessionId: string) => void;
36
36
  readonly receipt: (line: string) => void;
37
+ /** Holds typeahead out of the offer modal (see RecoveryPromptDeps.typeahead). */
38
+ readonly typeahead?: RecoveryTypeaheadHooks;
37
39
  }
38
40
 
39
41
  /**
@@ -59,11 +61,12 @@ export interface TuiStartupRecoveryDeps {
59
61
  * plain store resume, after the follow-up Keep/Remove question the
60
62
  * established flow always asks on decline.
61
63
  *
62
- * The modal itself is deferred to the next macrotask, mirroring
63
- * `scheduleRecoveryOffer`'s own reasoning: `applyInitialTuiCliState` runs
64
- * before the shell's first render, so asking a question here synchronously
65
- * would draw it at a blank terminal. The synchronous `checkRecoveryForSession`
66
- * probe above needs no such deferral, only opening the modal does.
64
+ * The modal opens synchronously, before the shell's first frame, like the
65
+ * general boot offer (startRecoveryOffer in runtime/recovery-prompt.ts): the
66
+ * first frame already carries the question, so the composer is never live
67
+ * underneath it, and keys typed before it was painted are held out of it by
68
+ * the typeahead gate (recovery.typeahead). This runs before the general
69
+ * offer, so it still asks first.
67
70
  */
68
71
  async function resumeNamedSessionWithRecoveryCheck(options: {
69
72
  readonly sessionId: string;
@@ -88,39 +91,31 @@ async function resumeNamedSessionWithRecoveryCheck(options: {
88
91
  return;
89
92
  }
90
93
 
91
- await new Promise<void>((resolve) => {
92
- const timer = setTimeout(() => {
93
- void (async () => {
94
- try {
95
- const deps = buildRecoveryOfferWiring({
96
- surface,
97
- sessionManager: recovery.sessionManager,
98
- runtime: recovery.runtime,
99
- conversation: recovery.conversation,
100
- commandContext,
101
- writeLastSessionPointer: recovery.writeLastSessionPointer,
102
- receipt: recovery.receipt,
103
- render,
104
- });
105
- const outcome = await offerRecoverySnapshot({ ...deps, targetSessionId: sessionId });
106
- if (outcome !== 'resumed') {
107
- await plainResume();
108
- }
109
- } catch {
110
- // Best-effort by construction (mirrors offerRecoverySnapshot's own
111
- // guarantee): a failure here must fall back to the plain resume
112
- // rather than leave the boot with nothing resumed at all.
113
- await plainResume().catch(() => {
114
- // A failing plain resume here is no worse than the pre-existing
115
- // behavior when session resume itself fails.
116
- });
117
- } finally {
118
- resolve();
119
- }
120
- })();
121
- }, 0);
122
- timer.unref?.();
123
- });
94
+ try {
95
+ const deps = buildRecoveryOfferWiring({
96
+ surface,
97
+ sessionManager: recovery.sessionManager,
98
+ runtime: recovery.runtime,
99
+ conversation: recovery.conversation,
100
+ commandContext,
101
+ writeLastSessionPointer: recovery.writeLastSessionPointer,
102
+ receipt: recovery.receipt,
103
+ render,
104
+ ...(recovery.typeahead ? { typeahead: recovery.typeahead } : {}),
105
+ });
106
+ const outcome = await offerRecoverySnapshot({ ...deps, targetSessionId: sessionId });
107
+ if (outcome !== 'resumed') {
108
+ await plainResume();
109
+ }
110
+ } catch {
111
+ // Best-effort by construction (mirrors offerRecoverySnapshot's own
112
+ // guarantee): a failure here must fall back to the plain resume rather
113
+ // than leave the boot with nothing resumed at all.
114
+ await plainResume().catch(() => {
115
+ // A failing plain resume here is no worse than the pre-existing
116
+ // behavior when session resume itself fails.
117
+ });
118
+ }
124
119
  }
125
120
 
126
121
  /**
@@ -0,0 +1,157 @@
1
+ /**
2
+ * agent-view-render.ts, one agent opened full screen.
3
+ *
4
+ * The agent becomes the session: its own transcript is the spine (drawn by
5
+ * the same lane-graph path as main, conversation-rendering.ts), the agents
6
+ * it spawned branch off it as lanes, and steers you sent that it has not
7
+ * picked up yet sit at the bottom marked as queued. Its lanes are drawn with
8
+ * the color base shifted (lane-graph/paint.ts withLaneColorBase) so its spine
9
+ * keeps the color its lane has in main.
10
+ *
11
+ * row 0 ◆ engineer · the task · started by main 2m 14s ago
12
+ * row 1 (blank)
13
+ * rows the agent's transcript
14
+ *
15
+ * A WRFC chain owner takes no turns of its own: its view is its chain, each
16
+ * phase agent a lane off its spine drawn from that agent's own transcript
17
+ * (work-tree-model.ts buildWrfcOwnerModel).
18
+ *
19
+ * The thin bar down column 0, the blank rows above and below the body, and
20
+ * the scroll window are the caller's (shell/session-views.ts); this returns
21
+ * every line.
22
+ */
23
+
24
+ import { createEmptyLine, type Line } from '@pellux/goodvibes-sdk/platform/types';
25
+ import type { ConversationMessageSnapshot } from '@pellux/goodvibes-sdk/platform/core';
26
+ import { appendConversationMessages, drawConversationTurn, type ConversationRenderContext } from './conversation-rendering.ts';
27
+ import { buildWrfcOwnerModel } from './work-tree-model.ts';
28
+ import type { AgentLaneInfo, WorkTreeSources } from './work-tree-sources.ts';
29
+ import { laneColor, withLaneColorBase } from '../renderer/lane-graph/paint.ts';
30
+ import type { TreeGlyphSetName } from '../renderer/lane-graph/glyphs.ts';
31
+ import { UIFactory } from '../renderer/ui-factory.ts';
32
+ import { activeTokens } from '../renderer/theme.ts';
33
+ import { formatElapsed } from '../utils/format-elapsed.ts';
34
+ import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
35
+
36
+ export interface AgentViewInput {
37
+ readonly width: number;
38
+ readonly agent: AgentLaneInfo;
39
+ /** The agent's transcript (its lane info carries none for a WRFC owner). */
40
+ readonly messages: readonly ConversationMessageSnapshot[];
41
+ /** The agent's lane color index in main. */
42
+ readonly colorIndex: number;
43
+ /** Who started it: "main" or the parent agent's name. */
44
+ readonly parentName: string;
45
+ /** Main's work-tree sources (timings, spawned agents, waiting calls). */
46
+ readonly sources: WorkTreeSources;
47
+ /** Fold state for this view's beads; kept by the caller across frames. */
48
+ readonly collapse: Map<string, boolean>;
49
+ readonly glyphSet: TreeGlyphSetName;
50
+ readonly frame: number;
51
+ readonly now: number;
52
+ /** Steers sent from this view that the agent has not picked up yet, oldest first. */
53
+ readonly queuedSteers: readonly string[];
54
+ }
55
+
56
+ const TEXT_X = 3;
57
+
58
+ function put(line: Line, x: number, endX: number, text: string, style: { fg: string; bold?: boolean }): number {
59
+ let cx = x;
60
+ for (const ch of text) {
61
+ const w = getDisplayWidth(ch);
62
+ if (w <= 0) continue;
63
+ if (cx + w > endX || cx >= line.length) break;
64
+ line[cx] = { char: ch, fg: style.fg, bg: '', bold: style.bold ?? false, dim: false, underline: false, italic: false, strikethrough: false };
65
+ if (w === 2 && cx + 1 < line.length) line[cx + 1] = { ...line[cx]!, char: '' };
66
+ cx += w;
67
+ }
68
+ return cx;
69
+ }
70
+
71
+ function stateText(agent: AgentLaneInfo, parentName: string, now: number): { text: string; tone: 'faint' | 'good' | 'bad' | 'warn' } {
72
+ const started = agent.startedAt !== undefined ? `started by ${parentName} ${formatElapsed(Math.max(0, now - agent.startedAt))} ago` : `started by ${parentName}`;
73
+ switch (agent.status) {
74
+ case 'running': return { text: started, tone: 'faint' };
75
+ case 'pending': return { text: `waiting to start · ${started}`, tone: 'faint' };
76
+ case 'completed': {
77
+ const took = agent.startedAt !== undefined && agent.completedAt !== undefined ? ` in ${formatElapsed(agent.completedAt - agent.startedAt)}` : '';
78
+ return { text: `finished${took} · ${agent.toolCallCount} tool${agent.toolCallCount === 1 ? '' : 's'}`, tone: 'good' };
79
+ }
80
+ case 'failed': return { text: `failed${agent.error ? `: ${agent.error.split('\n')[0]}` : ''}`, tone: 'bad' };
81
+ case 'cancelled': return { text: 'stopped', tone: 'warn' };
82
+ }
83
+ }
84
+
85
+ /** The ◆ head row. */
86
+ function headRow(input: AgentViewInput): Line {
87
+ const t = activeTokens();
88
+ const line = createEmptyLine(input.width);
89
+ for (const cell of line) cell.bg = '';
90
+ const end = input.width - 3;
91
+ const color = laneColor(input.colorIndex);
92
+ let x = put(line, TEXT_X, end, '◆', { fg: color, bold: true });
93
+ // A chain owner is named for what it is, as its lane in main is.
94
+ x = put(line, x + 1, end, input.agent.wrfcPhases ? 'WRFC chain' : input.agent.hostedLabel ?? input.agent.name, { fg: color, bold: true });
95
+ const state = stateText(input.agent, input.parentName, input.now);
96
+ const stateFg = state.tone === 'good' ? t.success : state.tone === 'bad' ? t.error : state.tone === 'warn' ? t.warning : t.textFaint;
97
+ const task = input.agent.task.split('\n').find((l) => l.trim().length > 0)?.trim() ?? '';
98
+ // The state is kept whole; the task yields its room first.
99
+ const stateW = getDisplayWidth(state.text) + 3;
100
+ const taskRoom = end - x - 3 - stateW;
101
+ if (task && taskRoom >= 8) {
102
+ x = put(line, x + 1, end, '·', { fg: t.textFaint });
103
+ x = put(line, x + 1, end, truncateDisplay(task, taskRoom), { fg: t.textMuted });
104
+ }
105
+ x = put(line, x + 1, end, '·', { fg: t.textFaint });
106
+ put(line, x + 1, end, truncateDisplay(state.text, Math.max(0, end - x - 1)), { fg: stateFg });
107
+ return line;
108
+ }
109
+
110
+ /** Every line of the agent view, top to bottom. */
111
+ export function renderAgentView(input: AgentViewInput): Line[] {
112
+ const t = activeTokens();
113
+ const lines: Line[] = [headRow(input), createEmptyLine(input.width)];
114
+ const running = input.agent.status === 'running' || input.agent.status === 'pending';
115
+ const context: ConversationRenderContext = {
116
+ history: { addLine: (l) => { lines.push(l); }, addLines: (ls) => { lines.push(...ls); }, getLineCount: () => lines.length },
117
+ blockRegistry: [],
118
+ collapseState: input.collapse,
119
+ errorLineRegistry: [],
120
+ messageKindRegistry: new Map(),
121
+ configManager: null,
122
+ splashOptions: {},
123
+ // The agent's own turns are live while the agent runs, not while main does.
124
+ workTreeSources: { ...input.sources, turnActive: () => running, turnTiming: undefined },
125
+ treeGlyphSet: input.glyphSet,
126
+ focusId: null,
127
+ frame: input.frame,
128
+ };
129
+ const phases = input.agent.wrfcPhases;
130
+ if (phases) {
131
+ if (phases.length > 0) {
132
+ const model = buildWrfcOwnerModel({ info: input.agent, sources: context.workTreeSources ?? {}, collapse: input.collapse });
133
+ withLaneColorBase(input.colorIndex, () => { lines.push(...drawConversationTurn(context, model, input.width).lines); });
134
+ }
135
+ } else {
136
+ withLaneColorBase(input.colorIndex, () => appendConversationMessages(context, [...input.messages], input.width, []));
137
+ }
138
+ const empty = phases ? phases.length === 0 : input.messages.length === 0;
139
+ if (empty) {
140
+ const note = createEmptyLine(input.width);
141
+ for (const cell of note) cell.bg = '';
142
+ const text = phases
143
+ ? (running ? 'The chain has not started a phase yet.' : 'This chain ran no phases.')
144
+ : running ? 'No transcript yet: the agent has not taken a turn.' : 'This agent left no transcript.';
145
+ put(note, TEXT_X + 2, input.width - 3, text, { fg: t.textFaint });
146
+ lines.push(note);
147
+ }
148
+ for (const steer of input.queuedSteers) {
149
+ lines.push(createEmptyLine(input.width));
150
+ lines.push(...UIFactory.createMessageBar(input.width, steer));
151
+ const note = createEmptyLine(input.width);
152
+ for (const cell of note) cell.bg = '';
153
+ put(note, TEXT_X + 2, input.width - 3, '⧗ queued · it arrives at the agent\'s next turn', { fg: t.textFaint });
154
+ lines.push(note);
155
+ }
156
+ return lines;
157
+ }