@pellux/goodvibes-tui 1.18.0 → 1.19.1

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.
@@ -13,18 +13,9 @@ import { resolvePairingWebOrigin } from '../core/pairing-origin.ts';
13
13
  import { attachWsOnlyGatewayVerbHandlers } from '@pellux/goodvibes-terminal-shell';
14
14
  import { WatcherRegistry } from '@pellux/goodvibes-sdk/platform/watchers';
15
15
  import { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
16
- import {
17
- HomeGraphService,
18
- GOODVIBES_AGENT_KNOWLEDGE_DB_FILE,
19
- HOME_GRAPH_KNOWLEDGE_EXTENSION,
20
- KnowledgeService,
21
- KnowledgeSemanticService,
22
- KnowledgeStore,
23
- ProjectPlanningService,
24
- createProviderBackedKnowledgeSemanticLlm,
25
- createWebKnowledgeGapRepairer,
26
- projectPlanningProjectIdFromPath,
27
- } from '@pellux/goodvibes-sdk/platform/knowledge';
16
+ import { createWebKnowledgeGapRepairer } from '@pellux/goodvibes-sdk/platform/knowledge';
17
+ import type { HomeGraphService, KnowledgeService, ProjectPlanningService } from '@pellux/goodvibes-sdk/platform/knowledge';
18
+ import { createKnowledgeServices } from './knowledge-services.ts';
28
19
  import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
29
20
  import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
30
21
  import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
@@ -34,7 +25,7 @@ import type { StoreSnapshotScheduler } from '@pellux/goodvibes-sdk/platform/stat
34
25
  import type { UserPermissionRuleStore } from '@pellux/goodvibes-sdk/platform/permissions';
35
26
  import { buildExecPromptAnswerHandler } from '@pellux/goodvibes-sdk/platform/runtime/permissions/exec-prompt-wiring';
36
27
  import { buildLocalhostFetchApproval } from '@pellux/goodvibes-sdk/platform/runtime/permissions/localhost-fetch-approval';
37
- import { createNotificationDispatcher, wireRuntimeNotificationBridge, type NotificationDispatcher } from './notification-dispatch.ts';
28
+ import { createNotificationDispatcher, wireRuntimeNotificationBridge, wireMemoryPressureNotice, type NotificationDispatcher } from './notification-dispatch.ts';
38
29
  import { createDurabilityServices } from './durability-services.ts';
39
30
  import { MemorySpineClient, createLocalMemoryAccess } from '@pellux/goodvibes-sdk/platform/runtime/memory-spine';
40
31
  import { WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
@@ -48,6 +39,9 @@ import {
48
39
  type ShellPathService, type FeatureFlagManager, createFeatureFlagManager, PolicyRuntimeState,
49
40
  } from '@/runtime/index.ts';
50
41
  import { VoiceProviderRegistry, VoiceService, ensureBuiltinVoiceProviders } from '@pellux/goodvibes-sdk/platform/voice';
42
+ import { CacheRegistry, PauseController, type MemoryGovernor } from '@pellux/goodvibes-sdk/platform/runtime/memory';
43
+ import { wireMemoryGovernance } from './memory-governance-services.ts';
44
+ import { wireVoiceSetup } from './voice-setup-services.ts';
51
45
  import { WebSearchProviderRegistry, WebSearchService } from '@pellux/goodvibes-sdk/platform/web-search';
52
46
  import { PanelManager } from '../panels/panel-manager.ts';
53
47
  import { HookActivityTracker } from '@pellux/goodvibes-sdk/platform/hooks';
@@ -70,7 +64,7 @@ import { createFleetServices } from './fleet-services.ts';
70
64
  import { createWorkstreamServices, type OrchestrationEngine, type WorkstreamCommandService } from './workstream-services.ts';
71
65
  import { wireFleetNeedsInputPush } from './fleet-needs-input-push.ts';
72
66
  import { codeIndexDbPath, createCodeIndexServices, isCodeInjectionSettingEnabled } from './code-index-services.ts';
73
- import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
67
+ import type { WorkPlanStore } from '../work-plans/work-plan-store.ts';
74
68
  import { registerDaemonHandlers, type DaemonHandlerSurfaces } from '../daemon/handlers/index.ts';
75
69
  import type { HandlerContext, HandlerLogger } from '../daemon/handlers/context.ts';
76
70
  import { createDaemonCredentialStore } from '../daemon/handlers/credentials.ts';
@@ -84,8 +78,6 @@ import { registerRemoteSurface } from '../daemon/handlers/remote/index.ts';
84
78
  import { WorkspaceTrustManager } from './trust/workspace-trust.ts';
85
79
  import { ensureConfiguredModelIsRoutable } from './provider-fallback.ts';
86
80
 
87
- const REGULAR_KNOWLEDGE_DB_FILE = 'knowledge-wiki.sqlite';
88
- const HOME_GRAPH_KNOWLEDGE_DB_FILE = 'knowledge-home-graph.sqlite';
89
81
  export interface RuntimeServicesOptions {
90
82
  readonly runtimeBus: RuntimeEventBus;
91
83
  readonly runtimeStore: RuntimeStore;
@@ -213,6 +205,12 @@ export interface RuntimeServices {
213
205
  readonly storeSnapshotScheduler: StoreSnapshotScheduler;
214
206
  readonly memoryConsolidationScheduler: MemoryConsolidationScheduler;
215
207
  readonly powerManager: PowerManager;
208
+ /** The daemon's memory governor (default ON). Backs ops.memory.get and defends the daemon's footprint by tier. */
209
+ readonly memoryGovernor: MemoryGovernor;
210
+ /** Registry of every retained cache the governor can shrink (knowledge stores + shared session broker). */
211
+ readonly cacheRegistry: CacheRegistry;
212
+ /** Controller the governor uses to pause/resume the deferrable background jobs under pressure. */
213
+ readonly pauseController: PauseController;
216
214
  readonly sessionLiveTurnControls: SessionLiveTurnControlsHolder;
217
215
  /** Unified live process registry (agents, WRFC chains, workflows, watchers, background processes) backing the Fleet panel; archive-aware — finished subtrees can be moved to the session archive view. */
218
216
  readonly processRegistry: ArchivableProcessRegistry;
@@ -243,6 +241,19 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
243
241
  bindFeatureSettingsBridge(configManager, featureFlags);
244
242
  }
245
243
  const runtimeDispatch = createDomainDispatch(options.runtimeStore);
244
+ // Memory governance seams built EARLY (mirrors the SDK's own createRuntimeServices)
245
+ // so the scheduler gates and the knowledge background jobs can consult the pause
246
+ // controller before the MemoryGovernor (constructed at the composition tail)
247
+ // drives it. The admission gate is late-bound: expensive entry points capture
248
+ // this closure now and the governor binds into it at the tail — until then
249
+ // everything is admitted (the daemon is still booting).
250
+ const cacheRegistry = new CacheRegistry();
251
+ const pauseController = new PauseController();
252
+ const MEMORY_BACKGROUND_JOB_IDS = ['knowledge-self-improvement', 'memory-consolidation', 'code-index-reindex'];
253
+ const admitExpensiveWorkRef: { current: ((label: string) => { allowed: boolean; reason?: string | undefined }) | null } = { current: null };
254
+ const admitExpensiveWork = (label: string): { allowed: boolean; reason?: string | undefined } =>
255
+ admitExpensiveWorkRef.current?.(label) ?? { allowed: true };
256
+ const isKnowledgeBackgroundPaused = (): boolean => pauseController.isPaused('knowledge-self-improvement');
246
257
  const gatewayMethods = new GatewayMethodCatalog();
247
258
  const panelManager = new PanelManager();
248
259
  // (the purge): MIGRATE-TO-MODAL surface + redirect registration moved to
@@ -431,59 +442,13 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
431
442
  return record.id;
432
443
  },
433
444
  });
434
- const knowledgeStore = new KnowledgeStore({
435
- configManager,
436
- dbFileName: REGULAR_KNOWLEDGE_DB_FILE,
437
- });
438
- const agentKnowledgeStore = new KnowledgeStore({
439
- configManager,
440
- dbFileName: GOODVIBES_AGENT_KNOWLEDGE_DB_FILE,
441
- });
442
- const homeGraphKnowledgeStore = new KnowledgeStore({
443
- configManager,
444
- dbFileName: HOME_GRAPH_KNOWLEDGE_DB_FILE,
445
- });
446
- const knowledgeSemanticLlm = createProviderBackedKnowledgeSemanticLlm(providerRegistry, {
447
- timeoutMs: 20_000,
448
- maxConcurrent: 1,
449
- });
450
- const knowledgeSemanticService = new KnowledgeSemanticService(knowledgeStore, {
451
- llm: knowledgeSemanticLlm,
452
- maxLlmSourcesPerReindex: 3,
453
- });
454
- const homeGraphSemanticService = new KnowledgeSemanticService(homeGraphKnowledgeStore, {
455
- llm: knowledgeSemanticLlm,
456
- maxLlmSourcesPerReindex: 3,
457
- objectProfiles: HOME_GRAPH_KNOWLEDGE_EXTENSION.objectProfiles,
458
- });
459
- const agentKnowledgeSemanticService = new KnowledgeSemanticService(agentKnowledgeStore, {
460
- llm: knowledgeSemanticLlm,
461
- maxLlmSourcesPerReindex: 3,
462
- });
463
- const knowledgeService = new KnowledgeService(knowledgeStore, artifactStore, undefined, {
464
- memoryRegistry,
465
- runtimeBus: options.runtimeBus,
466
- semanticService: knowledgeSemanticService,
467
- });
468
- knowledgeService.attachRuntimeBus(options.runtimeBus);
469
- const agentKnowledgeService = new KnowledgeService(agentKnowledgeStore, artifactStore, undefined, {
470
- memoryRegistry,
471
- runtimeBus: options.runtimeBus,
472
- semanticService: agentKnowledgeSemanticService,
473
- });
474
- agentKnowledgeService.attachRuntimeBus(options.runtimeBus);
475
- const homeGraphService = new HomeGraphService(homeGraphKnowledgeStore, artifactStore, {
476
- semanticService: homeGraphSemanticService,
477
- });
478
- const projectPlanningProjectId = projectPlanningProjectIdFromPath(workingDirectory);
479
- const projectPlanningService = new ProjectPlanningService(knowledgeStore, {
480
- defaultProjectId: projectPlanningProjectId,
481
- });
482
- const workPlanStore = new WorkPlanStore({
483
- homeDirectory,
484
- projectId: projectPlanningProjectId,
485
- projectRoot: workingDirectory,
486
- });
445
+ // Knowledge/wiki + home-graph stack (governor backpressure wired in) — see knowledge-services.ts.
446
+ const {
447
+ knowledgeStore, agentKnowledgeStore, homeGraphKnowledgeStore,
448
+ knowledgeSemanticService, homeGraphSemanticService, agentKnowledgeSemanticService,
449
+ knowledgeService, agentKnowledgeService, homeGraphService,
450
+ projectPlanningService, projectPlanningProjectId, workPlanStore,
451
+ } = createKnowledgeServices({ configManager, providerRegistry, artifactStore, memoryRegistry, runtimeBus: options.runtimeBus, workingDirectory, homeDirectory, isBackgroundPaused: isKnowledgeBackgroundPaused, admitExpensiveWork });
487
452
  const voiceProviders = new VoiceProviderRegistry();
488
453
  ensureBuiltinVoiceProviders(voiceProviders, { readConfig: (key) => configManager.get(key as Parameters<typeof configManager.get>[0]) });
489
454
  const voiceService = new VoiceService(voiceProviders);
@@ -595,7 +560,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
595
560
  // Repo source-tree code index, sharing memoryEmbeddingRegistry
596
561
  // with MemoryStore above. Auto-build is config-gated (default off) — see
597
562
  // code-index-services.ts's header doc.
598
- const { codeIndexStore, codeIndexReindexScheduler } = createCodeIndexServices({ workingDirectory, configManager, memoryEmbeddingRegistry });
563
+ const { codeIndexStore, codeIndexReindexScheduler } = createCodeIndexServices({ workingDirectory, configManager, memoryEmbeddingRegistry, isReindexPaused: () => pauseController.isPaused('code-index-reindex'), admitExpensiveWork });
599
564
  // Store snapshots + durable remembered-approval rules + the live credential
600
565
  // chain, mirroring the SDK composition — see durability-services.ts.
601
566
  const { storeSnapshotScheduler, userPermissionRuleStore } = createDurabilityServices({
@@ -614,10 +579,36 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
614
579
  });
615
580
  const modeManager = new ModeManager(); const fileUndoManager = new FileUndoManager();
616
581
  const workspaceCheckpointManager = createWorkspaceCheckpointing({ workspaceRoot: workingDirectory, runtimeBus: options.runtimeBus, configManager });
617
- const { memoryConsolidationScheduler, powerManager, sessionLiveTurnControls } = wireIdlePowerAndLiveTurn({ configManager, memoryRegistry, runtimeBus: options.runtimeBus, isIdle: () => sessionBroker.countBusySessions() === 0, snapshotTick: () => storeSnapshotScheduler.tick(), heartbeat: async () => { await automationManager.triggerHeartbeat({ source: 'wake-catchup' }); }, powerSeam: options.powerSeam });
582
+ // memory-consolidation honors governor backpressure: it ticks only when idle
583
+ // AND the 'memory-consolidation' job is not paused AND expensive work is
584
+ // admitted (mirrors the SDK's own createRuntimeServices idle gate).
585
+ const { memoryConsolidationScheduler, powerManager, sessionLiveTurnControls } = wireIdlePowerAndLiveTurn({ configManager, memoryRegistry, runtimeBus: options.runtimeBus, isIdle: () => sessionBroker.countBusySessions() === 0 && !pauseController.isPaused('memory-consolidation') && admitExpensiveWork('memory consolidation').allowed, snapshotTick: () => storeSnapshotScheduler.tick(), heartbeat: async () => { await automationManager.triggerHeartbeat({ source: 'wake-catchup' }); }, powerSeam: options.powerSeam });
586
+
587
+ // Construct + start the MemoryGovernor (default ON — a safety feature) with
588
+ // the standard KNOWN cache adapters (knowledge stores + shared session
589
+ // broker), then late-bind the admission gate the expensive entry points
590
+ // captured earlier. Mirrors the SDK's own createRuntimeServices composition tail.
591
+ const { memoryGovernor } = wireMemoryGovernance({
592
+ configManager,
593
+ runtimeBus: options.runtimeBus,
594
+ cacheRegistry,
595
+ pauseController,
596
+ jobIds: MEMORY_BACKGROUND_JOB_IDS,
597
+ receiptPath: shellPaths.resolveProjectPath('tui', 'memory', 'tripwire-receipt.json'),
598
+ knowledgeStores: [knowledgeStore, agentKnowledgeStore, homeGraphKnowledgeStore],
599
+ sessionBroker,
600
+ // Graceful tripwire shutdown flushes in-flight state via ASYNC store
601
+ // snapshots so the governor's 10s shutdown ceiling stays enforceable.
602
+ onTripwireShutdown: async () => { await storeSnapshotScheduler.snapshotAllAsync('tripwire'); },
603
+ });
604
+ admitExpensiveWorkRef.current = (label) => memoryGovernor.admitExpensiveWork(label);
605
+
606
+ // Managed local-voice provisioning (voice.local.status/install) — single-flight
607
+ // one-act install + no-network status; see voice-setup-services.ts.
608
+ const { voiceSetup } = wireVoiceSetup({ configManager, shellPaths, voiceProviders, admitExpensiveWork });
618
609
 
619
- // Terminal-shell wrapper over the SDK registerGatewayVerbGroups (gateway-verbs.ts); checkin.*/fleet-needs-input/pairing.* register only when their deps are present.
620
- attachWsOnlyGatewayVerbHandlers(gatewayMethods, { processRegistry, workspaceCheckpointManager, conversationRewindPort: createSessionConversationRewindPort(), sessionBroker, secretsManager, stepUpService, approvalBroker, requestApproval: (input) => approvalBroker.requestApproval(input), watcherRegistry, userPermissionRuleStore, shellPaths, configManager, runtimeStore: options.runtimeStore, channelDeliveryRouter, providerRegistry, automationManager, sessionLister: sessionBroker, sessionIntake: sessionBroker, workingDirectory, memoryRegistry, pairingTokens, sessionLiveTurnControls, powerManager, relayAvailable: () => configManager.get('relay.enabled') === true, pairingWebOrigin: () => resolvePairingWebOrigin(configManager).origin, ...wireFleetNeedsInputPush({ registry: processRegistry, runtimeBus: options.runtimeBus, sessionBroker }) });
610
+ // Terminal-shell wrapper over the SDK registerGatewayVerbGroups (gateway-verbs.ts); checkin.*/fleet-needs-input/pairing.* register only when their deps are present. memoryGovernor lights up ops.memory.get; voiceSetup lights up voice.local.status/install.
611
+ attachWsOnlyGatewayVerbHandlers(gatewayMethods, { processRegistry, workspaceCheckpointManager, conversationRewindPort: createSessionConversationRewindPort(), sessionBroker, secretsManager, stepUpService, approvalBroker, requestApproval: (input) => approvalBroker.requestApproval(input), watcherRegistry, userPermissionRuleStore, shellPaths, configManager, runtimeStore: options.runtimeStore, channelDeliveryRouter, providerRegistry, automationManager, sessionLister: sessionBroker, sessionIntake: sessionBroker, workingDirectory, memoryRegistry, pairingTokens, sessionLiveTurnControls, powerManager, memoryGovernor, voiceSetup, relayAvailable: () => configManager.get('relay.enabled') === true, pairingWebOrigin: () => resolvePairingWebOrigin(configManager).origin, ...wireFleetNeedsInputPush({ registry: processRegistry, runtimeBus: options.runtimeBus, sessionBroker }) });
621
612
  const integrationHelpers = new IntegrationHelperService({
622
613
  workingDirectory,
623
614
  homeDirectory,
@@ -680,6 +671,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
680
671
  // panel feed (panel_only and burst-collapsed decisions land there).
681
672
  const notificationDispatcher = createNotificationDispatcher(configManager);
682
673
  wireRuntimeNotificationBridge(options.runtimeBus, notificationDispatcher);
674
+ // OPS_MEMORY_PRESSURE is lifted into the notice feed on its own targeted
675
+ // bridge (the high-churn 'ops' domain stays out of the wholesale allowlist).
676
+ wireMemoryPressureNotice(options.runtimeBus, notificationDispatcher);
683
677
 
684
678
  return {
685
679
  workingDirectory,
@@ -782,6 +776,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
782
776
  storeSnapshotScheduler,
783
777
  memoryConsolidationScheduler,
784
778
  powerManager,
779
+ memoryGovernor,
780
+ cacheRegistry,
781
+ pauseController,
785
782
  sessionLiveTurnControls,
786
783
  processRegistry,
787
784
  modeManager,
@@ -0,0 +1,52 @@
1
+ // ---------------------------------------------------------------------------
2
+ // voice-setup-services.ts — managed local-voice provisioning composition (TUI wiring)
3
+ //
4
+ // Backs the daemon's voice.local.status / voice.local.install verbs. The
5
+ // provisioning policy — a single-flight one-act install that provisions the
6
+ // piper engine + a default voice (and whisper STT where a bundle is published),
7
+ // live install-progress folded onto status(), ownership-aware preconfigure of
8
+ // the voice.local.* keys (values THIS installer previously wrote update to the
9
+ // new managed paths; genuinely user-set values still win; a user-cleared
10
+ // installer value stays cleared), a no-network status() read, and critical-tier
11
+ // admission gating — lives in the SDK's createVoiceSetupService. Earlier the
12
+ // composer was not exported, so the TUI rebuilt it from the voice primitives;
13
+ // SDK 1.10.1 exports it through ./platform/runtime/voice-setup, so this module
14
+ // is now a thin adapter that maps the TUI's injected seams (configManager,
15
+ // shellPaths, voiceProviders) onto the SDK service's flat deps.
16
+ //
17
+ // Extracted into its own module rather than built inline in services.ts, which
18
+ // sits at the architecture check's 800-line cap (scripts/check-architecture.ts)
19
+ // — new service construction gets its own module and a single wiring call there.
20
+ // ---------------------------------------------------------------------------
21
+
22
+ import {
23
+ createVoiceSetupService,
24
+ type VoiceSetupService,
25
+ } from '@pellux/goodvibes-sdk/platform/runtime/voice-setup';
26
+ import type { VoiceProviderRegistry } from '@pellux/goodvibes-sdk/platform/voice';
27
+ import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
28
+ import type { ShellPathService } from '@/runtime/index.ts';
29
+
30
+ export type { VoiceSetupService };
31
+
32
+ export interface VoiceSetupServicesDeps {
33
+ readonly configManager: ConfigManager;
34
+ readonly shellPaths: Pick<ShellPathService, 'resolveUserPath'>;
35
+ readonly voiceProviders: VoiceProviderRegistry;
36
+ /** Critical-tier admission gate — a provision run allocates archive + model buffers. */
37
+ readonly admitExpensiveWork: (label: string) => { allowed: boolean; reason?: string | undefined };
38
+ }
39
+
40
+ /** Compose the managed-voice single-flight install + no-network status read. */
41
+ export function wireVoiceSetup(deps: VoiceSetupServicesDeps): { voiceSetup: VoiceSetupService } {
42
+ const { configManager, voiceProviders } = deps;
43
+ const voiceSetup = createVoiceSetupService({
44
+ managedVoiceRoot: deps.shellPaths.resolveUserPath('voice'),
45
+ getConfig: (key) => String(configManager.get(key as Parameters<typeof configManager.get>[0]) ?? ''),
46
+ setConfig: (key, value) => configManager.setDynamic(key as Parameters<typeof configManager.setDynamic>[0], value),
47
+ // A successful (re-)install clears any tripped local-engine breaker.
48
+ resetLocalEngineFailureState: () => voiceProviders.get('local')?.resetEngineFailureState?.(),
49
+ admitExpensiveWork: deps.admitExpensiveWork,
50
+ });
51
+ return { voiceSetup };
52
+ }
package/src/version.ts CHANGED
@@ -6,10 +6,19 @@ 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.18.0';
9
+ let _version = '1.19.1';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
- _version = pkg.version ?? _version;
12
+ // Only trust a version read from OUR OWN package.json. A Bun single-file
13
+ // compiled binary resolves import.meta.dir to a virtual root where the
14
+ // `../package.json` path can land on a DIFFERENT package.json (a bundled
15
+ // dependency's, or the embedded runtime's) that reports a placeholder like
16
+ // "0.0.0" — exactly the wrong-version banner a bare daemon launch showed in
17
+ // the field. Guarding on the package name means the prebuild-baked fallback
18
+ // above wins in that case instead of a stray version.
19
+ if (pkg?.name === '@pellux/goodvibes-tui' && typeof pkg.version === 'string' && pkg.version.length > 0) {
20
+ _version = pkg.version;
21
+ }
13
22
  } catch {
14
23
  // Compiled binary or missing package.json — use fallback
15
24
  }