@pellux/goodvibes-tui 1.1.0 → 1.9.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 (267) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +28 -18
  3. package/docs/foundation-artifacts/operator-contract.json +4045 -1763
  4. package/package.json +2 -2
  5. package/src/audio/spoken-turn-controller.ts +12 -2
  6. package/src/audio/spoken-turn-wiring.ts +2 -1
  7. package/src/cli/help.ts +8 -1
  8. package/src/cli/service-posture.ts +2 -1
  9. package/src/cli/status.ts +2 -1
  10. package/src/cli/surface-command.ts +2 -2
  11. package/src/core/alert-gating.ts +67 -0
  12. package/src/core/approval-alert.ts +72 -0
  13. package/src/core/budget-breach-notifier.ts +106 -0
  14. package/src/core/composer-state.ts +11 -3
  15. package/src/core/conversation-line-cache.ts +27 -3
  16. package/src/core/conversation-rendering.ts +90 -14
  17. package/src/core/conversation.ts +18 -0
  18. package/src/core/focus-tracker.ts +41 -0
  19. package/src/core/long-task-notifier.ts +33 -6
  20. package/src/core/stream-event-wiring.ts +20 -1
  21. package/src/core/system-message-noise.ts +87 -0
  22. package/src/core/system-message-router.ts +104 -51
  23. package/src/core/turn-cancellation.ts +25 -0
  24. package/src/core/turn-event-wiring.ts +72 -3
  25. package/src/daemon/cli.ts +29 -2
  26. package/src/daemon/handlers/register.ts +8 -1
  27. package/src/daemon/service-commands.ts +329 -0
  28. package/src/export/cost-utils.ts +36 -0
  29. package/src/input/autocomplete.ts +27 -1
  30. package/src/input/command-registry.ts +84 -5
  31. package/src/input/commands/checkpoint-runtime.ts +280 -0
  32. package/src/input/commands/codebase-runtime.ts +232 -0
  33. package/src/input/commands/config.ts +43 -3
  34. package/src/input/commands/eval.ts +1 -1
  35. package/src/input/commands/health-runtime.ts +10 -2
  36. package/src/input/commands/image-runtime.ts +112 -0
  37. package/src/input/commands/intelligence-runtime.ts +11 -1
  38. package/src/input/commands/local-auth-runtime.ts +4 -1
  39. package/src/input/commands/local-runtime.ts +9 -3
  40. package/src/input/commands/marketplace-runtime.ts +2 -2
  41. package/src/input/commands/memory.ts +73 -35
  42. package/src/input/commands/operator-panel-runtime.ts +71 -31
  43. package/src/input/commands/planning-runtime.ts +116 -4
  44. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  45. package/src/input/commands/plugin-runtime.ts +2 -2
  46. package/src/input/commands/policy-dispatch.ts +21 -0
  47. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  48. package/src/input/commands/qrcode-runtime.ts +26 -6
  49. package/src/input/commands/recall-review.ts +35 -0
  50. package/src/input/commands/remote-runtime-setup.ts +5 -3
  51. package/src/input/commands/remote-runtime.ts +3 -3
  52. package/src/input/commands/runtime-services.ts +54 -13
  53. package/src/input/commands/services-runtime.ts +1 -1
  54. package/src/input/commands/session-content.ts +20 -9
  55. package/src/input/commands/session-workflow.ts +16 -1
  56. package/src/input/commands/settings-sync-runtime.ts +16 -4
  57. package/src/input/commands/shell-core.ts +25 -8
  58. package/src/input/commands/skills-runtime.ts +2 -8
  59. package/src/input/commands/subscription-runtime.ts +2 -2
  60. package/src/input/commands/test-runtime.ts +277 -0
  61. package/src/input/commands/websearch-runtime.ts +101 -0
  62. package/src/input/commands/work-plan-runtime.ts +36 -10
  63. package/src/input/commands/workstream-runtime.ts +488 -0
  64. package/src/input/commands.ts +12 -0
  65. package/src/input/config-modal-types.ts +175 -0
  66. package/src/input/config-modal.ts +592 -0
  67. package/src/input/feed-context-factory.ts +10 -8
  68. package/src/input/handler-command-route.ts +37 -20
  69. package/src/input/handler-content-actions.ts +17 -2
  70. package/src/input/handler-feed-routes.ts +74 -114
  71. package/src/input/handler-feed.ts +79 -27
  72. package/src/input/handler-interactions.ts +1 -3
  73. package/src/input/handler-modal-routes.ts +136 -6
  74. package/src/input/handler-modal-stack.ts +14 -7
  75. package/src/input/handler-modal-token-routes.ts +16 -49
  76. package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
  77. package/src/input/handler-onboarding.ts +71 -59
  78. package/src/input/handler-picker-routes.ts +26 -102
  79. package/src/input/handler-shortcuts.ts +74 -14
  80. package/src/input/handler-types.ts +2 -6
  81. package/src/input/handler-ui-state.ts +10 -22
  82. package/src/input/handler.ts +12 -29
  83. package/src/input/keybindings.ts +23 -8
  84. package/src/input/model-picker-types.ts +24 -6
  85. package/src/input/model-picker.ts +77 -2
  86. package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
  87. package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
  88. package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
  89. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
  90. package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
  91. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  92. package/src/input/panel-integration-actions.ts +9 -170
  93. package/src/input/panel-mouse-geometry.ts +97 -0
  94. package/src/input/panel-paste-flood-guard.ts +86 -0
  95. package/src/input/selection-modal.ts +11 -0
  96. package/src/input/session-picker-modal.ts +44 -4
  97. package/src/input/settings-modal-data.ts +146 -2
  98. package/src/input/settings-modal-mutations.ts +6 -4
  99. package/src/input/settings-modal-types.ts +4 -2
  100. package/src/main.ts +49 -51
  101. package/src/panels/agent-inspector-shared.ts +2 -1
  102. package/src/panels/base-panel.ts +22 -1
  103. package/src/panels/builtin/agent.ts +21 -107
  104. package/src/panels/builtin/development.ts +15 -61
  105. package/src/panels/builtin/knowledge.ts +8 -32
  106. package/src/panels/builtin/operations.ts +84 -428
  107. package/src/panels/builtin/session.ts +21 -112
  108. package/src/panels/builtin/shared.ts +18 -46
  109. package/src/panels/builtin-modals.ts +218 -0
  110. package/src/panels/builtin-panels.ts +5 -0
  111. package/src/panels/cost-tracker-panel.ts +36 -10
  112. package/src/panels/diff-panel.ts +12 -43
  113. package/src/panels/eval-registry.ts +60 -0
  114. package/src/panels/fleet-deep-link.ts +31 -0
  115. package/src/panels/fleet-panel-format.ts +62 -0
  116. package/src/panels/fleet-panel-worktree-detail.ts +48 -0
  117. package/src/panels/fleet-panel.ts +758 -0
  118. package/src/panels/fleet-read-model.ts +520 -0
  119. package/src/panels/fleet-steer.ts +125 -0
  120. package/src/panels/fleet-stop.ts +153 -0
  121. package/src/panels/fleet-tabs.ts +230 -0
  122. package/src/panels/fleet-transcript.ts +356 -0
  123. package/src/panels/index.ts +7 -31
  124. package/src/panels/modals/hooks-modal.ts +187 -0
  125. package/src/panels/modals/keybindings-modal.ts +166 -0
  126. package/src/panels/modals/knowledge-modal.ts +158 -0
  127. package/src/panels/modals/local-auth-modal.ts +132 -0
  128. package/src/panels/modals/marketplace-modal.ts +218 -0
  129. package/src/panels/modals/memory-modal.ts +180 -0
  130. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  131. package/src/panels/modals/modal-theme.ts +42 -0
  132. package/src/panels/modals/pairing-modal.ts +163 -0
  133. package/src/panels/modals/planning-modal.ts +304 -0
  134. package/src/panels/modals/plugins-modal.ts +174 -0
  135. package/src/panels/modals/policy-modal.ts +256 -0
  136. package/src/panels/modals/provider-health-modal.ts +136 -0
  137. package/src/panels/modals/remote-modal.ts +115 -0
  138. package/src/panels/modals/sandbox-modal.ts +150 -0
  139. package/src/panels/modals/security-modal.ts +217 -0
  140. package/src/panels/modals/services-modal.ts +179 -0
  141. package/src/panels/modals/settings-sync-modal.ts +142 -0
  142. package/src/panels/modals/skills-modal.ts +189 -0
  143. package/src/panels/modals/subscription-modal.ts +172 -0
  144. package/src/panels/modals/work-plan-modal.ts +177 -0
  145. package/src/panels/panel-confirm-overlay.ts +72 -0
  146. package/src/panels/panel-manager.ts +123 -9
  147. package/src/panels/polish-core.ts +38 -25
  148. package/src/panels/project-planning-answer-actions.ts +8 -11
  149. package/src/panels/skills-panel.ts +7 -51
  150. package/src/panels/types.ts +37 -0
  151. package/src/permissions/hunk-selection.ts +179 -0
  152. package/src/permissions/prompt.ts +216 -13
  153. package/src/renderer/autocomplete-overlay.ts +28 -2
  154. package/src/renderer/compaction-history-modal.ts +19 -3
  155. package/src/renderer/compaction-preview.ts +13 -2
  156. package/src/renderer/compaction-quality.ts +100 -0
  157. package/src/renderer/compositor.ts +2 -3
  158. package/src/renderer/config-modal.ts +95 -0
  159. package/src/renderer/conversation-overlays.ts +10 -17
  160. package/src/renderer/fleet-tab-strip.ts +56 -0
  161. package/src/renderer/footer-tips.ts +15 -3
  162. package/src/renderer/fullscreen-primitives.ts +32 -22
  163. package/src/renderer/git-status.ts +43 -1
  164. package/src/renderer/help-overlay.ts +2 -2
  165. package/src/renderer/layout.ts +0 -4
  166. package/src/renderer/markdown.ts +7 -3
  167. package/src/renderer/modal-factory.ts +25 -20
  168. package/src/renderer/model-workspace.ts +62 -4
  169. package/src/renderer/overlay-box.ts +21 -17
  170. package/src/renderer/panel-workspace-bar.ts +8 -2
  171. package/src/renderer/process-indicator.ts +14 -3
  172. package/src/renderer/selection-modal-overlay.ts +6 -1
  173. package/src/renderer/session-picker-modal.ts +196 -3
  174. package/src/renderer/settings-modal-helpers.ts +2 -0
  175. package/src/renderer/settings-modal.ts +7 -0
  176. package/src/renderer/shell-surface.ts +8 -1
  177. package/src/renderer/status-glyphs.ts +14 -15
  178. package/src/renderer/system-message.ts +15 -3
  179. package/src/renderer/terminal-bg-probe.ts +339 -0
  180. package/src/renderer/terminal-escapes.ts +20 -0
  181. package/src/renderer/theme-mode-config.ts +67 -0
  182. package/src/renderer/theme.ts +91 -1
  183. package/src/renderer/thinking.ts +11 -3
  184. package/src/renderer/tool-call.ts +15 -9
  185. package/src/renderer/tool-result-summary.ts +148 -0
  186. package/src/renderer/turn-injection.ts +133 -0
  187. package/src/renderer/ui-factory.ts +154 -85
  188. package/src/renderer/ui-primitives.ts +30 -129
  189. package/src/runtime/bootstrap-command-context.ts +27 -1
  190. package/src/runtime/bootstrap-command-parts.ts +41 -10
  191. package/src/runtime/bootstrap-core.ts +47 -15
  192. package/src/runtime/bootstrap-hook-bridge.ts +7 -0
  193. package/src/runtime/bootstrap-shell.ts +48 -17
  194. package/src/runtime/bootstrap.ts +129 -22
  195. package/src/runtime/code-index-services.ts +135 -0
  196. package/src/runtime/legacy-daemon-migration.ts +516 -0
  197. package/src/runtime/memory-fold.ts +26 -0
  198. package/src/runtime/onboarding/derivation.ts +7 -2
  199. package/src/runtime/onboarding/snapshot.ts +27 -1
  200. package/src/runtime/onboarding/types.ts +31 -1
  201. package/src/runtime/operator-token-cleanup.ts +82 -1
  202. package/src/runtime/orchestrator-core-services.ts +59 -0
  203. package/src/runtime/process-lifecycle.ts +3 -1
  204. package/src/runtime/resume-notice.ts +209 -0
  205. package/src/runtime/services.ts +98 -46
  206. package/src/runtime/session-inbound-inputs.ts +252 -0
  207. package/src/runtime/session-spine-transport.ts +64 -0
  208. package/src/runtime/terminal-output-guard.ts +15 -8
  209. package/src/runtime/ui-services.ts +27 -3
  210. package/src/runtime/workstream-services.ts +327 -0
  211. package/src/runtime/wrfc-persistence.ts +124 -17
  212. package/src/shell/blocking-input.ts +68 -4
  213. package/src/shell/recovery-input-helpers.ts +170 -1
  214. package/src/shell/ui-openers.ts +171 -26
  215. package/src/utils/format-duration.ts +8 -18
  216. package/src/utils/splash-lines.ts +1 -1
  217. package/src/utils/terminal-width.ts +52 -0
  218. package/src/version.ts +1 -1
  219. package/src/panels/agent-inspector-panel.ts +0 -786
  220. package/src/panels/approval-panel.ts +0 -252
  221. package/src/panels/automation-control-panel.ts +0 -479
  222. package/src/panels/cockpit-panel.ts +0 -481
  223. package/src/panels/cockpit-read-model.ts +0 -238
  224. package/src/panels/communication-panel.ts +0 -256
  225. package/src/panels/control-plane-panel.ts +0 -470
  226. package/src/panels/debug-panel.ts +0 -615
  227. package/src/panels/docs-panel.ts +0 -384
  228. package/src/panels/eval-panel.ts +0 -627
  229. package/src/panels/file-explorer-panel.ts +0 -673
  230. package/src/panels/file-preview-panel.ts +0 -517
  231. package/src/panels/hooks-panel.ts +0 -401
  232. package/src/panels/incident-review-panel.ts +0 -406
  233. package/src/panels/intelligence-panel.ts +0 -383
  234. package/src/panels/knowledge-graph-panel.ts +0 -515
  235. package/src/panels/marketplace-panel.ts +0 -399
  236. package/src/panels/memory-panel.ts +0 -558
  237. package/src/panels/ops-control-panel.ts +0 -329
  238. package/src/panels/ops-strategy-panel.ts +0 -321
  239. package/src/panels/orchestration-panel.ts +0 -465
  240. package/src/panels/panel-list-panel.ts +0 -566
  241. package/src/panels/plan-dashboard-panel.ts +0 -707
  242. package/src/panels/policy-panel.ts +0 -517
  243. package/src/panels/project-planning-panel.ts +0 -731
  244. package/src/panels/provider-health-panel.ts +0 -678
  245. package/src/panels/provider-health-tracker.ts +0 -310
  246. package/src/panels/provider-health-views.ts +0 -567
  247. package/src/panels/qr-panel.ts +0 -280
  248. package/src/panels/remote-panel.ts +0 -534
  249. package/src/panels/routes-panel.ts +0 -241
  250. package/src/panels/sandbox-panel.ts +0 -456
  251. package/src/panels/security-panel.ts +0 -447
  252. package/src/panels/services-panel.ts +0 -329
  253. package/src/panels/session-browser-panel.ts +0 -496
  254. package/src/panels/settings-sync-panel.ts +0 -398
  255. package/src/panels/subscription-panel.ts +0 -342
  256. package/src/panels/symbol-outline-panel.ts +0 -619
  257. package/src/panels/system-messages-panel.ts +0 -364
  258. package/src/panels/tasks-panel.ts +0 -608
  259. package/src/panels/thinking-panel.ts +0 -333
  260. package/src/panels/tool-inspector-panel.ts +0 -537
  261. package/src/panels/work-plan-panel.ts +0 -540
  262. package/src/panels/worktree-panel.ts +0 -360
  263. package/src/panels/wrfc-panel.ts +0 -790
  264. package/src/renderer/agent-detail-modal.ts +0 -466
  265. package/src/renderer/live-tail-modal.ts +0 -156
  266. package/src/renderer/process-modal.ts +0 -671
  267. package/src/renderer/qr-renderer.ts +0 -120
@@ -1,8 +1,10 @@
1
- import { createOAuthLocalListener } from '@pellux/goodvibes-sdk/platform/config';
1
+ import { createOAuthLocalListener, resolveDaemonEnabled } from '@pellux/goodvibes-sdk/platform/config';
2
2
  import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibes-sdk/platform/config';
3
3
  import { summarizeError, openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
4
4
  import { getProviderIdFromModel } from '../config/provider-model.ts';
5
5
  import { buildProviderAccountSnapshot } from '@/runtime/index.ts';
6
+ import { handleConnectExistingDaemonForHandler, handleMigrateLegacyDaemonServiceForHandler } from './handler-onboarding-daemon-adopt.ts';
7
+ import { detectLegacyUnit, resolveConfiguredServiceName } from '../runtime/legacy-daemon-migration.ts';
6
8
  import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardApplyFeedback } from './onboarding/onboarding-wizard.ts';
7
9
  import { handleCloudflareOnboardingActionForHandler, maybeProvisionCloudflareOnFinalApplyForHandler } from './handler-onboarding-cloudflare.ts';
8
10
  import { applyOnboardingRequest, collectOnboardingSnapshot, deleteWizardProgress, verifyOnboardingRequest, writeOnboardingCheckMarker, writeWizardProgress } from '../runtime/onboarding/index.ts';
@@ -74,6 +76,41 @@ function formatRuntimeStoppedFailureMessage(
74
76
  return `${binding.label} was disabled for ${disabledSurface}, but ${binding.host}:${binding.port} is still occupied. Settings were saved; another GoodVibes process or external service may still be running on that port.${statusDetail}`;
75
77
  }
76
78
 
79
+ /**
80
+ * Verification item(s) for one runtime endpoint given whether it is expected
81
+ * to be running. Shared by both the "nothing server-backed selected" early
82
+ * path and the full server-backed path so daemon-by-default (the loopback
83
+ * daemon can be `expected` even when nothing else was selected) is handled
84
+ * identically in both places.
85
+ */
86
+ function buildEndpointVerificationItems(
87
+ handler: InputHandler,
88
+ request: OnboardingApplyRequest,
89
+ endpoint: OnboardingRuntimeEndpoint,
90
+ expected: boolean,
91
+ state: OnboardingExternalServiceState | undefined,
92
+ ): OnboardingVerificationItem[] {
93
+ const idPrefix = endpoint === 'daemon' ? 'daemon' : 'http-listener';
94
+ if (expected) {
95
+ const active = isRuntimeEndpointActive(state, endpoint);
96
+ return [{
97
+ id: `runtime:${idPrefix}-active`,
98
+ status: active ? 'pass' : 'fail',
99
+ message: active
100
+ ? formatRuntimeActiveSuccessMessage(endpoint, state)
101
+ : formatRuntimeActiveFailureMessage(handler, request, endpoint, state),
102
+ target: 'service',
103
+ }];
104
+ }
105
+ if (!isRuntimeEndpointOccupyingConfiguredPort(state, endpoint)) return [];
106
+ return [{
107
+ id: `runtime:${idPrefix}-stopped`,
108
+ status: 'fail',
109
+ message: formatRuntimeStoppedFailureMessage(handler, request, endpoint, state),
110
+ target: 'service',
111
+ }];
112
+ }
113
+
77
114
  function showOnboardingApplyFeedbackForHandler(handler: InputHandler, feedback: OnboardingWizardApplyFeedback): void {
78
115
  handler.onboardingWizard.setApplyFeedback(feedback);
79
116
  const reviewIndex = handler.onboardingWizard.steps.findIndex((step) => step.id === 'review');
@@ -185,6 +222,15 @@ export function handleModelPickerCommitForHandler(handler: InputHandler): boolea
185
222
  }
186
223
 
187
224
  export async function handleOnboardingActionForHandler(handler: InputHandler, action: OnboardingWizardAction): Promise<void> {
225
+ if (action === 'connect-existing-daemon') {
226
+ await handleConnectExistingDaemonForHandler(handler);
227
+ return;
228
+ }
229
+ if (action === 'migrate-legacy-daemon-service') {
230
+ const confirm = handler.onboardingWizard.getBooleanFieldValue('network.migrate-legacy-daemon-confirm', false);
231
+ await handleMigrateLegacyDaemonServiceForHandler(handler, confirm);
232
+ return;
233
+ }
188
234
  if (action === 'start-openai-subscription') {
189
235
  await handler.handleOpenAiSubscriptionStart();
190
236
  return;
@@ -309,6 +355,7 @@ export async function handleOnboardingActionForHandler(handler: InputHandler, ac
309
355
  handler.requestRender();
310
356
  }
311
357
 
358
+
312
359
  export async function refreshOnboardingHydrationForHandler(handler: InputHandler, options: {
313
360
  readonly preserveValues?: boolean;
314
361
  readonly targetStepId?: string;
@@ -336,6 +383,9 @@ export async function refreshOnboardingHydrationForHandler(handler: InputHandler
336
383
  secretsManager: handler.uiServices.platform.secretsManager,
337
384
  }),
338
385
  },
386
+ legacyDaemon: {
387
+ detect: () => ({ ...detectLegacyUnit({ homeDir: handler.uiServices.environment.homeDirectory }), trackedServiceName: resolveConfiguredServiceName(handler.uiServices.platform.configManager) }),
388
+ },
339
389
  });
340
390
  if (!handler.onboardingWizard.active || hydrationSerial !== handler.onboardingHydrationSerial) return;
341
391
  handler.onboardingWizard.hydrateRuntimeState({ snapshot }, { resetValues: !(options.preserveValues ?? false) });
@@ -572,7 +622,8 @@ export function getOnboardingRuntimePostureForHandler(handler: InputHandler, req
572
622
  const serviceEnabled = getConfigValue('service.enabled') === true;
573
623
  const serviceAutostart = getConfigValue('service.autostart') === true;
574
624
  const restartOnFailure = getConfigValue('service.restartOnFailure') === true;
575
- const daemonEnabled = getConfigValue('danger.daemon') === true || getConfigValue('controlPlane.enabled') === true;
625
+ const daemonReader = { get: (key: string): boolean | string | number | undefined => getConfigValue(key) as boolean | string | number | undefined };
626
+ const daemonEnabled = resolveDaemonEnabled(daemonReader) || getConfigValue('controlPlane.enabled') === true;
576
627
  const listenerEnabled = getConfigValue('danger.httpListener') === true;
577
628
  const webEnabled = getConfigValue('web.enabled') === true;
578
629
  const controlPlaneRemote = getConfigValue('controlPlane.hostMode') === 'network'
@@ -593,7 +644,10 @@ export function getOnboardingRuntimePostureForHandler(handler: InputHandler, req
593
644
  restartOnFailure,
594
645
  expectedDaemon: daemonEnabled || webEnabled,
595
646
  expectedHttpListener: listenerEnabled,
596
- serverBacked: serviceEnabled || daemonEnabled || listenerEnabled || webEnabled,
647
+ // Daemon-by-default: the loopback session daemon can be expected to run
648
+ // even when nothing else was explicitly enabled here, so it does not
649
+ // belong in "did onboarding opt into a network-facing bundle."
650
+ serverBacked: serviceEnabled || listenerEnabled || webEnabled,
597
651
  remoteExposure,
598
652
  };
599
653
  }
@@ -683,28 +737,20 @@ export function verifyOnboardingRuntimePostureForHandler(handler: InputHandler,
683
737
  }];
684
738
  }
685
739
 
686
- const stoppedItems: OnboardingVerificationItem[] = [];
687
- if (isRuntimeEndpointOccupyingConfiguredPort(externalState, 'daemon')) {
688
- stoppedItems.push({
689
- id: 'runtime:daemon-stopped',
690
- status: 'fail',
691
- message: formatRuntimeStoppedFailureMessage(handler, request, 'daemon', externalState),
692
- target: 'service',
693
- });
694
- }
695
- if (isRuntimeEndpointOccupyingConfiguredPort(externalState, 'httpListener')) {
696
- stoppedItems.push({
697
- id: 'runtime:http-listener-stopped',
698
- status: 'fail',
699
- message: formatRuntimeStoppedFailureMessage(handler, request, 'httpListener', externalState),
700
- target: 'service',
701
- });
702
- }
740
+ // Daemon-by-default: expectedDaemon can be true here (loopback daemon
741
+ // running for cross-surface visibility) even though nothing else was
742
+ // selected, so this is not simply "everything must be stopped."
743
+ const stoppedItems: OnboardingVerificationItem[] = [
744
+ ...buildEndpointVerificationItems(handler, request, 'daemon', posture.expectedDaemon, externalState),
745
+ ...buildEndpointVerificationItems(handler, request, 'httpListener', posture.expectedHttpListener, externalState),
746
+ ];
703
747
  if (externalState && stoppedItems.length === 0) {
704
748
  stoppedItems.push({
705
749
  id: 'runtime:external-services-stopped',
706
750
  status: 'pass',
707
- message: 'Background daemon and HTTP listener are stopped for Local TUI Only.',
751
+ message: posture.expectedDaemon
752
+ ? 'HTTP listener is stopped for Local TUI Only; the background daemon is running as expected for cross-surface visibility on this machine.'
753
+ : 'Background daemon and HTTP listener are stopped for Local TUI Only.',
708
754
  target: 'service',
709
755
  });
710
756
  }
@@ -742,44 +788,10 @@ export function verifyOnboardingRuntimePostureForHandler(handler: InputHandler,
742
788
  });
743
789
  }
744
790
 
745
- if (posture.expectedDaemon) {
746
- const daemonActive = isRuntimeEndpointActive(externalState, 'daemon');
747
- items.push({
748
- id: 'runtime:daemon-active',
749
- status: daemonActive ? 'pass' : 'fail',
750
- message: daemonActive
751
- ? formatRuntimeActiveSuccessMessage('daemon', externalState)
752
- : formatRuntimeActiveFailureMessage(handler, request, 'daemon', externalState),
753
- target: 'service',
754
- });
755
- }
756
- if (!posture.expectedDaemon && isRuntimeEndpointOccupyingConfiguredPort(externalState, 'daemon')) {
757
- items.push({
758
- id: 'runtime:daemon-stopped',
759
- status: 'fail',
760
- message: formatRuntimeStoppedFailureMessage(handler, request, 'daemon', externalState),
761
- target: 'service',
762
- });
763
- }
764
- if (posture.expectedHttpListener) {
765
- const httpListenerActive = isRuntimeEndpointActive(externalState, 'httpListener');
766
- items.push({
767
- id: 'runtime:http-listener-active',
768
- status: httpListenerActive ? 'pass' : 'fail',
769
- message: httpListenerActive
770
- ? formatRuntimeActiveSuccessMessage('httpListener', externalState)
771
- : formatRuntimeActiveFailureMessage(handler, request, 'httpListener', externalState),
772
- target: 'service',
773
- });
774
- }
775
- if (!posture.expectedHttpListener && isRuntimeEndpointOccupyingConfiguredPort(externalState, 'httpListener')) {
776
- items.push({
777
- id: 'runtime:http-listener-stopped',
778
- status: 'fail',
779
- message: formatRuntimeStoppedFailureMessage(handler, request, 'httpListener', externalState),
780
- target: 'service',
781
- });
782
- }
791
+ items.push(
792
+ ...buildEndpointVerificationItems(handler, request, 'daemon', posture.expectedDaemon, externalState),
793
+ ...buildEndpointVerificationItems(handler, request, 'httpListener', posture.expectedHttpListener, externalState),
794
+ );
783
795
 
784
796
  return items;
785
797
  }
@@ -5,7 +5,6 @@ import type { CapabilityFilter, CategoryFilter, ModelPickerModal } from './model
5
5
  import { MODEL_PICKER_CHROME_LINES } from '../renderer/model-picker-overlay.ts';
6
6
  import { resolveAndValidatePath } from '@pellux/goodvibes-sdk/platform/utils';
7
7
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
8
- import type { ProcessEntry } from '../renderer/process-modal.ts';
9
8
  import type { BlockActionId } from '../renderer/block-actions.ts';
10
9
 
11
10
  type ModelPickerRouteState = {
@@ -23,13 +22,15 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
23
22
 
24
23
  if (token.type === 'key') {
25
24
  if (token.logicalName === 'escape') {
26
- if (state.modelPicker.searchFocused && state.modelPicker.mode !== 'contextCap') {
27
- if (state.modelPicker.query.length > 0) {
28
- state.modelPicker.clearQuery();
29
- } else {
30
- state.modelPicker.blurSearch();
31
- }
32
- } else if (state.modelPicker.query.length > 0) {
25
+ // W3-T2: search now starts focused by default (see
26
+ // ModelPickerModal.openAllModels() doc comment). Escape with an empty
27
+ // query used to only blurSearch() here, leaving the picker open and
28
+ // requiring a SECOND Escape to actually close it — confusing on a
29
+ // freshly-opened, untouched picker where there is nothing to "clear".
30
+ // Clearing a non-empty query is still a distinct first Escape (whether
31
+ // or not search is focused); an empty query falls through to the same
32
+ // effort/contextCap/previousMode/close cascade as before.
33
+ if (state.modelPicker.mode !== 'contextCap' && state.modelPicker.query.length > 0) {
33
34
  state.modelPicker.clearQuery();
34
35
  } else if (state.modelPicker.mode === 'effort') {
35
36
  state.modelPicker.mode = 'model';
@@ -93,6 +94,13 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
93
94
  }
94
95
  state.modelPicker.close();
95
96
  if (state.modalStack[state.modalStack.length - 1] === 'modelPicker') state.modalStack.pop();
97
+ } else if (mode === 'embeddingProvider') {
98
+ const selectedProvider = state.modelPicker.embeddingProviders[idx];
99
+ if (selectedProvider) {
100
+ state.commandContext?.completeEmbeddingProviderSelection?.(selectedProvider.id);
101
+ }
102
+ state.modelPicker.close();
103
+ if (state.modalStack[state.modalStack.length - 1] === 'modelPicker') state.modalStack.pop();
96
104
  } else if (mode === 'contextCap') {
97
105
  const capModel = state.modelPicker.contextCapPendingModel;
98
106
  if (capModel) {
@@ -123,7 +131,12 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
123
131
  }
124
132
  if (state.modelPicker.canFocusSearch() && !state.modelPicker.searchFocused && state.modelPicker.selectedIndex === 0) {
125
133
  state.modelPicker.focusSearch();
126
- } else if (!state.modelPicker.searchFocused) {
134
+ } else if (state.modelPicker.searchFocused) {
135
+ // Symmetric with 'down' (below): search now starts focused by default
136
+ // (W3-T2), so 'up' must have a way out of it too, or it would be a
137
+ // silent no-op the very first time a user reaches for the list.
138
+ state.modelPicker.blurSearch();
139
+ } else {
127
140
  const maxVis = Math.max(5, state.getViewportHeight() - MODEL_PICKER_CHROME_LINES - 4);
128
141
  state.modelPicker.moveUp(maxVis);
129
142
  }
@@ -201,99 +214,10 @@ function cycleCapabilityFilter(modelPicker: ModelPickerModal): void {
201
214
  modelPicker.setCapabilityFilter(cycle[(cur + 1) % cycle.length]!);
202
215
  }
203
216
 
204
- type ProcessRouteState = {
205
- processModal: {
206
- active: boolean;
207
- moveUp: () => void;
208
- moveDown: () => void;
209
- getSelected: () => ProcessEntry | undefined;
210
- close: () => void;
211
- open: () => void;
212
- killSelected: () => boolean;
213
- refresh: () => void;
214
- };
215
- liveTailModal: {
216
- open: (entry: ProcessEntry) => void;
217
- };
218
- agentDetailModal: {
219
- open: (id: string) => void;
220
- };
221
- modalOpened: (name: string) => void;
222
- requestRender: () => void;
223
- handleEscape: () => void;
224
- };
225
-
226
- export function handleProcessModalToken(state: ProcessRouteState, token: InputToken): boolean {
227
- if (!state.processModal.active) return false;
228
-
229
- if (token.type === 'key') {
230
- if (token.logicalName === 'escape') {
231
- state.handleEscape();
232
- return true;
233
- }
234
- if (token.logicalName === 'up') state.processModal.moveUp();
235
- else if (token.logicalName === 'down') state.processModal.moveDown();
236
- else if (token.logicalName === 'enter') {
237
- const entry = state.processModal.getSelected();
238
- if (entry) {
239
- if (entry.type === 'agent') {
240
- state.modalOpened('agentDetail');
241
- state.processModal.close();
242
- state.agentDetailModal.open(entry.id);
243
- } else {
244
- state.modalOpened('liveTail');
245
- state.processModal.close();
246
- state.liveTailModal.open(entry);
247
- }
248
- }
249
- }
250
- } else if (token.type === 'text' && token.value === 'k') {
251
- const killed = state.processModal.killSelected();
252
- if (killed) state.processModal.refresh();
253
- }
254
-
255
- state.requestRender();
256
- return true;
257
- }
258
-
259
- type LiveTailRouteState = {
260
- liveTailModal: {
261
- active: boolean;
262
- scrollUp: () => void;
263
- scrollDown: () => void;
264
- killProcess: () => void;
265
- close: () => void;
266
- };
267
- processModal: {
268
- open: () => void;
269
- };
270
- requestRender: () => void;
271
- handleEscape: () => void;
272
- };
273
-
274
- export function handleLiveTailToken(state: LiveTailRouteState, token: InputToken): boolean {
275
- if (!state.liveTailModal.active) return false;
276
-
277
- const killAndReturn = (): void => {
278
- state.liveTailModal.killProcess();
279
- state.handleEscape();
280
- };
281
-
282
- if (token.type === 'key') {
283
- if (token.logicalName === 'escape') {
284
- state.handleEscape();
285
- return true;
286
- }
287
- if (token.logicalName === 'up') state.liveTailModal.scrollUp();
288
- else if (token.logicalName === 'down') state.liveTailModal.scrollDown();
289
- else if (token.logicalName === 'k') killAndReturn();
290
- } else if (token.type === 'text' && token.value === 'k') {
291
- killAndReturn();
292
- }
293
-
294
- state.requestRender();
295
- return true;
296
- }
217
+ // W6.2 e / W6.1 retirement: the process-modal and live-tail-modal token routes
218
+ // were removed with those modals (F2 now opens Fleet, which subsumes the live
219
+ // process tree). ProcessModal/AgentDetailModal/LiveTailModal were structurally
220
+ // unreachable after the F2 repoint and were deleted.
297
221
 
298
222
  type EscapeOnlyModalRouteState = {
299
223
  active: boolean;
@@ -55,13 +55,16 @@ export function handleGlobalShortcutToken(
55
55
  ): boolean {
56
56
  if (token.type !== 'key') return false;
57
57
 
58
- // Fast-path: bare pageup/pagedown have no keybinding entry.
59
- if (token.logicalName === 'pageup') {
58
+ // Fast-path: BARE pageup/pagedown scroll the transcript. The `!token.ctrl`
59
+ // guard is load-bearing: Ctrl+PageUp/PageDown are the panel-tab-prev/next
60
+ // chords, so they must fall through to the keybinding lookup below instead of
61
+ // being swallowed here as a scroll.
62
+ if (token.logicalName === 'pageup' && !token.ctrl) {
60
63
  if (state.panelFocused) return false;
61
64
  state.scroll(-Math.max(1, viewportHeight - 2));
62
65
  return true;
63
66
  }
64
- if (token.logicalName === 'pagedown') {
67
+ if (token.logicalName === 'pagedown' && !token.ctrl) {
65
68
  if (state.panelFocused) return false;
66
69
  state.scroll(Math.max(1, viewportHeight - 2));
67
70
  return true;
@@ -72,6 +75,23 @@ export function handleGlobalShortcutToken(
72
75
  return true;
73
76
  }
74
77
 
78
+ // Bare F2 is also not in the keybinding table (hardcoded, like pageup/
79
+ // pagedown/escape above) — it must be handled here, GLOBALLY, rather than
80
+ // in handlePromptKeyToken (handler-feed-routes.ts) where it used to live.
81
+ // That location only ever runs when the panel workspace does NOT already
82
+ // own focus (handlePanelFocusToken, which runs before it, swallows every
83
+ // unclaimed key once panelFocused is true and reports it handled). The
84
+ // practical effect was that F2 could OPEN+focus the Fleet panel exactly
85
+ // once; every subsequent press while already focused vanished silently —
86
+ // "F2 pressed 4x never closed the panel" (UX-C evaluator finding). Routing
87
+ // it here, before handlePanelFocusToken ever sees the token, gives F2 the
88
+ // same toggle semantics as Ctrl+O below, matching how Ctrl+P/panel-picker
89
+ // was already reachable regardless of panelFocused.
90
+ if (token.logicalName === 'f2' && !token.ctrl && !token.meta) {
91
+ toggleFleetPanel(state);
92
+ return true;
93
+ }
94
+
75
95
  // O(1) lookup via inverted map.
76
96
  const kb = state.keybindingsManager;
77
97
  const action = kb.lookup(token);
@@ -101,6 +121,20 @@ export function handleGlobalShortcutToken(
101
121
  case 'panel-close': {
102
122
  const pm = state.panelManager;
103
123
  const active = pm.getActivePanel();
124
+ // Wave-3: give the active panel a chance to consume Ctrl+X for an
125
+ // in-panel action (FleetPanel session-tab detach) before it closes the
126
+ // panel outright — see Panel.interceptPanelClose's doc comment.
127
+ if (active?.interceptPanelClose?.()) {
128
+ // UX-C fix: a consumed Ctrl+X (the Fleet panel's session-tab detach)
129
+ // used to leave panelFocused untouched, so focus stayed on the panel
130
+ // — the evaluator's "Ctrl+X detach landed focus in the panel and a
131
+ // typed question became nav keys". Detach is a leave-taking action:
132
+ // like the ordinary close below, it hands control back to the
133
+ // composer rather than leaving the user stranded on the fleet tree.
134
+ state.panelFocused = false;
135
+ state.requestRender();
136
+ return true;
137
+ }
104
138
  if (active) {
105
139
  pm.close(active.id);
106
140
  state.requestRender();
@@ -150,27 +184,27 @@ export function handleGlobalShortcutToken(
150
184
  // Alt+1..9: jump directly to the Nth workspace tab. Routed globally (like
151
185
  // panel-tab-next/prev) so the jump works whether focus is on the prompt or
152
186
  // the workspace; gated on visibility, matching cyclePanelTab semantics.
187
+ // UX-C: a chord jump is "I'm going panel-driving" (focus rule 1a) — the
188
+ // jump now also grabs keyboard focus, matching F2/Ctrl+O/Ctrl+P, so j/k
189
+ // land in the newly-active tab immediately instead of the composer.
153
190
  const pm = state.panelManager;
154
191
  if (pm.isVisible()) {
155
192
  const index = Number(action.slice('panel-tab-'.length)) - 1;
156
193
  pm.activateWorkspaceIndex(index);
194
+ pm.focusPanels();
195
+ state.panelFocused = true;
157
196
  state.requestRender();
158
197
  }
159
198
  return true;
160
199
  }
161
200
 
162
201
  case 'panel-ops': {
163
- // Ctrl+O: open the Ops Control panel. Prefer the command-context callback
164
- // (wired only when the operator-control-plane feature flag is on) so it
165
- // reuses whatever show/open semantics bootstrap.ts set up; otherwise fall
166
- // back to opening the always-registered 'ops-control' panel type directly
167
- // it renders an honest not-configured state when its deps are absent.
168
- if (state.commandContext?.openOpsPanel) {
169
- state.commandContext.openOpsPanel();
170
- } else {
171
- state.panelManager.open('ops-control');
172
- state.requestRender();
173
- }
202
+ // Ctrl+O: TOGGLE the Fleet panel (UX-C same semantics as F2 above; see
203
+ // toggleFleetPanel's doc comment). The former Ops Control panel was
204
+ // retired to an 'ops-control' -> 'fleet' alias (W6.1); rather than route
205
+ // through the now-aliased openOpsPanel callback (which opens without
206
+ // transferring focus), this operates on 'fleet' directly.
207
+ toggleFleetPanel(state);
174
208
  return true;
175
209
  }
176
210
 
@@ -348,3 +382,29 @@ export function handleGlobalShortcutToken(
348
382
  return false;
349
383
  }
350
384
  }
385
+
386
+ /**
387
+ * toggleFleetPanel — the shared F2 / Ctrl+O TOGGLE (UX-C item 2): if the
388
+ * Fleet panel is open AND the panel workspace currently owns keyboard focus
389
+ * with Fleet as the active tab, the chord CLOSES it and returns focus to the
390
+ * composer; if Fleet is open but not the focused/active tab, the chord brings
391
+ * it to front and focuses it; if Fleet isn't open at all, the chord opens and
392
+ * focuses it. Uses `state.panelFocused` (not a panelManager query) for the
393
+ * focus check, consistent with every other case in this file and with what
394
+ * the mocked PanelManager test doubles in global-shortcuts.test.ts actually
395
+ * implement.
396
+ */
397
+ function toggleFleetPanel(state: GlobalShortcutRouteState): void {
398
+ const pm = state.panelManager;
399
+ const fleetOpen = pm.getAllOpen().some((p) => p.id === 'fleet');
400
+ const fleetIsFocusedActive = fleetOpen && state.panelFocused && pm.getActivePanel()?.id === 'fleet';
401
+ if (fleetIsFocusedActive) {
402
+ pm.close('fleet');
403
+ state.panelFocused = false;
404
+ } else {
405
+ pm.open('fleet');
406
+ pm.focusPanels();
407
+ state.panelFocused = true;
408
+ }
409
+ state.requestRender();
410
+ }
@@ -23,14 +23,12 @@ import type { SelectionManager } from './selection.ts';
23
23
  import type { InfiniteBuffer } from '../core/history.ts';
24
24
  import type { AutocompleteEngine } from './autocomplete.ts';
25
25
  import type { BookmarkModal } from './bookmark-modal.ts';
26
- import type { AgentDetailModal } from '../renderer/agent-detail-modal.ts';
27
- import type { LiveTailModal } from '../renderer/live-tail-modal.ts';
28
26
  import type { SettingsModal } from './settings-modal.ts';
29
27
  import type { McpWorkspace } from './mcp-workspace.ts';
30
28
  import type { SessionPickerModal } from './session-picker-modal.ts';
29
+ import type { ConfigModal } from './config-modal.ts';
31
30
  import type { ProfilePickerModal } from './profile-picker-modal.ts';
32
31
  import type { ContextInspectorModal } from '../renderer/context-inspector.ts';
33
- import type { ProcessModal } from '../renderer/process-modal.ts';
34
32
  import type { FilePickerModal } from './file-picker.ts';
35
33
  import type { BlockActionsMenu } from '../renderer/block-actions.ts';
36
34
  import type { SelectionModal } from './selection-modal.ts';
@@ -105,14 +103,12 @@ export interface InputHandlerLike {
105
103
 
106
104
  // ── Modal objects ─────────────────────────────────────────────────────────
107
105
  bookmarkModal: BookmarkModal;
108
- agentDetailModal: AgentDetailModal;
109
- liveTailModal: LiveTailModal;
110
106
  settingsModal: SettingsModal;
111
107
  mcpWorkspace: McpWorkspace;
112
108
  sessionPickerModal: SessionPickerModal;
113
109
  profilePickerModal: ProfilePickerModal;
110
+ configModal: ConfigModal;
114
111
  contextInspectorModal: ContextInspectorModal;
115
- processModal: ProcessModal;
116
112
  modelPicker: ModelPickerModal;
117
113
  filePicker: FilePickerModal;
118
114
  blockActionsMenu: BlockActionsMenu;
@@ -55,14 +55,12 @@ export type ActiveModalState = {
55
55
  helpOverlayActive: boolean;
56
56
  shortcutsOverlayActive: boolean;
57
57
  bookmarkModal: { active: boolean; close: () => void };
58
- agentDetailModal: { active: boolean; close: () => void };
59
- liveTailModal: { active: boolean; close: () => void };
60
58
  settingsModal: { active: boolean; close: () => void };
61
59
  mcpWorkspace?: { active: boolean; close: () => void; reopen: () => void };
62
60
  sessionPickerModal: { active: boolean; close: () => void };
63
61
  profilePickerModal: { active: boolean; close: () => void };
62
+ configModal: { active: boolean; close: () => void };
64
63
  contextInspectorModal: { active: boolean; close: () => void };
65
- processModal: { active: boolean; close: () => void };
66
64
  modelPicker: { active: boolean; close: () => void };
67
65
  filePicker: { active: boolean; close: () => void };
68
66
  blockActionsMenu: { active: boolean; close: () => void };
@@ -75,14 +73,12 @@ export function getActiveModalName(state: ActiveModalState): string | null {
75
73
  if (state.helpOverlayActive) return 'help';
76
74
  if (state.shortcutsOverlayActive) return 'shortcuts';
77
75
  if (state.bookmarkModal.active) return 'bookmark';
78
- if (state.agentDetailModal.active) return 'agentDetail';
79
- if (state.liveTailModal.active) return 'liveTail';
80
76
  if (state.settingsModal.active) return 'settings';
81
77
  if (state.mcpWorkspace?.active) return 'mcpWorkspace';
82
78
  if (state.sessionPickerModal.active) return 'sessionPicker';
83
79
  if (state.profilePickerModal.active) return 'profilePicker';
80
+ if (state.configModal.active) return 'config';
84
81
  if (state.contextInspectorModal.active) return 'contextInspector';
85
- if (state.processModal.active) return 'process';
86
82
  if (state.modelPicker.active) return 'modelPicker';
87
83
  if (state.filePicker.active) return 'filePicker';
88
84
  if (state.blockActionsMenu.active) return 'blockActions';
@@ -96,14 +92,12 @@ export type ModalCloseOps = {
96
92
  resetHelp: () => void;
97
93
  resetShortcuts: () => void;
98
94
  closeBookmark: () => void;
99
- closeAgentDetail: () => void;
100
- closeLiveTail: () => void;
101
95
  closeSettings: () => void;
102
96
  closeMcpWorkspace: () => void;
103
97
  closeSessionPicker: () => void;
104
98
  closeProfilePicker: () => void;
99
+ closeConfigModal: () => void;
105
100
  closeContextInspector: () => void;
106
- closeProcess: () => void;
107
101
  closeModelPicker: () => void;
108
102
  closeFilePicker: () => void;
109
103
  closeBlockActions: () => void;
@@ -123,12 +117,6 @@ export function closeModalByName(name: string, ops: ModalCloseOps): void {
123
117
  case 'bookmark':
124
118
  ops.closeBookmark();
125
119
  break;
126
- case 'agentDetail':
127
- ops.closeAgentDetail();
128
- break;
129
- case 'liveTail':
130
- ops.closeLiveTail();
131
- break;
132
120
  case 'settings':
133
121
  ops.closeSettings();
134
122
  break;
@@ -141,12 +129,12 @@ export function closeModalByName(name: string, ops: ModalCloseOps): void {
141
129
  case 'profilePicker':
142
130
  ops.closeProfilePicker();
143
131
  break;
132
+ case 'config':
133
+ ops.closeConfigModal();
134
+ break;
144
135
  case 'contextInspector':
145
136
  ops.closeContextInspector();
146
137
  break;
147
- case 'process':
148
- ops.closeProcess();
149
- break;
150
138
  case 'modelPicker':
151
139
  ops.closeModelPicker();
152
140
  break;
@@ -172,10 +160,10 @@ export type ModalOpenOps = {
172
160
  openHelp: () => void;
173
161
  openShortcuts: () => void;
174
162
  openBookmark: () => void;
175
- openProcess: () => void;
176
163
  openContextInspector: () => void;
177
164
  openMcpWorkspace?: () => void;
178
165
  openOnboarding?: () => void;
166
+ openConfigModal?: () => void;
179
167
  openCommandMode: () => void;
180
168
  };
181
169
 
@@ -190,15 +178,15 @@ export function reopenModalByName(name: string, ops: ModalOpenOps): void {
190
178
  case 'bookmark':
191
179
  ops.openBookmark();
192
180
  break;
193
- case 'process':
194
- ops.openProcess();
195
- break;
196
181
  case 'contextInspector':
197
182
  ops.openContextInspector();
198
183
  break;
199
184
  case 'mcpWorkspace':
200
185
  ops.openMcpWorkspace?.();
201
186
  break;
187
+ case 'config':
188
+ ops.openConfigModal?.();
189
+ break;
202
190
  case 'onboarding':
203
191
  ops.openOnboarding?.();
204
192
  break;