@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
@@ -1,5 +1,5 @@
1
1
  import { MODAL_TONES } from './modal-theme.ts';
2
- import { infoRow } from './modal-surface-helpers.ts';
2
+ import { infoRow, headerRow } from './modal-surface-helpers.ts';
3
3
  import { listHookPointContracts } from '@pellux/goodvibes-sdk/platform/hooks';
4
4
  import type {
5
5
  HookActivityTracker,
@@ -18,11 +18,11 @@ import type {
18
18
  // ---------------------------------------------------------------------------
19
19
  // Hooks → config-modal surface (group-B port). Two tabs: 'Hooks' (active
20
20
  // registrations + contract posture) and 'Activity' (recent runtime activity +
21
- // managed authoring + last simulation, the panel's expand-activity view, now a
21
+ // managed authoring + last simulation, the earlier view's expand-activity view, now a
22
22
  // dedicated tab). All sources are in-memory (dispatcher/workbench/tracker hold
23
23
  // live state), so buildView reads live and refresh() is a no-op. Toggle/remove/
24
24
  // simulate route to their `/hooks` command path. Selection-blind port: the
25
- // panel's selected-hook contract detail is folded into each hook row label.
25
+ // view's selected-hook contract detail is folded into each hook row label.
26
26
  // ---------------------------------------------------------------------------
27
27
 
28
28
  export type HooksModalDispatcher = Pick<HookDispatcher, 'listHooks' | 'getChains'>;
@@ -68,11 +68,11 @@ class HooksModalSurface implements ConfigModalSurface {
68
68
  if (hooks.length === 0) {
69
69
  const rows: ConfigModalRow[] = [
70
70
  infoRow('empty:0', 'No hooks are currently registered.'),
71
- infoRow('empty:1', `contracts ${contracts.length} chains ${chains.length} managed ${managedHooks.length}`, { dim: true }),
72
- infoRow('empty:2', `hooks file: ${hooksFilePath}`, { dim: true }),
73
- infoRow('empty:title', 'Next steps'),
74
- infoRow('empty:hooks', '/hooks — review hook contracts and managed authoring actions', { dim: true }),
75
- infoRow('empty:settings', '/settings — review hook/runtime behavior in the settings surface', { dim: true }),
71
+ infoRow('empty:1', `contracts ${contracts.length} chains ${chains.length} managed ${managedHooks.length}`, { fg: MODAL_TONES.dim }),
72
+ infoRow('empty:2', `hooks file: ${hooksFilePath}`, { fg: MODAL_TONES.dim }),
73
+ headerRow('empty:title', 'Next steps'),
74
+ infoRow('empty:hooks', '/hooks — review hook contracts and managed authoring actions', { fg: MODAL_TONES.dim }),
75
+ infoRow('empty:settings', '/settings — review hook/runtime behavior in the settings surface', { fg: MODAL_TONES.dim }),
76
76
  ];
77
77
  return { id: 'hooks', label: 'Hooks', rows, emptyText: '' };
78
78
  }
@@ -101,7 +101,7 @@ class HooksModalSurface implements ConfigModalSurface {
101
101
  const rows: ConfigModalRow[] = [];
102
102
  const recentActivity = this.deps.hookActivityTracker.listRecent(20);
103
103
  if (recentActivity.length === 0) {
104
- rows.push(infoRow('act:none', 'No hook activity recorded yet.', { dim: true }));
104
+ rows.push(infoRow('act:none', 'No hook activity recorded yet.', { fg: MODAL_TONES.dim }));
105
105
  } else {
106
106
  recentActivity.forEach((record, i) => {
107
107
  const decisionText = record.ok ? (record.decision ?? 'ok') : 'error';
@@ -110,18 +110,18 @@ class HooksModalSurface implements ConfigModalSurface {
110
110
  });
111
111
  }
112
112
 
113
- rows.push(infoRow('auth:title', 'Authoring'));
113
+ rows.push(headerRow('auth:title', 'Authoring'));
114
114
  const recentAuthoring = this.deps.hookWorkbench.listRecentActions(3);
115
115
  if (recentAuthoring.length === 0) {
116
- rows.push(infoRow('auth:none', 'No managed hook authoring actions recorded yet.', { dim: true }));
116
+ rows.push(infoRow('auth:none', 'No managed hook authoring actions recorded yet.', { fg: MODAL_TONES.dim }));
117
117
  } else {
118
- recentAuthoring.forEach((action, i) => rows.push(infoRow(`auth:${i}`, `${action.kind.padEnd(14)} ${action.target}`, { dim: true })));
118
+ recentAuthoring.forEach((action, i) => rows.push(infoRow(`auth:${i}`, `${action.kind.padEnd(14)} ${action.target}`, { fg: MODAL_TONES.dim })));
119
119
  }
120
120
 
121
121
  const lastSimulation = this.deps.hookWorkbench.getLastSimulation();
122
122
  if (lastSimulation) {
123
123
  rows.push(infoRow('sim:path', `last simulation: ${lastSimulation.eventPath}`));
124
- rows.push(infoRow('sim:matches', `matches: hooks=${lastSimulation.matchedHooks.length} chains=${lastSimulation.matchedChains.length}`, { dim: true }));
124
+ rows.push(infoRow('sim:matches', `matches: hooks=${lastSimulation.matchedHooks.length} chains=${lastSimulation.matchedChains.length}`, { fg: MODAL_TONES.dim }));
125
125
  }
126
126
  return { id: 'activity', label: 'Activity', rows, emptyText: 'No hook activity recorded yet.' };
127
127
  }
@@ -1,4 +1,5 @@
1
- import { infoRow } from './modal-surface-helpers.ts';
1
+ import { MODAL_TONES } from './modal-theme.ts';
2
+ import { infoRow, headerRow } from './modal-surface-helpers.ts';
2
3
  import type {
3
4
  ConfigModalActionContext,
4
5
  ConfigModalRow,
@@ -10,14 +11,13 @@ import { KeybindingsManager } from '../../input/keybindings.ts';
10
11
 
11
12
  // ---------------------------------------------------------------------------
12
13
  // Docs + Shortcuts → 'keybindings' config-modal surface (group-B port).
13
- // A MERGE of the retired DocsPanel (tools/models/shortcuts) and the
14
+ // A MERGE of the retired Docs view (tools/models/shortcuts) and the
14
15
  // shortcuts-overlay reference. Three tabs: 'Tools', 'Models', and 'Shortcuts'
15
16
  // (the categorized keyboard reference, driven live off keybindingsManager so
16
17
  // user overrides show up, followed by an exhaustive 'All Bindings (live)'
17
- // table). Enter opens the tool-inspector successor ('fleet') or switches the
18
- // active model, both via the command path (fleet is a panel; /model is a
19
- // settings mutation). Selection-blind port: the panel's selected tool/model
20
- // detail is folded into each row label. The panel's live '/' filter is dropped
18
+ // table). Enter opens Agents on processes running the selected tool
19
+ // (/agents --target <tool>:tool) or switches the active model (/model). Selection-blind port: the earlier view's selected tool/model
20
+ // detail is folded into each row label. The earlier view's live '/' filter is dropped
21
21
  // (the host has no query state).
22
22
  // ---------------------------------------------------------------------------
23
23
 
@@ -58,7 +58,7 @@ class KeybindingsModalSurface implements ConfigModalSurface {
58
58
  }
59
59
 
60
60
  private toolsTab(): ConfigModalTab {
61
- if (!this.deps.toolRegistry) return { id: 'tools', label: 'Tools', rows: [infoRow('tools:none', 'Tool registry not wired into this session.', { dim: true })], emptyText: 'Tool registry not wired into this session.' };
61
+ if (!this.deps.toolRegistry) return { id: 'tools', label: 'Tools', rows: [infoRow('tools:none', 'Tool registry not wired into this session.', { fg: MODAL_TONES.dim })], emptyText: 'Tool registry not wired into this session.' };
62
62
  const tools = this.deps.toolRegistry.list();
63
63
  const rows: ConfigModalRow[] = tools.map((tool) => {
64
64
  const d = tool.definition;
@@ -70,11 +70,11 @@ class KeybindingsModalSurface implements ConfigModalSurface {
70
70
  const detail = [d.description, meta.join(' | ') || null].filter((s): s is string => Boolean(s)).join(' · ');
71
71
  return { id: `tool:${d.name}`, label: detail ? `${d.name.padEnd(22)} ${detail}` : d.name };
72
72
  });
73
- return { id: 'tools', label: 'Tools', rows, emptyText: 'No tools registered.', hints: ['enter open in fleet'] };
73
+ return { id: 'tools', label: 'Tools', rows, emptyText: 'No tools registered.', hints: ['enter open in agents'] };
74
74
  }
75
75
 
76
76
  private modelsTab(): ConfigModalTab {
77
- if (!this.deps.providerRegistry) return { id: 'models', label: 'Models', rows: [infoRow('models:none', 'Provider registry not wired into this session.', { dim: true })], emptyText: 'Provider registry not wired into this session.' };
77
+ if (!this.deps.providerRegistry) return { id: 'models', label: 'Models', rows: [infoRow('models:none', 'Provider registry not wired into this session.', { fg: MODAL_TONES.dim })], emptyText: 'Provider registry not wired into this session.' };
78
78
  const activeKey = this.deps.providerRegistry.getCurrentModel?.()?.registryKey;
79
79
  const rows: ConfigModalRow[] = this.sortedModels().map((model) => {
80
80
  const ctxK = model.contextWindow > 0 ? `${(model.contextWindow / 1000).toFixed(0)}k` : '?';
@@ -86,19 +86,18 @@ class KeybindingsModalSurface implements ConfigModalSurface {
86
86
 
87
87
  private shortcutsTab(): ConfigModalTab {
88
88
  const km = this.deps.keybindingsManager;
89
- if (!km) return { id: 'shortcuts', label: 'Shortcuts', rows: [infoRow('sc:none', 'Keybindings manager not wired into this session.', { dim: true })], emptyText: 'Keybindings manager not wired into this session.' };
89
+ if (!km) return { id: 'shortcuts', label: 'Shortcuts', rows: [infoRow('sc:none', 'Keybindings manager not wired into this session.', { fg: MODAL_TONES.dim })], emptyText: 'Keybindings manager not wired into this session.' };
90
90
  const kb = (action: Parameters<KeybindingsManager['getComboLabel']>[0]) => km.getComboLabel(action);
91
91
  const rows: ConfigModalRow[] = [];
92
92
  let n = 0;
93
93
  const category = (title: string, entries: ReadonlyArray<readonly [string, string]>): void => {
94
- rows.push(infoRow(`sc:t:${n++}`, title));
94
+ rows.push(headerRow(`sc:t:${n++}`, title));
95
95
  for (const [key, desc] of entries) rows.push(infoRow(`sc:${n++}`, `${key.padEnd(20)} ${desc}`));
96
96
  };
97
- category('Navigation', [['Up / Down', 'Scroll / history recall'], ['PageUp / PageDn', 'Scroll by full page'], ['Home / End', 'Jump to start / end of line'], [kb('search'), 'Search conversation'], ['n / N (search)', 'Next / previous match'], ['Mouse wheel', 'Scroll conversation or hovered panel']]);
97
+ category('Navigation', [['Up / Down', 'Scroll / history recall'], ['PageUp / PageDn', 'Scroll by full page'], ['Home / End', 'Jump to start / end of line'], [kb('search'), 'Search conversation'], ['n / N (search)', 'Next / previous match'], ['Mouse wheel', 'Scroll the conversation']]);
98
98
  category('Editing', [['Enter', 'Submit message'], ['Shift+Enter', 'Insert newline'], ['@', 'Open file picker'], ['/', 'Slash command mode'], [kb('paste'), 'Paste (image priority)'], [`${kb('undo')} / ${kb('redo')}`, 'Undo / redo'], [kb('clear-prompt'), 'Clear prompt'], [kb('delete-word'), 'Delete word backward'], [kb('kill-line'), 'Kill to end of line']]);
99
- category('Actions', [['Tab', 'Collapse/expand block'], [kb('bookmark'), 'Bookmark block'], [kb('block-copy'), 'Copy block to clipboard'], [kb('block-save'), 'Save block to file'], [kb('copy-selection'), 'Copy selection'], ['F2', 'Process monitor'], ['?', 'Help overlay'], [`${kb('clear-cancel')} x2`, 'Exit']]);
100
- category('Panels', [['Tab', 'Swap focus between input and panel workspace'], [kb('panel-picker'), 'Open / focus / hide panel workspace'], [kb('panel-tab-next'), 'Next workspace panel tab'], [kb('panel-tab-prev'), 'Previous workspace panel tab'], [`${kb('panel-tab-1')}...${kb('panel-tab-9')}`, 'Jump to workspace tab 1-9'], [kb('panel-focus-toggle'), 'Swap focus between top / bottom pane'], [kb('panel-close'), 'Close active panel'], [kb('panel-close-all'), 'Close all panels'], [kb('panel-ops'), 'Open the Ops Control panel']]);
101
- category('In-Panel Controls', [['j / k', 'Move selection down / up'], ['g / G', 'Jump to top / bottom'], ['/', 'Filter the list']]);
99
+ category('Actions', [['Tab', 'Collapse/expand block'], [kb('bookmark'), 'Bookmark block'], [kb('block-copy'), 'Copy block to clipboard'], [kb('block-save'), 'Save block to file'], [kb('copy-selection'), 'Copy selection'], ['F2', 'Agents'], ['?', 'Help overlay'], [`${kb('clear-cancel')} x2`, 'Exit']]);
100
+ category('Views', [[kb('command-palette'), 'Command palette: every command and view'], [`F2 / ${kb('open-agents')}`, 'Agents'], ['/usage', 'Usage: context, tokens and cost'], ['/changes', 'Changes: files, diff, review'], ['/notifications', 'Notification history'], ['Esc', 'Close the top modal (one level)']]);
102
101
  const allBindings = km.getAll().map((entry) => [entry.combos.length > 0 ? entry.combos.map((combo) => km.formatCombo(combo)).join(', ') : '(unbound)', entry.description] as const);
103
102
  category('All Bindings (live)', allBindings);
104
103
  return { id: 'shortcuts', label: 'Shortcuts', rows, emptyText: 'No shortcuts recorded.' };
@@ -112,19 +111,12 @@ class KeybindingsModalSurface implements ConfigModalSurface {
112
111
  if (id === 'refresh') { ctx.setStatus('Docs & shortcuts are read live.'); ctx.requestRender(); return; }
113
112
  if (id !== 'activate') return;
114
113
  if (ctx.tabId === 'tools') {
115
- // item 4: pass the tool name through as a deep-link target.
116
- // Honest caveat (documented, not a bug to silently paper over): fleet's
117
- // ProcessKind set has no 'tool' node, a docs Tools row names a static
118
- // tool DEFINITION, not a live process, so FleetPanel.receiveDeepLink
119
- // will not currently find a match and shows its honest "node no longer
120
- // present" line. The plumbing is still worth wiring now (matches the
121
- // work-plan agent/wrfc jumps' shape), it starts resolving for free the
122
- // day fleet grows a per-tool-call node (the retired DocsPanel's own
123
- // comment already anticipated this: "no filter-by-tool equivalent
124
- // there yet").
114
+ // The tool name rides as a `<tool>:tool` target: the Agents modal
115
+ // selects the first process whose current step is that tool, or says
116
+ // nothing is running it right now.
125
117
  const toolName = ctx.row?.id.startsWith('tool:') ? ctx.row.id.slice('tool:'.length) : null;
126
- void ctx.executeCommand?.('panel', toolName ? ['open', 'fleet', '--target', `${toolName}:tool`] : ['open', 'fleet']);
127
- ctx.setStatus('Opened the tool inspector (fleet).');
118
+ void ctx.executeCommand?.('agents', toolName ? ['--target', `${toolName}:tool`] : []);
119
+ ctx.setStatus(toolName ? `Opened Agents on ${toolName}.` : 'Opened Agents.');
128
120
  return;
129
121
  }
130
122
  if (ctx.tabId === 'models') {
@@ -1,4 +1,5 @@
1
- import { infoRow } from './modal-surface-helpers.ts';
1
+ import { MODAL_TONES } from './modal-theme.ts';
2
+ import { infoRow, headerRow } from './modal-surface-helpers.ts';
2
3
  import type {
3
4
  ConfigModalActionContext,
4
5
  ConfigModalRow,
@@ -11,10 +12,10 @@ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
11
12
  // ---------------------------------------------------------------------------
12
13
  // Knowledge (graph) → config-modal surface (group-B port). Two tabs:
13
14
  // 'Browse' (sources/nodes/issues + schedules) and 'Review' (the open-issue
14
- // queue), the panel's browse/review toggle, now host tabs. Issue review
15
+ // queue), the earlier view's browse/review toggle, now host tabs. Issue review
15
16
  // mutations (accept/reject/resolve/reopen) route to the `/knowledge
16
17
  // review-issue` command. 'm' cross-opens the memory surface. Selection-blind
17
- // port: the panel's selected-row id/detail is folded into each row label.
18
+ // port: the earlier view's selected-row id/detail is folded into each row label.
18
19
  // refresh() is the only place this module touches the SDK.
19
20
  // ---------------------------------------------------------------------------
20
21
 
@@ -108,8 +109,8 @@ class KnowledgeModalSurface implements ConfigModalSurface {
108
109
  ];
109
110
  const rows: ConfigModalRow[] = this.browseRows.map((row) => this.rowFor(row));
110
111
  if (this.schedules.length > 0) {
111
- rows.push(infoRow('sched:title', 'Schedules'));
112
- this.schedules.slice(0, 4).forEach((s, i) => rows.push(infoRow(`sched:${i}`, `${s.enabled ? 'on ' : 'off'} ${s.label}`, s.enabled ? undefined : { dim: true })));
112
+ rows.push(headerRow('sched:title', 'Schedules'));
113
+ this.schedules.slice(0, 4).forEach((s, i) => rows.push(infoRow(`sched:${i}`, `${s.enabled ? 'on ' : 'off'} ${s.label}`, s.enabled ? undefined : { fg: MODAL_TONES.dim })));
113
114
  }
114
115
  return { id: 'browse', label: 'Browse', header, rows, emptyText: 'No ingested knowledge yet.', hints: ['m memory'] };
115
116
  }
@@ -5,7 +5,7 @@ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
5
5
  import { postureLine, kv } from './modal-surface-helpers.ts';
6
6
 
7
7
  /** Mutation surface the surface needs to gate p/d/b on, a subset of UserAuthManager,
8
- * mirroring LocalAuthPanel's own `hasLocalAuthMutations` guard. */
8
+ * mirroring the retired local-auth view's own `hasLocalAuthMutations` guard. */
9
9
  type LocalAuthMutations = Pick<UserAuthManager, 'addUser' | 'deleteUser' | 'rotatePassword' | 'clearBootstrapCredentialFile'>;
10
10
 
11
11
  function hasLocalAuthMutations(value: LocalAuthInspectionQuery): value is LocalAuthInspectionQuery & LocalAuthMutations {
@@ -25,14 +25,13 @@ function usernameOf(row: ConfigModalRow | null): string | null {
25
25
  }
26
26
 
27
27
  /**
28
- * Local-auth config-modal surface (migrated from the `local-auth` panel).
29
- * `inspect()` is synchronous, so buildView reads it live every tick. All
30
- * mutations (add-user, rotate-password, delete-user, clear-bootstrap-file)
31
- * dispatch through the existing `/local-auth` command rather than touching
32
- * the auth manager directly, the command opens masked password entry for
33
- * add-user/rotate-password when no password argument is supplied, which is
34
- * exactly how the retired panel kept plaintext out of history and the
35
- * transcript. This surface never renders, accepts, or holds a password.
28
+ * Local-auth config-modal surface. `inspect()` is synchronous, so buildView
29
+ * reads it live every tick. add-user and rotate-password open the masked
30
+ * password prompt (input/masked-entry-modal.ts) on top of this modal, the one
31
+ * path that keeps a plaintext password out of argv, history and the
32
+ * transcript; delete-user and clear-bootstrap-file dispatch through the
33
+ * `/local-auth` command. This surface never renders, accepts, or holds a
34
+ * password.
36
35
  */
37
36
  class LocalAuthModalSurface implements ConfigModalSurface {
38
37
  readonly name = 'local-auth-modal';
@@ -42,7 +41,10 @@ class LocalAuthModalSurface implements ConfigModalSurface {
42
41
  * consulted; see settings-sync-modal's `hasStaged` for the same idiom). */
43
42
  private bootstrapPresent = false;
44
43
 
45
- constructor(private readonly authManager: LocalAuthInspectionQuery) {}
44
+ constructor(
45
+ private readonly authManager: LocalAuthInspectionQuery,
46
+ private readonly openMaskedEntry: (kind: 'add-user' | 'rotate-password', username?: string) => void,
47
+ ) {}
46
48
 
47
49
  readonly actions: ConfigModalAction[] = [
48
50
  { key: 'a', id: 'add-user', label: 'add user' },
@@ -93,20 +95,16 @@ class LocalAuthModalSurface implements ConfigModalSurface {
93
95
 
94
96
  onAction(id: string, ctx: ConfigModalActionContext): void {
95
97
  switch (id) {
96
- // add-user / rotate-password require the masked password-entry sub-mode,
97
- // which renders on the LocalAuthPanel and cannot draw or capture input
98
- // underneath this fullscreen modal. Point the operator at the command
99
- // (which opens masked entry) rather than dispatching it into a hidden
100
- // surface, the secure flow stays a keystroke away, just not from here.
98
+ // The password prompt opens on top of this modal and never echoes.
101
99
  case 'add-user':
102
- ctx.print('To add a user securely, run /local-auth add-user <username> (opens masked password entry).');
103
- ctx.setStatus('Run /local-auth add-user <username> for masked entry.');
100
+ if (!hasLocalAuthMutations(this.authManager)) { ctx.setStatus('Local auth mutations are not available in this session.'); break; }
101
+ this.openMaskedEntry('add-user');
104
102
  break;
105
103
  case 'rotate-pw': {
106
104
  const username = usernameOf(ctx.row);
107
105
  if (!username) return;
108
- ctx.print(`To rotate securely, run /local-auth rotate-password ${username} (opens masked password entry).`);
109
- ctx.setStatus(`Run /local-auth rotate-password ${username} for masked entry.`);
106
+ if (!hasLocalAuthMutations(this.authManager)) { ctx.setStatus('Local auth mutations are not available in this session.'); break; }
107
+ this.openMaskedEntry('rotate-password', username);
110
108
  break;
111
109
  }
112
110
  case 'delete': {
@@ -127,6 +125,9 @@ class LocalAuthModalSurface implements ConfigModalSurface {
127
125
  }
128
126
  }
129
127
 
130
- export function createLocalAuthModalSurface(authManager: LocalAuthInspectionQuery): ConfigModalSurface {
131
- return new LocalAuthModalSurface(authManager);
128
+ export function createLocalAuthModalSurface(
129
+ authManager: LocalAuthInspectionQuery,
130
+ openMaskedEntry: (kind: 'add-user' | 'rotate-password', username?: string) => void = () => {},
131
+ ): ConfigModalSurface {
132
+ return new LocalAuthModalSurface(authManager, openMaskedEntry);
132
133
  }
@@ -5,7 +5,7 @@ import type {
5
5
  ConfigModalView,
6
6
  } from '../../input/config-modal-types.ts';
7
7
  import { MODAL_TONES } from './modal-theme.ts';
8
- import { infoRow } from './modal-surface-helpers.ts';
8
+ import { infoRow, headerRow } from './modal-surface-helpers.ts';
9
9
  import {
10
10
  loadEcosystemCatalog,
11
11
  listInstalledEcosystemEntries,
@@ -20,7 +20,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
20
20
  import { tmpdir } from 'node:os';
21
21
  import { join } from 'node:path';
22
22
 
23
- /** Live deps the marketplace modal captures (mirrors the panel factory). */
23
+ /** Live deps the marketplace modal captures (mirrors the earlier view factory). */
24
24
  export interface MarketplaceModalDeps {
25
25
  readonly readModel?: UiReadModel<UiMarketplaceSnapshot>;
26
26
  readonly ecosystemPaths?: EcosystemCatalogPathOptions;
@@ -41,16 +41,16 @@ const KINDS: readonly EcosystemEntryKind[] = ['plugin', 'skill', 'hook-pack', 'p
41
41
  * Marketplace → config-modal surface (group-B port). Local-first
42
42
  * publish/import catalog (NOT a remote store) with provenance, compatibility,
43
43
  * and install posture. Disk catalog loads happen in refresh() (never in
44
- * buildView), mirroring the panel's explicit render()/refresh() split.
44
+ * buildView), mirroring the earlier view's explicit render()/refresh() split.
45
45
  * Install/uninstall route to the `/marketplace` command path (charter: no
46
46
  * destructive confirm folded into a modal).
47
47
  *
48
- * Honest empty-state: the panel's original "No curated marketplace
48
+ * Honest empty-state: the earlier view's original "No curated marketplace
49
49
  * entries found yet." copy implied a curated remote catalog. It isn't one,
50
50
  * loadEcosystemCatalog only ever reads .goodvibes/ecosystem/<kind>s.json under
51
51
  * the project/home, populated solely by `/marketplace publish` and
52
52
  * `/marketplace bundle import`. The empty-state copy below (locked byte-for-byte
53
- * in the golden) names that reality. Selection-blind port: the panel's
53
+ * in the golden) names that reality. Selection-blind port: the earlier view's
54
54
  * selected-entry compatibility/risk/state detail is folded into each row label.
55
55
  */
56
56
  class MarketplaceModalSurface implements ConfigModalSurface {
@@ -129,10 +129,10 @@ class MarketplaceModalSurface implements ConfigModalSurface {
129
129
  rows.push(infoRow('empty:0', 'This is your local plugin, skill, hook-pack, and policy-pack catalog; not a remote store.'));
130
130
  rows.push(infoRow('empty:1', "It's empty because nothing has been published or imported into this workspace yet. Entries appear here once you publish a local component or import a bundle."));
131
131
  }
132
- rows.push(infoRow('empty:title', 'Populate it', { bold: true }));
133
- rows.push(infoRow('empty:publish', '/marketplace publish <kind> <path> — publish local plugins/skills into the catalog', { dim: true }));
134
- rows.push(infoRow('empty:import', '/marketplace bundle import <path> — import a catalog bundle from disk', { dim: true }));
135
- rows.push(infoRow('empty:review', '/marketplace catalog review — inspect the current local catalog posture', { dim: true }));
132
+ rows.push(headerRow('empty:title', 'Populate it'));
133
+ rows.push(infoRow('empty:publish', '/marketplace publish <kind> <path> — publish local plugins/skills into the catalog', { fg: MODAL_TONES.dim }));
134
+ rows.push(infoRow('empty:import', '/marketplace bundle import <path> — import a catalog bundle from disk', { fg: MODAL_TONES.dim }));
135
+ rows.push(infoRow('empty:review', '/marketplace catalog review — inspect the current local catalog posture', { fg: MODAL_TONES.dim }));
136
136
  return {
137
137
  title: 'Marketplace',
138
138
  tabs: [{ id: 'catalog', label: 'Catalog', rows, emptyText: '' }],
@@ -165,9 +165,9 @@ class MarketplaceModalSurface implements ConfigModalSurface {
165
165
  // Recommendations (displayed with their command; digit-jump dropped).
166
166
  const recommendations = snapshot?.recommendations ?? [];
167
167
  if (recommendations.length > 0) {
168
- rows.push(infoRow('rec:title', 'Recommended', { bold: true }));
168
+ rows.push(headerRow('rec:title', 'Recommended'));
169
169
  for (const [i, rec] of recommendations.slice(0, 3).entries()) {
170
- rows.push(infoRow(`rec:${i}`, `${rec.title}: ${rec.command}`, { dim: true }));
170
+ rows.push(infoRow(`rec:${i}`, `${rec.title}: ${rec.command}`, { fg: MODAL_TONES.dim }));
171
171
  }
172
172
  }
173
173
 
@@ -13,12 +13,12 @@ import { classifyConsolidationFetchError } from '../memory-consolidation-gateway
13
13
  // ---------------------------------------------------------------------------
14
14
  // Memory → config-modal surface (group-B port). Two tabs: 'All Records'
15
15
  // (the full record list) and 'Review Queue' (the operator review queue), the
16
- // same split the panel toggled with Tab, now real host tabs. Review-state
16
+ // same split the earlier view toggled with Tab, now real host tabs. Review-state
17
17
  // mutations (reviewed/stale/contradicted/fresh) and record delete route to the
18
18
  // existing `/recall review` / `/recall remove` command path (charter: no
19
19
  // destructive mutation direct-called from a modal). Selection-blind port: the
20
- // panel's selected-record scope/class/tags/provenance detail is folded into
21
- // each row label. `memoryRegistry` absent → the retired MemoryPanel's
20
+ // view's selected-record scope/class/tags/provenance detail is folded into
21
+ // each row label. `memoryRegistry` absent → the retired Memory view's
22
22
  // "not configured" copy renders as an honest degraded state.
23
23
  //
24
24
  // Read path (memory-spine adoption): reads go through the spine client's
@@ -68,7 +68,7 @@ export interface MemoryModalDeps {
68
68
  /**
69
69
  * Resolve the memory-consolidation-receipts gateway (memory-consolidation-gateway.ts),
70
70
  * lazily, called fresh each time the Proposals tab (re)fetches, exactly like
71
- * the Fleet panel's `resolveGateway` (fleet-gateway.ts / fleet-acts.ts). Absent
71
+ * the Agents modal's `resolveGateway` (fleet-gateway.ts / fleet-acts.ts). Absent
72
72
  * in a session with no daemon-backed control plane wired: the Proposals tab
73
73
  * then renders its "unavailable" honest state without ever attempting a fetch.
74
74
  */
@@ -105,8 +105,8 @@ function rankForReview(records: readonly MemoryRecordLike[], limit: number): Mem
105
105
  .slice(0, limit);
106
106
  }
107
107
 
108
- // Mirrors registerKnowledgePanels's withUnconfiguredFallback copy for the
109
- // retired MemoryPanel (src/panels/builtin/knowledge.ts) verbatim.
108
+ // Mirrors the retired knowledge-view registration's withUnconfiguredFallback copy for the
109
+ // retired memory view verbatim.
110
110
  const NOT_CONFIGURED_TITLE = 'Memory registry not configured for this session.';
111
111
  const NOT_CONFIGURED_BODY = 'This runtime was not wired with a project memory registry at bootstrap, so no memory data is available.';
112
112
 
@@ -324,8 +324,8 @@ class MemoryModalSurface implements ConfigModalSurface {
324
324
  };
325
325
  switch (id) {
326
326
  case 'markReviewed': review('reviewed', Math.max(record.confidence, 85)); break;
327
- case 'markStale': review('stale', Math.min(record.confidence, 40), 'marked stale from the memory panel'); break;
328
- case 'markContradicted': review('contradicted', 0, 'marked contradicted from the memory panel'); break;
327
+ case 'markStale': review('stale', Math.min(record.confidence, 40), 'marked stale from the memory modal'); break;
328
+ case 'markContradicted': review('contradicted', 0, 'marked contradicted from the memory modal'); break;
329
329
  case 'markFresh': review('fresh', Math.max(record.confidence, 60)); break;
330
330
  case 'remove': void ctx.executeCommand?.('recall', ['remove', record.id]); ctx.setStatus(`Dispatched /recall remove ${record.id}.`); break;
331
331
  }
@@ -1,13 +1,14 @@
1
- import { DEFAULT_PANEL_PALETTE } from '../polish.ts';
2
- import type { ModalSectionStyle } from '../../renderer/modal-factory.ts';
1
+ import { DEFAULT_VIEW_PALETTE } from '../polish.ts';
2
+ import type { ModalSectionStyle } from '../../input/config-modal-types.ts';
3
3
  import type { ConfigModalRow } from '../../input/config-modal-types.ts';
4
4
 
5
5
  /**
6
6
  * Shared formatting helpers for config-modal surfaces. Row styling reuses
7
- * DEFAULT_PANEL_PALETTE (the retired panels' palette) so a migrated modal reads
8
- * as the same surface it replaced, the goldens stay panel-consistent.
7
+ * DEFAULT_VIEW_PALETTE (theme tokens). The frame, tabs, search row, group
8
+ * headers and keycap hints are drawn by the modal surface kit in
9
+ * renderConfigModal; surfaces only describe rows.
9
10
  */
10
- export const PALETTE = DEFAULT_PANEL_PALETTE;
11
+ export const PALETTE = DEFAULT_VIEW_PALETTE;
11
12
 
12
13
  export type Tone = 'good' | 'bad' | 'warn' | 'info' | 'dim' | 'value';
13
14
 
@@ -52,3 +53,12 @@ export function postureLine(cells: string[]): string {
52
53
  export function infoRow(id: string, label: string, style?: ModalSectionStyle): ConfigModalRow {
53
54
  return { id, label, selectable: false, ...(style ? { style } : {}) };
54
55
  }
56
+
57
+ /**
58
+ * A group header row (a section title such as "Next steps"). The kit draws it
59
+ * as a ✦ lowercase accent header with a blank row above it; it is never
60
+ * selectable and never filtered out by the search row.
61
+ */
62
+ export function headerRow(id: string, label: string): ConfigModalRow {
63
+ return { id, label, selectable: false, header: true };
64
+ }
@@ -86,7 +86,7 @@ export interface PairingModalDeps {
86
86
  }
87
87
 
88
88
  // Fixed-width placeholder, deliberately NOT derived from the real secret's
89
- // length (mirrors QrPanel.SECRET_MASK), so masking doesn't leak length.
89
+ // length (mirrors the retired pairing view's SECRET_MASK), so masking doesn't leak length.
90
90
  const SECRET_MASK = '••••••••••••';
91
91
 
92
92
  class PairingModalSurface implements ConfigModalSurface {
@@ -149,20 +149,20 @@ class PairingModalSurface implements ConfigModalSurface {
149
149
  const displayLink = this.revealed ? deepLink : SECRET_MASK;
150
150
 
151
151
  const rows: ConfigModalRow[] = [];
152
- rows.push(infoRow('intro', 'Scan with the GoodVibes web app to pair this device; it opens already signed in.', { dim: true }));
152
+ rows.push(infoRow('intro', 'Scan with the GoodVibes web app to pair this device; it opens already signed in.', { fg: MODAL_TONES.dim }));
153
153
  rows.push(infoRow('url', `Web app ${url}`));
154
154
  rows.push(infoRow('name', `Device ${tokenName}`));
155
155
  rows.push(infoRow('token', `Token ${displayToken}`, { fg: MODAL_TONES.reasoning }));
156
- rows.push(infoRow('link', `Link ${displayLink}`, { dim: true }));
156
+ rows.push(infoRow('link', `Link ${displayLink}`, { fg: MODAL_TONES.dim }));
157
157
  if (this.deps.controlPlaneReadModel) {
158
158
  const connected = this.deps.controlPlaneReadModel.getSnapshot().activeClientIds.length;
159
- rows.push(infoRow('companions', `Devices connected: ${connected}`, connected > 0 ? { fg: MODAL_TONES.good } : { dim: true }));
159
+ rows.push(infoRow('companions', `Devices connected: ${connected}`, connected > 0 ? { fg: MODAL_TONES.good } : { fg: MODAL_TONES.dim }));
160
160
  }
161
161
 
162
162
  // The offer set carried by this pairing, each named with its plain-language
163
163
  // consequence, each declinable in the web app.
164
164
  if (offers.length > 0) {
165
- rows.push(infoRow('offers-h', 'Offers (each declinable when you pair):', { dim: true }));
165
+ rows.push(infoRow('offers-h', 'Offers (each declinable when you pair):', { fg: MODAL_TONES.dim }));
166
166
  formatPairingOffers(offers).forEach((line, i) => rows.push(infoRow(`offer:${i}`, line)));
167
167
  }
168
168
  // The labeled browser-capability list, what the paired device actually gets
@@ -170,7 +170,7 @@ class PairingModalSurface implements ConfigModalSurface {
170
170
  // never hidden behind a dead button.
171
171
  const capabilities = formatPostureCapabilities(posture);
172
172
  if (capabilities.length > 0) {
173
- rows.push(infoRow('caps-h', 'This device will get:', { dim: true }));
173
+ rows.push(infoRow('caps-h', 'This device will get:', { fg: MODAL_TONES.dim }));
174
174
  capabilities.forEach((line, i) => rows.push(infoRow(`cap:${i}`, line)));
175
175
  }
176
176
  // The ONE honest LAN line, the SDK posture's own notice (LAN_PLAIN_HTTP_NOTICE),
@@ -205,16 +205,16 @@ class PairingModalSurface implements ConfigModalSurface {
205
205
  if (!ts || !ts.available) return; // absence stays quiet
206
206
  const httpsUrl = this.serveReceipt?.url ?? ts.httpsUrl;
207
207
  if (httpsUrl) {
208
- rows.push(infoRow('ts-h', 'Encrypted access (Tailscale):', { dim: true }));
208
+ rows.push(infoRow('ts-h', 'Encrypted access (Tailscale):', { fg: MODAL_TONES.dim }));
209
209
  rows.push(infoRow('ts-url', ` ${httpsUrl}`, { fg: MODAL_TONES.good }));
210
210
  } else {
211
211
  rows.push(infoRow('ts-h', 'Tailscale detected: press t to serve this app over https (MagicDNS).', { fg: MODAL_TONES.reasoning }));
212
212
  }
213
213
  const receipt = this.serveReceipt ?? ts.lastServe;
214
214
  if (receipt) {
215
- rows.push(infoRow('ts-receipt', ` ${receipt.ok ? '✓' : '✗'} ${receipt.detail}`, receipt.ok ? { dim: true } : { fg: MODAL_TONES.warn }));
215
+ rows.push(infoRow('ts-receipt', ` ${receipt.ok ? '✓' : '✗'} ${receipt.detail}`, receipt.ok ? { fg: MODAL_TONES.dim } : { fg: MODAL_TONES.warn }));
216
216
  } else if (this.serving) {
217
- rows.push(infoRow('ts-serving', ' Serving over tailscale…', { dim: true }));
217
+ rows.push(infoRow('ts-serving', ' Serving over tailscale…', { fg: MODAL_TONES.dim }));
218
218
  }
219
219
  }
220
220
 
@@ -53,11 +53,6 @@ interface PlanningModalSnapshot {
53
53
 
54
54
  interface TextLine { readonly content: string; readonly fg?: string; }
55
55
 
56
- const GOOD = MODAL_TONES.good;
57
- const WARN = MODAL_TONES.warn;
58
- const BAD = MODAL_TONES.bad;
59
- const INFO = MODAL_TONES.info;
60
-
61
56
  function getCurrentQuestion(state: ProjectPlanningState, evaluation: ProjectPlanningEvaluation | null): ProjectPlanningQuestion | null {
62
57
  const open = state.openQuestions.find((question) => (question.status ?? 'open') === 'open');
63
58
  return open ?? evaluation?.nextQuestion ?? null;
@@ -65,7 +60,7 @@ function getCurrentQuestion(state: ProjectPlanningState, evaluation: ProjectPlan
65
60
 
66
61
  function buildStateLines(state: ProjectPlanningState, evaluation: ProjectPlanningEvaluation | null): TextLine[] {
67
62
  const readiness = evaluation?.readiness ?? state.readiness;
68
- const readinessColor = readiness === 'executable' ? GOOD : readiness === 'needs-user-input' ? WARN : undefined;
63
+ const readinessColor = readiness === 'executable' ? MODAL_TONES.good : readiness === 'needs-user-input' ? MODAL_TONES.warn : undefined;
69
64
  const blockingGaps = (evaluation?.gaps ?? []).filter((gap) => gap.severity === 'blocking').length;
70
65
  const lines: TextLine[] = [
71
66
  { content: `readiness ${readiness} approved ${state.executionApproved ? 'yes' : 'no'} questions ${state.openQuestions.length} open / ${state.answeredQuestions.length} answered`, ...(readinessColor ? { fg: readinessColor } : {}) },
@@ -74,14 +69,14 @@ function buildStateLines(state: ProjectPlanningState, evaluation: ProjectPlannin
74
69
  ];
75
70
  if (state.scope) lines.push({ content: `scope: ${state.scope}` });
76
71
  if (state.knownContext.length) lines.push({ content: `known context: ${state.knownContext.join(' | ')}` });
77
- if (evaluation?.nextQuestion) lines.push({ content: `next question: ${evaluation.nextQuestion.prompt}`, fg: INFO });
72
+ if (evaluation?.nextQuestion) lines.push({ content: `next question: ${evaluation.nextQuestion.prompt}`, fg: MODAL_TONES.info });
78
73
  return lines;
79
74
  }
80
75
 
81
76
  function buildGapsLines(evaluation: ProjectPlanningEvaluation | null): TextLine[] {
82
77
  const gaps = evaluation?.gaps ?? [];
83
- if (gaps.length === 0) return [{ content: 'Readiness gaps: none.', fg: GOOD }];
84
- return [{ content: 'Readiness gaps:' }, ...gaps.slice(0, 12).map((gap) => ({ content: ` ${gap.severity.toUpperCase()} ${gap.kind}: ${gap.message}`, fg: gap.severity === 'blocking' ? BAD : WARN }))];
78
+ if (gaps.length === 0) return [{ content: 'Readiness gaps: none.', fg: MODAL_TONES.good }];
79
+ return [{ content: 'Readiness gaps:' }, ...gaps.slice(0, 12).map((gap) => ({ content: ` ${gap.severity.toUpperCase()} ${gap.kind}: ${gap.message}`, fg: gap.severity === 'blocking' ? MODAL_TONES.bad : MODAL_TONES.warn }))];
85
80
  }
86
81
 
87
82
  function buildTasksLines(state: ProjectPlanningState): TextLine[] {
@@ -90,18 +85,18 @@ function buildTasksLines(state: ProjectPlanningState): TextLine[] {
90
85
  lines.push({ content: ' No decomposed tasks recorded yet.' });
91
86
  } else {
92
87
  for (const task of state.tasks) {
93
- lines.push({ content: ` ${task.id}: ${task.title} [${task.status ?? 'pending'}]${task.canRunConcurrently ? ' - concurrent' : ''}`, ...(task.blockedOnUserInput ? { fg: WARN } : {}) });
88
+ lines.push({ content: ` ${task.id}: ${task.title} [${task.status ?? 'pending'}]${task.canRunConcurrently ? ' - concurrent' : ''}`, ...(task.blockedOnUserInput ? { fg: MODAL_TONES.warn } : {}) });
94
89
  if (task.dependencies?.length) lines.push({ content: ` dependencies: ${task.dependencies.join(', ')}` });
95
- if (task.verification?.length) lines.push({ content: ` verification: ${task.verification.join(' | ')}`, fg: GOOD });
90
+ if (task.verification?.length) lines.push({ content: ` verification: ${task.verification.join(' | ')}`, fg: MODAL_TONES.good });
96
91
  }
97
92
  }
98
93
  if (state.verificationGates.length) {
99
94
  lines.push({ content: 'Verification gates:' });
100
- for (const gate of state.verificationGates) lines.push({ content: ` ${gate.id}: ${gate.description} [${gate.status ?? 'pending'}]`, fg: gate.required === false ? undefined : GOOD });
95
+ for (const gate of state.verificationGates) lines.push({ content: ` ${gate.id}: ${gate.description} [${gate.status ?? 'pending'}]`, fg: gate.required === false ? undefined : MODAL_TONES.good });
101
96
  }
102
97
  if (state.agentAssignments.length) {
103
98
  lines.push({ content: 'Agent handoff candidates:' });
104
- for (const assignment of state.agentAssignments) lines.push({ content: ` ${assignment.taskId}: ${assignment.agentType ?? 'none'}${assignment.canRunConcurrently ? ' - can run concurrently' : ''}`, fg: INFO });
99
+ for (const assignment of state.agentAssignments) lines.push({ content: ` ${assignment.taskId}: ${assignment.agentType ?? 'none'}${assignment.canRunConcurrently ? ' - can run concurrently' : ''}`, fg: MODAL_TONES.info });
105
100
  }
106
101
  return lines;
107
102
  }
@@ -111,7 +106,7 @@ function buildDecisionsLines(state: ProjectPlanningState, storedDecisions: reado
111
106
  for (const decision of [...storedDecisions, ...state.decisions]) byId.set(decision.id, decision);
112
107
  const decisions = [...byId.values()];
113
108
  if (decisions.length === 0) return [{ content: 'Decisions: none recorded yet.' }];
114
- return [{ content: 'Decisions:' }, ...decisions.slice(0, 12).map((decision) => ({ content: ` ${decision.title}: ${decision.decision} [${decision.status ?? 'accepted'}]`, fg: decision.status === 'rejected' ? BAD : undefined }))];
109
+ return [{ content: 'Decisions:' }, ...decisions.slice(0, 12).map((decision) => ({ content: ` ${decision.title}: ${decision.decision} [${decision.status ?? 'accepted'}]`, fg: decision.status === 'rejected' ? MODAL_TONES.bad : undefined }))];
115
110
  }
116
111
 
117
112
  function buildLanguageLines(language: ProjectPlanningLanguageArtifact | null): TextLine[] {
@@ -119,9 +114,9 @@ function buildLanguageLines(language: ProjectPlanningLanguageArtifact | null): T
119
114
  const lines: TextLine[] = [{ content: 'Project language:' }];
120
115
  for (const term of language.terms.slice(0, 8)) {
121
116
  lines.push({ content: ` ${term.term}: ${term.definition}` });
122
- if (term.avoid?.length) lines.push({ content: ` avoid: ${term.avoid.join(', ')}`, fg: BAD });
117
+ if (term.avoid?.length) lines.push({ content: ` avoid: ${term.avoid.join(', ')}`, fg: MODAL_TONES.bad });
123
118
  }
124
- for (const ambiguity of language.ambiguities.slice(0, 8)) lines.push({ content: ` resolved ambiguity - ${ambiguity.phrase}: ${ambiguity.resolution}`, fg: INFO });
119
+ for (const ambiguity of language.ambiguities.slice(0, 8)) lines.push({ content: ` resolved ambiguity - ${ambiguity.phrase}: ${ambiguity.resolution}`, fg: MODAL_TONES.info });
125
120
  return lines;
126
121
  }
127
122
 
@@ -177,7 +172,7 @@ class PlanningModalSurface implements ConfigModalSurface {
177
172
 
178
173
  buildView(): ConfigModalView {
179
174
  if (!this.snapshot) {
180
- return { title: 'Planning', tabs: [{ id: 'planning', label: 'Planning', rows: [infoRow('load', this.loading ? 'Loading project planning state...' : 'Project planning state unavailable.', { dim: true })] }], hints: ['r refresh'] };
175
+ return { title: 'Planning', tabs: [{ id: 'planning', label: 'Planning', rows: [infoRow('load', this.loading ? 'Loading project planning state...' : 'Project planning state unavailable.', { fg: MODAL_TONES.dim })] }], hints: ['r refresh'] };
181
176
  }
182
177
 
183
178
  const { status, state, evaluation, decisions, language } = this.snapshot;
@@ -197,9 +192,9 @@ class PlanningModalSurface implements ConfigModalSurface {
197
192
  const { question, actions } = this.currentAnswerActions();
198
193
  if (question) {
199
194
  line({ content: 'Answer Current Question' });
200
- line({ content: question.prompt, fg: INFO });
195
+ line({ content: question.prompt, fg: MODAL_TONES.info });
201
196
  if (question.whyItMatters) line({ content: `Why this matters: ${question.whyItMatters}` });
202
- if (question.recommendedAnswer && !isGenericRecommendation(question.recommendedAnswer)) line({ content: `Recommendation: ${question.recommendedAnswer}`, fg: GOOD });
197
+ if (question.recommendedAnswer && !isGenericRecommendation(question.recommendedAnswer)) line({ content: `Recommendation: ${question.recommendedAnswer}`, fg: MODAL_TONES.good });
203
198
  for (const action of actions) {
204
199
  rows.push({ id: action.id, label: `${action.label} - ${action.detail}`, ...(action.disabled ? { selectable: false } : {}) });
205
200
  }
@@ -270,7 +265,7 @@ function buildGoldenService(): PlanningModalService {
270
265
  openQuestions: [{ id: 'golden-question-1', prompt: 'What is in scope for this golden fixture?', whyItMatters: 'Keeps the render deterministic.', recommendedAnswer: 'Use a focused first-pass scope for this goal.', status: 'open' }],
271
266
  answeredQuestions: [{ id: 'golden-question-0', prompt: 'Is this a golden fixture?', status: 'answered', answer: 'Yes.', answeredAt: 0 }],
272
267
  decisions: [], assumptions: [], constraints: [], risks: [],
273
- tasks: [{ id: 'golden-task-1', title: 'Implement the golden fixture', status: 'pending', dependencies: [], verification: ['bun test src/test/panels/modals/planning-modal.test.ts'] }],
268
+ tasks: [{ id: 'golden-task-1', title: 'Implement the golden fixture', status: 'pending', dependencies: [], verification: ['bun test src/test/views/modals/planning-modal.test.ts'] }],
274
269
  dependencies: [], verificationGates: [{ id: 'golden-gate-1', description: 'Golden render is byte-stable.', status: 'pending', required: true }],
275
270
  agentAssignments: [{ taskId: 'golden-task-1', agentType: 'worker', canRunConcurrently: false }],
276
271
  readiness: 'needs-user-input', executionApproved: false, createdAt: 0, updatedAt: 0,