@pellux/goodvibes-tui 2.0.21 → 2.1.1

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 (391) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/README.md +30 -29
  3. package/docs/README.md +8 -5
  4. package/docs/foundation-artifacts/operator-contract.json +1 -1
  5. package/package.json +28 -19
  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-reference.ts +2 -2
  52. package/src/input/command-registry.ts +77 -35
  53. package/src/input/commands/acp-runtime.ts +19 -9
  54. package/src/input/commands/channel-runtime.ts +2 -2
  55. package/src/input/commands/ci-runtime.ts +1 -1
  56. package/src/input/commands/codebase-runtime.ts +1 -1
  57. package/src/input/commands/config.ts +7 -1
  58. package/src/input/commands/context-window.ts +25 -19
  59. package/src/input/commands/control-room-runtime.ts +18 -18
  60. package/src/input/commands/conversation-runtime.ts +0 -1
  61. package/src/input/commands/cost-runtime.ts +29 -20
  62. package/src/input/commands/diff-runtime.ts +35 -181
  63. package/src/input/commands/eval.ts +1 -1
  64. package/src/input/commands/experience-runtime.ts +3 -7
  65. package/src/input/commands/fresh-conversation.ts +86 -0
  66. package/src/input/commands/git-runtime.ts +5 -22
  67. package/src/input/commands/graph-runtime.ts +1 -1
  68. package/src/input/commands/guidance-runtime.ts +1 -1
  69. package/src/input/commands/health-runtime.ts +8 -9
  70. package/src/input/commands/hooks-runtime.ts +2 -2
  71. package/src/input/commands/hosted-runtime.ts +3 -3
  72. package/src/input/commands/incident-runtime.ts +3 -3
  73. package/src/input/commands/intelligence-runtime.ts +6 -9
  74. package/src/input/commands/knowledge.ts +2 -2
  75. package/src/input/commands/legacy-panel-command.ts +57 -0
  76. package/src/input/commands/local-auth-runtime.ts +8 -9
  77. package/src/input/commands/local-provider-runtime.ts +7 -4
  78. package/src/input/commands/local-runtime.ts +12 -15
  79. package/src/input/commands/local-setup-review.ts +1 -1
  80. package/src/input/commands/local-setup.ts +13 -19
  81. package/src/input/commands/memory.ts +1 -1
  82. package/src/input/commands/notify-runtime.ts +9 -0
  83. package/src/input/commands/operator-runtime.ts +10 -12
  84. package/src/input/commands/permissions-provenance.ts +3 -3
  85. package/src/input/commands/planning-runtime.ts +7 -7
  86. package/src/input/commands/platform-sandbox-runtime.ts +2 -2
  87. package/src/input/commands/platform-services-runtime.ts +1 -1
  88. package/src/input/commands/policy-dispatch.ts +2 -2
  89. package/src/input/commands/policy.ts +3 -3
  90. package/src/input/commands/provider.ts +1 -1
  91. package/src/input/commands/remote-runtime.ts +1 -1
  92. package/src/input/commands/review-runtime.ts +39 -94
  93. package/src/input/commands/rewind-runtime.ts +81 -108
  94. package/src/input/commands/runtime-services.ts +15 -17
  95. package/src/input/commands/services-runtime.ts +2 -2
  96. package/src/input/commands/session-content.ts +21 -5
  97. package/src/input/commands/session-workflow.ts +13 -38
  98. package/src/input/commands/settings-sync-runtime.ts +2 -2
  99. package/src/input/commands/shell-core.ts +19 -4
  100. package/src/input/commands/skills-runtime.ts +2 -2
  101. package/src/input/commands/status-runtime.ts +13 -0
  102. package/src/input/commands/subscription-runtime.ts +1 -1
  103. package/src/input/commands/tasks-runtime.ts +2 -8
  104. package/src/input/commands/work-plan-runtime.ts +4 -4
  105. package/src/input/commands/workstream-attempts.ts +24 -47
  106. package/src/input/commands/workstream-runtime.ts +2 -2
  107. package/src/input/commands/worktree-runtime.ts +3 -3
  108. package/src/input/commands.ts +3 -1
  109. package/src/input/composer-editor.ts +44 -0
  110. package/src/input/concealed-input.ts +1 -1
  111. package/src/input/config-modal-types.ts +20 -8
  112. package/src/input/config-modal.ts +106 -105
  113. package/src/input/confirm-dialog-types.ts +28 -0
  114. package/src/input/confirm-dialog.ts +70 -0
  115. package/src/input/delete-key-policy.ts +3 -3
  116. package/src/input/feed-context-factory.ts +15 -16
  117. package/src/input/handler-command-route.ts +7 -42
  118. package/src/input/handler-content-actions.ts +16 -15
  119. package/src/input/handler-feed-routes.ts +26 -211
  120. package/src/input/handler-feed.ts +68 -95
  121. package/src/input/handler-interactions.ts +5 -3
  122. package/src/input/handler-modal-routes.ts +215 -216
  123. package/src/input/handler-modal-stack.ts +81 -11
  124. package/src/input/handler-modal-token-routes.ts +4 -0
  125. package/src/input/handler-onboarding.ts +0 -1
  126. package/src/input/handler-picker-routes.ts +65 -107
  127. package/src/input/handler-session-view-route.ts +149 -0
  128. package/src/input/handler-shortcuts.ts +16 -154
  129. package/src/input/handler-types.ts +4 -2
  130. package/src/input/handler-ui-state.ts +45 -15
  131. package/src/input/handler-work-tree-route.ts +123 -0
  132. package/src/input/handler.ts +25 -70
  133. package/src/input/keybindings.ts +16 -71
  134. package/src/input/masked-entry-modal.ts +117 -0
  135. package/src/input/mcp-workspace.ts +48 -17
  136. package/src/input/model-picker-filter.ts +8 -0
  137. package/src/input/model-picker-open.ts +99 -0
  138. package/src/input/model-picker-types.ts +1 -1
  139. package/src/input/model-picker.ts +92 -9
  140. package/src/input/notifications-modal.ts +123 -0
  141. package/src/input/overlay-filter.ts +26 -0
  142. package/src/input/profile-picker-modal.ts +37 -8
  143. package/src/input/search.ts +3 -3
  144. package/src/input/selection-modal-queue.ts +2 -0
  145. package/src/input/selection-modal.ts +28 -16
  146. package/src/input/session-picker-modal.ts +33 -8
  147. package/src/input/settings-modal-activation.ts +7 -2
  148. package/src/input/settings-modal-data.ts +11 -6
  149. package/src/input/settings-modal-types.ts +1 -1
  150. package/src/input/settings-modal.ts +20 -20
  151. package/src/input/submission-intent.ts +1 -1
  152. package/src/input/submission-router.ts +4 -9
  153. package/src/input/surface-modal-host.ts +110 -0
  154. package/src/input/theme-settings-actions.ts +82 -0
  155. package/src/input/usage-modal.ts +183 -0
  156. package/src/input/views.ts +97 -0
  157. package/src/main.ts +175 -165
  158. package/src/permissions/hunk-selection.ts +1 -1
  159. package/src/permissions/prompt-card.ts +511 -0
  160. package/src/permissions/prompt.ts +59 -622
  161. package/src/renderer/agents-modal.ts +310 -0
  162. package/src/renderer/autocomplete-overlay.ts +42 -220
  163. package/src/renderer/back-to-bottom.ts +80 -0
  164. package/src/renderer/block-actions-overlay.ts +30 -48
  165. package/src/renderer/bookmark-modal.ts +48 -79
  166. package/src/renderer/changes-modal.ts +325 -0
  167. package/src/renderer/code-block.ts +184 -106
  168. package/src/renderer/command-palette.ts +93 -0
  169. package/src/renderer/composer-fragments.ts +4 -4
  170. package/src/renderer/composer.ts +146 -0
  171. package/src/renderer/compositor.ts +26 -160
  172. package/src/renderer/config-modal.ts +185 -81
  173. package/src/renderer/confirm-dialog.ts +50 -0
  174. package/src/renderer/context-inspector.ts +116 -154
  175. package/src/renderer/context-status-hint.ts +4 -4
  176. package/src/renderer/conversation-layout.ts +20 -0
  177. package/src/renderer/conversation-overlays.ts +94 -84
  178. package/src/renderer/conversation-surface.ts +19 -38
  179. package/src/renderer/diff-tint.ts +98 -0
  180. package/src/renderer/file-picker-overlay.ts +63 -90
  181. package/src/renderer/footer-targets.ts +38 -0
  182. package/src/renderer/git-status.ts +3 -4
  183. package/src/renderer/header-line.ts +140 -117
  184. package/src/renderer/help-overlay.ts +133 -219
  185. package/src/renderer/history-search-overlay.ts +15 -12
  186. package/src/renderer/lane-graph/bead.ts +800 -0
  187. package/src/renderer/lane-graph/glyphs.ts +134 -0
  188. package/src/renderer/lane-graph/layout.ts +248 -0
  189. package/src/renderer/lane-graph/paint.ts +537 -0
  190. package/src/renderer/lane-graph/semantic-memo.ts +77 -0
  191. package/src/renderer/layout-engine.ts +2 -57
  192. package/src/renderer/layout.ts +7 -13
  193. package/src/renderer/markdown-inline.ts +2 -1
  194. package/src/renderer/markdown-table.ts +17 -12
  195. package/src/renderer/markdown.ts +92 -34
  196. package/src/renderer/masked-entry-modal.ts +78 -0
  197. package/src/renderer/mcp-workspace.ts +166 -203
  198. package/src/renderer/model-workspace.ts +381 -436
  199. package/src/renderer/notifications-modal.ts +116 -0
  200. package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
  201. package/src/renderer/process-view.ts +192 -0
  202. package/src/renderer/profile-picker-modal.ts +58 -110
  203. package/src/renderer/search-overlay.ts +6 -3
  204. package/src/renderer/selection-modal-overlay.ts +87 -327
  205. package/src/renderer/semantic-diff.ts +2 -2
  206. package/src/renderer/session-chips.ts +102 -0
  207. package/src/renderer/session-picker-modal.ts +177 -297
  208. package/src/renderer/settings-modal-connections.ts +14 -23
  209. package/src/renderer/settings-modal-context.ts +367 -0
  210. package/src/renderer/settings-modal-helpers.ts +6 -11
  211. package/src/renderer/settings-modal.ts +322 -726
  212. package/src/renderer/shell-surface.ts +277 -175
  213. package/src/renderer/shortcuts-overlay.ts +217 -0
  214. package/src/renderer/status-line.ts +475 -0
  215. package/src/renderer/surface-compose.ts +174 -0
  216. package/src/renderer/surface-kit-extra.ts +303 -0
  217. package/src/renderer/surface-kit-list.ts +366 -0
  218. package/src/renderer/surface-kit-parts.ts +360 -0
  219. package/src/renderer/surface-kit.ts +568 -0
  220. package/src/renderer/syntax-highlighter.ts +280 -197
  221. package/src/renderer/syntax-theme.ts +51 -0
  222. package/src/renderer/system-message.ts +0 -1
  223. package/src/renderer/term-caps.ts +42 -1
  224. package/src/renderer/terminal-bg-probe.ts +116 -19
  225. package/src/renderer/terminal-palette-probe.ts +322 -0
  226. package/src/renderer/terminal-palette-reader.ts +183 -0
  227. package/src/renderer/terminal-palette.ts +58 -0
  228. package/src/renderer/theme-mode-config.ts +16 -1
  229. package/src/renderer/theme.ts +234 -207
  230. package/src/renderer/thinking.ts +0 -1
  231. package/src/renderer/throbber.ts +188 -0
  232. package/src/renderer/toast-center.ts +119 -0
  233. package/src/renderer/ui-factory.ts +39 -602
  234. package/src/renderer/ui-primitives.ts +8 -25
  235. package/src/renderer/usage-modal.ts +353 -0
  236. package/src/renderer/user-message.ts +58 -0
  237. package/src/renderer/voice-capture-chip.ts +53 -0
  238. package/src/runtime/bootstrap-command-context.ts +0 -5
  239. package/src/runtime/bootstrap-command-parts.ts +38 -89
  240. package/src/runtime/bootstrap-core.ts +13 -15
  241. package/src/runtime/bootstrap-hook-bridge.ts +12 -21
  242. package/src/runtime/bootstrap-notifier-sync.ts +17 -1
  243. package/src/runtime/bootstrap-shell.ts +16 -50
  244. package/src/runtime/bootstrap.ts +14 -14
  245. package/src/runtime/client/fleet-union.ts +5 -5
  246. package/src/runtime/client/hosted-exit.ts +2 -2
  247. package/src/runtime/client/hosted-sessions.ts +1 -1
  248. package/src/runtime/client/spine-adoption.ts +1 -1
  249. package/src/runtime/context-accounting-source.ts +5 -4
  250. package/src/runtime/index.ts +1 -0
  251. package/src/runtime/notification-dispatch.ts +102 -29
  252. package/src/runtime/recovery-offer-wiring.ts +4 -1
  253. package/src/runtime/recovery-prompt.ts +31 -15
  254. package/src/runtime/runtime-services-types.ts +10 -6
  255. package/src/runtime/services.ts +17 -12
  256. package/src/runtime/session-continuity-hints.ts +2 -5
  257. package/src/runtime/session-inbound-inputs.ts +1 -1
  258. package/src/runtime/session-resume-liveness-confirm.ts +14 -2
  259. package/src/runtime/startup-typeahead-gate.ts +103 -0
  260. package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
  261. package/src/runtime/store/domains/index.ts +0 -8
  262. package/src/runtime/store/selectors/index.ts +0 -28
  263. package/src/runtime/store/state.ts +0 -8
  264. package/src/runtime/ui-services.ts +4 -6
  265. package/src/runtime/usage-tracker.ts +348 -0
  266. package/src/runtime/wrfc-persistence.ts +4 -4
  267. package/src/shell/blocking-input.ts +92 -2
  268. package/src/shell/recovery-input-helpers.ts +1 -1
  269. package/src/shell/session-views.ts +539 -0
  270. package/src/shell/status-report.ts +86 -0
  271. package/src/shell/transcript-scroll.ts +68 -0
  272. package/src/shell/ui-openers.ts +84 -172
  273. package/src/shell/view-openers.ts +205 -0
  274. package/src/tools/wrfc-agent-guard.ts +22 -2
  275. package/src/utils/format-duration.ts +4 -8
  276. package/src/utils/splash-lines.ts +9 -1
  277. package/src/utils/terminal-width.ts +56 -0
  278. package/src/verification/live-verifier.ts +0 -10
  279. package/src/version.ts +1 -1
  280. package/src/{panels → views}/agent-inspector-shared.ts +2 -2
  281. package/src/{panels → views}/builtin-modals.ts +22 -21
  282. package/src/views/builtin-views.ts +135 -0
  283. package/src/{panels → views}/confirm-state.ts +9 -9
  284. package/src/{panels → views}/diff-review-model.ts +10 -3
  285. package/src/{panels → views}/eval-registry.ts +1 -1
  286. package/src/{panels → views}/fleet-acts.ts +49 -85
  287. package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
  288. package/src/{panels → views}/fleet-gateway.ts +5 -5
  289. package/src/{panels → views}/fleet-observed-render.ts +16 -16
  290. package/src/{panels → views}/fleet-read-model.ts +9 -9
  291. package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
  292. package/src/{panels → views}/fleet-spawn.ts +33 -49
  293. package/src/{panels → views}/fleet-steer.ts +12 -12
  294. package/src/{panels → views}/fleet-stop.ts +4 -4
  295. package/src/{panels → views}/fleet-transcript.ts +26 -27
  296. package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
  297. package/src/{panels → views}/hosted-session-feed.ts +6 -6
  298. package/src/views/modal-surface-registry.ts +41 -0
  299. package/src/{panels → views}/modals/devices-modal.ts +2 -2
  300. package/src/{panels → views}/modals/hooks-modal.ts +13 -13
  301. package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
  302. package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
  303. package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
  304. package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
  305. package/src/{panels → views}/modals/memory-modal.ts +8 -8
  306. package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
  307. package/src/{panels → views}/modals/pairing-modal.ts +9 -9
  308. package/src/{panels → views}/modals/planning-modal.ts +15 -20
  309. package/src/{panels → views}/modals/plugins-modal.ts +6 -6
  310. package/src/{panels → views}/modals/policy-modal.ts +13 -5
  311. package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
  312. package/src/{panels → views}/modals/remote-modal.ts +3 -3
  313. package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
  314. package/src/{panels → views}/modals/security-modal.ts +22 -16
  315. package/src/{panels → views}/modals/services-modal.ts +3 -3
  316. package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
  317. package/src/{panels → views}/modals/skills-modal.ts +9 -9
  318. package/src/{panels → views}/modals/subscription-modal.ts +3 -3
  319. package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
  320. package/src/views/notifications-feed.ts +314 -0
  321. package/src/{panels → views}/polish-core.ts +38 -36
  322. package/src/{panels → views}/polish-tables.ts +35 -26
  323. package/src/{panels → views}/polish.ts +101 -95
  324. package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
  325. package/src/{panels → views}/provider-health-routes.ts +14 -14
  326. package/src/{panels → views}/search-focus.ts +8 -8
  327. package/src/{panels → views}/session-maintenance.ts +11 -11
  328. package/src/views/skills-discovery.ts +159 -0
  329. package/src/views/view-deps.ts +80 -0
  330. package/src/views/view-panel-adapter.ts +53 -0
  331. package/src/{panels → views}/workstream-graph-render.ts +1 -1
  332. package/src/core/conversation-fold.ts +0 -101
  333. package/src/core/conversation-turn-structure.ts +0 -464
  334. package/src/core/conversation-user-receipts.ts +0 -42
  335. package/src/input/commands/operator-panel-runtime.ts +0 -186
  336. package/src/input/panel-integration-actions.ts +0 -33
  337. package/src/input/panel-mouse-geometry.ts +0 -97
  338. package/src/input/panel-paste-flood-guard.ts +0 -86
  339. package/src/panels/base-panel.ts +0 -301
  340. package/src/panels/builtin/agent.ts +0 -27
  341. package/src/panels/builtin/development.ts +0 -92
  342. package/src/panels/builtin/knowledge.ts +0 -12
  343. package/src/panels/builtin/operations.ts +0 -177
  344. package/src/panels/builtin/session.ts +0 -81
  345. package/src/panels/builtin/shared.ts +0 -357
  346. package/src/panels/builtin-panels.ts +0 -28
  347. package/src/panels/cost-tracker-panel.ts +0 -746
  348. package/src/panels/diff-panel.ts +0 -797
  349. package/src/panels/diff-review-panel.ts +0 -395
  350. package/src/panels/expandable-list-panel.ts +0 -189
  351. package/src/panels/fleet-deep-link.ts +0 -31
  352. package/src/panels/fleet-panel.ts +0 -799
  353. package/src/panels/git-panel.ts +0 -800
  354. package/src/panels/hosted-session-panel.ts +0 -221
  355. package/src/panels/index.ts +0 -21
  356. package/src/panels/local-auth-panel.ts +0 -478
  357. package/src/panels/notifications-feed.ts +0 -127
  358. package/src/panels/notifications-panel.ts +0 -160
  359. package/src/panels/panel-confirm-overlay.ts +0 -72
  360. package/src/panels/panel-manager.ts +0 -791
  361. package/src/panels/plugins-panel.ts +0 -371
  362. package/src/panels/scrollable-list-panel.ts +0 -498
  363. package/src/panels/skills-panel.ts +0 -454
  364. package/src/panels/token-budget-panel.ts +0 -688
  365. package/src/panels/types.ts +0 -191
  366. package/src/panels/wrfc-panel-format.ts +0 -133
  367. package/src/renderer/diff-view.ts +0 -134
  368. package/src/renderer/fleet-tab-strip.ts +0 -56
  369. package/src/renderer/footer-tips.ts +0 -53
  370. package/src/renderer/fullscreen-primitives.ts +0 -146
  371. package/src/renderer/fullscreen-workspace.ts +0 -194
  372. package/src/renderer/hint-grammar.ts +0 -52
  373. package/src/renderer/modal-factory.ts +0 -480
  374. package/src/renderer/overlay-box.ts +0 -154
  375. package/src/renderer/overlay-viewport.ts +0 -104
  376. package/src/renderer/panel-composite.ts +0 -145
  377. package/src/renderer/panel-workspace-bar.ts +0 -54
  378. package/src/renderer/process-indicator.ts +0 -163
  379. package/src/renderer/tab-strip.ts +0 -222
  380. package/src/renderer/text-layout.ts +0 -31
  381. package/src/renderer/tool-call.ts +0 -276
  382. package/src/renderer/tool-result-expanded-lines.ts +0 -48
  383. package/src/renderer/tool-result-summary.ts +0 -219
  384. package/src/runtime/diagnostics/panels/index.ts +0 -21
  385. package/src/runtime/diagnostics/panels/ops.ts +0 -156
  386. package/src/runtime/perf/panel-contracts.ts +0 -32
  387. package/src/runtime/store/domains/panels.ts +0 -117
  388. package/src/verification/verification-ledger.ts +0 -707
  389. /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
  390. /package/src/{panels → views}/modals/modal-theme.ts +0 -0
  391. /package/src/{panels → views}/provider-health-domains.ts +0 -0
@@ -12,7 +12,7 @@
12
12
  import { join } from 'node:path';
13
13
  import { Orchestrator, type OrchestratorUserInputOptions } from '@pellux/goodvibes-sdk/platform/core';
14
14
  import { AcpManager } from '@pellux/goodvibes-sdk/platform/acp';
15
- import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
15
+ import { getTierForContextWindow, getTierPromptSupplement } from '@pellux/goodvibes-sdk/platform/providers';
16
16
  import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
17
17
  import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
18
18
  import type { WorkspaceTrustLevel } from '@pellux/goodvibes-sdk/platform/runtime/operations';
@@ -39,6 +39,7 @@ import type { UiRuntimeServices } from './ui-services.ts';
39
39
  import { initializeBootstrapCore } from './bootstrap-core.ts';
40
40
  import { ensureBootModelResolvable } from './provider-fallback.ts';
41
41
  import { createBootstrapShell } from './bootstrap-shell.ts';
42
+ import type { ShellViews } from '../views/builtin-views.ts';
42
43
  import { announceResumeState } from './resume-notice.ts';
43
44
  import { announceInstallHealth } from './install-self-check-startup.ts';
44
45
  import { buildSharedOrchestratorCoreServices, refreshMemoryRecallSnapshot } from './orchestrator-core-services.ts';
@@ -86,6 +87,8 @@ export type BootstrapContext = RuntimeContext & {
86
87
  commandContext: CommandContext;
87
88
  /** Shell-facing read models, events, and narrow runtime services. */
88
89
  uiServices: UiRuntimeServices;
90
+ /** The always-on read models and config-modal surfaces behind the built-in modals. */
91
+ views: ShellViews;
89
92
  /** Persists and navigates input history across sessions. */
90
93
  inputHistory: InputHistory;
91
94
  /** Provides git branch/dirty state for the header. */
@@ -120,7 +123,7 @@ export type BootstrapContext = RuntimeContext & {
120
123
  * @remarks
121
124
  * Route operational messages through this rather than calling
122
125
  * conversation.addSystemMessage() directly so routing-target config
123
- * (panel/conversation/both) and the forced-inline critical prefixes stay
126
+ * (target 'panel' / 'conversation' / 'both') and the forced-inline critical prefixes stay
124
127
  * centralized in one place.
125
128
  */
126
129
  systemMessageRouter: SystemMessageRouter;
@@ -143,7 +146,7 @@ export type BootstrapContext = RuntimeContext & {
143
146
  * 4. Runtime bus subscriptions (WRFC, subagent, hook bridge)
144
147
  * 5. Providers, webhooks, PermissionManager, HookDispatcher
145
148
  * 6. Orchestrator + AcpManager
146
- * 7. MCP auto-connect + workspace/panel manager
149
+ * 7. MCP auto-connect + workspace/view manager
147
150
  * 8. Command registry + plugin init + CommandContext
148
151
  * 9. Input handler wiring
149
152
  * 10. Input history, splash options
@@ -195,7 +198,6 @@ export async function bootstrapRuntime(
195
198
  const {
196
199
  automationManager,
197
200
  hookDispatcher,
198
- panelManager,
199
201
  pluginManager,
200
202
  } = services;
201
203
  // A saved custom-provider model must never crash boot, see provider-fallback.ts.
@@ -219,9 +221,10 @@ export async function bootstrapRuntime(
219
221
  permissionManager,
220
222
  getSystemPrompt: () => {
221
223
  const currentModel = providerRegistry.getCurrentModel();
222
- const contextWindow = providerRegistry.getContextWindowForModel(currentModel);
223
- const tier = getTierForContextWindow(contextWindow);
224
- const supplement = getTierPromptSupplement(tier);
224
+ // An unknown window (null) picks the standard tier, never the small-model one.
225
+ const tier = getTierForContextWindow(providerRegistry.getKnownContextWindowForModel(currentModel));
226
+ // The main session is a conversation: never the agent-run completion-report demand.
227
+ const supplement = getTierPromptSupplement(tier, { audience: 'conversation' });
225
228
  return joinPromptParts(runtime.systemPrompt, TUI_ORCHESTRATION_GUARDRAILS, supplement);
226
229
  },
227
230
  hookDispatcher,
@@ -543,7 +546,7 @@ export async function bootstrapRuntime(
543
546
  label: 'external-services',
544
547
  run: async () => {
545
548
  // Register the persistent companion-pairing token as the daemon's shared
546
- // bearer, so tokens scanned from the /qrcode panel's QR actually
549
+ // bearer, so tokens scanned from the /qrcode modal's QR actually
547
550
  // authenticate against the daemon this surface adopts.
548
551
  const daemonHomeDir = join(services.homeDirectory, '.goodvibes', 'daemon');
549
552
  const companionTokenRecord = resolveDaemonCompanionToken(daemonHomeDir, GOODVIBES_TUI_SURFACE_ROOT);
@@ -689,6 +692,7 @@ export async function bootstrapRuntime(
689
692
  selection,
690
693
  commandContext,
691
694
  uiServices,
695
+ views: shell.views,
692
696
  inputHistory,
693
697
  gitStatusProvider,
694
698
  lastGitInfoRef,
@@ -753,12 +757,8 @@ export async function bootstrapRuntime(
753
757
  // task manager exist unconditionally so pre-gate tasks stay visible. ───────
754
758
  ctx.commandContext.ops.acpManager = acpManager;
755
759
  if (opsControlPlane) {
756
- ctx.commandContext.openOpsPanel = () => {
757
- if (ctx.commandContext.showPanel) ctx.commandContext.showPanel('ops-control');
758
- else {
759
- panelManager.open('ops-control');
760
- requestRender();
761
- }
760
+ ctx.commandContext.openOpsView = () => {
761
+ ctx.commandContext.openView?.('ops-control');
762
762
  };
763
763
  }
764
764
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * fleet-union.ts, the Fleet panel shows everything running, not just what this
2
+ * fleet-union.ts, the Agents modal shows everything running, not just what this
3
3
  * terminal started.
4
4
  *
5
5
  * ── What moved to the SDK, and what stays here ────────────────────────────
@@ -11,10 +11,10 @@
11
11
  * reason string a steer/archive against a daemon-only row gets, rather than a
12
12
  * bare false). This module is now a thin wrapper over that policy.
13
13
  *
14
- * What stays here is the panel binding: the TUI's own `FleetReadModel`
14
+ * What stays here is the modal binding: the TUI's own `FleetReadModel`
15
15
  * interface (interrupt/resume/kill/steer/archive, subscribe, snapshots) and
16
16
  * `buildFleetSnapshot`, the tree-builder + honest cost/token aggregator this
17
- * repo's Fleet panel renders from. Rebuilding through the SAME builder the
17
+ * repo's Agents modal renders from. Rebuilding through the SAME builder the
18
18
  * local view uses, rather than summing two halves, is what keeps the
19
19
  * rollups, the cost/token totals and the blocked-on-user ordering computed
20
20
  * once, over the whole fleet.
@@ -33,7 +33,7 @@
33
33
  * `interrupt`/`resume`/`kill`/`steer` reach this process's own children. A
34
34
  * daemon row has no child here to signal, so those refuse, and `steer`,
35
35
  * which has a reason channel, says why rather than returning a bare false
36
- * that reads as "the agent ignored you". The panel's own act surface
36
+ * that reads as "the agent ignored you". The modal's own act surface
37
37
  * (fleet-gateway.ts) already drives the daemon's verbs for the acts the
38
38
  * daemon serves.
39
39
  */
@@ -49,7 +49,7 @@ import {
49
49
  type DaemonVerbCaller,
50
50
  } from '@pellux/goodvibes-sdk/platform/runtime/client';
51
51
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
52
- import { buildFleetSnapshot, type FleetReadModel, type FleetSnapshot } from '../../panels/fleet-read-model.ts';
52
+ import { buildFleetSnapshot, type FleetReadModel, type FleetSnapshot } from '../../views/fleet-read-model.ts';
53
53
 
54
54
  /** Re-exported so an importer that only needs the SDK's policy has one import site. */
55
55
  export { DEFAULT_FLEET_REFRESH_MS, daemonOnlyFleetActRefusal, mergeFleetNodes, readDaemonFleetRows };
@@ -26,7 +26,7 @@ import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
26
26
  import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
27
27
  import { createDaemonVerbCaller } from './operator-endpoint.ts';
28
28
  import { createHostedSessionsClient } from './hosted-sessions.ts';
29
- import { getSharedHostedSessionFeed, type HostedSessionFeed } from '../../panels/hosted-session-feed.ts';
29
+ import { getSharedHostedSessionFeed, type HostedSessionFeed } from '../../views/hosted-session-feed.ts';
30
30
 
31
31
  /** How long the exit will wait for the detach before giving up on it. */
32
32
  export const HOSTED_DETACH_ON_EXIT_TIMEOUT_MS = 1_500;
@@ -34,7 +34,7 @@ export const HOSTED_DETACH_ON_EXIT_TIMEOUT_MS = 1_500;
34
34
  export interface LeaveHostedSessionOnExitOptions {
35
35
  readonly configManager: ConfigManager;
36
36
  readonly homeDirectory: string | (() => string);
37
- /** Defaults to the shared feed the `/hosted` command and the panel use. */
37
+ /** Defaults to the shared feed the `/hosted` command and the hosted-sessions modal use. */
38
38
  readonly feed?: HostedSessionFeed | undefined;
39
39
  readonly timeoutMs?: number | undefined;
40
40
  }
@@ -68,7 +68,7 @@ export function terminalHostedClientId(): string {
68
68
  return cachedClientId;
69
69
  }
70
70
 
71
- /** The narrow client the command layer and the panel share. */
71
+ /** The narrow client the command layer and the hosted-sessions modal share. */
72
72
  export interface HostedSessionsClient {
73
73
  /** This terminal's attach identity, the same string the records carry. */
74
74
  readonly clientId: string;
@@ -29,7 +29,7 @@
29
29
  * - `sessions.inputs.list` / `sessions.inputs.deliver`, the inbound steer
30
30
  * path, so a message another surface queued for THIS session lands in the
31
31
  * turn machinery here and is acknowledged on the wire.
32
- * - `sessions.list`, the cross-surface union the panels read, interval-
32
+ * - `sessions.list`, the cross-surface union the modals read, interval-
33
33
  * refreshed and served synchronously.
34
34
  * - the memory spine's wire transport, folded into the same handler by the
35
35
  * SDK, this module's own `syncMemorySpineToHostStatus` call is retired;
@@ -24,7 +24,7 @@ export interface CompactionActivityBus {
24
24
  * - getTurnInjections() → Orchestrator.getTurnInjections() (the same bounded
25
25
  * ring `/recall injections` renders from).
26
26
  * - getTokenState() → Orchestrator.usage / lastInputTokens (the same
27
- * counters the footer's context bar and CostTrackerPanel read), plus the
27
+ * counters the footer's context bar and the usage modal read), plus the
28
28
  * current model's context window from the provider registry.
29
29
  * - getCompactionState() → compactionCount from SessionLineageTracker (the
30
30
  * same counter session-maintenance and /compact read); isCompacting is
@@ -35,7 +35,7 @@ export interface CompactionActivityBus {
35
35
  */
36
36
  export interface ContextAccountingSourceDeps {
37
37
  readonly orchestrator: Pick<Orchestrator, 'getTurnInjections' | 'usage' | 'lastInputTokens'>;
38
- readonly providerRegistry: Pick<ProviderRegistry, 'getCurrentModel' | 'getContextWindowForModel'>;
38
+ readonly providerRegistry: Pick<ProviderRegistry, 'getCurrentModel' | 'getKnownContextWindowForModel'>;
39
39
  readonly sessionLineageTracker: Pick<SessionLineageTracker, 'getCompactionCount'>;
40
40
  readonly runtimeBus?: CompactionActivityBus | null | undefined;
41
41
  readonly sessionId: string;
@@ -83,11 +83,12 @@ export function createContextAccountingSource(deps: ContextAccountingSourceDeps)
83
83
  getTurnInjections: () => deps.orchestrator.getTurnInjections(),
84
84
  getTokenState: () => {
85
85
  const currentModel = deps.providerRegistry.getCurrentModel();
86
- const contextWindow = deps.providerRegistry.getContextWindowForModel(currentModel);
86
+ // null when nothing states the window (a guess, or disproven by a larger accepted request).
87
+ const contextWindow = deps.providerRegistry.getKnownContextWindowForModel(currentModel);
87
88
  return {
88
89
  measured: { ...deps.orchestrator.usage },
89
90
  lastInputTokens: deps.orchestrator.lastInputTokens,
90
- contextWindow: contextWindow > 0 ? contextWindow : null,
91
+ contextWindow: contextWindow !== null && contextWindow > 0 ? contextWindow : null,
91
92
  };
92
93
  },
93
94
  getCompactionState: () => ({
@@ -281,6 +281,7 @@ export {
281
281
  buildLocalReturnContextSummary,
282
282
  formatReturnContextForDisplay,
283
283
  getReturnContextMode,
284
+ loadedReturnContext,
284
285
  maybeAssistReturnContextSummary,
285
286
  persistConversation,
286
287
  generateUserSessionId,
@@ -5,7 +5,7 @@
5
5
  * The SDK's NotificationRouter decides where each domain notification goes
6
6
  * (conversation / status_bar / panel_only) and collapses bursts and batches.
7
7
  * Nothing in the running app routed real notifications before this: the
8
- * panel_only feed (panels/notifications-feed.ts) and its panel existed with no
8
+ * panel_only feed (views/notifications-feed.ts) and the notifications modal existed with no
9
9
  * upstream. This module builds the router, records every panel_only decision
10
10
  * (including burst-collapsed ones) into the shared feed, and bridges the
11
11
  * runtime event bus so real domain events become notifications.
@@ -14,12 +14,19 @@
14
14
  import { createNotificationRouter, type NotificationRouter } from '@pellux/goodvibes-sdk/platform/runtime/ui';
15
15
  import type { Notification, RoutingDecision, RuntimeEventBus, RuntimeEventDomain } from '@/runtime/index.ts';
16
16
  import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
17
- import { getSharedNotificationFeed, type PanelNotificationFeed } from '../panels/notifications-feed.ts';
17
+ import { getSharedNotificationFeed, type NotificationFeed } from '../views/notifications-feed.ts';
18
+ import { publishNotice, type NoticeSink } from '../core/notices.ts';
18
19
  import { memoryPressureLine, memoryPressureLevel, type MemoryPressurePayload } from '@pellux/goodvibes-sdk/platform/runtime/memory';
20
+ import { runtimeEventKey } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap';
19
21
 
20
22
  export interface NotificationDispatcher {
21
- /** Route a notification; a panel_only (or burst-collapsed) decision lands in the feed. Returns the decision. */
22
- dispatch(notification: Notification): RoutingDecision;
23
+ /**
24
+ * Route a notification; a panel_only (or burst-collapsed) decision lands in
25
+ * the feed. `eventKey` names the runtime event it came from (the SDK's
26
+ * runtimeEventKey), so the feed keeps one entry when the same event also
27
+ * arrives as a conversation notice. Returns the decision.
28
+ */
29
+ dispatch(notification: Notification, eventKey?: string): RoutingDecision;
23
30
  /** Surface batch-held notifications (call on a timer / when quiet-typing ends). */
24
31
  flush(): void;
25
32
  readonly router: NotificationRouter;
@@ -28,7 +35,7 @@ export interface NotificationDispatcher {
28
35
  /**
29
36
  * The runtime event domains whose events surface as operational notifications.
30
37
  * Deliberately a curated set of user-relevant, completion/attention-shaped
31
- * domains, not every domain, so the panel reflects meaningful operational
38
+ * domains, not every domain, so the notifications modal reflects meaningful operational
32
39
  * activity rather than raw event churn. The router's per-domain verbosity and
33
40
  * burst/batch policies still collapse floods within these.
34
41
  */
@@ -41,46 +48,102 @@ export const NOTIFICATION_BRIDGE_DOMAINS: readonly RuntimeEventDomain[] = [
41
48
  'security',
42
49
  ];
43
50
 
44
- /** Turn an UPPER_SNAKE event type into a short human title ("AGENT_COMPLETED" → "Agent completed"). */
45
- export function humanizeEventType(type: string): string {
46
- const words = type.toLowerCase().split(/[_\s]+/).filter(Boolean);
47
- if (words.length === 0) return type;
48
- return words.map((word, index) => (index === 0 ? word[0]!.toUpperCase() + word.slice(1) : word)).join(' ');
51
+ /** A runtime event a person reads in the notification history: its plain title and severity. */
52
+ export interface PersonFacingEvent {
53
+ readonly title: string;
54
+ readonly level: Notification['level'];
49
55
  }
50
56
 
51
- /** Derive a notification severity from an event type, errors/failures warn, the rest are informational. */
52
- export function levelForEventType(type: string): Notification['level'] {
53
- const upper = type.toUpperCase();
54
- if (/(FAILED|ERROR|CRASH|DENIED|BLOCKED)/.test(upper)) return 'warning';
55
- if (/(CRITICAL|FATAL|BREACH)/.test(upper)) return 'critical';
56
- return 'info';
57
+ /**
58
+ * The runtime events that become notification-history entries, by event type,
59
+ * with the plain title each one shows. Everything else on the bridged domains
60
+ * (stream deltas, progress ticks, gate results, state changes, queue and start
61
+ * events) is internal traffic and never enters the history: this table is an
62
+ * allowlist, so a new SDK event type stays out until someone names it here.
63
+ */
64
+ export const PERSON_FACING_EVENTS: Readonly<Record<string, PersonFacingEvent>> = {
65
+ // agents
66
+ AGENT_COMPLETED: { title: 'Agent finished', level: 'info' },
67
+ AGENT_FAILED: { title: 'Agent failed', level: 'warning' },
68
+ AGENT_CANCELLED: { title: 'Agent cancelled', level: 'info' },
69
+ AGENT_AWAITING_MESSAGE: { title: 'Agent is waiting for a reply', level: 'info' },
70
+ // tasks
71
+ TASK_COMPLETED: { title: 'Task finished', level: 'info' },
72
+ TASK_FAILED: { title: 'Task failed', level: 'warning' },
73
+ TASK_BLOCKED: { title: 'Task blocked', level: 'warning' },
74
+ TASK_CANCELLED: { title: 'Task cancelled', level: 'info' },
75
+ // workflows (review chains)
76
+ WORKFLOW_CHAIN_PASSED: { title: 'Review chain passed', level: 'info' },
77
+ WORKFLOW_CHAIN_FAILED: { title: 'Review chain failed', level: 'warning' },
78
+ WORKFLOW_CASCADE_ABORTED: { title: 'Review chain stopped', level: 'warning' },
79
+ WORKFLOW_AUTO_COMMITTED: { title: 'Reviewed changes committed', level: 'info' },
80
+ WORKFLOW_SCORE_REGRESSION: { title: 'Review score dropped', level: 'warning' },
81
+ // automation (scheduled jobs)
82
+ AUTOMATION_RUN_COMPLETED: { title: 'Scheduled job finished', level: 'info' },
83
+ AUTOMATION_RUN_FAILED: { title: 'Scheduled job failed', level: 'warning' },
84
+ AUTOMATION_JOB_AUTO_DISABLED: { title: 'Scheduled job turned off after repeated failures', level: 'warning' },
85
+ AUTOMATION_SCHEDULE_ERROR: { title: 'Schedule could not be read', level: 'warning' },
86
+ // deliveries (outgoing channel messages)
87
+ DELIVERY_FAILED: { title: 'Message delivery failed', level: 'warning' },
88
+ DELIVERY_DEAD_LETTERED: { title: 'Message could not be delivered', level: 'warning' },
89
+ // security
90
+ AUTH_FAILED: { title: 'Sign-in failed', level: 'warning' },
91
+ COMPANION_PAIR_REQUESTED: { title: 'Device pairing requested', level: 'info' },
92
+ COMPANION_PAIR_VERIFIED: { title: 'Device paired', level: 'info' },
93
+ COMPANION_TOKEN_REVOKED: { title: 'Device access revoked', level: 'info' },
94
+ TOKEN_ROTATION_WARNING: { title: 'Access token expires soon', level: 'warning' },
95
+ TOKEN_ROTATION_EXPIRED: { title: 'Access token expired', level: 'warning' },
96
+ TOKEN_BLOCKED: { title: 'Access token blocked', level: 'warning' },
97
+ TOKEN_SCOPE_VIOLATION: { title: 'Request refused: outside the token\'s permissions', level: 'warning' },
98
+ };
99
+
100
+ /** The history entry an event type makes, or undefined for internal traffic that is not recorded. */
101
+ export function personFacingEvent(type: string): PersonFacingEvent | undefined {
102
+ return Object.prototype.hasOwnProperty.call(PERSON_FACING_EVENTS, type) ? PERSON_FACING_EVENTS[type] : undefined;
103
+ }
104
+
105
+ /** The detail line an event payload carries (an error or cancel reason, a chain's landing outcome, or the commit it made), if any. */
106
+ export function eventDetail(payload: unknown): string | undefined {
107
+ if (!payload || typeof payload !== 'object') return undefined;
108
+ const record = payload as Record<string, unknown>;
109
+ // `note`: a passed review chain's landing outcome (committed, or why not, e.g. a refusing commit hook).
110
+ for (const field of ['error', 'reason', 'note']) {
111
+ const value = record[field];
112
+ if (typeof value === 'string' && value.trim()) return value.trim();
113
+ }
114
+ const commit = record['commitHash'];
115
+ if (typeof commit === 'string' && commit.trim()) return `commit ${commit.trim().slice(0, 7)}`;
116
+ return undefined;
57
117
  }
58
118
 
59
119
  export function createNotificationDispatcher(
60
120
  configManager: Pick<ConfigManager, 'get'>,
61
- feed: PanelNotificationFeed = getSharedNotificationFeed(),
121
+ feed: NotificationFeed = getSharedNotificationFeed(),
62
122
  ): NotificationDispatcher {
63
123
  const router = createNotificationRouter(undefined, undefined, configManager);
64
- const recordIfPanel = (notification: Notification, decision: RoutingDecision): void => {
124
+ /** Event keys of notifications the router is holding in a batch, for when the batch is flushed. */
125
+ const heldEventKeys = new WeakMap<Notification, string>();
126
+ const recordIfFeedTarget = (notification: Notification, decision: RoutingDecision, eventKey: string | undefined): void => {
65
127
  if (decision.suppressed) return;
66
- if (decision.target === 'panel_only') feed.record(notification, decision);
128
+ if (decision.target === 'panel_only') feed.record(notification, decision, eventKey);
67
129
  };
68
130
  return {
69
131
  router,
70
- dispatch(notification) {
132
+ dispatch(notification, eventKey) {
71
133
  const decision = router.route(notification);
72
- recordIfPanel(notification, decision);
134
+ if (eventKey) heldEventKeys.set(notification, eventKey);
135
+ recordIfFeedTarget(notification, decision, eventKey);
73
136
  return decision;
74
137
  },
75
138
  flush() {
76
139
  for (const { notification } of router.flush()) {
77
- // A flushed batch head surfaces as a batch-collapsed panel entry; the
140
+ // A flushed batch head surfaces as a batch-collapsed feed entry; the
78
141
  // feed folds all sharing this batch key into one running-count row.
79
142
  feed.record(notification, {
80
143
  target: 'panel_only',
81
144
  reasonCode: 'batch_window_collapsed',
82
145
  batchKey: `${notification.domain}:${notification.level}`,
83
- });
146
+ }, heldEventKeys.get(notification));
84
147
  }
85
148
  },
86
149
  };
@@ -114,9 +177,10 @@ export function wireMemoryPressureNotice(
114
177
  }
115
178
 
116
179
  /**
117
- * Bridge the runtime event bus to the dispatcher: each event in a curated
118
- * domain becomes a notification and is routed. Returns an unsubscribe function
119
- * that detaches every domain listener.
180
+ * Bridge the runtime event bus to the dispatcher: each person-facing event
181
+ * (PERSON_FACING_EVENTS) in a curated domain becomes a notification with its
182
+ * plain title and is routed. Internal events are not recorded at all. Returns
183
+ * an unsubscribe function that detaches every domain listener.
120
184
  */
121
185
  export function wireRuntimeNotificationBridge(
122
186
  runtimeBus: RuntimeEventBus,
@@ -125,14 +189,23 @@ export function wireRuntimeNotificationBridge(
125
189
  ): () => void {
126
190
  const unsubscribes = domains.map((domain) =>
127
191
  runtimeBus.onDomain(domain, (envelope) => {
192
+ const event = personFacingEvent(envelope.type);
193
+ if (!event) return;
194
+ const body = eventDetail(envelope.payload);
128
195
  dispatcher.dispatch({
129
196
  id: envelope.traceId ?? `${domain}-${envelope.type}-${envelope.ts}`,
130
197
  domain,
131
- level: levelForEventType(envelope.type),
132
- title: humanizeEventType(envelope.type),
198
+ level: event.level,
199
+ title: event.title,
200
+ ...(body ? { body } : {}),
133
201
  timestamp: envelope.ts,
134
- });
202
+ }, runtimeEventKey(envelope.type, envelope.payload));
135
203
  }),
136
204
  );
137
205
  return () => { for (const unsubscribe of unsubscribes) unsubscribe(); };
138
206
  }
207
+
208
+ /** The shell's conversation notice sink: every system notice is a toast and a history entry (core/notices.ts). */
209
+ export function createShellNoticeSink(feed: NotificationFeed = getSharedNotificationFeed()): NoticeSink {
210
+ return (content, { restored }) => publishNotice(feed, content, { restored });
211
+ }
@@ -27,7 +27,7 @@
27
27
  * numbers on screen describe the conversation actually in memory.
28
28
  */
29
29
  import { applyRecoverySnapshot, confirmRecoveryRestore } from '@pellux/goodvibes-sdk/platform/runtime/operations';
30
- import type { RecoveryPromptDeps } from './recovery-prompt.ts';
30
+ import type { RecoveryPromptDeps, RecoveryTypeaheadHooks } from './recovery-prompt.ts';
31
31
  import type { ConversationManager, ConversationMessageSnapshot } from '../core/conversation.ts';
32
32
  import type { CommandContext } from '../input/command-registry.ts';
33
33
  import type { SessionSurface } from '@/runtime/index.ts';
@@ -44,6 +44,8 @@ export interface RecoveryOfferWiringInput {
44
44
  readonly writeLastSessionPointer: (sessionId: string) => void;
45
45
  readonly receipt: (line: string) => void;
46
46
  readonly render: () => void;
47
+ /** Holds typeahead out of the offer modal (see RecoveryPromptDeps.typeahead). */
48
+ readonly typeahead?: RecoveryTypeaheadHooks;
47
49
  }
48
50
 
49
51
  export function buildRecoveryOfferWiring(input: RecoveryOfferWiringInput): RecoveryPromptDeps {
@@ -54,6 +56,7 @@ export function buildRecoveryOfferWiring(input: RecoveryOfferWiringInput): Recov
54
56
  get openSelection() { return input.commandContext.openSelection; },
55
57
  receipt: input.receipt,
56
58
  render: input.render,
59
+ ...(input.typeahead ? { typeahead: input.typeahead } : {}),
57
60
  applySnapshot: ({ sessionId }) => {
58
61
  const persist = (messages: ConversationMessageSnapshot[]): void => {
59
62
  input.sessionManager.save(sessionId, messages as never[], {
@@ -111,6 +111,19 @@ export interface RecoveryPromptDeps {
111
111
  * session has the newest snapshot".
112
112
  */
113
113
  readonly targetSessionId?: string;
114
+ /**
115
+ * Keys typed before the offer appeared must not answer it. Called right
116
+ * before the first modal opens (arm) and once the whole flow is over
117
+ * (settled), so the shell can hold typeahead out of the modal and replay it
118
+ * into the composer afterwards (runtime/startup-typeahead-gate.ts).
119
+ */
120
+ readonly typeahead?: RecoveryTypeaheadHooks;
121
+ }
122
+
123
+ /** See RecoveryPromptDeps.typeahead. */
124
+ export interface RecoveryTypeaheadHooks {
125
+ readonly onModalShown: () => void;
126
+ readonly onFlowSettled: () => void;
114
127
  }
115
128
 
116
129
  // ─── Honest fact formatting ─────────────────────────────────────────────────
@@ -284,6 +297,7 @@ function findOfferableSnapshot(deps: RecoveryPromptDeps): RecoveryFileInfo | nul
284
297
  * a failure anywhere in here must not take a boot down.
285
298
  */
286
299
  export async function offerRecoverySnapshot(deps: RecoveryPromptDeps): Promise<RecoveryPromptOutcome> {
300
+ let shown = false;
287
301
  try {
288
302
  // Housekeeping first: recovery time is when the removal ledger gets its
289
303
  // bounds actually applied to disk and says what it discarded. Without this
@@ -308,6 +322,8 @@ export async function offerRecoverySnapshot(deps: RecoveryPromptDeps): Promise<R
308
322
  const bytes = deps.snapshotBytes ? deps.snapshotBytes(info.sessionId) : defaultSnapshotBytes(deps.surface, info.sessionId);
309
323
  const facts = describeRecoverySnapshot(info, { nowMs, bytes });
310
324
 
325
+ deps.typeahead?.onModalShown();
326
+ shown = true;
311
327
  const answer = await ask(open, RECOVERY_OFFER_TITLE, buildRecoveryOfferItems(facts));
312
328
 
313
329
  if (answer === 'resume') {
@@ -359,28 +375,28 @@ export async function offerRecoverySnapshot(deps: RecoveryPromptDeps): Promise<R
359
375
  // Best-effort by construction: a recovery offer that fails must leave the
360
376
  // snapshot untouched and the boot unharmed.
361
377
  return 'none';
378
+ } finally {
379
+ if (shown) deps.typeahead?.onFlowSettled();
362
380
  }
363
381
  }
364
382
 
365
383
  // ─── Startup wiring ─────────────────────────────────────────────────────────
366
384
 
367
385
  /**
368
- * Raise the recovery offer once the shell is up.
386
+ * Raise the recovery offer at startup, BEFORE the shell's first frame.
369
387
  *
370
- * Deliberately fire-and-forget and deliberately AFTER the first render: the
371
- * modal is drawn by the render loop, so asking before a frame exists would
372
- * mean a question nobody can see, and awaiting it would hold the terminal
373
- * blank while the user was expected to answer. Scheduled on a macrotask so
374
- * the caller's own initial render has completed first.
388
+ * The modal is opened synchronously (offerRecoverySnapshot runs synchronously
389
+ * up to the modal), so the first frame the user ever sees already has the
390
+ * question on it: the composer is never live underneath a question that is
391
+ * about to appear. It used to be raised on a macrotask after the first frame,
392
+ * which on a busy boot came seconds after the composer was drawn and took the
393
+ * keys typed into it (the search row got the text, Enter resumed). Keys typed
394
+ * before the frame was painted are the typeahead gate's job
395
+ * (deps.typeahead, startup-typeahead-gate.ts).
375
396
  *
376
- * Every failure mode here ends with the snapshot untouched: `offerRecoverySnapshot`
377
- * swallows its own errors, and this wrapper adds a catch for the scheduling
378
- * boundary.
397
+ * Fire-and-forget: nothing awaits the answer, and every failure mode ends with
398
+ * the snapshot untouched (offerRecoverySnapshot swallows its own errors).
379
399
  */
380
- export function scheduleRecoveryOffer(deps: RecoveryPromptDeps): void {
381
- setTimeout(() => {
382
- void offerRecoverySnapshot(deps).catch(() => {
383
- // Best-effort: a failed offer must never take the shell down.
384
- });
385
- }, 0).unref?.();
400
+ export function startRecoveryOffer(deps: RecoveryPromptDeps): Promise<RecoveryPromptOutcome> {
401
+ return offerRecoverySnapshot(deps);
386
402
  }
@@ -54,7 +54,7 @@ import type { RuntimeEventBus, DistributedRuntimeManager, RemoteRunnerRegistry,
54
54
  import type { VoiceProviderRegistry, VoiceService } from '@pellux/goodvibes-sdk/platform/voice';
55
55
  import type { CacheRegistry, PauseController, MemoryGovernor } from '@pellux/goodvibes-sdk/platform/runtime/memory';
56
56
  import type { WebSearchProviderRegistry, WebSearchService } from '@pellux/goodvibes-sdk/platform/web-search';
57
- import type { PanelManager } from '../panels/panel-manager.ts';
57
+ import type { ViewPanelAdapter } from '../views/view-panel-adapter.ts';
58
58
  import type { HookActivityTracker } from '@pellux/goodvibes-sdk/platform/hooks';
59
59
  import type { HookDispatcher, HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks';
60
60
  import type { PluginManager } from '@pellux/goodvibes-sdk/platform/plugins';
@@ -118,7 +118,11 @@ export interface RuntimeServices {
118
118
  readonly runtimeBus: RuntimeEventBus;
119
119
  readonly runtimeStore: RuntimeStore;
120
120
  readonly runtimeDispatch: DomainDispatch;
121
- readonly panelManager: PanelManager;
121
+ /**
122
+ * The SDK's panel-manager contract (the operator API's panels.list /
123
+ * panels.open), answered by the modal views.
124
+ */
125
+ readonly panelManager: ViewPanelAdapter;
122
126
  readonly keybindingsManager: KeybindingsManager;
123
127
  readonly routeBindings: RouteBindingManager;
124
128
  readonly surfaceRegistry: SurfaceRegistry;
@@ -128,7 +132,7 @@ export interface RuntimeServices {
128
132
  readonly watcherRegistry: WatcherRegistry;
129
133
  /**
130
134
  * This surface's own record of the asks IT raised, what the approval card
131
- * and the panel render. Not authoritative: `requestApproval` below raises the
135
+ * and the modal renders. Not authoritative: `requestApproval` below raises the
132
136
  * same ask on the daemon, whose record every other surface reads.
133
137
  */
134
138
  readonly approvalBroker: ApprovalBroker;
@@ -168,7 +172,7 @@ export interface RuntimeServices {
168
172
  readonly localhostFetchApproval: ReturnType<typeof buildLocalhostFetchApproval>;
169
173
  /** Terminal prompt-answer handler that rides the approval broker; shared by the tool registry and orchestrator so an interactive command's prompt gets an ask/card on every surface. */
170
174
  readonly execPromptAnswerHandler: ReturnType<typeof buildExecPromptAnswerHandler>;
171
- /** Routes curated runtime-domain events into the panel_only notification feed (the panel's live producer). */
175
+ /** Routes curated runtime-domain events into the panel_only notification feed (the notifications modal's live producer). */
172
176
  readonly notificationDispatcher: NotificationDispatcher;
173
177
  /** Durable user-origin permission rules (remembered approvals); permissions.rules.* surface. Mirrors the SDK composition. */
174
178
  readonly userPermissionRuleStore: UserPermissionRuleStore;
@@ -280,10 +284,10 @@ export interface RuntimeServices {
280
284
  /** Controller the governor uses to pause/resume the deferrable background jobs under pressure. */
281
285
  readonly pauseController: PauseController;
282
286
  readonly sessionLiveTurnControls: SessionLiveTurnControlsHolder;
283
- /** Unified live process registry (agents, WRFC chains, workflows, watchers, background processes) backing the Fleet panel; archive-aware, finished subtrees can be moved to the session archive view. */
287
+ /** Unified live process registry (agents, WRFC chains, workflows, watchers, background processes) backing the Agents modal; archive-aware, finished subtrees can be moved to the session archive view. */
284
288
  readonly processRegistry: ArchivableProcessRegistry;
285
289
  /**
286
- * What the Fleet panel reads: this surface's own registry rows UNION the
290
+ * What the Agents modal reads: this surface's own registry rows UNION the
287
291
  * adopted daemon's, deduped by node id with the local (live, actionable) copy
288
292
  * winning. Interval-refreshed on the daemon half, see client/fleet-union.ts.
289
293
  */