@pellux/goodvibes-tui 1.1.0 → 1.7.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 (194) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +20 -11
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +2 -2
  5. package/src/core/alert-gating.ts +67 -0
  6. package/src/core/approval-alert.ts +72 -0
  7. package/src/core/budget-breach-notifier.ts +106 -0
  8. package/src/core/conversation-rendering.ts +19 -0
  9. package/src/core/conversation.ts +18 -0
  10. package/src/core/focus-tracker.ts +41 -0
  11. package/src/core/long-task-notifier.ts +33 -6
  12. package/src/core/system-message-router.ts +37 -51
  13. package/src/core/turn-cancellation.ts +20 -0
  14. package/src/core/turn-event-wiring.ts +64 -3
  15. package/src/export/cost-utils.ts +36 -0
  16. package/src/input/command-registry.ts +38 -1
  17. package/src/input/commands/checkpoint-runtime.ts +280 -0
  18. package/src/input/commands/codebase-runtime.ts +192 -0
  19. package/src/input/commands/eval.ts +1 -1
  20. package/src/input/commands/health-runtime.ts +1 -1
  21. package/src/input/commands/image-runtime.ts +112 -0
  22. package/src/input/commands/intelligence-runtime.ts +11 -1
  23. package/src/input/commands/local-auth-runtime.ts +4 -1
  24. package/src/input/commands/local-runtime.ts +9 -3
  25. package/src/input/commands/marketplace-runtime.ts +2 -2
  26. package/src/input/commands/memory.ts +6 -1
  27. package/src/input/commands/operator-panel-runtime.ts +40 -24
  28. package/src/input/commands/planning-runtime.ts +26 -3
  29. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  30. package/src/input/commands/plugin-runtime.ts +2 -2
  31. package/src/input/commands/policy-dispatch.ts +21 -0
  32. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  33. package/src/input/commands/qrcode-runtime.ts +3 -3
  34. package/src/input/commands/recall-review.ts +35 -0
  35. package/src/input/commands/remote-runtime.ts +3 -3
  36. package/src/input/commands/runtime-services.ts +54 -13
  37. package/src/input/commands/services-runtime.ts +1 -1
  38. package/src/input/commands/session-workflow.ts +16 -1
  39. package/src/input/commands/settings-sync-runtime.ts +1 -1
  40. package/src/input/commands/shell-core.ts +15 -7
  41. package/src/input/commands/skills-runtime.ts +2 -8
  42. package/src/input/commands/subscription-runtime.ts +2 -2
  43. package/src/input/commands/test-runtime.ts +277 -0
  44. package/src/input/commands/websearch-runtime.ts +101 -0
  45. package/src/input/commands/work-plan-runtime.ts +36 -10
  46. package/src/input/commands/workstream-runtime.ts +338 -0
  47. package/src/input/commands.ts +12 -0
  48. package/src/input/config-modal-types.ts +161 -0
  49. package/src/input/config-modal.ts +377 -0
  50. package/src/input/feed-context-factory.ts +7 -8
  51. package/src/input/handler-command-route.ts +27 -17
  52. package/src/input/handler-feed-routes.ts +61 -23
  53. package/src/input/handler-feed.ts +47 -23
  54. package/src/input/handler-interactions.ts +1 -3
  55. package/src/input/handler-modal-routes.ts +65 -0
  56. package/src/input/handler-modal-stack.ts +3 -5
  57. package/src/input/handler-modal-token-routes.ts +16 -49
  58. package/src/input/handler-picker-routes.ts +11 -94
  59. package/src/input/handler-shortcuts.ts +24 -14
  60. package/src/input/handler-types.ts +2 -6
  61. package/src/input/handler-ui-state.ts +10 -22
  62. package/src/input/handler.ts +6 -28
  63. package/src/input/keybindings.ts +22 -8
  64. package/src/input/model-picker-types.ts +24 -6
  65. package/src/input/model-picker.ts +58 -0
  66. package/src/input/panel-integration-actions.ts +9 -170
  67. package/src/input/settings-modal-data.ts +95 -0
  68. package/src/input/settings-modal-mutations.ts +6 -4
  69. package/src/main.ts +24 -27
  70. package/src/panels/agent-inspector-shared.ts +2 -1
  71. package/src/panels/base-panel.ts +22 -1
  72. package/src/panels/builtin/agent.ts +21 -107
  73. package/src/panels/builtin/development.ts +15 -61
  74. package/src/panels/builtin/knowledge.ts +8 -32
  75. package/src/panels/builtin/operations.ts +84 -428
  76. package/src/panels/builtin/session.ts +21 -112
  77. package/src/panels/builtin/shared.ts +9 -43
  78. package/src/panels/builtin-modals.ts +218 -0
  79. package/src/panels/builtin-panels.ts +5 -0
  80. package/src/panels/cost-tracker-panel.ts +36 -10
  81. package/src/panels/diff-panel.ts +12 -43
  82. package/src/panels/eval-registry.ts +60 -0
  83. package/src/panels/fleet-panel.ts +800 -0
  84. package/src/panels/fleet-read-model.ts +477 -0
  85. package/src/panels/fleet-steer.ts +92 -0
  86. package/src/panels/fleet-stop.ts +125 -0
  87. package/src/panels/fleet-tabs.ts +230 -0
  88. package/src/panels/fleet-transcript.ts +356 -0
  89. package/src/panels/index.ts +7 -31
  90. package/src/panels/modals/hooks-modal.ts +187 -0
  91. package/src/panels/modals/keybindings-modal.ts +151 -0
  92. package/src/panels/modals/knowledge-modal.ts +158 -0
  93. package/src/panels/modals/local-auth-modal.ts +132 -0
  94. package/src/panels/modals/marketplace-modal.ts +218 -0
  95. package/src/panels/modals/memory-modal.ts +180 -0
  96. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  97. package/src/panels/modals/modal-theme.ts +36 -0
  98. package/src/panels/modals/pairing-modal.ts +144 -0
  99. package/src/panels/modals/planning-modal.ts +282 -0
  100. package/src/panels/modals/plugins-modal.ts +174 -0
  101. package/src/panels/modals/policy-modal.ts +256 -0
  102. package/src/panels/modals/provider-health-modal.ts +136 -0
  103. package/src/panels/modals/remote-modal.ts +115 -0
  104. package/src/panels/modals/sandbox-modal.ts +150 -0
  105. package/src/panels/modals/security-modal.ts +217 -0
  106. package/src/panels/modals/services-modal.ts +179 -0
  107. package/src/panels/modals/settings-sync-modal.ts +142 -0
  108. package/src/panels/modals/skills-modal.ts +189 -0
  109. package/src/panels/modals/subscription-modal.ts +172 -0
  110. package/src/panels/modals/work-plan-modal.ts +149 -0
  111. package/src/panels/panel-confirm-overlay.ts +72 -0
  112. package/src/panels/panel-manager.ts +108 -5
  113. package/src/panels/project-planning-answer-actions.ts +4 -2
  114. package/src/panels/skills-panel.ts +7 -51
  115. package/src/panels/types.ts +13 -0
  116. package/src/permissions/hunk-selection.ts +179 -0
  117. package/src/permissions/prompt.ts +60 -4
  118. package/src/renderer/compaction-history-modal.ts +19 -3
  119. package/src/renderer/compaction-preview.ts +13 -2
  120. package/src/renderer/compaction-quality.ts +100 -0
  121. package/src/renderer/config-modal.ts +81 -0
  122. package/src/renderer/conversation-overlays.ts +10 -17
  123. package/src/renderer/fleet-tab-strip.ts +56 -0
  124. package/src/renderer/footer-tips.ts +10 -2
  125. package/src/renderer/git-status.ts +40 -0
  126. package/src/renderer/help-overlay.ts +2 -2
  127. package/src/renderer/model-workspace.ts +44 -1
  128. package/src/renderer/panel-workspace-bar.ts +8 -2
  129. package/src/renderer/turn-injection.ts +114 -0
  130. package/src/runtime/bootstrap-command-context.ts +21 -1
  131. package/src/runtime/bootstrap-command-parts.ts +34 -7
  132. package/src/runtime/bootstrap-core.ts +14 -4
  133. package/src/runtime/bootstrap-shell.ts +29 -16
  134. package/src/runtime/bootstrap.ts +11 -17
  135. package/src/runtime/code-index-services.ts +112 -0
  136. package/src/runtime/orchestrator-core-services.ts +49 -0
  137. package/src/runtime/process-lifecycle.ts +3 -1
  138. package/src/runtime/services.ts +91 -43
  139. package/src/runtime/ui-services.ts +8 -0
  140. package/src/runtime/workstream-services.ts +195 -0
  141. package/src/shell/blocking-input.ts +22 -1
  142. package/src/shell/ui-openers.ts +129 -17
  143. package/src/utils/format-duration.ts +8 -18
  144. package/src/utils/splash-lines.ts +1 -1
  145. package/src/version.ts +1 -1
  146. package/src/panels/agent-inspector-panel.ts +0 -786
  147. package/src/panels/approval-panel.ts +0 -252
  148. package/src/panels/automation-control-panel.ts +0 -479
  149. package/src/panels/cockpit-panel.ts +0 -481
  150. package/src/panels/cockpit-read-model.ts +0 -238
  151. package/src/panels/communication-panel.ts +0 -256
  152. package/src/panels/control-plane-panel.ts +0 -470
  153. package/src/panels/debug-panel.ts +0 -615
  154. package/src/panels/docs-panel.ts +0 -384
  155. package/src/panels/eval-panel.ts +0 -627
  156. package/src/panels/file-explorer-panel.ts +0 -673
  157. package/src/panels/file-preview-panel.ts +0 -517
  158. package/src/panels/hooks-panel.ts +0 -401
  159. package/src/panels/incident-review-panel.ts +0 -406
  160. package/src/panels/intelligence-panel.ts +0 -383
  161. package/src/panels/knowledge-graph-panel.ts +0 -515
  162. package/src/panels/marketplace-panel.ts +0 -399
  163. package/src/panels/memory-panel.ts +0 -558
  164. package/src/panels/ops-control-panel.ts +0 -329
  165. package/src/panels/ops-strategy-panel.ts +0 -321
  166. package/src/panels/orchestration-panel.ts +0 -465
  167. package/src/panels/panel-list-panel.ts +0 -566
  168. package/src/panels/plan-dashboard-panel.ts +0 -707
  169. package/src/panels/policy-panel.ts +0 -517
  170. package/src/panels/project-planning-panel.ts +0 -731
  171. package/src/panels/provider-health-panel.ts +0 -678
  172. package/src/panels/provider-health-tracker.ts +0 -310
  173. package/src/panels/provider-health-views.ts +0 -567
  174. package/src/panels/qr-panel.ts +0 -280
  175. package/src/panels/remote-panel.ts +0 -534
  176. package/src/panels/routes-panel.ts +0 -241
  177. package/src/panels/sandbox-panel.ts +0 -456
  178. package/src/panels/security-panel.ts +0 -447
  179. package/src/panels/services-panel.ts +0 -329
  180. package/src/panels/session-browser-panel.ts +0 -496
  181. package/src/panels/settings-sync-panel.ts +0 -398
  182. package/src/panels/subscription-panel.ts +0 -342
  183. package/src/panels/symbol-outline-panel.ts +0 -619
  184. package/src/panels/system-messages-panel.ts +0 -364
  185. package/src/panels/tasks-panel.ts +0 -608
  186. package/src/panels/thinking-panel.ts +0 -333
  187. package/src/panels/tool-inspector-panel.ts +0 -537
  188. package/src/panels/work-plan-panel.ts +0 -540
  189. package/src/panels/worktree-panel.ts +0 -360
  190. package/src/panels/wrfc-panel.ts +0 -790
  191. package/src/renderer/agent-detail-modal.ts +0 -466
  192. package/src/renderer/live-tail-modal.ts +0 -156
  193. package/src/renderer/process-modal.ts +0 -671
  194. package/src/renderer/qr-renderer.ts +0 -120
@@ -14,6 +14,7 @@ import type { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
14
14
  import { buildSubscriptionEntries } from './settings-modal-subscriptions.ts';
15
15
  import type { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
16
16
  import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
17
+ import { CODE_INDEX_ENABLED_CONFIG_KEY } from '../runtime/code-index-services.ts';
17
18
  import {
18
19
  SETTINGS_CATEGORIES,
19
20
  type FlagEntry,
@@ -130,6 +131,25 @@ export function buildSettingGroups(
130
131
  behaviorEntries.push(buildNotifyAfterSecondsSyntheticEntry(configManager));
131
132
  }
132
133
 
134
+ // Inject the W2.3 alert-class toggles + master focus gate. TUI-local
135
+ // synthetic settings, same rationale as notifyAfterSeconds above.
136
+ if (behaviorEntries) {
137
+ for (const entry of buildNotifyAlertSyntheticEntries(configManager)) {
138
+ if (!behaviorEntries.some((e) => e.setting.key === entry.setting.key)) {
139
+ behaviorEntries.push(entry);
140
+ }
141
+ }
142
+ }
143
+
144
+ // Wave 5 (wo804): inject the storage.codeIndexEnabled toggle into the
145
+ // storage category. TUI-local synthetic setting (not in the SDK ConfigKey
146
+ // union — see code-index-services.ts), same rationale as
147
+ // notifyAfterSeconds above: opt-in, default off, states its own bounds.
148
+ const storageEntries = groups.get('storage');
149
+ if (storageEntries && !storageEntries.some((e) => e.setting.key === (CODE_INDEX_ENABLED_CONFIG_KEY as ConfigKey))) {
150
+ storageEntries.push(buildCodeIndexEnabledSyntheticEntry(configManager));
151
+ }
152
+
133
153
  return groups;
134
154
  }
135
155
 
@@ -224,6 +244,81 @@ export function buildNotifyAfterSecondsSyntheticEntry(configManager: Pick<Config
224
244
  };
225
245
  }
226
246
 
247
+ // ---------------------------------------------------------------------------
248
+ // W2.3 alert-class synthetic settings — behavior.notifyOn* + notifyOnlyWhenUnfocused
249
+ // ---------------------------------------------------------------------------
250
+
251
+ /**
252
+ * The five W2.3 alert-gating booleans, all TUI-local (not yet in the SDK
253
+ * ConfigKey union), all defaulting to on. Read/written generically by
254
+ * core/alert-gating.ts (readBooleanConfig) and the per-alert-class modules
255
+ * (budget-breach-notifier.ts, approval-alert.ts, turn-event-wiring.ts,
256
+ * long-task-notifier.ts) — this is only the settings-modal-visible surface.
257
+ */
258
+ const NOTIFY_ALERT_SYNTHETIC_SETTINGS: ReadonlyArray<{ readonly key: string; readonly description: string }> = [
259
+ {
260
+ key: 'behavior.notifyOnBudgetBreach',
261
+ description: 'Alert when session cost crosses the configured budget (set via the Cost panel\'s "b" key).',
262
+ },
263
+ {
264
+ key: 'behavior.notifyOnAgentFailure',
265
+ description: 'Alert when a delegated or background agent fails.',
266
+ },
267
+ {
268
+ key: 'behavior.notifyOnChainFailure',
269
+ description: 'Alert when a WRFC review chain fails.',
270
+ },
271
+ {
272
+ key: 'behavior.notifyOnApprovalPending',
273
+ description: 'Alert when a tool call is waiting on your approval.',
274
+ },
275
+ {
276
+ key: 'behavior.notifyOnlyWhenUnfocused',
277
+ description: 'Master gate for the four alerts above: fire only when the terminal window is unfocused, or when focus state was never observed (terminal does not report focus). Turn off to always fire regardless of focus.',
278
+ },
279
+ ];
280
+
281
+ function buildBooleanSyntheticEntry(
282
+ configManager: Pick<ConfigManager, 'get'>,
283
+ key: string,
284
+ description: string,
285
+ defaultValue: boolean,
286
+ ): SettingEntry {
287
+ const raw = configManager.get(key as ConfigKey);
288
+ const currentValue = typeof raw === 'boolean' ? raw : defaultValue;
289
+ return {
290
+ setting: { key: key as ConfigKey, type: 'boolean', default: defaultValue, description },
291
+ currentValue,
292
+ isDefault: currentValue === defaultValue,
293
+ };
294
+ }
295
+
296
+ /** Build the five synthetic SettingEntry rows for the behavior category. */
297
+ export function buildNotifyAlertSyntheticEntries(configManager: Pick<ConfigManager, 'get'>): SettingEntry[] {
298
+ return NOTIFY_ALERT_SYNTHETIC_SETTINGS.map((spec) => buildBooleanSyntheticEntry(configManager, spec.key, spec.description, true));
299
+ }
300
+
301
+ // ---------------------------------------------------------------------------
302
+ // Wave 5 (wo804) — storage.codeIndexEnabled synthetic setting
303
+ // ---------------------------------------------------------------------------
304
+
305
+ /**
306
+ * The repo source-tree code index's auto-build-on-startup toggle
307
+ * (code-index-services.ts). TUI-local, default OFF: /codebase build is the
308
+ * explicit trigger unless a user opts in here. Honest bounds stated inline
309
+ * so enabling this isn't a surprise — see code-index-services.ts's
310
+ * CODE_INDEX_MAX_FILES/CODE_INDEX_MAX_FILE_BYTES for the numbers this
311
+ * description would otherwise duplicate as magic numbers.
312
+ */
313
+ export function buildCodeIndexEnabledSyntheticEntry(configManager: Pick<ConfigManager, 'get'>): SettingEntry {
314
+ return buildBooleanSyntheticEntry(
315
+ configManager,
316
+ CODE_INDEX_ENABLED_CONFIG_KEY,
317
+ 'Auto-build the repo source-tree code index on startup (bounded file/size scan; see /codebase status for bounds). Off by default — /codebase build indexes on demand.',
318
+ false,
319
+ );
320
+ }
321
+
227
322
  // ---------------------------------------------------------------------------
228
323
  // buildFlagEntries — snapshot of current feature flag states
229
324
  // ---------------------------------------------------------------------------
@@ -113,13 +113,15 @@ export function persistFlagState(
113
113
  if (newState === 'killed') return; // never persist killed state
114
114
 
115
115
  try {
116
- const current = (configManager.getCategory('featureFlags') as Record<string, PersistedFlagState>) ?? {};
117
116
  if (newState === defaultState) {
118
- delete current[flagId];
117
+ // Back at the default: the override must be REMOVED, not merged.
118
+ // getCategory clones and mergeCategory only sets keys, so the old
119
+ // delete-then-merge approach left the stale override on disk and the
120
+ // flag silently reloaded in the overridden state on the next start.
121
+ configManager.removeCategoryKey('featureFlags', flagId);
119
122
  } else {
120
- current[flagId] = newState;
123
+ configManager.mergeCategory('featureFlags', { [flagId]: newState } as Record<string, PersistedFlagState>);
121
124
  }
122
- configManager.mergeCategory('featureFlags', current);
123
125
  } catch (e) {
124
126
  logger.error('SettingsModal: failed to persist flag state', { flagId, error: summarizeError(e) });
125
127
  }
package/src/main.ts CHANGED
@@ -12,7 +12,7 @@ import { registerAllTools } from '@pellux/goodvibes-sdk/platform/tools';
12
12
  import { FileUndoManager } from '@pellux/goodvibes-sdk/platform/state';
13
13
  import { PermissionManager } from '@pellux/goodvibes-sdk/platform/permissions';
14
14
  import { AcpManager } from '@pellux/goodvibes-sdk/platform/acp';
15
- import { PermissionPromptUI } from './permissions/prompt.ts';
15
+ import { PermissionPromptUI, buildPendingPermissionExtras } from './permissions/prompt.ts';
16
16
  import { CommandRegistry } from './input/command-registry.ts';
17
17
  import type { CommandContext } from './input/command-registry.ts';
18
18
  import { renderProcessIndicator } from './renderer/process-indicator.ts';
@@ -32,6 +32,7 @@ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
32
32
  import { registerBuiltinPanels } from './panels/builtin-panels.ts';
33
33
  import { bootstrapRuntime } from './runtime/bootstrap.ts';
34
34
  import type { BootstrapContext } from './runtime/bootstrap.ts';
35
+ import { buildSharedOrchestratorCoreServices } from './runtime/orchestrator-core-services.ts';
35
36
  import type { HITLMode } from '@pellux/goodvibes-sdk/platform/state';
36
37
  import {
37
38
  checkRecoveryFile,
@@ -62,12 +63,16 @@ import { wireStreamEventMetrics, type StreamMetrics, type WireStreamEventMetrics
62
63
  import { wireTurnEventHandlers } from './core/turn-event-wiring.ts';
63
64
  import { buildContextStatusHint } from './renderer/context-status-hint.ts';
64
65
  import { evaluateSessionMaintenance } from '@/runtime/index.ts';
66
+ import { createCancelGeneration } from './core/turn-cancellation.ts';
67
+ import { wrapRequestPermissionWithAlert } from './core/approval-alert.ts';
68
+ import { setPanelFrameRequester } from './panels/base-panel.ts';
65
69
 
66
70
  const ALT_SCREEN_ENTER = '\x1b[?1049h'; const ALT_SCREEN_EXIT = '\x1b[?1049l';
67
71
  const MOUSE_ENABLE = '\x1b[?1000h\x1b[?1002h\x1b[?1006h'; const MOUSE_DISABLE = '\x1b[?1006l\x1b[?1002l\x1b[?1000l';
68
72
  const CURSOR_HIDE = '\x1b[?25l'; const CURSOR_SHOW = '\x1b[?25h'; const CLEAR_SCREEN = '\x1b[2J\x1b[3J\x1b[H';
69
73
  const KEYBOARD_EXT_ENABLE = '\x1b[>4;2m' + '\x1b[?1u'; const KEYBOARD_EXT_DISABLE = '\x1b[>4;0m' + '\x1b[?1l';
70
74
  const PASTE_ENABLE = '\x1b[?2004h'; const PASTE_DISABLE = '\x1b[?2004l';
75
+ const FOCUS_ENABLE = '\x1b[?1004h'; const FOCUS_DISABLE = '\x1b[?1004l';
71
76
 
72
77
  async function main() {
73
78
  const stdout = process.stdout;
@@ -103,22 +108,17 @@ async function main() {
103
108
  permissionPromptRef,
104
109
  _writeLastSessionPointer: writeLastSessionPointer,
105
110
  systemMessageRouter,
106
- setOpenAgentDetail,
107
111
  } = ctx;
108
112
  const workingDir = ctx.services.workingDirectory;
109
113
  const homeDirectory = ctx.services.homeDirectory;
110
114
  const { approvalBroker, agentManager, modeManager, processManager, providerRegistry, secretsManager, subscriptionManager } = ctx.services;
111
115
  conversation.setSessionMemoryStore(ctx.services.sessionMemoryStore);
112
116
  conversation.setSessionLineageTracker(ctx.services.sessionLineageTracker);
117
+ // Shared payload (single source of truth, includes wo805's memoryRegistry —
118
+ // see orchestrator-core-services.ts) plus this site's favoritesStore.
113
119
  orchestrator.setCoreServices({
114
- configManager,
115
- providerRegistry,
120
+ ...buildSharedOrchestratorCoreServices({ services: ctx.services, configManager, providerRegistry }),
116
121
  favoritesStore: ctx.services.favoritesStore,
117
- planManager: ctx.services.planManager,
118
- adaptivePlanner: ctx.services.adaptivePlanner,
119
- sessionMemoryStore: ctx.services.sessionMemoryStore,
120
- sessionLineageTracker: ctx.services.sessionLineageTracker,
121
- idempotencyStore: ctx.services.idempotencyStore,
122
122
  });
123
123
  ctx.services.wrfcController.setPlanManager(ctx.services.planManager);
124
124
  let activeConversationWidth = stdout.columns || 80;
@@ -219,7 +219,7 @@ async function main() {
219
219
  stdout,
220
220
  ctx,
221
221
  noAltScreen: cli.flags.noAltScreen,
222
- ansi: { CLEAR_SCREEN, ALT_SCREEN_EXIT, PASTE_DISABLE, KEYBOARD_EXT_DISABLE, MOUSE_DISABLE, CURSOR_SHOW },
222
+ ansi: { CLEAR_SCREEN, ALT_SCREEN_EXIT, PASTE_DISABLE, KEYBOARD_EXT_DISABLE, MOUSE_DISABLE, CURSOR_SHOW, FOCUS_DISABLE },
223
223
  getInput: () => input,
224
224
  render: () => renderScheduler.flushNow(), // resize: synchronous immediate path
225
225
  getPromptContentWidth,
@@ -303,12 +303,7 @@ async function main() {
303
303
  }
304
304
  };
305
305
 
306
- const cancelGeneration = () => {
307
- spokenTurns.stop('Spoken output stopped.');
308
- if (orchestrator.isThinking) {
309
- orchestrator.abort();
310
- }
311
- };
306
+ const cancelGeneration = createCancelGeneration(orchestrator, spokenTurns);
312
307
 
313
308
  const jumpToBookmark = (key: string) => {
314
309
  conversation.getDisplayBlocks();
@@ -337,6 +332,7 @@ async function main() {
337
332
  commandContext.pasteFromClipboard = () => input.handlePaste();
338
333
  commandContext.executeCommand = (name, args) => commandRegistry.execute(name, args, commandContext);
339
334
  commandContext.cancelGeneration = cancelGeneration;
335
+ commandContext.isGenerating = () => orchestrator.isThinking;
340
336
  commandContext.jumpToBookmark = jumpToBookmark;
341
337
  commandContext.scrollToLine = scrollToLine;
342
338
  commandContext.clearScreen = () => {
@@ -345,14 +341,14 @@ async function main() {
345
341
  render();
346
342
  };
347
343
  commandContext.requestFullRepaint = () => { compositor.resetDiff(); render(); };
348
- permissionPromptRef.requestPermission = (request) =>
344
+ permissionPromptRef.requestPermission = wrapRequestPermissionWithAlert((request) =>
349
345
  new Promise((resolve) => {
350
346
  pendingPermission = {
351
347
  ...request,
352
- resolve: (approved: boolean, remember = false) => resolve({ approved, remember }),
348
+ ...buildPendingPermissionExtras(request, resolve),
353
349
  };
354
350
  render();
355
- });
351
+ }), { focusTracker: ctx.services.focusTracker, configGet: (k: string) => configManager.get(k as Parameters<typeof configManager.get>[0]), webhookNotifier: ctx.services.webhookNotifier });
356
352
 
357
353
  const input: InputHandler = new InputHandler(
358
354
  () => render(),
@@ -385,7 +381,7 @@ async function main() {
385
381
  replayEngine: ctx.services.replayEngine,
386
382
  webhookNotifier: ctx.services.webhookNotifier,
387
383
  policyRuntimeState: ctx.services.policyRuntimeState,
388
- externalServices: uiServices.platform.externalServices,
384
+ externalServices: uiServices.platform.externalServices, focusTracker: ctx.services.focusTracker,
389
385
  },
390
386
  shell: {
391
387
  bookmarkManager: ctx.services.bookmarkManager,
@@ -415,9 +411,8 @@ async function main() {
415
411
  input.setConversationManager(conversation);
416
412
  input.setContentWidth(getPromptContentWidth());
417
413
  input.filePicker.setOnUpdate(() => render());
418
- input.agentDetailModal.setOnRefresh(() => render());
419
- input.processModal.setOnRefresh(() => render());
420
- setOpenAgentDetail((id) => input.agentDetailModal.open(id));
414
+ // W6.1 retirement: agentDetailModal/processModal setOnRefresh wiring removed —
415
+ // those modals were deleted (Fleet subsumes the live process tree via F2).
421
416
 
422
417
  // Model picker callback is handled in bootstrap.ts — do not duplicate here.
423
418
  input.setHistory(inputHistory);
@@ -557,7 +552,7 @@ async function main() {
557
552
  // Calculate how many rows are consumed by overlays (thinking, permissions, queue, file picker)
558
553
  let overlayRows = 0;
559
554
  if (orchestrator.isThinking) overlayRows += 2; // spinner + blank
560
- if (pendingPermission) overlayRows += PermissionPromptUI.getPromptHeight(pendingPermission);
555
+ if (pendingPermission) overlayRows += PermissionPromptUI.getPromptHeight(pendingPermission, pendingPermission.hunkState);
561
556
  overlayRows += orchestrator.messageQueue.length * 3; // queued messages
562
557
  // File picker and model picker overlay rows computed from actual rendered line count below
563
558
  // Selection modal overlay rows are computed from actual rendered line count below
@@ -606,7 +601,7 @@ async function main() {
606
601
  }
607
602
 
608
603
  if (pendingPermission) {
609
- viewport.push(...PermissionPromptUI.createPromptLines(conversationWidth, pendingPermission));
604
+ viewport.push(...PermissionPromptUI.createPromptLines(conversationWidth, pendingPermission, pendingPermission.hunkState));
610
605
  }
611
606
 
612
607
  orchestrator.messageQueue.forEach(msg => {
@@ -657,6 +652,7 @@ async function main() {
657
652
  const terminalOutputGuard = installTuiTerminalOutputGuard({ stdout, stderr: process.stderr, notify: (message) => { systemMessageRouter.low(message); render(); } });
658
653
 
659
654
  setRenderRequest(renderNow); // bootstrap's 16ms coalescer calls the composite directly
655
+ setPanelFrameRequester(render); // live panels repaint when idle (Wave-6 replay: fleet sat stale until keypress)
660
656
  orchestratorRefs.requestRender = render;
661
657
  commandContext.renderRequest = render;
662
658
  wireShellUiOpeners({
@@ -672,6 +668,7 @@ async function main() {
672
668
  subscriptionManager,
673
669
  secretsManager,
674
670
  serviceRegistry: ctx.services.serviceRegistry,
671
+ memoryEmbeddingRegistry: ctx.services.memoryEmbeddingRegistry,
675
672
  workingDirectory: workingDir,
676
673
  homeDirectory,
677
674
  getConfiguredProviderIds: ctx._getConfiguredProviderIds,
@@ -694,7 +691,7 @@ async function main() {
694
691
  gitStatusProvider,
695
692
  lastGitInfoRef,
696
693
  buildSessionContinuityHints,
697
- render, webhookNotifier: ctx.services.webhookNotifier,
694
+ render, webhookNotifier: ctx.services.webhookNotifier, focusTracker: ctx.services.focusTracker,
698
695
  });
699
696
  unsubs.push(...turnUnsubs);
700
697
 
@@ -736,7 +733,7 @@ async function main() {
736
733
  stdin.setRawMode(true);
737
734
  stdin.resume();
738
735
  stdin.setEncoding('utf8');
739
- allowTerminalWrite(() => stdout.write((cli.flags.noAltScreen ? '' : ALT_SCREEN_ENTER) + CLEAR_SCREEN + CURSOR_HIDE + MOUSE_ENABLE + KEYBOARD_EXT_ENABLE + PASTE_ENABLE));
736
+ allowTerminalWrite(() => stdout.write((cli.flags.noAltScreen ? '' : ALT_SCREEN_ENTER) + CLEAR_SCREEN + CURSOR_HIDE + MOUSE_ENABLE + KEYBOARD_EXT_ENABLE + PASTE_ENABLE + FOCUS_ENABLE));
740
737
 
741
738
  applyInitialTuiCliState({ cli, input, commandRegistry, commandContext, shellPaths: ctx.services.shellPaths, render });
742
739
 
@@ -7,7 +7,8 @@ export type AgentInspectorEntryKind = 'user' | 'assistant' | 'tool_call' | 'tool
7
7
 
8
8
  // ---------------------------------------------------------------------------
9
9
  // Shared agent status / stall constants
10
- // Used by AgentInspectorPanel, AgentDetailModal, and cockpit read-model consumers.
10
+ // Used by the Fleet panel's agent views and read-model consumers (formerly also
11
+ // AgentDetailModal, which was retired in W6.1).
11
12
  // ---------------------------------------------------------------------------
12
13
 
13
14
  /** Terminal statuses — cancel not offered; stall check skipped. */
@@ -9,6 +9,20 @@ import { fitDisplay } from '../utils/terminal-width.ts';
9
9
  /** Canonical error-surface foreground (bad/red), kept out of the render body. */
10
10
  const ERROR_FG = '#ef4444';
11
11
 
12
+ /**
13
+ * Frame requester wired by main.ts to the render scheduler. Without it,
14
+ * markDirty() only sets a flag the compositor reads when a frame is ALREADY
15
+ * being composed for another reason (input, streaming) — so a live panel
16
+ * (fleet ticks, registry subscriptions) sat visibly stale while the app was
17
+ * idle until the next keypress (Wave-6 replay finding). The scheduler
18
+ * coalesces same-tick requests, so this stays cheap.
19
+ */
20
+ let panelFrameRequester: (() => void) | null = null;
21
+
22
+ export function setPanelFrameRequester(requester: (() => void) | null): void {
23
+ panelFrameRequester = requester;
24
+ }
25
+
12
26
  export abstract class BasePanel implements Panel {
13
27
  public needsRender = true;
14
28
  public isTransient = false;
@@ -219,7 +233,14 @@ export abstract class BasePanel implements Panel {
219
233
  /** R2: Called by the compositor after a successful render to clear the dirty flag. */
220
234
  public markRendered(): void { this.needsRender = false; }
221
235
 
222
- protected markDirty(): void { this.needsRender = true; }
236
+ protected markDirty(): void {
237
+ // Request only on the false→true transition: a pending frame already
238
+ // covers repeat marks, and a panel that dirties itself mid-render must
239
+ // not schedule frames forever.
240
+ const wasDirty = this.needsRender;
241
+ this.needsRender = true;
242
+ if (!wasDirty) panelFrameRequester?.();
243
+ }
223
244
 
224
245
  /**
225
246
  * Check whether the panel is currently permitted to render.
@@ -1,113 +1,27 @@
1
1
  import type { PanelManager } from '../panel-manager.ts';
2
- import { AgentInspectorPanel } from '../agent-inspector-panel.ts';
3
- import { ThinkingPanel } from '../thinking-panel.ts';
4
- import { ToolInspectorPanel } from '../tool-inspector-panel.ts';
5
- import { WrfcPanel } from '../wrfc-panel.ts';
6
- import { ProjectPlanningPanel } from '../project-planning-panel.ts';
7
- import { WorkPlanPanel } from '../work-plan-panel.ts';
8
2
  import type { ResolvedBuiltinPanelDeps } from './shared.ts';
9
- import { requireUiServices } from './shared.ts';
10
3
 
4
+ // W6.1 (the purge): thinking, tools, inspector, and wrfc were registered here
5
+ // before the purge. thinking/tools were DELETE-disposition (no surviving
6
+ // human surface — thinking already renders inline in the transcript, tool
7
+ // results render inline plus per-node in Fleet); inspector/wrfc were
8
+ // RETIRE-INTO-FLEET (their live console is subsumed by the Fleet panel,
9
+ // operations.ts:84). See registerOperationsPanels for the fleet
10
+ // registration and .goodvibes/audit/2026-07-04-wave6-briefs.json (W6.1) for
11
+ // the full disposition map.
11
12
  export function registerAgentPanels(manager: PanelManager, deps: ResolvedBuiltinPanelDeps): void {
12
- manager.registerType({
13
- id: 'thinking',
14
- name: 'Thinking',
15
- icon: 'T',
16
- category: 'ai',
17
- description: 'Stream model reasoning tokens in real-time with collapsible blocks per turn',
18
- preload: true,
19
- retainOnClose: true,
20
- factory: () => new ThinkingPanel(requireUiServices(deps).events.turns),
21
- });
13
+ // Compat: '/panel open agent-logs' and 'inspector' (and any saved
14
+ // layout/muscle memory) still resolve — redirected straight to fleet.
15
+ // Repointed from agent-logs->inspector (WO-110) now that inspector itself
16
+ // is retired; alias resolution is a single hop (PanelManager._resolveId),
17
+ // so this must point at the real surviving id, not at another alias.
18
+ manager.registerAlias('agent-logs', 'fleet');
19
+ manager.registerAlias('inspector', 'fleet');
20
+ manager.registerAlias('wrfc', 'fleet');
22
21
 
23
- manager.registerType({
24
- id: 'tools',
25
- name: 'Tools',
26
- icon: 'X',
27
- category: 'ai',
28
- description: 'Chronological tool call inspector with expandable args/results and filtering',
29
- preload: true,
30
- retainOnClose: true,
31
- factory: () => {
32
- const ui = requireUiServices(deps);
33
- return new ToolInspectorPanel(ui.events.tools, ui.events.turns);
34
- },
35
- });
36
-
37
- // WO-110: agent-logs merged into inspector — one deep agent console with
38
- // the correct JSONL parser + cancel, plus agent-logs' follow/pause/filter
39
- // ergonomics. Registration moved here (category 'agent') from
40
- // builtin/development.ts. (WO-113 retired the 'context' registration that
41
- // previously lived here: ContextVisualizerPanel merged into TokenBudgetPanel,
42
- // aliased in builtin/session.ts.)
43
- manager.registerType({
44
- id: 'inspector',
45
- name: 'Inspector',
46
- icon: 'I',
47
- category: 'agent',
48
- description: 'Live per-agent console: timeline, tool calls, WRFC/cost badges, pause/filter/follow, and cancel',
49
- preload: true,
50
- retainOnClose: true,
51
- factory: () => {
52
- const ui = requireUiServices(deps);
53
- return new AgentInspectorPanel({
54
- agentManager: ui.agents.agentManager,
55
- agentMessageBus: ui.agents.agentMessageBus,
56
- agentEvents: ui.events.agents,
57
- workingDirectory: ui.environment.workingDirectory,
58
- cancelAgent: (agentId: string) => ui.agents.agentManager.cancel(agentId),
59
- requestRender: deps.requestRender,
60
- });
61
- },
62
- });
63
-
64
- // Compat: '/panel open agent-logs' (and any saved layout/muscle memory)
65
- // still resolves — redirected to the merged inspector console.
66
- manager.registerAlias('agent-logs', 'inspector');
67
-
68
- manager.registerType({
69
- id: 'wrfc',
70
- name: 'WRFC',
71
- icon: 'W',
72
- category: 'agent',
73
- description: 'WRFC chain view: write, review, fix, and confirm cycle status',
74
- preload: true,
75
- retainOnClose: true,
76
- factory: () => {
77
- const ui = requireUiServices(deps);
78
- return new WrfcPanel(ui.events.workflows, {
79
- controller: ui.agents.wrfcController,
80
- cancelChain: (agentId: string) => ui.agents.agentManager.cancel(agentId),
81
- });
82
- },
83
- });
84
-
85
- manager.registerType({
86
- id: 'work-plan',
87
- name: 'Work Plan',
88
- // WO-152: was 'L' (collided with panel-list).
89
- icon: '◧',
90
- category: 'agent',
91
- description: 'Persistent workspace checklist for multi-step work and cross-session task tracking',
92
- preload: true,
93
- retainOnClose: true,
94
- factory: () => new WorkPlanPanel(deps.workPlanStore),
95
- });
96
-
97
- manager.registerType({
98
- id: 'project-planning',
99
- name: 'Planning',
100
- icon: 'P',
101
- category: 'agent',
102
- description: 'Passive project planning artifacts: readiness, questions, decisions, language, task graph, and agent handoff metadata',
103
- preload: true,
104
- retainOnClose: true,
105
- factory: () => new ProjectPlanningPanel({
106
- service: deps.projectPlanningService,
107
- projectId: deps.projectPlanningProjectId,
108
- requestRender: deps.requestRender,
109
- submitAnswer: deps.submitPlanningAnswer,
110
- dismissPlanning: deps.dismissPlanning,
111
- }),
112
- });
22
+ // W6.1 (the purge) — group B (WO-P): 'work-plan' and 'project-planning'
23
+ // migrated to the 'work-plan-modal' / 'planning-modal' config-modal surfaces.
24
+ // Their surfaces AND panel→modal redirects are registered centrally in
25
+ // registerBuiltinModals (builtin-modals.ts). Panel registrations retired here.
26
+ void deps;
113
27
  }
@@ -1,15 +1,18 @@
1
1
  import type { PanelManager } from '../panel-manager.ts';
2
2
  import { GitPanel } from '../git-panel.ts';
3
3
  import { DiffPanel } from '../diff-panel.ts';
4
- import { PlanDashboardPanel } from '../plan-dashboard-panel.ts';
5
4
  import { CostTrackerPanel } from '../cost-tracker-panel.ts';
6
- import { IntelligencePanel } from '../intelligence-panel.ts';
7
- import { FileExplorerPanel } from '../file-explorer-panel.ts';
8
- import { FilePreviewPanel } from '../file-preview-panel.ts';
9
- import { SymbolOutlinePanel } from '../symbol-outline-panel.ts';
10
5
  import type { ResolvedBuiltinPanelDeps } from './shared.ts';
11
6
  import { requireUiServices, withUnconfiguredFallback } from './shared.ts';
12
7
 
8
+ // W6.1 (the purge): plan, intelligence, explorer, preview, and symbols were
9
+ // registered here before the purge. plan was RETIRE-INTO-FLEET (its
10
+ // execution-plan view is subsumed by Fleet — operations.ts:84); the other
11
+ // four were DELETE-disposition (no surviving human surface — intelligence's
12
+ // read-model still backs the surviving `/intelligence` CLI subcommands, see
13
+ // intelligence-runtime.ts; explorer/preview/symbols had no dedicated
14
+ // read-model to preserve). See .goodvibes/audit/2026-07-04-wave6-briefs.json
15
+ // (W6.1) for the full disposition map.
13
16
  export function registerDevelopmentPanels(manager: PanelManager, deps: ResolvedBuiltinPanelDeps): void {
14
17
  manager.registerType({
15
18
  id: 'git',
@@ -26,19 +29,9 @@ export function registerDevelopmentPanels(manager: PanelManager, deps: ResolvedB
26
29
  ),
27
30
  });
28
31
 
29
- manager.registerType({
30
- id: 'plan',
31
- name: 'Plan',
32
- // Distinct from Planning's 'P' (builtin/agent.ts) — interim glyph per
33
- // WO-128; WO-152 owns the registry-wide icon-uniqueness assertion.
34
- icon: '▤',
35
- category: 'agent',
36
- description: 'Active execution plan with phase progress and item status',
37
- factory: () => {
38
- const ui = requireUiServices(deps);
39
- return new PlanDashboardPanel(ui.events.workflows, { planManager: deps.planManager });
40
- },
41
- });
32
+ // Compat: '/panel open plan' (and any saved layout/muscle memory) still
33
+ // resolves — redirected to fleet, which absorbs the execution-plan view.
34
+ manager.registerAlias('plan', 'fleet');
42
35
 
43
36
  manager.registerType({
44
37
  id: 'diff',
@@ -73,52 +66,13 @@ export function registerDevelopmentPanels(manager: PanelManager, deps: ResolvedB
73
66
  return new CostTrackerPanel(ui.events.turns, ui.events.agents, getOrchestratorUsage!, {
74
67
  budgetThreshold,
75
68
  getAgentStatus: (id) => ui.agents.agentManager.getStatus(id),
69
+ configAccess: {
70
+ get: (key) => deps.configManager.get(key as Parameters<typeof deps.configManager.get>[0]),
71
+ set: (key, value) => deps.configManager.set(key as Parameters<typeof deps.configManager.set>[0], value as never),
72
+ },
76
73
  });
77
74
  },
78
75
  ),
79
76
  });
80
77
  }
81
-
82
- manager.registerType({
83
- id: 'intelligence',
84
- name: 'Intelligence',
85
- icon: 'J',
86
- category: 'development',
87
- description: 'Workspace diagnostics, symbol search, hover, and completion readiness with recovery guidance',
88
- factory: () => new IntelligencePanel(requireUiServices(deps).readModels.intelligence),
89
- });
90
-
91
- manager.registerType({
92
- id: 'explorer',
93
- name: 'Explorer',
94
- icon: 'E',
95
- category: 'development',
96
- description: 'File system browser with keyboard navigation',
97
- factory: () => {
98
- const ui = requireUiServices(deps);
99
- return new FileExplorerPanel(ui.environment.workingDirectory, ui.environment.workingDirectory);
100
- },
101
- });
102
-
103
- manager.registerType({
104
- id: 'preview',
105
- name: 'Preview',
106
- // WO-152: was 'V' in the registry vs the live panel's own 'P' (base-panel
107
- // super() call) — a pre-existing registry/instance icon mismatch as well
108
- // as a collision (project-planning and plugins both also used 'P').
109
- // Unified to a single unique glyph in both places.
110
- icon: '◑',
111
- category: 'development',
112
- description: 'Syntax-highlighted file preview',
113
- factory: () => new FilePreviewPanel(),
114
- });
115
-
116
- manager.registerType({
117
- id: 'symbols',
118
- name: 'Symbols',
119
- icon: 'S',
120
- category: 'development',
121
- description: 'Symbol outline for the active file: functions, classes, and exports',
122
- factory: () => new SymbolOutlinePanel(),
123
- });
124
78
  }
@@ -1,36 +1,12 @@
1
1
  import type { PanelManager } from '../panel-manager.ts';
2
- import { MemoryPanel } from '../memory-panel.ts';
3
- import { KnowledgeGraphPanel } from '../knowledge-graph-panel.ts';
4
- import { requireKnowledgeApi, withUnconfiguredFallback, type ResolvedBuiltinPanelDeps } from './shared.ts';
2
+ import type { ResolvedBuiltinPanelDeps } from './shared.ts';
5
3
 
4
+ // W6.1 (the purge) — group B: 'knowledge' and 'memory' migrated to the
5
+ // 'knowledge-modal' / 'memory-modal' config-modal surfaces. Both surfaces and
6
+ // their panel→modal redirects are registered centrally in registerBuiltinModals
7
+ // (src/panels/builtin-modals.ts). This registrar is intentionally empty
8
+ // post-migration, kept as a stable call site invoked from registerBuiltinPanels.
6
9
  export function registerKnowledgePanels(manager: PanelManager, deps: ResolvedBuiltinPanelDeps): void {
7
- manager.registerType({
8
- id: 'knowledge',
9
- name: 'Knowledge',
10
- icon: 'K',
11
- category: 'agent',
12
- description: 'Live SDK knowledge graph: nodes, sources, issue review queue, and search',
13
- factory: () => new KnowledgeGraphPanel(requireKnowledgeApi(deps), () => manager.open('memory')),
14
- });
15
- // WO-152: always registered (was gated behind `if (deps.memoryRegistry)`,
16
- // so `/panel open memory` reported "Unknown panel" on builds without a
17
- // memory registry wired). Falls back to a "dependency not configured"
18
- // empty state.
19
- {
20
- const { memoryRegistry } = deps;
21
- manager.registerType({
22
- id: 'memory',
23
- name: 'Memory',
24
- icon: 'M',
25
- category: 'agent',
26
- description: 'Project memory: decisions, constraints, incidents, and patterns with provenance links',
27
- factory: withUnconfiguredFallback(
28
- memoryRegistry !== undefined,
29
- 'memory', 'Memory', 'M', 'agent',
30
- ' Memory registry not configured for this session.',
31
- 'This runtime was not wired with a project memory registry at bootstrap, so no memory data is available.',
32
- () => new MemoryPanel(memoryRegistry!),
33
- ),
34
- });
35
- }
10
+ void manager;
11
+ void deps;
36
12
  }