@pellux/goodvibes-tui 0.27.0 → 0.29.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 (143) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/src/core/context-usage.ts +53 -0
  5. package/src/core/conversation.ts +4 -6
  6. package/src/core/session-recovery.ts +2 -0
  7. package/src/core/turn-event-wiring.ts +1 -0
  8. package/src/input/commands/cost-runtime.ts +49 -0
  9. package/src/input/commands/eval.ts +4 -3
  10. package/src/input/commands/operator-runtime.ts +5 -1
  11. package/src/input/commands.ts +2 -0
  12. package/src/input/handler-feed-routes.ts +60 -26
  13. package/src/input/handler-feed.ts +17 -8
  14. package/src/input/handler-picker-routes.ts +18 -13
  15. package/src/input/handler-shortcuts.ts +51 -0
  16. package/src/input/handler-ui-state.ts +4 -0
  17. package/src/input/handler.ts +43 -6
  18. package/src/input/keybindings.ts +53 -8
  19. package/src/input/model-picker.ts +5 -0
  20. package/src/input/panel-integration-actions.ts +117 -1
  21. package/src/main.ts +5 -2
  22. package/src/panels/agent-inspector-panel.ts +139 -45
  23. package/src/panels/agent-inspector-shared.ts +147 -3
  24. package/src/panels/approval-panel.ts +204 -102
  25. package/src/panels/automation-control-panel.ts +370 -103
  26. package/src/panels/base-panel.ts +26 -2
  27. package/src/panels/builtin/agent.ts +28 -37
  28. package/src/panels/builtin/development.ts +40 -32
  29. package/src/panels/builtin/knowledge.ts +15 -6
  30. package/src/panels/builtin/operations.ts +178 -117
  31. package/src/panels/builtin/session.ts +35 -9
  32. package/src/panels/builtin/shared.ts +98 -6
  33. package/src/panels/cockpit-panel.ts +253 -75
  34. package/src/panels/cockpit-read-model.ts +2 -1
  35. package/src/panels/communication-panel.ts +159 -56
  36. package/src/panels/confirm-state.ts +8 -1
  37. package/src/panels/control-plane-panel.ts +354 -95
  38. package/src/panels/cost-tracker-panel.ts +243 -57
  39. package/src/panels/debug-panel.ts +347 -159
  40. package/src/panels/diff-panel.ts +257 -82
  41. package/src/panels/docs-panel.ts +166 -64
  42. package/src/panels/eval-panel.ts +336 -109
  43. package/src/panels/expandable-list-panel.ts +189 -0
  44. package/src/panels/file-explorer-panel.ts +238 -159
  45. package/src/panels/file-preview-panel.ts +141 -59
  46. package/src/panels/git-panel.ts +360 -191
  47. package/src/panels/hooks-panel.ts +181 -19
  48. package/src/panels/incident-review-panel.ts +252 -43
  49. package/src/panels/index.ts +0 -4
  50. package/src/panels/intelligence-panel.ts +310 -103
  51. package/src/panels/knowledge-graph-panel.ts +480 -58
  52. package/src/panels/local-auth-panel.ts +251 -33
  53. package/src/panels/marketplace-panel.ts +219 -33
  54. package/src/panels/memory-panel.ts +83 -81
  55. package/src/panels/ops-control-panel.ts +211 -32
  56. package/src/panels/ops-strategy-panel.ts +131 -45
  57. package/src/panels/orchestration-panel.ts +259 -67
  58. package/src/panels/panel-list-panel.ts +184 -136
  59. package/src/panels/panel-manager.ts +120 -13
  60. package/src/panels/plan-dashboard-panel.ts +507 -74
  61. package/src/panels/plugins-panel.ts +227 -34
  62. package/src/panels/policy-panel.ts +264 -55
  63. package/src/panels/polish-core.ts +162 -0
  64. package/src/panels/polish-tables.ts +258 -0
  65. package/src/panels/polish.ts +67 -149
  66. package/src/panels/project-planning-answer-actions.ts +134 -0
  67. package/src/panels/project-planning-panel.ts +84 -113
  68. package/src/panels/provider-health-panel.ts +438 -480
  69. package/src/panels/provider-health-routes.ts +203 -0
  70. package/src/panels/provider-health-tracker.ts +194 -6
  71. package/src/panels/provider-health-views.ts +560 -0
  72. package/src/panels/qr-panel.ts +136 -38
  73. package/src/panels/remote-panel.ts +123 -38
  74. package/src/panels/routes-panel.ts +87 -24
  75. package/src/panels/sandbox-panel.ts +232 -65
  76. package/src/panels/scrollable-list-panel.ts +143 -145
  77. package/src/panels/security-panel.ts +204 -52
  78. package/src/panels/services-panel.ts +174 -76
  79. package/src/panels/session-browser-panel.ts +101 -6
  80. package/src/panels/session-maintenance.ts +4 -122
  81. package/src/panels/settings-sync-panel.ts +346 -68
  82. package/src/panels/skills-panel.ts +157 -89
  83. package/src/panels/subscription-panel.ts +105 -34
  84. package/src/panels/symbol-outline-panel.ts +287 -154
  85. package/src/panels/system-messages-panel.ts +172 -38
  86. package/src/panels/tasks-panel.ts +348 -134
  87. package/src/panels/thinking-panel.ts +66 -32
  88. package/src/panels/token-budget-panel.ts +233 -46
  89. package/src/panels/tool-inspector-panel.ts +170 -54
  90. package/src/panels/types.ts +65 -4
  91. package/src/panels/work-plan-panel.ts +393 -39
  92. package/src/panels/worktree-panel.ts +239 -61
  93. package/src/panels/wrfc-panel-format.ts +133 -0
  94. package/src/panels/wrfc-panel.ts +146 -150
  95. package/src/renderer/compaction-preview.ts +2 -1
  96. package/src/renderer/compositor.ts +46 -43
  97. package/src/renderer/conversation-overlays.ts +25 -11
  98. package/src/renderer/footer-tips.ts +41 -0
  99. package/src/renderer/fullscreen-primitives.ts +22 -16
  100. package/src/renderer/help-overlay.ts +91 -14
  101. package/src/renderer/hint-grammar.ts +52 -0
  102. package/src/renderer/layout.ts +7 -7
  103. package/src/renderer/modal-factory.ts +23 -15
  104. package/src/renderer/model-picker-overlay.ts +30 -11
  105. package/src/renderer/model-workspace.ts +2 -3
  106. package/src/renderer/overlay-box.ts +16 -10
  107. package/src/renderer/panel-composite.ts +7 -20
  108. package/src/renderer/panel-workspace-bar.ts +14 -8
  109. package/src/renderer/process-indicator.ts +3 -1
  110. package/src/renderer/progress.ts +8 -6
  111. package/src/renderer/search-overlay.ts +27 -6
  112. package/src/renderer/session-picker-modal.ts +6 -4
  113. package/src/renderer/settings-modal.ts +70 -10
  114. package/src/renderer/shell-surface.ts +41 -15
  115. package/src/renderer/status-glyphs.ts +11 -9
  116. package/src/renderer/tab-strip.ts +148 -34
  117. package/src/renderer/theme.ts +60 -3
  118. package/src/renderer/ui-factory.ts +45 -36
  119. package/src/renderer/ui-primitives.ts +23 -2
  120. package/src/runtime/bootstrap-shell.ts +35 -18
  121. package/src/runtime/diagnostics/panels/index.ts +0 -3
  122. package/src/runtime/ui/model-picker/health-enrichment.ts +3 -0
  123. package/src/runtime/ui/model-picker/types.ts +4 -0
  124. package/src/shell/ui-openers.ts +14 -22
  125. package/src/utils/format-duration.ts +55 -0
  126. package/src/utils/format-number.ts +71 -0
  127. package/src/utils/splash-lines.ts +44 -3
  128. package/src/version.ts +1 -1
  129. package/src/work-plans/work-plan-store.ts +13 -0
  130. package/src/panels/agent-logs-panel.ts +0 -666
  131. package/src/panels/agent-logs-shared.ts +0 -129
  132. package/src/panels/context-visualizer-panel.ts +0 -214
  133. package/src/panels/forensics-panel.ts +0 -364
  134. package/src/panels/panel-picker.ts +0 -106
  135. package/src/panels/provider-account-snapshot.ts +0 -259
  136. package/src/panels/provider-accounts-panel.ts +0 -218
  137. package/src/panels/provider-stats-panel.ts +0 -366
  138. package/src/panels/schedule-panel.ts +0 -342
  139. package/src/panels/watchers-panel.ts +0 -193
  140. package/src/renderer/panel-picker-overlay.ts +0 -202
  141. package/src/renderer/panel-tab-bar.ts +0 -69
  142. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  143. package/src/runtime/diagnostics/panels/policy.ts +0 -177
@@ -1,16 +1,15 @@
1
1
  import type { PanelManager } from '../panel-manager.ts';
2
2
  import { CockpitPanel } from '../cockpit-panel.ts';
3
+ import { AgentInspectorPanel } from '../agent-inspector-panel.ts';
3
4
  import { ApprovalPanel } from '../approval-panel.ts';
4
5
  import { PluginsPanel } from '../plugins-panel.ts';
5
6
  import { SkillsPanel } from '../skills-panel.ts';
6
7
  import { ServicesPanel } from '../services-panel.ts';
7
8
  import { AutomationControlPanel } from '../automation-control-panel.ts';
8
9
  import { RoutesPanel } from '../routes-panel.ts';
9
- import { WatchersPanel } from '../watchers-panel.ts';
10
10
  import { ControlPlanePanel } from '../control-plane-panel.ts';
11
11
  import { SubscriptionPanel } from '../subscription-panel.ts';
12
12
  import { LocalAuthPanel } from '../local-auth-panel.ts';
13
- import { ProviderAccountsPanel } from '../provider-accounts-panel.ts';
14
13
  import { SettingsSyncPanel } from '../settings-sync-panel.ts';
15
14
  import { WorktreePanel } from '../worktree-panel.ts';
16
15
  import { HooksPanel } from '../hooks-panel.ts';
@@ -20,25 +19,35 @@ import { SandboxPanel } from '../sandbox-panel.ts';
20
19
  import { TasksPanel } from '../tasks-panel.ts';
21
20
  import { OrchestrationPanel } from '../orchestration-panel.ts';
22
21
  import { OpsStrategyPanel } from '../ops-strategy-panel.ts';
22
+ import { OpsControlPanel } from '../ops-control-panel.ts';
23
23
  import { CommunicationPanel } from '../communication-panel.ts';
24
24
  import { RemotePanel } from '../remote-panel.ts';
25
- import { ProviderStatsPanel } from '../provider-stats-panel.ts';
26
25
  import { ProviderHealthPanel } from '../provider-health-panel.ts';
27
26
  import { DebugPanel } from '../debug-panel.ts';
28
27
  import { IncidentReviewPanel } from '../incident-review-panel.ts';
29
- import { ForensicsPanel } from '../forensics-panel.ts';
30
28
  import { PolicyPanel } from '../policy-panel.ts';
31
29
  import { EvalPanel } from '../eval-panel.ts';
32
- import { createProviderAccountSnapshotQuery } from '../provider-account-snapshot.ts';
33
- import {
34
- createEnvironmentVariableQuery,
35
- createProviderRuntimeInspectionQuery,
36
- } from '../../runtime/ui-service-queries.ts';
30
+ import { createProviderRuntimeInspectionQuery } from '../../runtime/ui-service-queries.ts';
37
31
  import { createRuntimeProviderApi } from '@/runtime/index.ts';
32
+ import { selectModel } from '../../runtime/store/selectors/index.ts';
38
33
  import type { ResolvedBuiltinPanelDeps } from './shared.ts';
39
- import { requireAutomationManager, requireControlPlanePanelDeps, requireHookPanelDeps, requirePluginManager, requireUiServices } from './shared.ts';
34
+ import { requireAutomationManager, requireControlPlanePanelDeps, requireHookPanelDeps, requirePluginManager, requireUiServices, withUnconfiguredFallback } from './shared.ts';
40
35
  import { createCockpitRosterReadModel } from '../cockpit-read-model.ts';
41
36
 
37
+ // WO-152: the former single 'monitoring' category (33 panels pre-merge) is
38
+ // split into five operator domains, applied per-registration below. Kept in
39
+ // this file's original registration order (rather than physically regrouped
40
+ // by category) because several registrations below close over shared local
41
+ // state — `ui`, `providerRuntime`, `runtimeStore`, `rosterReadModel` — defined
42
+ // once near the top of this function. Domain membership registered here:
43
+ // providers: services, subscription, remote, provider-health
44
+ // security-policy: approval, local-auth, settings-sync, security, sandbox, policy
45
+ // automation-control: plugins, skills, automation, worktrees, hooks, marketplace
46
+ // incidents-diagnostics: debug, incident, eval
47
+ // runtime-ops: cockpit, routes, control-plane, tasks, orchestration,
48
+ // ops, ops-control, communication (plus system-messages
49
+ // and tokens/cost, registered in builtin/session.ts
50
+ // and builtin/development.ts respectively)
42
51
  export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBuiltinPanelDeps): void {
43
52
  const ui = requireUiServices(deps);
44
53
  const providerRuntime = createProviderRuntimeInspectionQuery(createRuntimeProviderApi({
@@ -46,12 +55,7 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
46
55
  favoritesStore: ui.providers.favoritesStore,
47
56
  providerRegistry: ui.providers.providerRegistry,
48
57
  }));
49
- const providerAccounts = createProviderAccountSnapshotQuery({
50
- providerModels: deps.providerRegistry,
51
- services: deps.serviceRegistry,
52
- subscriptions: deps.subscriptionManager,
53
- environment: createEnvironmentVariableQuery(process.env),
54
- });
58
+ const runtimeStore = deps.runtimeStore;
55
59
 
56
60
  const rosterReadModel = createCockpitRosterReadModel(ui.agents.agentManager);
57
61
  // Subscribe to agent lifecycle events so the roster re-renders on state changes.
@@ -70,7 +74,7 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
70
74
  id: 'cockpit',
71
75
  name: 'Cockpit',
72
76
  icon: 'O',
73
- category: 'monitoring',
77
+ category: 'runtime-ops',
74
78
  description: 'Unified operator summary for orchestration, permissions, communication, MCP, plugins, and integrations',
75
79
  factory: () => new CockpitPanel(
76
80
  ui.readModels.cockpit,
@@ -78,6 +82,13 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
78
82
  {
79
83
  openAgentDetail: (agentId: string) => deps.openAgentDetail?.(agentId),
80
84
  cancelAgent: (agentId: string) => ui.agents.agentManager.cancel(agentId),
85
+ // WO-130: roster Enter jumps to the Inspector console (WO-110's
86
+ // inspectAgent deep-link target) instead of only the quick-peek modal.
87
+ inspectAgent: (agentId: string) => {
88
+ const panel = manager.open('inspector');
89
+ if (panel instanceof AgentInspectorPanel) panel.inspectAgent(agentId);
90
+ },
91
+ openPanel: (panelId: string) => deps.openPanel?.(panelId),
81
92
  },
82
93
  ),
83
94
  });
@@ -86,7 +97,7 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
86
97
  id: 'approval',
87
98
  name: 'Approval',
88
99
  icon: 'A',
89
- category: 'monitoring',
100
+ category: 'security-policy',
90
101
  description: 'Action-specific approval workspace for why-prompted, why-denied, and what-if review',
91
102
  factory: () => new ApprovalPanel(deps.policyRuntimeState),
92
103
  });
@@ -94,8 +105,9 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
94
105
  manager.registerType({
95
106
  id: 'plugins',
96
107
  name: 'Plugins',
97
- icon: 'P',
98
- category: 'monitoring',
108
+ // WO-152: was 'P' (collided with project-planning and preview).
109
+ icon: '',
110
+ category: 'automation-control',
99
111
  description: 'Plugin trust, quarantine, capability, and activation status',
100
112
  factory: () => new PluginsPanel(requirePluginManager(deps)),
101
113
  });
@@ -103,12 +115,19 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
103
115
  manager.registerType({
104
116
  id: 'skills',
105
117
  name: 'Skills',
106
- icon: 'K',
107
- category: 'monitoring',
118
+ // WO-152: was 'K' (collided with knowledge and tokens).
119
+ icon: '',
120
+ category: 'automation-control',
108
121
  description: 'Project-local and global skill discovery with origin and dependency details',
109
122
  factory: () => new SkillsPanel({
110
123
  componentHealthMonitor: deps.componentHealthMonitor,
111
124
  shellPaths: ui.environment.shellPaths,
125
+ ecosystemPaths: {
126
+ cwd: ui.environment.shellPaths.workingDirectory,
127
+ homeDir: ui.environment.shellPaths.homeDirectory,
128
+ projectCatalogRoot: ui.environment.shellPaths.resolveProjectPath('tui', 'ecosystem'),
129
+ userCatalogRoot: ui.environment.shellPaths.resolveUserPath('tui', 'ecosystem'),
130
+ },
112
131
  }),
113
132
  });
114
133
 
@@ -116,7 +135,7 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
116
135
  id: 'services',
117
136
  name: 'Services',
118
137
  icon: 'V',
119
- category: 'monitoring',
138
+ category: 'providers',
120
139
  description: 'Configured external services, credential presence, and connection health tests',
121
140
  factory: () => new ServicesPanel(deps.serviceRegistry, deps.subscriptionManager),
122
141
  });
@@ -124,47 +143,39 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
124
143
  manager.registerType({
125
144
  id: 'automation',
126
145
  name: 'Automation',
127
- icon: 'M',
128
- category: 'monitoring',
129
- description: 'Automation jobs, runs, deliveries, and failure posture across the control plane',
130
- factory: () => new AutomationControlPanel(ui.readModels.automation),
146
+ // WO-152: was 'M' (collided with memory and marketplace).
147
+ icon: '',
148
+ category: 'automation-control',
149
+ description: 'Automation jobs, runs, deliveries, and watcher-fed sources across the control plane, with real enable/disable and run-now controls',
150
+ factory: () => new AutomationControlPanel(ui.readModels.automation, ui.readModels.watchers, {
151
+ automationManager: requireAutomationManager(deps),
152
+ watcherRegistry: deps.watcherRegistry,
153
+ }),
131
154
  });
132
155
 
133
156
  manager.registerType({
134
157
  id: 'routes',
135
158
  name: 'Routes',
136
159
  icon: 'R',
137
- category: 'monitoring',
160
+ category: 'runtime-ops',
138
161
  description: 'Cross-surface route bindings and shared session attachment state',
139
162
  factory: () => new RoutesPanel(ui.readModels.routes),
140
163
  });
141
164
 
142
- manager.registerType({
143
- id: 'watchers',
144
- name: 'Watchers',
145
- icon: 'W',
146
- category: 'monitoring',
147
- description: 'Watcher health, lag, and degraded source state for automation inputs',
148
- factory: () => new WatchersPanel(ui.readModels.watchers),
149
- });
150
-
151
165
  manager.registerType({
152
166
  id: 'control-plane',
153
167
  name: 'Control Plane',
154
168
  icon: 'C',
155
- category: 'monitoring',
169
+ category: 'runtime-ops',
156
170
  description: 'Daemon control-plane state, clients, approvals, and recent operator activity',
157
- factory: () => {
158
- requireControlPlanePanelDeps(deps);
159
- return new ControlPlanePanel(ui.readModels.controlPlane);
160
- },
171
+ factory: () => new ControlPlanePanel(ui.readModels.controlPlane, requireControlPlanePanelDeps(deps)),
161
172
  });
162
173
 
163
174
  manager.registerType({
164
175
  id: 'subscription',
165
176
  name: 'Subscriptions',
166
177
  icon: 'B',
167
- category: 'monitoring',
178
+ category: 'providers',
168
179
  description: 'OAuth-backed provider subscriptions and supported provider override posture',
169
180
  factory: () => new SubscriptionPanel(deps.serviceRegistry, deps.subscriptionManager),
170
181
  });
@@ -173,25 +184,20 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
173
184
  id: 'local-auth',
174
185
  name: 'Local Auth',
175
186
  icon: 'U',
176
- category: 'monitoring',
187
+ category: 'security-policy',
177
188
  description: 'Local daemon/listener auth users, bootstrap posture, and active sessions',
178
189
  factory: () => new LocalAuthPanel(deps.localUserAuthManager),
179
190
  });
180
191
 
181
- manager.registerType({
182
- id: 'accounts',
183
- name: 'Accounts',
184
- icon: 'Q',
185
- category: 'monitoring',
186
- description: 'Provider auth routes, subscription quota-window hints, and billing-path safety notes',
187
- factory: () => new ProviderAccountsPanel({ providerAccounts }),
188
- });
189
-
190
192
  manager.registerType({
191
193
  id: 'settings-sync',
192
194
  name: 'Settings Sync',
193
- icon: 'Y',
194
- category: 'monitoring',
195
+ // WO-152: registry previously said 'Y' while the live panel's own
196
+ // super() call used 'S' — a pre-existing registry/instance mismatch as
197
+ // well as a collision ('Y' with communication/eval, 'S' with symbols).
198
+ // Unified to a single unique glyph in both places.
199
+ icon: '▱',
200
+ category: 'security-policy',
195
201
  description: 'Local, synced, and managed settings posture with recent sync events and active locks',
196
202
  factory: () => new SettingsSyncPanel(deps.configManager),
197
203
  });
@@ -199,17 +205,19 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
199
205
  manager.registerType({
200
206
  id: 'worktrees',
201
207
  name: 'Worktrees',
202
- icon: 'W',
203
- category: 'monitoring',
208
+ // WO-152: was 'W' (collided with wrfc).
209
+ icon: '',
210
+ category: 'automation-control',
204
211
  description: 'Orchestrator-owned git worktree lifecycle, attachments, and cleanup state',
205
- factory: () => new WorktreePanel(deps.worktreeRegistry),
212
+ factory: () => new WorktreePanel(deps.worktreeRegistry, deps.requestRender),
206
213
  });
207
214
 
208
215
  manager.registerType({
209
216
  id: 'hooks',
210
217
  name: 'Hooks',
211
- icon: 'H',
212
- category: 'monitoring',
218
+ // WO-152: was 'H' (collided with sessions).
219
+ icon: '',
220
+ category: 'automation-control',
213
221
  description: 'Registered hooks, chains, contracts, and execution policy details',
214
222
  factory: () => {
215
223
  const hookDeps = requireHookPanelDeps(deps);
@@ -220,8 +228,9 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
220
228
  manager.registerType({
221
229
  id: 'security',
222
230
  name: 'Security',
223
- icon: 'U',
224
- category: 'monitoring',
231
+ // WO-152: was 'U' (collided with local-auth and policy).
232
+ icon: '',
233
+ category: 'security-policy',
225
234
  description: 'Security review workspace for token audit, policy posture, MCP quarantine, and incident pressure',
226
235
  factory: () => new SecurityPanel(ui.readModels.security),
227
236
  });
@@ -229,8 +238,9 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
229
238
  manager.registerType({
230
239
  id: 'marketplace',
231
240
  name: 'Marketplace',
232
- icon: 'M',
233
- category: 'monitoring',
241
+ // WO-152: was 'M' (collided with memory and automation).
242
+ icon: '',
243
+ category: 'automation-control',
234
244
  description: 'Curated plugin and skill marketplace with provenance, compatibility, and install posture',
235
245
  factory: () => {
236
246
  return new MarketplacePanel(ui.readModels.marketplace, {
@@ -245,26 +255,29 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
245
255
  manager.registerType({
246
256
  id: 'sandbox',
247
257
  name: 'Sandbox',
248
- icon: 'X',
249
- category: 'monitoring',
258
+ // WO-152: was 'X' (collided with tools).
259
+ icon: '',
260
+ category: 'security-policy',
250
261
  description: 'VM isolation posture for MCP servers and evaluation runtimes',
251
- factory: () => new SandboxPanel(deps.configManager, deps.sandboxSessionRegistry),
262
+ factory: () => new SandboxPanel(deps.configManager, deps.sandboxSessionRegistry, deps.requestRender),
252
263
  });
253
264
 
254
265
  manager.registerType({
255
266
  id: 'tasks',
256
267
  name: 'Tasks',
257
- icon: 'J',
258
- category: 'monitoring',
268
+ // WO-152: was 'J' (collided with intelligence and system-messages).
269
+ icon: '',
270
+ category: 'runtime-ops',
259
271
  description: 'Queued, running, blocked, failed, and completed task summaries from the runtime store',
260
- factory: () => new TasksPanel(ui.readModels.tasks, ui.readModels.worktrees),
272
+ factory: () => new TasksPanel(ui.readModels.tasks, ui.readModels.worktrees, deps.opsApi),
261
273
  });
262
274
 
263
275
  manager.registerType({
264
276
  id: 'orchestration',
265
277
  name: 'Orchestration',
266
- icon: 'Q',
267
- category: 'monitoring',
278
+ // WO-152: was 'Q' (collided with qr-code and ops-control).
279
+ icon: '',
280
+ category: 'runtime-ops',
268
281
  description: 'Task-graph status, node roles, and bounded recursion guard activity',
269
282
  factory: () => new OrchestrationPanel(ui.readModels.orchestration),
270
283
  });
@@ -272,47 +285,64 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
272
285
  manager.registerType({
273
286
  id: 'ops',
274
287
  name: 'Ops',
275
- icon: 'O',
276
- category: 'monitoring',
288
+ // WO-152: was 'O' (collided with cockpit). Also unifies a pre-existing
289
+ // registry ('monitoring') / live-instance (ops-strategy-panel.ts said
290
+ // 'agent') category mismatch onto a single 'runtime-ops' value.
291
+ icon: '◫',
292
+ category: 'runtime-ops',
277
293
  description: 'Adaptive planner strategy timeline, override posture, and recent execution-mode decisions',
278
- factory: () => new OpsStrategyPanel(ui.events.planner, deps.adaptivePlanner),
294
+ factory: () => new OpsStrategyPanel(ui.events.planner, deps.adaptivePlanner, deps.planRuntime),
295
+ });
296
+
297
+ // WO-120: the operator intervention console behind the operator-control-plane
298
+ // feature flag. Registration is unconditional (matching the ControlPlanePanel
299
+ // pattern above) — the flag itself gates whether bootstrap.ts wires the
300
+ // openOpsPanel command-context callback and the Ctrl+O / `/ops view` route,
301
+ // not whether the panel type resolves for direct `/panel open ops-control`.
302
+ manager.registerType({
303
+ id: 'ops-control',
304
+ name: 'Ops Control',
305
+ // WO-152: was 'Q' (collided with qr-code and orchestration). Also unifies
306
+ // a pre-existing registry ('monitoring') / live-instance (ops-control-panel.ts
307
+ // said 'agent') category mismatch onto a single 'runtime-ops' value.
308
+ icon: '◓',
309
+ category: 'runtime-ops',
310
+ description: 'Operator intervention console: audit log plus cancel/pause/resume/retry on tasks and cancel on agents',
311
+ factory: () => new OpsControlPanel(ui.events.ops, deps.opsApi),
279
312
  });
280
313
 
281
314
  manager.registerType({
282
315
  id: 'communication',
283
316
  name: 'Communication',
284
317
  icon: 'Y',
285
- category: 'monitoring',
318
+ category: 'runtime-ops',
286
319
  description: 'Structured agent communication, blocked routes, and delivery status',
287
320
  preload: true,
288
- factory: () => new CommunicationPanel(ui.readModels.communication),
321
+ retainOnClose: true,
322
+ factory: () => new CommunicationPanel(
323
+ ui.readModels.communication,
324
+ (agentId: string) => deps.openAgentDetail?.(agentId),
325
+ ),
289
326
  });
290
327
 
291
328
  manager.registerType({
292
329
  id: 'remote',
293
330
  name: 'Remote',
294
- icon: 'R',
295
- category: 'monitoring',
331
+ // WO-152: was 'R' (collided with routes).
332
+ icon: '',
333
+ category: 'providers',
296
334
  description: 'Self-hosted daemon and ACP transport state with active remote connections',
297
335
  factory: () => new RemotePanel(ui.readModels.remote),
298
336
  });
299
337
 
300
- manager.registerType({
301
- id: 'providers',
302
- name: 'Providers',
303
- icon: 'R',
304
- category: 'monitoring',
305
- description: 'Per-provider performance metrics: latency, error rate, request count, sparkline trends',
306
- factory: () => new ProviderStatsPanel(ui.events.turns, ui.events.providers, deps.requestRender, ui.readModels.providers),
307
- });
308
-
309
338
  manager.registerType({
310
339
  id: 'provider-health',
311
340
  name: 'Health',
312
341
  icon: 'N',
313
- category: 'monitoring',
314
- description: 'Provider health dashboard: real-time status, latency, errors, and rate-limit cooldowns',
342
+ category: 'providers',
343
+ description: 'Provider console: status, latency timelines, error attribution, auth routes, fallback chain, and repair actions',
315
344
  preload: true,
345
+ retainOnClose: true,
316
346
  factory: () => new ProviderHealthPanel(
317
347
  providerRuntime,
318
348
  {
@@ -328,16 +358,32 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
328
358
  intelligence: ui.readModels.intelligence,
329
359
  continuity: ui.readModels.continuity,
330
360
  worktrees: ui.readModels.worktrees,
361
+ ...(runtimeStore
362
+ ? {
363
+ modelState: {
364
+ get: () => selectModel(runtimeStore.getState()),
365
+ subscribe: (listener: () => void) => runtimeStore.subscribe(listener),
366
+ },
367
+ }
368
+ : {}),
331
369
  },
332
370
  deps.requestRender,
333
371
  ),
334
372
  });
335
373
 
374
+ // WO-112 compat (wired at integration): the retired 'providers' and
375
+ // 'accounts' panel ids still resolve — redirected to the merged
376
+ // provider-health console. '/accounts panel|open' depends on the
377
+ // 'accounts' alias.
378
+ manager.registerAlias('providers', 'provider-health');
379
+ manager.registerAlias('accounts', 'provider-health');
380
+
336
381
  manager.registerType({
337
382
  id: 'debug',
338
383
  name: 'Debug',
339
- icon: 'B',
340
- category: 'monitoring',
384
+ // WO-152: was 'B' (collided with subscription).
385
+ icon: '',
386
+ category: 'incidents-diagnostics',
341
387
  description: 'API debug panel: per-call log with model, provider, tokens, latency, status, and error history',
342
388
  factory: () => {
343
389
  const panel = new DebugPanel(ui.events.turns, deps.requestRender);
@@ -346,44 +392,59 @@ export function registerOperationsPanels(manager: PanelManager, deps: ResolvedBu
346
392
  },
347
393
  });
348
394
 
349
- if (deps.forensicsRegistry) {
350
- const { forensicsRegistry } = deps;
351
- manager.registerType({
352
- id: 'incident',
353
- name: 'Incident',
354
- icon: 'N',
355
- category: 'monitoring',
356
- description: 'Incident workspace with root cause, permission, budget, and replay evidence',
357
- factory: () => new IncidentReviewPanel(forensicsRegistry),
358
- });
359
- manager.registerType({
360
- id: 'forensics',
361
- name: 'Forensics',
362
- icon: 'F',
363
- category: 'monitoring',
364
- description: 'Failure Forensics: auto-classified failure reports with causal chains, phase timings, and jump links',
365
- factory: () => new ForensicsPanel(forensicsRegistry),
366
- });
367
- }
395
+ // WO-152: always registered (was gated behind `if (deps.forensicsRegistry)`,
396
+ // so `/panel open incident` reported "Unknown panel" on builds without a
397
+ // forensics registry wired). IncidentReviewPanel already accepts an
398
+ // optional registry and renders its own "not configured" empty state when
399
+ // it is absent, so no fallback wrapper is needed here — only the
400
+ // conditional registration itself is removed.
401
+ manager.registerType({
402
+ id: 'incident',
403
+ name: 'Incident',
404
+ // WO-152: was 'N' (collided with provider-health).
405
+ icon: '◪',
406
+ category: 'incidents-diagnostics',
407
+ description: 'Incident workspace with root cause, permission, budget, replay, causal-chain, phase-timing, and jump-link evidence',
408
+ factory: () => new IncidentReviewPanel(deps.forensicsRegistry),
409
+ });
410
+ // WO-114 compat (Forensics merged into Incident): the retired 'forensics'
411
+ // panel id still resolves — redirected to the merged Incident workspace so
412
+ // pre-existing openForensicsPanel()/`/forensics` callers land on the same
413
+ // Incident instance instead of stacking a duplicate registration.
414
+ manager.registerAlias('forensics', 'incident');
368
415
 
369
416
  manager.registerType({
370
417
  id: 'policy',
371
418
  name: 'Policy',
372
- icon: 'U',
373
- category: 'monitoring',
419
+ // WO-152: was 'U' (collided with local-auth and security).
420
+ icon: '',
421
+ category: 'security-policy',
374
422
  description: 'Policy governance: active/candidate bundles, divergence gate, rollout history, and simulation evidence',
375
423
  factory: () => new PolicyPanel(deps.policyRuntimeState),
376
424
  });
377
425
 
378
- if (deps.evalRegistry) {
426
+ // WO-152: always registered (was gated behind `if (deps.evalRegistry)`, so
427
+ // `/panel open eval` reported "Unknown panel" on builds without an eval
428
+ // registry wired). Falls back to a "dependency not configured" empty state.
429
+ {
379
430
  const { evalRegistry } = deps;
380
431
  manager.registerType({
381
432
  id: 'eval',
382
433
  name: 'Eval',
383
- icon: 'Y',
384
- category: 'monitoring',
434
+ // WO-152: registry previously said 'Y' while the live panel's own
435
+ // super() call used 'V' — a pre-existing registry/instance mismatch as
436
+ // well as a collision ('Y' with communication/settings-sync, 'V' with
437
+ // services). Unified to a single unique glyph in both places.
438
+ icon: '▮',
439
+ category: 'incidents-diagnostics',
385
440
  description: 'Evaluation harness: benchmark suite results, scorecards, and regression gates',
386
- factory: () => new EvalPanel(evalRegistry),
441
+ factory: withUnconfiguredFallback(
442
+ evalRegistry !== undefined,
443
+ 'eval', 'Eval', '▮', 'incidents-diagnostics',
444
+ ' Eval registry not configured for this session.',
445
+ 'This runtime was not wired with an evaluation harness registry at bootstrap, so no eval data is available.',
446
+ () => new EvalPanel(evalRegistry!, ui.environment.shellPaths.workingDirectory),
447
+ ),
387
448
  });
388
449
  }
389
450
  }
@@ -42,6 +42,7 @@ function readBootstrapPassword(credentialPath: string): string | undefined {
42
42
  }
43
43
 
44
44
  export function registerSessionPanels(manager: PanelManager, deps: ResolvedBuiltinPanelDeps): void {
45
+ const ui = requireUiServices(deps);
45
46
  manager.registerType({
46
47
  id: 'qr-code',
47
48
  name: 'QR Code',
@@ -71,7 +72,7 @@ export function registerSessionPanels(manager: PanelManager, deps: ResolvedBuilt
71
72
  surface: 'tui',
72
73
  });
73
74
  };
74
- return new QrPanel(connectionInfo, regenerate, copyToClipboard);
75
+ return new QrPanel(connectionInfo, regenerate, copyToClipboard, ui.readModels.controlPlane, deps.localUserAuthManager);
75
76
  },
76
77
  });
77
78
 
@@ -90,7 +91,7 @@ export function registerSessionPanels(manager: PanelManager, deps: ResolvedBuilt
90
91
  icon: '?',
91
92
  category: 'session',
92
93
  description: 'Tool list, model capabilities, and keyboard shortcut reference',
93
- factory: () => new DocsPanel(deps.toolRegistry, deps.providerRegistry),
94
+ factory: () => new DocsPanel(deps.toolRegistry, deps.providerRegistry, requireUiServices(deps).shell.keybindingsManager),
94
95
  });
95
96
 
96
97
  manager.registerType({
@@ -105,25 +106,50 @@ export function registerSessionPanels(manager: PanelManager, deps: ResolvedBuilt
105
106
  manager.registerType({
106
107
  id: 'system-messages',
107
108
  name: 'System Messages',
108
- icon: 'J',
109
- category: 'monitoring',
109
+ // WO-152: was 'J' (collided with intelligence + tasks).
110
+ icon: '',
111
+ category: 'runtime-ops',
110
112
  description: 'Operational system messages routed away from the main conversation (scans, discovery, plugin events, tool status)',
111
113
  preload: true,
114
+ retainOnClose: true,
112
115
  factory: () => deps.systemMessagesPanel,
113
116
  });
114
117
 
115
118
  manager.registerType({
116
119
  id: 'tokens',
117
120
  name: 'Tokens',
118
- icon: 'K',
119
- category: 'monitoring',
120
- description: 'Token budget tracker: per-turn and cumulative usage with context window gauge',
121
+ // WO-152: registry previously said 'K' while the live panel's own
122
+ // super() call used 'T' — a pre-existing registry/instance mismatch as
123
+ // well as a collision ('K' with knowledge/skills, 'T' with thinking).
124
+ // Unified to a single unique glyph in both places.
125
+ icon: '▢',
126
+ category: 'providers',
127
+ description: 'Token + context console: gauge, true composition, per-turn history, inline cost, and one-key compact',
128
+ // Preloaded (absorbed from the retired ContextVisualizerPanel) so turn
129
+ // history and pressure accumulate in the background even before the user
130
+ // opens the tab.
131
+ preload: true,
132
+ retainOnClose: true,
121
133
  factory: () => {
122
- const panel = new TokenBudgetPanel(deps.sessionMemoryStore, deps.configManager, deps.requestRender);
134
+ const panel = new TokenBudgetPanel(
135
+ deps.sessionMemoryStore,
136
+ deps.configManager,
137
+ deps.requestRender,
138
+ requireUiServices(deps).events.turns,
139
+ );
123
140
  if (deps.orchestrator && deps.getCtxWindow) {
124
- panel.wire(deps.orchestrator, deps.getCtxWindow, requireUiServices(deps).readModels.session);
141
+ panel.wire(
142
+ deps.orchestrator,
143
+ deps.getCtxWindow,
144
+ requireUiServices(deps).readModels.session,
145
+ () => deps.providerRegistry.getCurrentModel().id,
146
+ );
125
147
  }
126
148
  return panel;
127
149
  },
128
150
  });
151
+
152
+ // WO-113 compat: the retired 'context' panel id still resolves — redirected
153
+ // to the merged tokens console ('/panel open context', saved layouts).
154
+ manager.registerAlias('context', 'tokens');
129
155
  }