@pellux/goodvibes-tui 1.9.0 → 1.9.2

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 (75) hide show
  1. package/CHANGELOG.md +24 -2
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/src/audio/player.ts +91 -5
  5. package/src/audio/spoken-turn-controller.ts +30 -2
  6. package/src/audio/spoken-turn-wiring.ts +3 -0
  7. package/src/config/credential-availability.ts +122 -0
  8. package/src/config/index.ts +15 -0
  9. package/src/core/turn-cancellation.ts +1 -1
  10. package/src/daemon/cli.ts +4 -4
  11. package/src/daemon/service-commands.ts +8 -8
  12. package/src/input/command-registry.ts +5 -5
  13. package/src/input/commands/config.ts +1 -1
  14. package/src/input/commands/memory.ts +1 -1
  15. package/src/input/commands/planning-runtime.ts +1 -1
  16. package/src/input/commands/recall-review.ts +2 -2
  17. package/src/input/commands/session-content.ts +1 -1
  18. package/src/input/feed-context-factory.ts +1 -1
  19. package/src/input/handler-content-actions.ts +2 -2
  20. package/src/input/handler-feed-routes.ts +2 -2
  21. package/src/input/handler-feed.ts +1 -1
  22. package/src/input/handler-onboarding-daemon-adopt.ts +3 -3
  23. package/src/input/handler-picker-routes.ts +2 -2
  24. package/src/input/handler-shortcuts.ts +1 -1
  25. package/src/input/model-picker.ts +2 -2
  26. package/src/input/onboarding/onboarding-wizard-apply.ts +3 -3
  27. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +3 -3
  28. package/src/input/onboarding/onboarding-wizard-steps.ts +1 -1
  29. package/src/input/session-picker-modal.ts +1 -1
  30. package/src/input/settings-modal-data.ts +2 -2
  31. package/src/main.ts +5 -4
  32. package/src/panels/base-panel.ts +1 -1
  33. package/src/panels/builtin/operations.ts +4 -4
  34. package/src/panels/builtin/shared.ts +1 -1
  35. package/src/panels/fleet-panel-format.ts +1 -1
  36. package/src/panels/fleet-panel.ts +15 -15
  37. package/src/panels/fleet-read-model.ts +11 -11
  38. package/src/panels/fleet-steer.ts +2 -2
  39. package/src/panels/fleet-stop.ts +2 -2
  40. package/src/panels/fleet-tabs.ts +4 -4
  41. package/src/panels/fleet-transcript.ts +5 -5
  42. package/src/panels/types.ts +1 -1
  43. package/src/renderer/compaction-quality.ts +1 -1
  44. package/src/renderer/fleet-tab-strip.ts +2 -2
  45. package/src/renderer/footer-tips.ts +1 -1
  46. package/src/renderer/model-workspace.ts +1 -1
  47. package/src/renderer/session-picker-modal.ts +4 -4
  48. package/src/renderer/shell-surface.ts +1 -1
  49. package/src/renderer/status-glyphs.ts +3 -3
  50. package/src/renderer/terminal-bg-probe.ts +1 -1
  51. package/src/renderer/theme.ts +2 -2
  52. package/src/renderer/turn-injection.ts +3 -3
  53. package/src/renderer/ui-factory.ts +7 -7
  54. package/src/renderer/ui-primitives.ts +3 -3
  55. package/src/runtime/bootstrap-core.ts +15 -15
  56. package/src/runtime/bootstrap-hook-bridge.ts +2 -2
  57. package/src/runtime/bootstrap-shell.ts +3 -3
  58. package/src/runtime/bootstrap.ts +5 -5
  59. package/src/runtime/code-index-services.ts +2 -2
  60. package/src/runtime/index.ts +11 -3
  61. package/src/runtime/legacy-daemon-migration.ts +9 -9
  62. package/src/runtime/memory-fold.ts +30 -1
  63. package/src/runtime/onboarding/snapshot.ts +3 -3
  64. package/src/runtime/onboarding/types.ts +2 -2
  65. package/src/runtime/operator-token-cleanup.ts +2 -2
  66. package/src/runtime/orchestrator-core-services.ts +2 -2
  67. package/src/runtime/process-lifecycle.ts +19 -5
  68. package/src/runtime/services.ts +14 -14
  69. package/src/runtime/session-inbound-inputs.ts +1 -1
  70. package/src/runtime/session-spine-transport.ts +2 -2
  71. package/src/runtime/ui-services.ts +1 -1
  72. package/src/runtime/workstream-services.ts +1 -1
  73. package/src/runtime/wrfc-persistence.ts +2 -2
  74. package/src/shell/ui-openers.ts +2 -2
  75. package/src/version.ts +1 -1
@@ -57,7 +57,7 @@ export interface ProcessLifecycleDeps {
57
57
  readonly unsubs: ReadonlyArray<() => void>;
58
58
  readonly getRecoveryInterval: () => ReturnType<typeof setInterval> | null;
59
59
  readonly setRecoveryInterval: (value: ReturnType<typeof setInterval> | null) => void;
60
- readonly getStopSpokenOutputForExit: () => (() => void) | null;
60
+ readonly getStopSpokenOutputForExit: () => (() => void | Promise<void>) | null;
61
61
  }
62
62
 
63
63
  export interface ProcessLifecycleHandlers {
@@ -153,7 +153,16 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
153
153
  // must not re-run teardown or double-fire ctx.shutdown.
154
154
  if (exiting) return;
155
155
  exiting = true;
156
- getStopSpokenOutputForExit()?.();
156
+ // Exit lets the spoken audio the user is already hearing finish inside a
157
+ // short bounded window (capped inside stopForExit, under the 3s shutdown
158
+ // budget below) while the rest of teardown proceeds; queued-but-unplayed
159
+ // speech is dropped. Deliberate interrupts (Ctrl+C, /tts stop) still cut
160
+ // instantly through controller.stop() before this path is ever reached.
161
+ let spokenOutputDrain: Promise<void> = Promise.resolve();
162
+ try {
163
+ spokenOutputDrain = Promise.resolve(getStopSpokenOutputForExit()?.()).then(() => undefined);
164
+ } catch { /* non-fatal to exit */ }
165
+ spokenOutputDrain = spokenOutputDrain.catch(() => undefined);
157
166
  unsubs.forEach(fn => fn());
158
167
  const interval = getRecoveryInterval();
159
168
  if (interval !== null) { clearInterval(interval); setRecoveryInterval(null); }
@@ -169,9 +178,14 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
169
178
  try {
170
179
  // Race the graceful shutdown against a hard timeout — externalServices.stop() can hang
171
180
  // and we must still exit; deferredStartup.drain only budgets 100ms internally.
172
- await Promise.race([
173
- ctx.shutdown({ ...snapshot, ...buildPersistedSessionContext(snapshot.messages, ctx.conversation.getTitleSource(), buildSessionContinuityHints()) }).then(() => { shutdownOk = true; }),
174
- new Promise<void>((resolve) => setTimeout(resolve, 3000)),
181
+ // The spoken-audio drain runs concurrently and is internally capped below
182
+ // this budget, so it never extends the exit beyond the hard timeout.
183
+ await Promise.all([
184
+ spokenOutputDrain,
185
+ Promise.race([
186
+ ctx.shutdown({ ...snapshot, ...buildPersistedSessionContext(snapshot.messages, ctx.conversation.getTitleSource(), buildSessionContinuityHints()) }).then(() => { shutdownOk = true; }),
187
+ new Promise<void>((resolve) => setTimeout(resolve, 3000)),
188
+ ]),
175
189
  ]);
176
190
  } catch (err) {
177
191
  logger.debug('ctx.shutdown error during exitApp (non-fatal)', { error: summarizeError(err) });
@@ -222,12 +222,12 @@ export interface RuntimeServices {
222
222
  readonly agentOrchestrator: AgentOrchestrator;
223
223
  readonly wrfcController: WrfcController;
224
224
  readonly processManager: ProcessManager;
225
- /** Wave 4 (wo703): the phase/work-item orchestration engine — see runtime/workstream-services.ts. */
225
+ /** The phase/work-item orchestration engine — see runtime/workstream-services.ts. */
226
226
  readonly orchestrationEngine: OrchestrationEngine;
227
227
  readonly workstreamCommands: WorkstreamCommandService;
228
- /** Wave 5 (wo804): the repo source-tree code index — see runtime/code-index-services.ts. */
228
+ /** The repo source-tree code index — see runtime/code-index-services.ts. */
229
229
  readonly codeIndexStore: CodeIndexStore;
230
- readonly codeIndexReindexScheduler: CodeIndexReindexScheduler; // Wave-5 Stage B tool-site reindex
230
+ readonly codeIndexReindexScheduler: CodeIndexReindexScheduler; // tool-site reindex
231
231
  /** W2.1/W2.2: unified live process registry (agents, WRFC chains, workflows, watchers, background processes) backing the Fleet panel. */
232
232
  readonly processRegistry: ProcessRegistry;
233
233
  readonly modeManager: ModeManager;
@@ -338,7 +338,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
338
338
  executor: agentOrchestrator,
339
339
  configManager,
340
340
  });
341
- agentOrchestrator.setConversationSink({ // Wave-3 Part C6 bridge (mirrors the SDK's own createRuntimeServices)
341
+ agentOrchestrator.setConversationSink({ // Conversation-snapshot bridge (mirrors the SDK's own createRuntimeServices)
342
342
  register: (agentId, source) => agentManager.registerConversationSource(agentId, source),
343
343
  release: (agentId) => agentManager.releaseConversationSource(agentId),
344
344
  });
@@ -387,7 +387,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
387
387
  });
388
388
  const artifactStore = new ArtifactStore({ configManager });
389
389
  const memoryEmbeddingRegistry = new MemoryEmbeddingProviderRegistry({ configManager });
390
- // W6-C2 (E6): open the ONE home-scoped canonical store; legacy per-project TUI memory folds in at boot (foldTuiLegacyMemory).
390
+ // Open the ONE home-scoped canonical store; legacy per-project TUI memory folds in at boot (foldTuiLegacyMemory).
391
391
  const memoryDbPath = resolveCanonicalMemoryDbPath(homeDirectory);
392
392
  const memoryStore = new MemoryStore(memoryDbPath, {
393
393
  embeddingRegistry: memoryEmbeddingRegistry,
@@ -595,18 +595,18 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
595
595
  artifactStore,
596
596
  });
597
597
  const processManager = new ProcessManager();
598
- // Wave 4 (wo703): the phase/work-item orchestration engine, constructed
598
+ // The phase/work-item orchestration engine, constructed
599
599
  // before the process registry so its fleet nodes (workstream/phase/
600
600
  // work-item) can be folded in below via the registry's optional
601
601
  // orchestrationEngine dep.
602
602
  const { orchestrationEngine, workstreamCommands } = createWorkstreamServices({
603
603
  agentManager, configManager, adaptivePlanner, runtimeBus: options.runtimeBus, projectRoot: workingDirectory,
604
604
  });
605
- // Wave 5 (wo804): repo source-tree code index, sharing memoryEmbeddingRegistry
605
+ // Repo source-tree code index, sharing memoryEmbeddingRegistry
606
606
  // with MemoryStore above. Auto-build is config-gated (default off) — see
607
607
  // code-index-services.ts's header doc.
608
608
  const { codeIndexStore, codeIndexReindexScheduler } = createCodeIndexServices({ workingDirectory, configManager, memoryEmbeddingRegistry });
609
- const codeInjectionOrchestratorDeps = { codeIndex: codeIndexStore, isCodeInjectionSettingEnabled: () => isCodeInjectionSettingEnabled(configManager), codeIndexReindexScheduler }; // Wave-5 Stage B seam (agent here; main via orchestrator-core-services.ts)
609
+ const codeInjectionOrchestratorDeps = { codeIndex: codeIndexStore, isCodeInjectionSettingEnabled: () => isCodeInjectionSettingEnabled(configManager), codeIndexReindexScheduler }; // Code-injection seam (agent here; main via orchestrator-core-services.ts)
610
610
  // W2.1/W2.2: one shared process registry aggregating the managers above —
611
611
  // the Fleet panel (panels/fleet-read-model.ts) is its first consumer.
612
612
  // Constructed once here (not per-consumer) so the coalesced tick and the
@@ -614,15 +614,15 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
614
614
  const processRegistry = createProcessRegistry({
615
615
  agentManager,
616
616
  wrfcController,
617
- orchestrationEngine, // Wave 4 (wo703): folds workstream/phase/work-item nodes into the fleet
618
- codeIndexService: codeIndexStore, // Wave 5 (wo804): folds a single 'code-index' node into the fleet
617
+ orchestrationEngine, // Folds workstream/phase/work-item nodes into the fleet
618
+ codeIndexService: codeIndexStore, // Folds a single 'code-index' node into the fleet
619
619
  processManager,
620
620
  watcherRegistry,
621
621
  workflow,
622
622
  approvalBroker,
623
623
  sessionBroker,
624
- messageBus: agentMessageBus, // Wave-3: backs steer()/`steerable` (wo612 builds the composer UI on top)
625
- automationManager, // Wave 6 (wo-F item d4): folds /schedule AutomationJobs into the fleet as 'schedule' nodes
624
+ messageBus: agentMessageBus, // Backs steer()/`steerable` (the Fleet steer composer builds on top)
625
+ automationManager, // Folds /schedule AutomationJobs into the fleet as 'schedule' nodes
626
626
  runtimeBus: options.runtimeBus,
627
627
  // Honest pricing: never fabricate a cost for an unrecognized model.
628
628
  priceUsage: (model, usage) => {
@@ -681,7 +681,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
681
681
  remoteRunnerRegistry,
682
682
  knowledgeService,
683
683
  memoryRegistry,
684
- ...codeInjectionOrchestratorDeps, // Wave-5 Stage B: agent-run code injection + tool-site reindex
684
+ ...codeInjectionOrchestratorDeps, // Agent-run code injection + tool-site reindex
685
685
  archetypeLoader,
686
686
  configManager,
687
687
  providerRegistry,
@@ -790,7 +790,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
790
790
  workspaceCheckpointManager,
791
791
  integrationHelpers,
792
792
  async rerootStores(newWorkingDir: string): Promise<void> {
793
- // W6-C2 (E6): the memory store is the home-scoped canonical cross-surface store and
793
+ // The memory store is the home-scoped canonical cross-surface store and
794
794
  // deliberately does NOT reroot per-project (that would re-silo memory, the E6
795
795
  // regression). Only working-tree-bound stores (code index, project index) reroot.
796
796
  await codeIndexStore.reroot(newWorkingDir, codeIndexDbPath(newWorkingDir));
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * session-inbound-inputs.ts
3
3
  *
4
- * D3 (One-Platform Wave 2, live-surface steer delivery the inbound half of the
4
+ * Live-surface steer delivery (the inbound half of the
5
5
  * session spine): the TUI's INBOUND path for steer/follow-up inputs that another
6
6
  * live surface (e.g. the webui) queued against THIS TUI's session.
7
7
  *
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * session-spine-transport.ts
3
3
  *
4
- * W3-T1 (One-Platform Wave 3, TUI session-spine cutover): the thin typed-client
4
+ * The TUI session-spine cutover: the thin typed-client
5
5
  * adapter that lets the TUI's bootstrap drive the SDK's
6
6
  * `@pellux/goodvibes-sdk/platform/runtime/session-spine` `SessionSpineClient`
7
- * (extracted in W3-S4) via its injected `SpineTransport` seam.
7
+ * via its injected `SpineTransport` seam.
8
8
  *
9
9
  * The TUI is SDK-clean here: `SpineSessionsClient` is exactly the narrow wire
10
10
  * surface the old TUI-local `session-spine-client.ts` declared (structurally
@@ -114,7 +114,7 @@ export interface UiRuntimeSharedServices {
114
114
  readonly remoteSupervisor: RuntimeServices['remoteSupervisor'] & RemoteSupervisor;
115
115
  /** W2.2: the shared live process registry backing the Fleet panel. */
116
116
  readonly processRegistry: RuntimeServices['processRegistry'];
117
- /** Wave-3 (W3.2): the shared runtime event bus — the Fleet panel subscribes to its 'communication' domain for the honest steer-consumed signal. */
117
+ /** The shared runtime event bus — the Fleet panel subscribes to its 'communication' domain for the honest steer-consumed signal. */
118
118
  readonly runtimeBus: RuntimeServices['runtimeBus'];
119
119
  };
120
120
  }
@@ -1,5 +1,5 @@
1
1
  // ---------------------------------------------------------------------------
2
- // workstream-services.ts — Wave 4 (wo703)
2
+ // workstream-services.ts — phase/work-item orchestration engine
3
3
  //
4
4
  // Constructs the TUI's OrchestrationEngine instance (@pellux/goodvibes-sdk/
5
5
  // platform/orchestration, landed on SDK main as wo701/W4.1) and a thin
@@ -19,7 +19,7 @@
19
19
  * additionally exposes the recovered set for inspection.
20
20
  *
21
21
  * A chain that looked interrupted (non-terminal) in the old snapshot can be
22
- * reaped to terminal IN PLACE by `WrfcController.importChain` (Wave 6, wo-F:
22
+ * reaped to terminal IN PLACE by `WrfcController.importChain` (the zombie-reap pass:
23
23
  * no member agent survived the restart — see wrfc-controller.ts). This
24
24
  * module always re-checks `chain.state` AFTER the import call, never the
25
25
  * pre-import classification, so a reaped chain is treated as history, not
@@ -209,7 +209,7 @@ class WrfcPersistenceImpl implements WrfcPersistence {
209
209
  // chain. The accessor is optional for read-only test doubles.
210
210
  //
211
211
  // NOTE: importChain may reap this chain to a terminal state IN PLACE
212
- // (Wave 6, wo-F zombie-reap: no member agent survived the restart) —
212
+ // (zombie reap: no member agent survived the restart) —
213
213
  // always read chain.state AFTER this call below, never the pre-import
214
214
  // classification captured by candidateInterrupted.
215
215
  this.controller.importChain?.(chain);
@@ -231,7 +231,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
231
231
 
232
232
  commandContext.openModelPicker = () => {
233
233
  void (async () => {
234
- // D5: getSelectableModels() is catalog-driven and can list models whose
234
+ // getSelectableModels() is catalog-driven and can list models whose
235
235
  // `provider` id (e.g. 'google', sourced from the pricing catalog) was never
236
236
  // handed to providerRegistry.register()/registerRuntimeProvider(). Selecting
237
237
  // such a model fails hard at turn time with ProviderNotFoundError
@@ -264,7 +264,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
264
264
 
265
265
  commandContext.openProviderPicker = () => {
266
266
  void (async () => {
267
- // D5: listModels() surfaces every catalog provider id, not just the ones
267
+ // listModels() surfaces every catalog provider id, not just the ones
268
268
  // actually registered on this runtime (see openModelPicker above for the
269
269
  // same class of bug). Intersect against providerRegistry.has() so the
270
270
  // provider picker never offers a provider that will fail with
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '1.9.0';
9
+ let _version = '1.9.2';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  _version = pkg.version ?? _version;