@pellux/goodvibes-tui 1.0.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 (209) hide show
  1. package/CHANGELOG.md +130 -127
  2. package/README.md +32 -14
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +2 -2
  5. package/src/cli/ensure-goodvibes-gitignore.ts +32 -0
  6. package/src/cli/entrypoint.ts +2 -0
  7. package/src/core/alert-gating.ts +67 -0
  8. package/src/core/approval-alert.ts +72 -0
  9. package/src/core/budget-breach-notifier.ts +106 -0
  10. package/src/core/conversation-rendering.ts +19 -0
  11. package/src/core/conversation.ts +58 -0
  12. package/src/core/focus-tracker.ts +41 -0
  13. package/src/core/long-task-notifier.ts +33 -6
  14. package/src/core/stream-event-wiring.ts +104 -2
  15. package/src/core/stream-stall-watchdog.ts +41 -17
  16. package/src/core/system-message-router.ts +37 -51
  17. package/src/core/turn-cancellation.ts +20 -0
  18. package/src/core/turn-event-wiring.ts +64 -3
  19. package/src/export/cost-utils.ts +126 -8
  20. package/src/input/command-registry.ts +54 -1
  21. package/src/input/commands/checkpoint-runtime.ts +280 -0
  22. package/src/input/commands/codebase-runtime.ts +192 -0
  23. package/src/input/commands/diff-runtime.ts +61 -30
  24. package/src/input/commands/eval.ts +1 -1
  25. package/src/input/commands/health-runtime.ts +1 -1
  26. package/src/input/commands/image-runtime.ts +112 -0
  27. package/src/input/commands/intelligence-runtime.ts +11 -1
  28. package/src/input/commands/local-auth-runtime.ts +4 -1
  29. package/src/input/commands/local-runtime.ts +9 -3
  30. package/src/input/commands/marketplace-runtime.ts +2 -2
  31. package/src/input/commands/memory.ts +6 -1
  32. package/src/input/commands/operator-panel-runtime.ts +40 -24
  33. package/src/input/commands/planning-runtime.ts +26 -3
  34. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  35. package/src/input/commands/plugin-runtime.ts +2 -2
  36. package/src/input/commands/policy-dispatch.ts +21 -0
  37. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  38. package/src/input/commands/qrcode-runtime.ts +3 -3
  39. package/src/input/commands/recall-review.ts +35 -0
  40. package/src/input/commands/remote-runtime.ts +3 -3
  41. package/src/input/commands/runtime-services.ts +54 -13
  42. package/src/input/commands/services-runtime.ts +1 -1
  43. package/src/input/commands/session-content.ts +12 -0
  44. package/src/input/commands/session-workflow.ts +19 -1
  45. package/src/input/commands/settings-sync-runtime.ts +1 -1
  46. package/src/input/commands/share-runtime.ts +9 -2
  47. package/src/input/commands/shell-core.ts +15 -7
  48. package/src/input/commands/skills-runtime.ts +2 -8
  49. package/src/input/commands/subscription-runtime.ts +2 -2
  50. package/src/input/commands/test-runtime.ts +277 -0
  51. package/src/input/commands/websearch-runtime.ts +101 -0
  52. package/src/input/commands/work-plan-runtime.ts +36 -10
  53. package/src/input/commands/workstream-runtime.ts +338 -0
  54. package/src/input/commands.ts +12 -0
  55. package/src/input/config-modal-types.ts +161 -0
  56. package/src/input/config-modal.ts +377 -0
  57. package/src/input/feed-context-factory.ts +7 -8
  58. package/src/input/handler-command-route.ts +27 -17
  59. package/src/input/handler-content-actions.ts +22 -8
  60. package/src/input/handler-feed-routes.ts +107 -7
  61. package/src/input/handler-feed.ts +49 -13
  62. package/src/input/handler-interactions.ts +3 -3
  63. package/src/input/handler-modal-routes.ts +65 -0
  64. package/src/input/handler-modal-stack.ts +3 -5
  65. package/src/input/handler-modal-token-routes.ts +16 -49
  66. package/src/input/handler-picker-routes.ts +11 -94
  67. package/src/input/handler-shortcuts.ts +24 -14
  68. package/src/input/handler-types.ts +3 -6
  69. package/src/input/handler-ui-state.ts +10 -22
  70. package/src/input/handler.ts +10 -28
  71. package/src/input/keybindings.ts +22 -8
  72. package/src/input/model-picker-types.ts +24 -6
  73. package/src/input/model-picker.ts +58 -0
  74. package/src/input/panel-integration-actions.ts +9 -170
  75. package/src/input/settings-modal-data.ts +95 -0
  76. package/src/input/settings-modal-mutations.ts +6 -4
  77. package/src/main.ts +41 -44
  78. package/src/panels/agent-inspector-shared.ts +35 -11
  79. package/src/panels/base-panel.ts +22 -1
  80. package/src/panels/builtin/agent.ts +21 -107
  81. package/src/panels/builtin/development.ts +16 -62
  82. package/src/panels/builtin/knowledge.ts +8 -32
  83. package/src/panels/builtin/operations.ts +84 -428
  84. package/src/panels/builtin/session.ts +21 -112
  85. package/src/panels/builtin/shared.ts +9 -43
  86. package/src/panels/builtin-modals.ts +218 -0
  87. package/src/panels/builtin-panels.ts +5 -0
  88. package/src/panels/cost-tracker-panel.ts +63 -14
  89. package/src/panels/diff-panel.ts +123 -60
  90. package/src/panels/eval-registry.ts +60 -0
  91. package/src/panels/fleet-panel.ts +800 -0
  92. package/src/panels/fleet-read-model.ts +477 -0
  93. package/src/panels/fleet-steer.ts +92 -0
  94. package/src/panels/fleet-stop.ts +125 -0
  95. package/src/panels/fleet-tabs.ts +230 -0
  96. package/src/panels/fleet-transcript.ts +356 -0
  97. package/src/panels/git-panel.ts +9 -9
  98. package/src/panels/index.ts +7 -31
  99. package/src/panels/local-auth-panel.ts +10 -0
  100. package/src/panels/modals/hooks-modal.ts +187 -0
  101. package/src/panels/modals/keybindings-modal.ts +151 -0
  102. package/src/panels/modals/knowledge-modal.ts +158 -0
  103. package/src/panels/modals/local-auth-modal.ts +132 -0
  104. package/src/panels/modals/marketplace-modal.ts +218 -0
  105. package/src/panels/modals/memory-modal.ts +180 -0
  106. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  107. package/src/panels/modals/modal-theme.ts +36 -0
  108. package/src/panels/modals/pairing-modal.ts +144 -0
  109. package/src/panels/modals/planning-modal.ts +282 -0
  110. package/src/panels/modals/plugins-modal.ts +174 -0
  111. package/src/panels/modals/policy-modal.ts +256 -0
  112. package/src/panels/modals/provider-health-modal.ts +136 -0
  113. package/src/panels/modals/remote-modal.ts +115 -0
  114. package/src/panels/modals/sandbox-modal.ts +150 -0
  115. package/src/panels/modals/security-modal.ts +217 -0
  116. package/src/panels/modals/services-modal.ts +179 -0
  117. package/src/panels/modals/settings-sync-modal.ts +142 -0
  118. package/src/panels/modals/skills-modal.ts +189 -0
  119. package/src/panels/modals/subscription-modal.ts +172 -0
  120. package/src/panels/modals/work-plan-modal.ts +149 -0
  121. package/src/panels/panel-confirm-overlay.ts +72 -0
  122. package/src/panels/panel-manager.ts +108 -5
  123. package/src/panels/project-planning-answer-actions.ts +4 -2
  124. package/src/panels/scrollable-list-panel.ts +9 -0
  125. package/src/panels/skills-panel.ts +7 -51
  126. package/src/panels/token-budget-panel.ts +5 -3
  127. package/src/panels/types.ts +26 -0
  128. package/src/permissions/hunk-selection.ts +179 -0
  129. package/src/permissions/prompt.ts +60 -4
  130. package/src/renderer/compaction-history-modal.ts +19 -3
  131. package/src/renderer/compaction-preview.ts +13 -2
  132. package/src/renderer/compaction-quality.ts +100 -0
  133. package/src/renderer/config-modal.ts +81 -0
  134. package/src/renderer/conversation-overlays.ts +10 -17
  135. package/src/renderer/fleet-tab-strip.ts +56 -0
  136. package/src/renderer/footer-tips.ts +10 -2
  137. package/src/renderer/git-status.ts +40 -0
  138. package/src/renderer/help-overlay.ts +2 -2
  139. package/src/renderer/model-workspace.ts +44 -1
  140. package/src/renderer/panel-workspace-bar.ts +8 -2
  141. package/src/renderer/shell-surface.ts +8 -1
  142. package/src/renderer/turn-injection.ts +114 -0
  143. package/src/renderer/ui-factory.ts +91 -7
  144. package/src/runtime/bootstrap-command-context.ts +24 -1
  145. package/src/runtime/bootstrap-command-parts.ts +36 -7
  146. package/src/runtime/bootstrap-core.ts +19 -4
  147. package/src/runtime/bootstrap-hook-bridge.ts +5 -0
  148. package/src/runtime/bootstrap-shell.ts +41 -17
  149. package/src/runtime/bootstrap.ts +11 -17
  150. package/src/runtime/code-index-services.ts +112 -0
  151. package/src/runtime/orchestrator-core-services.ts +49 -0
  152. package/src/runtime/process-lifecycle.ts +3 -1
  153. package/src/runtime/services.ts +91 -43
  154. package/src/runtime/ui-services.ts +8 -0
  155. package/src/runtime/workstream-services.ts +195 -0
  156. package/src/shell/blocking-input.ts +22 -1
  157. package/src/shell/ui-openers.ts +129 -17
  158. package/src/utils/format-duration.ts +8 -18
  159. package/src/utils/splash-lines.ts +1 -1
  160. package/src/version.ts +1 -1
  161. package/src/panels/agent-inspector-panel.ts +0 -786
  162. package/src/panels/approval-panel.ts +0 -252
  163. package/src/panels/automation-control-panel.ts +0 -479
  164. package/src/panels/cockpit-panel.ts +0 -481
  165. package/src/panels/cockpit-read-model.ts +0 -233
  166. package/src/panels/communication-panel.ts +0 -256
  167. package/src/panels/control-plane-panel.ts +0 -470
  168. package/src/panels/debug-panel.ts +0 -609
  169. package/src/panels/docs-panel.ts +0 -375
  170. package/src/panels/eval-panel.ts +0 -627
  171. package/src/panels/file-explorer-panel.ts +0 -664
  172. package/src/panels/file-preview-panel.ts +0 -517
  173. package/src/panels/hooks-panel.ts +0 -401
  174. package/src/panels/incident-review-panel.ts +0 -406
  175. package/src/panels/intelligence-panel.ts +0 -383
  176. package/src/panels/knowledge-graph-panel.ts +0 -506
  177. package/src/panels/marketplace-panel.ts +0 -399
  178. package/src/panels/memory-panel.ts +0 -558
  179. package/src/panels/ops-control-panel.ts +0 -329
  180. package/src/panels/ops-strategy-panel.ts +0 -321
  181. package/src/panels/orchestration-panel.ts +0 -465
  182. package/src/panels/panel-list-panel.ts +0 -557
  183. package/src/panels/plan-dashboard-panel.ts +0 -707
  184. package/src/panels/policy-panel.ts +0 -517
  185. package/src/panels/project-planning-panel.ts +0 -721
  186. package/src/panels/provider-health-panel.ts +0 -678
  187. package/src/panels/provider-health-tracker.ts +0 -306
  188. package/src/panels/provider-health-views.ts +0 -560
  189. package/src/panels/qr-panel.ts +0 -280
  190. package/src/panels/remote-panel.ts +0 -534
  191. package/src/panels/routes-panel.ts +0 -241
  192. package/src/panels/sandbox-panel.ts +0 -456
  193. package/src/panels/security-panel.ts +0 -447
  194. package/src/panels/services-panel.ts +0 -329
  195. package/src/panels/session-browser-panel.ts +0 -487
  196. package/src/panels/settings-sync-panel.ts +0 -398
  197. package/src/panels/subscription-panel.ts +0 -342
  198. package/src/panels/symbol-outline-panel.ts +0 -619
  199. package/src/panels/system-messages-panel.ts +0 -364
  200. package/src/panels/tasks-panel.ts +0 -608
  201. package/src/panels/thinking-panel.ts +0 -333
  202. package/src/panels/tool-inspector-panel.ts +0 -537
  203. package/src/panels/work-plan-panel.ts +0 -530
  204. package/src/panels/worktree-panel.ts +0 -360
  205. package/src/panels/wrfc-panel.ts +0 -790
  206. package/src/renderer/agent-detail-modal.ts +0 -465
  207. package/src/renderer/live-tail-modal.ts +0 -156
  208. package/src/renderer/process-modal.ts +0 -656
  209. package/src/renderer/qr-renderer.ts +0 -120
@@ -1,5 +1,6 @@
1
1
  import { join } from 'node:path';
2
- import type { ConversationManager } from '../core/conversation';
2
+ import { readBudgetAlertUsd, BUDGET_ALERT_USD_DEFAULT } from '../export/cost-utils.ts';
3
+ import { sumConversationUsage, type ConversationManager } from '../core/conversation';
3
4
  import type { Orchestrator } from '../core/orchestrator';
4
5
  import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
5
6
  import type { RuntimeEventBus } from '@/runtime/index.ts';
@@ -15,7 +16,6 @@ import { GitStatusProvider } from '../renderer/git-status.ts';
15
16
  import type { GitHeaderInfo } from '../renderer/git-status.ts';
16
17
  import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
17
18
  import { registerBuiltinPanels } from '../panels/builtin-panels.ts';
18
- import { SystemMessagesPanel } from '../panels/system-messages-panel.ts';
19
19
  import { createSystemMessageRouter, type SystemMessageRouter } from '../core/system-message-router.ts';
20
20
  import { getConfigSnapshot } from '../config/index.ts';
21
21
  import { createBootstrapCommandContext } from './bootstrap-command-context.ts';
@@ -40,11 +40,6 @@ export interface BootstrapShellState {
40
40
  readonly lastGitInfoRef: { value: GitHeaderInfo | undefined };
41
41
  readonly inputHistory: InputHistory;
42
42
  readonly systemMessageRouter: SystemMessageRouter;
43
- /**
44
- * Wire the agent detail modal opener after InputHandler is constructed.
45
- * Call with `(id) => input.agentDetailModal.open(id)` from main.ts.
46
- */
47
- readonly setOpenAgentDetail: (fn: (agentId: string) => void) => void;
48
43
  }
49
44
 
50
45
  export interface BootstrapShellOptions {
@@ -92,7 +87,15 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
92
87
  completeModelSelectionSideEffect,
93
88
  } = options;
94
89
 
95
- const systemMessagesPanel = new SystemMessagesPanel(configManager, services.componentHealthMonitor);
90
+ // W0.9: after any resume seam replays historical messages into `conversation`,
91
+ // the freshly-constructed `orchestrator` still has its zeroed default usage
92
+ // (SDK gap — Orchestrator.usage is never persisted/reseeded). Recompute it
93
+ // from the replayed history so the footer doesn't show Input: 0 post-resume.
94
+ const hydrateSessionUsage = (): void => {
95
+ const { usage, lastInputTokens } = sumConversationUsage(conversation.getMessageSnapshot());
96
+ orchestrator.usage = usage;
97
+ orchestrator.lastInputTokens = lastInputTokens;
98
+ };
96
99
  const resumeSession = createResumeSessionHandler({
97
100
  runtimeBus,
98
101
  runtime,
@@ -107,6 +110,7 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
107
110
  configManager,
108
111
  providerRegistry: services.providerRegistry,
109
112
  homeDirectory: services.homeDirectory,
113
+ hydrateSessionUsage,
110
114
  });
111
115
 
112
116
  const foundationClients = createRuntimeFoundationClients({
@@ -128,16 +132,22 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
128
132
  runtimeBus,
129
133
  });
130
134
 
131
- const openAgentDetailRef: { fn: (agentId: string) => void } = { fn: (_agentId: string) => {} };
132
-
133
135
  // WO-139: initial cost-budget alert threshold (USD; 0/unset = disabled).
134
136
  // Once the session starts, the real control surface is the CostTrackerPanel
135
137
  // itself — the in-panel 'b' key and /cost budget <usd> both call
136
- // CostTrackerPanel.setBudgetThreshold() directly on the live panel instance.
138
+ // CostTrackerPanel.setBudgetThreshold() directly on the live panel instance,
139
+ // which (W2.3) now writes through to the behavior.budgetAlertUsd config key
140
+ // so the background budget-breach notifier reads the same value. The env
141
+ // var remains a first-run convenience only: it seeds the config key when
142
+ // that key has never been set, so it doesn't silently override a value the
143
+ // user has already configured in a prior session.
137
144
  const parsedBudgetThreshold = Number(process.env.GOODVIBES_COST_BUDGET_USD);
138
145
  const initialCostBudgetThreshold = Number.isFinite(parsedBudgetThreshold) && parsedBudgetThreshold > 0
139
146
  ? parsedBudgetThreshold
140
147
  : 0;
148
+ if (initialCostBudgetThreshold > 0 && readBudgetAlertUsd((k) => configManager.get(k as Parameters<typeof configManager.get>[0])) === BUDGET_ALERT_USD_DEFAULT) {
149
+ configManager.set('behavior.budgetAlertUsd' as Parameters<typeof configManager.set>[0], initialCostBudgetThreshold as never);
150
+ }
141
151
 
142
152
  let commandContextRef: CommandContext | null = null;
143
153
  registerBuiltinPanels(services.panelManager, {
@@ -172,7 +182,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
172
182
  componentHealthMonitor: services.componentHealthMonitor,
173
183
  worktreeRegistry: services.worktreeRegistry,
174
184
  sandboxSessionRegistry: services.sandboxSessionRegistry,
175
- systemMessagesPanel,
176
185
  memoryRegistry: services.memoryRegistry,
177
186
  uiServices,
178
187
  pluginManager: services.pluginManager,
@@ -180,7 +189,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
180
189
  hookActivityTracker: services.hookActivityTracker,
181
190
  hookWorkbench: services.hookWorkbench,
182
191
  mcpRegistry: services.mcpRegistry,
183
- openAgentDetail: (agentId: string) => openAgentDetailRef.fn(agentId),
184
192
  daemonHomeDir: join(services.homeDirectory, '.goodvibes', 'daemon'),
185
193
  opsApi,
186
194
  planRuntime,
@@ -193,7 +201,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
193
201
 
194
202
  const systemMessageRouter = createSystemMessageRouter(
195
203
  conversation,
196
- systemMessagesPanel,
197
204
  (kind) => {
198
205
  const ui = getConfigSnapshot(configManager).ui;
199
206
  if (kind === 'wrfc') return ui.wrfcMessages;
@@ -223,6 +230,9 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
223
230
  mcpRegistry: services.mcpRegistry,
224
231
  voiceProviderRegistry: services.voiceProviders,
225
232
  voiceService: services.voiceService,
233
+ webSearchService: services.webSearchService,
234
+ mediaProviders: services.mediaProviders,
235
+ artifactStore: services.artifactStore,
226
236
  forensicsRegistry,
227
237
  policyRuntimeState,
228
238
  readModels: uiServices.readModels,
@@ -230,6 +240,7 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
230
240
  remoteRuntime,
231
241
  planRuntime,
232
242
  fileUndoManager: services.fileUndoManager,
243
+ workspaceCheckpointManager: services.workspaceCheckpointManager,
233
244
  memoryRegistry: services.memoryRegistry,
234
245
  integrationHelpers: services.integrationHelpers,
235
246
  automationManager: services.automationManager,
@@ -258,6 +269,11 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
258
269
  sessionMemoryStore: services.sessionMemoryStore,
259
270
  sessionLineageTracker: services.sessionLineageTracker,
260
271
  wrfcController: services.wrfcController,
272
+ workstreamEngine: services.workstreamCommands,
273
+ codeIndexStore: services.codeIndexStore,
274
+ // Wave 5 (wo805): expose the MAIN session's per-turn passive-injection ring
275
+ // so `/recall injections` (no agent id) renders it — see recall-review.ts.
276
+ getMainSessionTurnInjections: () => orchestrator.getTurnInjections(),
261
277
  changeTracker: services.sessionChangeTracker,
262
278
  planManager: services.planManager,
263
279
  adaptivePlanner: services.adaptivePlanner,
@@ -282,6 +298,7 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
282
298
  },
283
299
  completeModelSelectionSideEffect,
284
300
  componentHealthMonitor: services.componentHealthMonitor,
301
+ hydrateSessionUsage,
285
302
  });
286
303
  commandContextRef = commandContext;
287
304
 
@@ -291,6 +308,16 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
291
308
  lastGitInfoRef.value = info;
292
309
  requestRender();
293
310
  }).catch(() => { /* non-fatal */ });
311
+ // W1.6 FIX 2: the header's git segment otherwise only refreshes on
312
+ // TURN_COMPLETED/TOOL_SUCCEEDED/TOOL_FAILED (see turn-event-wiring.ts's
313
+ // refreshGit()) — if the user runs `git init` externally and never submits
314
+ // another turn, the header stays stuck on the startup-time fallback
315
+ // indefinitely. Poll at the same 5s cadence GitPanel already uses for its
316
+ // own self-poll (git-panel.ts) so the two mechanisms are cadence-consistent.
317
+ gitStatusProvider.startPolling(5_000, (info) => {
318
+ lastGitInfoRef.value = info;
319
+ requestRender();
320
+ });
294
321
 
295
322
  const saveHistory = configManager.get('behavior.saveHistory') as boolean;
296
323
  const inputHistory = new InputHistory({
@@ -305,8 +332,5 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
305
332
  lastGitInfoRef,
306
333
  inputHistory,
307
334
  systemMessageRouter,
308
- setOpenAgentDetail: (fn: (agentId: string) => void) => {
309
- openAgentDetailRef.fn = fn;
310
- },
311
335
  };
312
336
  }
@@ -42,6 +42,7 @@ import type { UiRuntimeServices } from './ui-services.ts';
42
42
  import { createDeferredStartupCoordinator } from '@/runtime/index.ts';
43
43
  import { initializeBootstrapCore } from './bootstrap-core.ts';
44
44
  import { createBootstrapShell } from './bootstrap-shell.ts';
45
+ import { buildSharedOrchestratorCoreServices } from './orchestrator-core-services.ts';
45
46
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
46
47
  import { DaemonServer } from '@pellux/goodvibes-sdk/platform/daemon';
47
48
  import { HttpListener } from '@pellux/goodvibes-sdk/platform/daemon';
@@ -110,19 +111,15 @@ export type BootstrapContext = RuntimeContext & {
110
111
  /** Command registry used by InputHandler. main.ts needs this to wire input. */
111
112
  commandRegistry: import('../input/command-registry.ts').CommandRegistry;
112
113
  /**
113
- * System message router instantiated at startup, wired to conversation and panel manager.
114
+ * System message router instantiated at startup, wired to conversation.
114
115
  *
115
116
  * @remarks
116
117
  * Route operational messages through this rather than calling
117
- * conversation.addSystemMessage() directly so that low-priority messages
118
- * stay out of the main conversation and go to the SystemMessagesPanel instead.
118
+ * conversation.addSystemMessage() directly so routing-target config
119
+ * (panel/conversation/both) and the forced-inline critical prefixes stay
120
+ * centralized in one place.
119
121
  */
120
122
  systemMessageRouter: SystemMessageRouter;
121
- /**
122
- * Wire the agent detail modal opener after InputHandler is constructed in main.ts.
123
- * Call with `(id) => input.agentDetailModal.open(id)` once the InputHandler is ready.
124
- */
125
- setOpenAgentDetail: (fn: (agentId: string) => void) => void;
126
123
  };
127
124
 
128
125
  // ── Bootstrap function ────────────────────────────────────────────────────
@@ -235,15 +232,11 @@ export async function bootstrapRuntime(
235
232
  logger.debug('companion handleUserInput safety catch', { error: String(err) });
236
233
  });
237
234
  };
235
+ // Shared payload (single source of truth, includes wo805's memoryRegistry —
236
+ // see orchestrator-core-services.ts) plus this site's cacheHitTracker.
238
237
  orchestrator.setCoreServices({
239
- configManager,
240
- providerRegistry,
238
+ ...buildSharedOrchestratorCoreServices({ services, configManager, providerRegistry }),
241
239
  cacheHitTracker: services.cacheHitTracker,
242
- planManager: services.planManager,
243
- adaptivePlanner: services.adaptivePlanner,
244
- sessionMemoryStore: services.sessionMemoryStore,
245
- sessionLineageTracker: services.sessionLineageTracker,
246
- idempotencyStore: services.idempotencyStore,
247
240
  });
248
241
  conversation.setSessionLineageTracker(services.sessionLineageTracker);
249
242
 
@@ -297,7 +290,9 @@ export async function bootstrapRuntime(
297
290
  const gitStatusProvider = shell.gitStatusProvider;
298
291
  const inputHistory = shell.inputHistory;
299
292
  const lastGitInfoRef = shell.lastGitInfoRef;
300
- const setOpenAgentDetail = shell.setOpenAgentDetail;
293
+ // W1.6 FIX 2: dispose the header's live-repo-state poll (git-status.ts
294
+ // startPolling) on shutdown, same pattern as acpTaskSyncInterval above.
295
+ bootstrapUnsubs.push(() => gitStatusProvider.stopPolling());
301
296
  const pluginCommandRegistry = {
302
297
  register(command: {
303
298
  readonly name: string;
@@ -628,7 +623,6 @@ export async function bootstrapRuntime(
628
623
  _getConfiguredProviderIds: () => services.providerRegistry.getConfiguredProviderIds(),
629
624
  commandRegistry,
630
625
  systemMessageRouter,
631
- setOpenAgentDetail,
632
626
  shutdown: async (sessionData) => {
633
627
  // Clear bootstrap-owned subscriptions
634
628
  bootstrapUnsubs.forEach(fn => fn());
@@ -0,0 +1,112 @@
1
+ // ---------------------------------------------------------------------------
2
+ // code-index-services.ts — Wave 5 (wo804, W5.3 Stage A TUI wiring)
3
+ //
4
+ // Constructs the TUI's repo source-tree code index: CodeIndexStore
5
+ // (@pellux/goodvibes-sdk/platform/state, landed on SDK main as wo802/W5.3
6
+ // Stage A). Extracted into its own module rather than built inline in
7
+ // services.ts: services.ts sits at the architecture check's 800-line cap
8
+ // (scripts/check-architecture.ts), so any new service gets its own
9
+ // construction module and a single wiring call there (mirrors
10
+ // createWorkstreamServices's one-function-bundle shape).
11
+ //
12
+ // Schema-initialized eagerly (mirrors memoryStore: construction + init()
13
+ // happen unconditionally so the store is queryable — /codebase status, the
14
+ // fleet node — even before any build has ever run). The actual walk/chunk/
15
+ // embed build is NEVER auto-triggered by default: the hundreds of existing
16
+ // test fixtures (and every headless invocation) construct RuntimeServices
17
+ // without asking for a full source-tree walk, and init() alone never runs
18
+ // one (mirrors the SDK's own RuntimeServices wiring in
19
+ // platform/runtime/services.ts, which gates its equivalent scheduleBuild()
20
+ // call behind an explicit autoStartCodeIndex option).
21
+ //
22
+ // REALITY-WINS DIVERGENCE from the SDK's own shape: the SDK's
23
+ // RuntimeServicesOptions exposes autoStartCodeIndex as a constructor-time
24
+ // boolean because that call site is a library entrypoint threaded by each
25
+ // embedder. This TUI's createRuntimeServices has no such per-call knob —
26
+ // auto-start is decided from a TUI-local config key instead
27
+ // (CODE_INDEX_ENABLED_CONFIG_KEY, default OFF), so every construction path
28
+ // (interactive main.ts, the daemon, and every test fixture) shares one
29
+ // honest, user-visible on/off switch rather than needing to thread a new
30
+ // boolean through every call site. With it off (the default), `/codebase
31
+ // build` is the explicit, visible trigger — exactly the shape wo804's brief
32
+ // asks for.
33
+ //
34
+ // Shares memoryEmbeddingRegistry with MemoryStore (constructed in
35
+ // services.ts) so code + memory retrieval use one embedding provider and one
36
+ // dimensionality, per the SDK design doc (code-index-store.ts's constructor
37
+ // takes the SAME MemoryEmbeddingProviderRegistry instance memory uses).
38
+ // ---------------------------------------------------------------------------
39
+
40
+ import { join } from 'node:path';
41
+ import { CodeIndexStore } from '@pellux/goodvibes-sdk/platform/state';
42
+ import type { MemoryEmbeddingProviderRegistry } from '@pellux/goodvibes-sdk/platform/state';
43
+ import type { ConfigKey, ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
44
+ import { readBooleanConfig } from '../core/alert-gating.ts';
45
+
46
+ /**
47
+ * TUI-local synthetic config key (not yet in the SDK's ConfigKey union) that
48
+ * gates the code index's auto-build on construction. Default OFF — see this
49
+ * module's header doc. Surfaced in /config via a synthetic entry
50
+ * (settings-modal-data.ts) exactly like behavior.notifyAfterSeconds.
51
+ */
52
+ export const CODE_INDEX_ENABLED_CONFIG_KEY = 'storage.codeIndexEnabled';
53
+
54
+ /**
55
+ * Honest, generous bounds passed EXPLICITLY to CodeIndexStore (values equal
56
+ * to its own internal defaults) so /codebase status and the settings
57
+ * description can state them in one place without reaching into store
58
+ * internals or duplicating magic numbers.
59
+ */
60
+ export const CODE_INDEX_MAX_FILES = 5000;
61
+ export const CODE_INDEX_MAX_FILE_BYTES = 512 * 1024;
62
+ export const CODE_INDEX_MAX_TOTAL_BYTES = 256 * 1024 * 1024;
63
+
64
+ export interface CodeIndexServicesDeps {
65
+ readonly workingDirectory: string;
66
+ readonly configManager: Pick<ConfigManager, 'get'>;
67
+ readonly memoryEmbeddingRegistry: MemoryEmbeddingProviderRegistry;
68
+ }
69
+
70
+ export interface CodeIndexServices {
71
+ readonly codeIndexStore: CodeIndexStore;
72
+ }
73
+
74
+ /** Absolute path to the TUI's code-index sqlite file, sibling to memory.sqlite under .goodvibes/tui/. */
75
+ export function codeIndexDbPath(workingDirectory: string): string {
76
+ return join(workingDirectory, '.goodvibes', 'tui', 'code-index.sqlite');
77
+ }
78
+
79
+ /**
80
+ * Whether the code index's initial build should auto-start on construction.
81
+ * Exported so /codebase status and the settings modal read the exact same
82
+ * config key + default this module decides auto-start from.
83
+ */
84
+ export function isCodeIndexAutoStartEnabled(configManager: Pick<ConfigManager, 'get'>): boolean {
85
+ return readBooleanConfig(
86
+ (key) => configManager.get(key as ConfigKey),
87
+ CODE_INDEX_ENABLED_CONFIG_KEY,
88
+ false,
89
+ );
90
+ }
91
+
92
+ /**
93
+ * Constructs the TUI's CodeIndexStore. Schema-init runs unconditionally
94
+ * (init() never throws — it degrades to an honest `available: false` +
95
+ * recorded error on failure, mirrored by CodeIndexStats/describeDegradation);
96
+ * the initial full build only fires when isCodeIndexAutoStartEnabled() is
97
+ * true, via the SAME fire-and-forget scheduleBuild() an explicit
98
+ * `/codebase build` invocation uses.
99
+ */
100
+ export function createCodeIndexServices(deps: CodeIndexServicesDeps): CodeIndexServices {
101
+ const codeIndexStore = new CodeIndexStore(
102
+ deps.workingDirectory,
103
+ codeIndexDbPath(deps.workingDirectory),
104
+ deps.memoryEmbeddingRegistry,
105
+ { maxFiles: CODE_INDEX_MAX_FILES, maxFileBytes: CODE_INDEX_MAX_FILE_BYTES, maxTotalBytes: CODE_INDEX_MAX_TOTAL_BYTES },
106
+ );
107
+ codeIndexStore.init();
108
+ if (isCodeIndexAutoStartEnabled(deps.configManager)) {
109
+ codeIndexStore.scheduleBuild();
110
+ }
111
+ return { codeIndexStore };
112
+ }
@@ -0,0 +1,49 @@
1
+ import type { OrchestratorCoreServices } from '@pellux/goodvibes-sdk/platform/core';
2
+ import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
3
+ import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
4
+ import type { RuntimeServices } from './services.ts';
5
+
6
+ /** The slice of the runtime services bag the shared orchestrator payload draws from. */
7
+ export type OrchestratorCoreServicesSource = Pick<
8
+ RuntimeServices,
9
+ | 'planManager'
10
+ | 'adaptivePlanner'
11
+ | 'sessionMemoryStore'
12
+ | 'sessionLineageTracker'
13
+ | 'idempotencyStore'
14
+ | 'memoryRegistry'
15
+ >;
16
+
17
+ /**
18
+ * The single source of truth for the `Orchestrator.setCoreServices()` payload
19
+ * fields shared by BOTH call sites (runtime/bootstrap.ts and main.ts). Each
20
+ * site spreads this and adds its own site-specific extras (bootstrap:
21
+ * cacheHitTracker; main: favoritesStore) — setCoreServices() merges, so the
22
+ * later main.ts call only overlays, never erases.
23
+ *
24
+ * Wave-5 (wo805) BLOCKER regression guard: `memoryRegistry` here is what turns
25
+ * on per-turn passive knowledge injection for the MAIN interactive session —
26
+ * the SDK turn loop hard-gates on `coreServices.memoryRegistry` (undefined is
27
+ * a silent no-op: Orchestrator.getTurnInjections() stays empty forever and
28
+ * `/recall injections` renders a misleading empty state). Both call sites once
29
+ * omitted it independently; routing them through this one function (with
30
+ * src/test/runtime/orchestrator-core-services.test.ts pinning the field) is
31
+ * what keeps that from regressing.
32
+ */
33
+ export function buildSharedOrchestratorCoreServices(input: {
34
+ readonly services: OrchestratorCoreServicesSource;
35
+ readonly configManager: ConfigManager;
36
+ readonly providerRegistry: ProviderRegistry;
37
+ }): OrchestratorCoreServices {
38
+ const { services, configManager, providerRegistry } = input;
39
+ return {
40
+ configManager,
41
+ providerRegistry,
42
+ planManager: services.planManager,
43
+ adaptivePlanner: services.adaptivePlanner,
44
+ sessionMemoryStore: services.sessionMemoryStore,
45
+ sessionLineageTracker: services.sessionLineageTracker,
46
+ idempotencyStore: services.idempotencyStore,
47
+ memoryRegistry: services.memoryRegistry,
48
+ };
49
+ }
@@ -34,6 +34,8 @@ export interface ProcessLifecycleAnsi {
34
34
  readonly KEYBOARD_EXT_DISABLE: string;
35
35
  readonly MOUSE_DISABLE: string;
36
36
  readonly CURSOR_SHOW: string;
37
+ /** Disables terminal focus-event reporting (DECSET ?1004l) — see main.ts FOCUS_ENABLE. */
38
+ readonly FOCUS_DISABLE: string;
37
39
  }
38
40
 
39
41
  export interface ProcessLifecycleDeps {
@@ -128,7 +130,7 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
128
130
  if (terminalRestored) return;
129
131
  terminalRestored = true;
130
132
  const exitScreen = noAltScreen ? ansi.CLEAR_SCREEN : ansi.CLEAR_SCREEN + ansi.ALT_SCREEN_EXIT;
131
- allowTerminalWrite(() => stdout.write(ansi.PASTE_DISABLE + ansi.KEYBOARD_EXT_DISABLE + ansi.MOUSE_DISABLE + ansi.CURSOR_SHOW + exitScreen));
133
+ allowTerminalWrite(() => stdout.write(ansi.PASTE_DISABLE + ansi.KEYBOARD_EXT_DISABLE + ansi.MOUSE_DISABLE + ansi.FOCUS_DISABLE + ansi.CURSOR_SHOW + exitScreen));
132
134
  getTerminalOutputGuard().dispose();
133
135
  try { stdin.setRawMode(false); } catch { /* stdin may not be a TTY */ }
134
136
  };
@@ -1,11 +1,9 @@
1
1
  import { join } from 'node:path';
2
- import { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
2
+ import { FocusTracker } from '../core/focus-tracker.ts';
3
+ import { ConfigManager, ServiceRegistry, SubscriptionManager, ToolLLM } from '@pellux/goodvibes-sdk/platform/config';
3
4
  import { SecretsManager } from '../config/secrets.ts';
4
- import { ServiceRegistry } from '@pellux/goodvibes-sdk/platform/config';
5
- import { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
6
5
  import { AutomationDeliveryManager, AutomationManager, AutomationRouteStore } from '@pellux/goodvibes-sdk/platform/automation';
7
- import { ChannelPluginRegistry, ChannelPolicyManager, RouteBindingManager, SurfaceRegistry } from '@pellux/goodvibes-sdk/platform/channels';
8
- import { ChannelDeliveryRouter } from '@pellux/goodvibes-sdk/platform/channels';
6
+ import { ChannelDeliveryRouter, ChannelPluginRegistry, ChannelPolicyManager, RouteBindingManager, SurfaceRegistry } from '@pellux/goodvibes-sdk/platform/channels';
9
7
  import { ApprovalBroker, GatewayMethodCatalog, SharedSessionBroker } from '@pellux/goodvibes-sdk/platform/control-plane';
10
8
  import { WatcherRegistry } from '@pellux/goodvibes-sdk/platform/watchers';
11
9
  import { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
@@ -23,16 +21,10 @@ import {
23
21
  } from '@pellux/goodvibes-sdk/platform/knowledge';
24
22
  import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
25
23
  import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
26
- import { AgentManager } from '@pellux/goodvibes-sdk/platform/tools';
27
- import { AgentMessageBus } from '@pellux/goodvibes-sdk/platform/agents';
28
- import { WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
29
- import { AgentOrchestrator } from '@pellux/goodvibes-sdk/platform/agents';
30
- import { ArchetypeLoader } from '@pellux/goodvibes-sdk/platform/agents';
31
- import { ProcessManager } from '@pellux/goodvibes-sdk/platform/tools';
32
- import { ModeManager } from '@pellux/goodvibes-sdk/platform/state';
33
- import { FileUndoManager } from '@pellux/goodvibes-sdk/platform/state';
34
- import { MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
35
- import { MemoryStore } from '@pellux/goodvibes-sdk/platform/state';
24
+ import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
25
+ import { AgentManager, OverflowHandler, ProcessManager, createWorkflowServices, type WorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
26
+ import { FileStateCache, FileUndoManager, MemoryEmbeddingProviderRegistry, MemoryRegistry, MemoryStore, ModeManager, ProjectIndex, type CodeIndexStore } from '@pellux/goodvibes-sdk/platform/state';
27
+ import { WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
36
28
  import type { RuntimeEventBus } from '@/runtime/index.ts';
37
29
  import { createDomainDispatch } from './store/index.ts';
38
30
  import type { DomainDispatch, RuntimeStore } from './store/index.ts';
@@ -41,40 +33,20 @@ import { RemoteRunnerRegistry, RemoteSupervisor } from '@/runtime/index.ts';
41
33
  import { IntegrationHelperService } from '@/runtime/index.ts';
42
34
  import { VoiceProviderRegistry, VoiceService, ensureBuiltinVoiceProviders } from '@pellux/goodvibes-sdk/platform/voice';
43
35
  import { WebSearchProviderRegistry, WebSearchService } from '@pellux/goodvibes-sdk/platform/web-search';
44
- import { MemoryEmbeddingProviderRegistry } from '@pellux/goodvibes-sdk/platform/state';
45
36
  import { PanelManager } from '../panels/panel-manager.ts';
46
37
  import { HookActivityTracker } from '@pellux/goodvibes-sdk/platform/hooks';
47
38
  import { HookDispatcher, createHookWorkbench, type HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks';
48
39
  import { PluginManager } from '@pellux/goodvibes-sdk/platform/plugins';
49
40
  import { BookmarkManager } from '@pellux/goodvibes-sdk/platform/bookmarks';
50
41
  import { ProfileManager } from '@pellux/goodvibes-sdk/platform/profiles';
51
- import { SessionManager } from '@pellux/goodvibes-sdk/platform/sessions';
52
- import { CrossSessionTaskRegistry } from '@pellux/goodvibes-sdk/platform/sessions';
53
- import { ApiTokenAuditor } from '@pellux/goodvibes-sdk/platform/security';
54
- import { UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
42
+ import { SessionManager, CrossSessionTaskRegistry, SessionChangeTracker } from '@pellux/goodvibes-sdk/platform/sessions';
43
+ import { ApiTokenAuditor, UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
55
44
  import { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
56
45
  import { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
57
- import { DeterministicReplayEngine } from '@pellux/goodvibes-sdk/platform/core';
58
- import { ProviderOptimizer } from '@pellux/goodvibes-sdk/platform/providers';
59
- import { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
60
- import type { ModelDefinition } from '@pellux/goodvibes-sdk/platform/providers';
61
- import { ProviderCapabilityRegistry } from '@pellux/goodvibes-sdk/platform/providers';
62
- import { CacheHitTracker } from '@pellux/goodvibes-sdk/platform/providers';
63
- import { FavoritesStore } from '@pellux/goodvibes-sdk/platform/providers';
64
- import { BenchmarkStore } from '@pellux/goodvibes-sdk/platform/providers';
65
- import { ModelLimitsService } from '@pellux/goodvibes-sdk/platform/providers';
66
- import { inferFallbackContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
46
+ import { BenchmarkStore, CacheHitTracker, FavoritesStore, inferFallbackContextWindow, type ModelDefinition, ModelLimitsService, ProviderCapabilityRegistry, ProviderOptimizer, ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
67
47
  import { KeybindingsManager } from '../input/keybindings.ts';
68
- import { SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
69
- import { SessionLineageTracker } from '@pellux/goodvibes-sdk/platform/core';
70
- import { SessionChangeTracker } from '@pellux/goodvibes-sdk/platform/sessions';
71
- import { ExecutionPlanManager } from '@pellux/goodvibes-sdk/platform/core';
72
- import { AdaptivePlanner } from '@pellux/goodvibes-sdk/platform/core';
73
- import { FileStateCache } from '@pellux/goodvibes-sdk/platform/state';
74
- import { ProjectIndex } from '@pellux/goodvibes-sdk/platform/state';
48
+ import { AdaptivePlanner, DeterministicReplayEngine, ExecutionPlanManager, SessionLineageTracker, SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
75
49
  import { IdempotencyStore } from '@/runtime/index.ts';
76
- import { OverflowHandler } from '@pellux/goodvibes-sdk/platform/tools';
77
- import { ToolLLM } from '@pellux/goodvibes-sdk/platform/config';
78
50
  import { ComponentHealthMonitor } from '@/runtime/index.ts';
79
51
  import { WorktreeRegistry } from '@/runtime/index.ts';
80
52
  import { SandboxSessionRegistry } from '@/runtime/index.ts';
@@ -83,10 +55,10 @@ import { isFeatureFlagEnabled } from './surface-feature-flags.ts';
83
55
  import type { FeatureFlagManager } from '@/runtime/index.ts';
84
56
  import { createFeatureFlagManager } from '@/runtime/index.ts';
85
57
  import { PolicyRuntimeState } from '@/runtime/index.ts';
86
- import {
87
- createWorkflowServices,
88
- type WorkflowServices,
89
- } from '@pellux/goodvibes-sdk/platform/tools';
58
+ import { createProcessRegistry, type ProcessRegistry } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
59
+ import { calcSessionCost, isModelPriced } from '../export/cost-utils.ts';
60
+ import { createWorkstreamServices, type OrchestrationEngine, type WorkstreamCommandService } from './workstream-services.ts';
61
+ import { codeIndexDbPath, createCodeIndexServices } from './code-index-services.ts';
90
62
  import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
91
63
  import {
92
64
  registerDaemonHandlers,
@@ -221,6 +193,8 @@ export interface RuntimeServices {
221
193
  readonly worktreeRegistry: WorktreeRegistry;
222
194
  readonly sandboxSessionRegistry: SandboxSessionRegistry;
223
195
  readonly webhookNotifier: WebhookNotifier;
196
+ /** Terminal focus tracker (W2.3) — fed by input/handler-feed.ts, read by the alert notifiers in core/. */
197
+ readonly focusTracker: FocusTracker;
224
198
  readonly replayEngine: DeterministicReplayEngine;
225
199
  readonly providerOptimizer: ProviderOptimizer;
226
200
  readonly providerCapabilityRegistry: ProviderCapabilityRegistry;
@@ -248,8 +222,16 @@ export interface RuntimeServices {
248
222
  readonly agentOrchestrator: AgentOrchestrator;
249
223
  readonly wrfcController: WrfcController;
250
224
  readonly processManager: ProcessManager;
225
+ /** Wave 4 (wo703): the phase/work-item orchestration engine — see runtime/workstream-services.ts. */
226
+ readonly orchestrationEngine: OrchestrationEngine;
227
+ readonly workstreamCommands: WorkstreamCommandService;
228
+ /** Wave 5 (wo804): the repo source-tree code index — see runtime/code-index-services.ts. */
229
+ readonly codeIndexStore: CodeIndexStore;
230
+ /** W2.1/W2.2: unified live process registry (agents, WRFC chains, workflows, watchers, background processes) backing the Fleet panel. */
231
+ readonly processRegistry: ProcessRegistry;
251
232
  readonly modeManager: ModeManager;
252
233
  readonly fileUndoManager: FileUndoManager;
234
+ readonly workspaceCheckpointManager: WorkspaceCheckpointManager;
253
235
  readonly integrationHelpers: IntegrationHelperService;
254
236
  /**
255
237
  * Re-root path-bound stores (MemoryStore, ProjectIndex) to a new working directory.
@@ -272,6 +254,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
272
254
  const runtimeDispatch = createDomainDispatch(options.runtimeStore);
273
255
  const gatewayMethods = new GatewayMethodCatalog();
274
256
  const panelManager = new PanelManager();
257
+ // W6.1 (the purge): MIGRATE-TO-MODAL surface + redirect registration moved to
258
+ // registerBuiltinPanels (builtin-panels.ts), where the panels' resolved deps
259
+ // are available for the surfaces to close over.
275
260
  const keybindingsManager = new KeybindingsManager({
276
261
  configPath: shellPaths.resolveUserPath('tui', 'keybindings.json'),
277
262
  });
@@ -352,6 +337,10 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
352
337
  executor: agentOrchestrator,
353
338
  configManager,
354
339
  });
340
+ agentOrchestrator.setConversationSink({ // Wave-3 Part C6 bridge (mirrors the SDK's own createRuntimeServices)
341
+ register: (agentId, source) => agentManager.registerConversationSource(agentId, source),
342
+ release: (agentId) => agentManager.releaseConversationSource(agentId),
343
+ });
355
344
  agentManager.setRuntimeBus(options.runtimeBus);
356
345
  const wrfcController = new WrfcController(options.runtimeBus, agentMessageBus, {
357
346
  agentManager,
@@ -580,6 +569,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
580
569
  const componentHealthMonitor = new ComponentHealthMonitor();
581
570
  const worktreeRegistry = new WorktreeRegistry(workingDirectory);
582
571
  const webhookNotifier = new WebhookNotifier();
572
+ const focusTracker = new FocusTracker();
583
573
  const replayEngine = new DeterministicReplayEngine(workingDirectory);
584
574
  const providerOptimizer = new ProviderOptimizer(
585
575
  providerRegistry,
@@ -603,8 +593,57 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
603
593
  artifactStore,
604
594
  });
605
595
  const processManager = new ProcessManager();
596
+ // Wave 4 (wo703): the phase/work-item orchestration engine, constructed
597
+ // before the process registry so its fleet nodes (workstream/phase/
598
+ // work-item) can be folded in below via the registry's optional
599
+ // orchestrationEngine dep.
600
+ const { orchestrationEngine, workstreamCommands } = createWorkstreamServices({
601
+ agentManager, configManager, adaptivePlanner, runtimeBus: options.runtimeBus, projectRoot: workingDirectory,
602
+ });
603
+ // Wave 5 (wo804): repo source-tree code index, sharing memoryEmbeddingRegistry
604
+ // with MemoryStore above. Auto-build is config-gated (default off) — see
605
+ // code-index-services.ts's header doc.
606
+ const { codeIndexStore } = createCodeIndexServices({ workingDirectory, configManager, memoryEmbeddingRegistry });
607
+ // W2.1/W2.2: one shared process registry aggregating the managers above —
608
+ // the Fleet panel (panels/fleet-read-model.ts) is its first consumer.
609
+ // Constructed once here (not per-consumer) so the coalesced tick and the
610
+ // agent-activity side-table are shared, not duplicated.
611
+ const processRegistry = createProcessRegistry({
612
+ agentManager,
613
+ wrfcController,
614
+ orchestrationEngine, // Wave 4 (wo703): folds workstream/phase/work-item nodes into the fleet
615
+ codeIndexService: codeIndexStore, // Wave 5 (wo804): folds a single 'code-index' node into the fleet
616
+ processManager,
617
+ watcherRegistry,
618
+ workflow,
619
+ approvalBroker,
620
+ sessionBroker,
621
+ messageBus: agentMessageBus, // Wave-3: backs steer()/`steerable` (wo612 builds the composer UI on top)
622
+ automationManager, // Wave 6 (wo-F item d4): folds /schedule AutomationJobs into the fleet as 'schedule' nodes
623
+ runtimeBus: options.runtimeBus,
624
+ // Honest pricing: never fabricate a cost for an unrecognized model.
625
+ priceUsage: (model, usage) => {
626
+ const modelId = model ?? 'unknown';
627
+ if (!isModelPriced(modelId)) return null;
628
+ return calcSessionCost(usage.inputTokens, usage.outputTokens, usage.cacheReadTokens, usage.cacheWriteTokens, modelId);
629
+ },
630
+ });
606
631
  const modeManager = new ModeManager();
607
632
  const fileUndoManager = new FileUndoManager();
633
+ const workspaceCheckpointManager = new WorkspaceCheckpointManager({
634
+ workspaceRoot: workingDirectory,
635
+ runtimeBus: options.runtimeBus,
636
+ });
637
+ // Fire-and-forget: subscriptions go live immediately if init() succeeds.
638
+ // If it rejects, WorkspaceCheckpointManager caches that rejection on
639
+ // `initPromise` and never clears it, so every later call (create/list/
640
+ // diff/restore — each awaits init() first) re-throws the same error
641
+ // forever: checkpointing becomes entirely unavailable for the rest of the
642
+ // session, not "degraded to manual-only". The `.catch(() => {})` here only
643
+ // exists to prevent an unhandled rejection at startup; the checkpoint
644
+ // commands (checkpoint-runtime.ts) are what actually catch and report the
645
+ // failure to the user, on first use.
646
+ void workspaceCheckpointManager.init().catch(() => {});
608
647
  const integrationHelpers = new IntegrationHelperService({
609
648
  workingDirectory,
610
649
  homeDirectory,
@@ -709,6 +748,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
709
748
  worktreeRegistry,
710
749
  sandboxSessionRegistry,
711
750
  webhookNotifier,
751
+ focusTracker,
712
752
  replayEngine,
713
753
  providerOptimizer,
714
754
  providerCapabilityRegistry,
@@ -736,12 +776,20 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
736
776
  agentOrchestrator,
737
777
  wrfcController,
738
778
  processManager,
779
+ orchestrationEngine,
780
+ workstreamCommands,
781
+ codeIndexStore,
782
+ processRegistry,
739
783
  modeManager,
740
784
  fileUndoManager,
785
+ workspaceCheckpointManager,
741
786
  integrationHelpers,
742
787
  async rerootStores(newWorkingDir: string): Promise<void> {
743
788
  const newMemoryDbPath = join(newWorkingDir, '.goodvibes', 'tui', 'memory.sqlite');
744
789
  await memoryStore.reroot(newMemoryDbPath);
790
+ // Wave 5 (wo804) risk #7: the code index must follow memory to the new
791
+ // tree, or it keeps pointing at the old working directory.
792
+ await codeIndexStore.reroot(newWorkingDir, codeIndexDbPath(newWorkingDir));
745
793
  await projectIndex.reroot(newWorkingDir);
746
794
  },
747
795
  };