@pellux/goodvibes-tui 1.25.0 → 1.27.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 (78) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +9605 -3119
  4. package/package.json +9 -7
  5. package/scripts/postinstall.js +57 -1
  6. package/src/audio/activation-sound.ts +117 -0
  7. package/src/audio/capture.ts +112 -0
  8. package/src/audio/onnxruntime-assets.d.ts +24 -0
  9. package/src/audio/voice-capture-wiring.ts +119 -0
  10. package/src/audio/voice-input-session.ts +181 -0
  11. package/src/audio/wake-inference.ts +182 -0
  12. package/src/audio/wake-runtime.ts +341 -0
  13. package/src/cli/bundle-command.ts +4 -1
  14. package/src/cli/help.ts +10 -0
  15. package/src/cli/management-utils.ts +4 -1
  16. package/src/cli/redaction.ts +68 -3
  17. package/src/config/goodvibes-home.ts +10 -0
  18. package/src/config/secret-config.ts +64 -8
  19. package/src/core/turn-event-wiring.ts +7 -16
  20. package/src/core/voice-capture-status.ts +53 -0
  21. package/src/core/voice-stt-gateway.ts +75 -0
  22. package/src/core/wake-provision-runner.ts +86 -0
  23. package/src/core/wake-provision-status.ts +170 -0
  24. package/src/core/workstream-notification.ts +69 -0
  25. package/src/daemon/cli.ts +86 -5
  26. package/src/daemon/handlers/credentials.ts +15 -5
  27. package/src/daemon/provision-wake-model.ts +81 -0
  28. package/src/daemon/send/channels.ts +200 -0
  29. package/src/daemon/send/command.ts +332 -0
  30. package/src/daemon/send/composition.ts +99 -0
  31. package/src/daemon/send/failure-text.ts +93 -0
  32. package/src/daemon/send/inert-text.ts +225 -0
  33. package/src/daemon/send/stdin.ts +24 -0
  34. package/src/input/command-registry.ts +6 -0
  35. package/src/input/commands/config.ts +68 -2
  36. package/src/input/commands/experience-runtime.ts +37 -3
  37. package/src/input/commands/local-provider-runtime.ts +9 -1
  38. package/src/input/commands/operator-runtime.ts +6 -1
  39. package/src/input/commands/payment-card-intake.ts +200 -0
  40. package/src/input/commands/profile-render.ts +250 -0
  41. package/src/input/commands/profile-runtime.ts +362 -0
  42. package/src/input/commands/profile-types.ts +381 -0
  43. package/src/input/commands.ts +7 -0
  44. package/src/input/composer-editor.ts +4 -0
  45. package/src/input/handler-shortcuts.ts +8 -0
  46. package/src/input/keybindings.ts +10 -1
  47. package/src/input/onboarding/onboarding-feature-units.ts +5 -5
  48. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -1
  49. package/src/input/payments-config.ts +167 -0
  50. package/src/input/provider-key-intake.ts +10 -2
  51. package/src/input/settings-modal-commit.ts +146 -0
  52. package/src/input/settings-modal-data.ts +30 -0
  53. package/src/input/settings-modal-reset.ts +6 -2
  54. package/src/input/settings-modal-secrets.ts +9 -2
  55. package/src/input/settings-modal-types.ts +30 -2
  56. package/src/input/settings-modal.ts +27 -107
  57. package/src/main.ts +12 -14
  58. package/src/panels/modals/marketplace-modal.ts +5 -0
  59. package/src/panels/modals/skills-modal.ts +5 -0
  60. package/src/renderer/process-indicator.ts +66 -0
  61. package/src/renderer/settings-modal-helpers.ts +8 -2
  62. package/src/renderer/settings-modal.ts +16 -1
  63. package/src/renderer/shell-surface.ts +22 -3
  64. package/src/runtime/bootstrap-core.ts +7 -7
  65. package/src/runtime/bootstrap.ts +3 -3
  66. package/src/runtime/device-posture-composition.ts +110 -0
  67. package/src/runtime/disposal-wiring.ts +16 -0
  68. package/src/runtime/install-self-check-startup.ts +23 -0
  69. package/src/runtime/onboarding/types.ts +6 -1
  70. package/src/runtime/path-shadow-startup.ts +243 -0
  71. package/src/runtime/path-shadow.ts +418 -0
  72. package/src/runtime/reachability-notice.ts +100 -0
  73. package/src/runtime/runtime-services-types.ts +260 -0
  74. package/src/runtime/services.ts +95 -209
  75. package/src/runtime/voice-setup-services.ts +50 -4
  76. package/src/shell/voice-capture-shell.ts +69 -0
  77. package/src/verification/verification-ledger.ts +163 -24
  78. package/src/version.ts +1 -1
@@ -1,14 +1,10 @@
1
1
  import { join } from 'node:path';
2
2
  import { FocusTracker } from '../core/focus-tracker.ts';
3
- import { ConfigManager, ServiceRegistry, SubscriptionManager, ToolLLM } from '@pellux/goodvibes-sdk/platform/config';
4
- import { SecretsManager } from '../config/secrets.ts';
3
+ import { ServiceRegistry, SubscriptionManager, ToolLLM } from '@pellux/goodvibes-sdk/platform/config';
5
4
  import { AutomationDeliveryManager, AutomationManager } from '@pellux/goodvibes-sdk/platform/automation';
6
- import { ChannelDeliveryRouter, ChannelPolicyManager, type ChannelPluginRegistry, type RouteBindingManager, type SurfaceRegistry } from '@pellux/goodvibes-sdk/platform/channels';
7
- import { ApprovalBroker, GatewayMethodCatalog, SessionLiveTurnControlsHolder, SharedSessionBroker } from '@pellux/goodvibes-sdk/platform/control-plane';
8
- import type { PowerManager } from '@pellux/goodvibes-sdk/platform/power';
5
+ import { ChannelDeliveryRouter, ChannelPolicyManager } from '@pellux/goodvibes-sdk/platform/channels';
6
+ import { ApprovalBroker, GatewayMethodCatalog, SharedSessionBroker } from '@pellux/goodvibes-sdk/platform/control-plane';
9
7
  import { wireIdlePowerAndLiveTurn } from './idle-power-services.ts';
10
- import { StepUpService } from '@pellux/goodvibes-sdk/daemon';
11
- import { PairingTokenManager } from '@pellux/goodvibes-sdk/platform/pairing';
12
8
  import { resolvePairingWebOrigin } from '../core/pairing-origin.ts';
13
9
  import { attachWsOnlyGatewayVerbHandlers } from '@pellux/goodvibes-terminal-shell';
14
10
  import { composeMailDeps } from './mail-composition.ts';
@@ -17,46 +13,35 @@ import { createDisposalScope, registerSurfaceRuntimePollers } from './disposal-w
17
13
  import { WatcherRegistry } from '@pellux/goodvibes-sdk/platform/watchers';
18
14
  import { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
19
15
  import { createWebKnowledgeGapRepairer } from '@pellux/goodvibes-sdk/platform/knowledge';
20
- import type { HomeGraphService, KnowledgeService, ProjectPlanningService } from '@pellux/goodvibes-sdk/platform/knowledge';
21
16
  import { createKnowledgeServices } from './knowledge-services.ts';
22
17
  import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
23
18
  import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
24
- import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
25
- import { AgentManager, ContextAccountingHolder, OverflowHandler, ProcessManager, cancelAllAgentRuns, createWorkflowServices, type WorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
26
- import { FileStateCache, FileUndoManager, MemoryConsolidationScheduler, MemoryEmbeddingProviderRegistry, MemoryRegistry, MemoryStore, ModeManager, ProjectIndex, resolveCanonicalMemoryDbPath, type CodeIndexStore, type CodeIndexReindexScheduler } from '@pellux/goodvibes-sdk/platform/state';
27
- import type { StoreSnapshotScheduler } from '@pellux/goodvibes-sdk/platform/state/store-snapshots';
28
- import type { UserPermissionRuleStore } from '@pellux/goodvibes-sdk/platform/permissions';
19
+ import { OverflowHandler, ProcessManager, cancelAllAgentRuns, createWorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
20
+ import { FileStateCache, FileUndoManager, MemoryEmbeddingProviderRegistry, MemoryRegistry, MemoryStore, ModeManager, ProjectIndex, resolveCanonicalMemoryDbPath } from '@pellux/goodvibes-sdk/platform/state';
29
21
  import { buildExecPromptAnswerHandler } from '@pellux/goodvibes-sdk/platform/runtime/permissions/exec-prompt-wiring';
30
22
  import { buildLocalhostFetchApproval } from '@pellux/goodvibes-sdk/platform/runtime/permissions/localhost-fetch-approval';
31
- import { createNotificationDispatcher, wireRuntimeNotificationBridge, wireMemoryPressureNotice, type NotificationDispatcher } from './notification-dispatch.ts';
23
+ import { createNotificationDispatcher, wireRuntimeNotificationBridge, wireMemoryPressureNotice } from './notification-dispatch.ts';
32
24
  import { createDurabilityServices } from './durability-services.ts';
33
25
  import { MemorySpineClient, createLocalMemoryAccess } from '@pellux/goodvibes-sdk/platform/runtime/memory-spine';
34
- import { WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
35
26
  import { createWorkspaceCheckpointing } from './workspace-checkpointing.ts';
36
27
  import { createSessionConversationRewindPort } from './conversation-rewind-port.ts';
37
28
  import { createDomainDispatch } from './store/index.ts';
38
- import type { DomainDispatch, RuntimeStore } from './store/index.ts';
39
- import {
40
- type RuntimeEventBus, DistributedRuntimeManager, RemoteRunnerRegistry, RemoteSupervisor, IntegrationHelperService,
41
- IdempotencyStore, ComponentHealthMonitor, WorktreeRegistry, SandboxSessionRegistry, createShellPathService,
42
- type ShellPathService, type FeatureFlagManager, createFeatureFlagManager, PolicyRuntimeState, type SessionSurface,
43
- } from '@/runtime/index.ts';
29
+ import { DistributedRuntimeManager, IntegrationHelperService, IdempotencyStore, ComponentHealthMonitor, WorktreeRegistry, createShellPathService, createFeatureFlagManager, PolicyRuntimeState } from '@/runtime/index.ts';
44
30
  import { createSessionStorageServices } from './session-storage-services.ts';
45
31
  import { VoiceProviderRegistry, VoiceService, ensureBuiltinVoiceProviders } from '@pellux/goodvibes-sdk/platform/voice';
46
- import { CacheRegistry, PauseController, type MemoryGovernor } from '@pellux/goodvibes-sdk/platform/runtime/memory';
32
+ import { CacheRegistry, PauseController } from '@pellux/goodvibes-sdk/platform/runtime/memory';
47
33
  import { wireMemoryGovernance } from './memory-governance-services.ts';
48
34
  import { wireVoiceSetup } from './voice-setup-services.ts';
49
35
  import { WebSearchProviderRegistry, WebSearchService } from '@pellux/goodvibes-sdk/platform/web-search';
50
36
  import { PanelManager } from '../panels/panel-manager.ts';
51
37
  import { HookActivityTracker } from '@pellux/goodvibes-sdk/platform/hooks';
52
- import { HookDispatcher, createHookWorkbench, type HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks';
38
+ import { HookDispatcher, createHookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks';
53
39
  import { PluginManager } from '@pellux/goodvibes-sdk/platform/plugins';
54
40
  import { BookmarkManager } from '@pellux/goodvibes-sdk/platform/bookmarks';
55
41
  import { ProfileManager } from '@pellux/goodvibes-sdk/platform/profiles';
56
- import { SessionManager, CrossSessionTaskRegistry, SessionChangeTracker } from '@pellux/goodvibes-sdk/platform/sessions';
42
+ import { CrossSessionTaskRegistry, SessionChangeTracker } from '@pellux/goodvibes-sdk/platform/sessions';
57
43
  import { ApiTokenAuditor, UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
58
44
  import { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
59
- import { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
60
45
  import { createRemoteExecutionServices } from './remote-execution-composition.ts';
61
46
  import { createAgentGraph } from './agent-graph-composition.ts';
62
47
  import { BenchmarkStore, CacheHitTracker, FavoritesStore, ModelLimitsService, ProviderCapabilityRegistry, ProviderOptimizer, ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
@@ -65,192 +50,20 @@ import { AdaptivePlanner, DeterministicReplayEngine, ExecutionPlanManager, Sessi
65
50
  import { deriveFeatureStates, bindFeatureSettingsBridge } from '@pellux/goodvibes-sdk/platform/runtime/state';
66
51
  import { createChannelComposition } from './channel-composition.ts';
67
52
  import { applyProviderOptimizerConfigMode, bindProviderOptimizerFeatureFlag } from './provider-optimizer-wiring.ts';
68
- import { type ArchivableProcessRegistry } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
69
53
  import { createFleetServices } from './fleet-services.ts';
70
- import { createWorkstreamServices, type OrchestrationEngine, type WorkstreamCommandService } from './workstream-services.ts';
54
+ import { createWorkstreamServices } from './workstream-services.ts';
71
55
  import { wireFleetNeedsInputPush } from './fleet-needs-input-push.ts';
72
56
  import { codeIndexDbPath, createCodeIndexServices, createStoreRerooter, isCodeInjectionSettingEnabled } from './code-index-services.ts';
73
- import type { WorkPlanStore } from '../work-plans/work-plan-store.ts';
74
- import type { DaemonHandlerSurfaces } from '../daemon/handlers/index.ts';
75
57
  import { createDaemonHandlerComposition } from './daemon-handler-composition.ts';
76
- import { createClusterServices, startClusterServices, type ClusterGroupComposition } from './cluster-group-composition.ts';
77
- import type { ClusterCoordinator } from '@pellux/goodvibes-sdk/platform/cluster';
58
+ import { createDevicePostureServices } from './device-posture-composition.ts';
59
+ // Re-exported so the shell's bootstrap reaches the install through the same
60
+ // module it already imports the runtime graph from.
61
+ export { installDevicePosture } from './device-posture-composition.ts';
62
+ import { createClusterServices, startClusterServices } from './cluster-group-composition.ts';
78
63
  import { WorkspaceTrustManager } from './trust/workspace-trust.ts';
79
64
  import { ensureConfiguredModelIsRoutable } from './provider-fallback.ts';
80
-
81
- export interface RuntimeServicesOptions {
82
- readonly runtimeBus: RuntimeEventBus;
83
- readonly runtimeStore: RuntimeStore;
84
- readonly configManager: ConfigManager;
85
- readonly localUserAuthManager?: UserAuthManager;
86
- readonly featureFlags?: FeatureFlagManager;
87
- readonly getConversationTitle?: () => string | undefined;
88
- readonly workingDir: string;
89
- readonly homeDirectory: string;
90
- /** Daemon state root (`--daemon-home`); absent ⇒ `<homeDirectory>/.goodvibes/daemon`.
91
- * Threaded into SecretsManager so the override MOVES the credential store —
92
- * without it an "isolated" daemon read the real home's secrets. One name for
93
- * one thing; resolveGoodVibesHomeOwnership is the single reader. */
94
- readonly daemonHomeDirectory?: string | undefined;
95
- /** Opt-in (daemon-side only): fold host-observed external coding-agent sessions
96
- * into the fleet as 'observed-external' rows. Interactive leaves it off and reads
97
- * the daemon snapshot. Mirrors the SDK's own createRuntimeServices option. */
98
- readonly observeExternalAgents?: boolean | undefined;
99
- /** Host power seam opt-in. Fork mirrors the SDK: non-spawning unavailable-seam
100
- * default (idle-power-services.ts); daemon + embedded runtime pass createHostPowerSeam(). */
101
- readonly powerSeam?: Parameters<typeof wireIdlePowerAndLiveTurn>[0]['powerSeam'];
102
- /** Live session id, read per crash-residue sweep so the running session is exempt — see durability-services.ts. */
103
- readonly currentSessionId?: (() => string | null) | undefined;
104
- }
105
-
106
- export interface RuntimeServices {
107
- readonly workingDirectory: string;
108
- readonly homeDirectory: string;
109
- /** The declare-once session-storage handle every session reader and writer threads through — see session-storage-services.ts. */
110
- readonly surface: SessionSurface;
111
- readonly shellPaths: ShellPathService;
112
- readonly configManager: ConfigManager;
113
- readonly featureFlags: FeatureFlagManager;
114
- readonly runtimeBus: RuntimeEventBus;
115
- readonly runtimeStore: RuntimeStore;
116
- readonly runtimeDispatch: DomainDispatch;
117
- readonly panelManager: PanelManager;
118
- readonly keybindingsManager: KeybindingsManager;
119
- readonly routeBindings: RouteBindingManager;
120
- readonly surfaceRegistry: SurfaceRegistry;
121
- readonly channelPlugins: ChannelPluginRegistry;
122
- readonly channelDeliveryRouter: ChannelDeliveryRouter;
123
- readonly watcherRegistry: WatcherRegistry;
124
- readonly approvalBroker: ApprovalBroker;
125
- /** Loopback-fetch approval that rides the approval broker; shared by the tool registry and orchestrator so every surface asks the same way. */
126
- readonly localhostFetchApproval: ReturnType<typeof buildLocalhostFetchApproval>;
127
- /** Terminal prompt-answer handler that rides the approval broker; shared by the tool registry and orchestrator so an interactive command's prompt gets an ask/card on every surface. */
128
- readonly execPromptAnswerHandler: ReturnType<typeof buildExecPromptAnswerHandler>;
129
- /** Routes curated runtime-domain events into the panel_only notification feed (the panel's live producer). */
130
- readonly notificationDispatcher: NotificationDispatcher;
131
- /** Durable user-origin permission rules (remembered approvals); permissions.rules.* surface. Mirrors the SDK composition. */
132
- readonly userPermissionRuleStore: UserPermissionRuleStore;
133
- readonly sessionBroker: SharedSessionBroker;
134
- readonly deliveryManager: AutomationDeliveryManager;
135
- readonly automationManager: AutomationManager;
136
- readonly gatewayMethods: GatewayMethodCatalog;
137
- readonly artifactStore: ArtifactStore;
138
- readonly knowledgeService: KnowledgeService;
139
- readonly agentKnowledgeService: KnowledgeService;
140
- readonly homeGraphService: HomeGraphService;
141
- readonly projectPlanningService: ProjectPlanningService;
142
- readonly projectPlanningProjectId: string;
143
- readonly workPlanStore: WorkPlanStore;
144
- readonly memoryStore: MemoryStore;
145
- readonly memoryRegistry: MemoryRegistry;
146
- /** Host-vs-client memory access: local until bootstrap.ts activates it for an adopted 'external' daemon (mirrors sessionSpine). */
147
- readonly memorySpine: MemorySpineClient;
148
- readonly serviceRegistry: ServiceRegistry;
149
- readonly secretsManager: SecretsManager;
150
- readonly stepUpService: StepUpService;
151
- readonly pairingTokens: PairingTokenManager; // backs pairing.tokens.* verbs + the settings device surface (mirrors the SDK composition)
152
- readonly subscriptionManager: SubscriptionManager;
153
- readonly localUserAuthManager: UserAuthManager;
154
- readonly profileManager: ProfileManager;
155
- readonly bookmarkManager: BookmarkManager;
156
- readonly sessionManager: SessionManager;
157
- readonly sessionOrchestration: CrossSessionTaskRegistry;
158
- readonly hookDispatcher: HookDispatcher;
159
- readonly hookActivityTracker: HookActivityTracker;
160
- readonly hookWorkbench: HookWorkbench;
161
- readonly pluginManager: PluginManager;
162
- readonly workflow: WorkflowServices;
163
- readonly voiceProviders: VoiceProviderRegistry;
164
- readonly voiceService: VoiceService;
165
- readonly webSearchProviders: WebSearchProviderRegistry;
166
- readonly webSearchService: WebSearchService;
167
- readonly mediaProviders: MediaProviderRegistry;
168
- readonly multimodalService: MultimodalService;
169
- readonly memoryEmbeddingRegistry: MemoryEmbeddingProviderRegistry;
170
- readonly channelPolicy: ChannelPolicyManager;
171
- readonly mcpRegistry: McpRegistry;
172
- readonly tokenAuditor: ApiTokenAuditor;
173
- readonly componentHealthMonitor: ComponentHealthMonitor;
174
- readonly worktreeRegistry: WorktreeRegistry;
175
- readonly sandboxSessionRegistry: SandboxSessionRegistry;
176
- readonly webhookNotifier: WebhookNotifier;
177
- /** Terminal focus tracker — fed by input/handler-feed.ts, read by the alert notifiers in core/. */
178
- readonly focusTracker: FocusTracker;
179
- readonly replayEngine: DeterministicReplayEngine;
180
- readonly providerOptimizer: ProviderOptimizer;
181
- readonly providerCapabilityRegistry: ProviderCapabilityRegistry;
182
- readonly cacheHitTracker: CacheHitTracker;
183
- readonly favoritesStore: FavoritesStore;
184
- readonly benchmarkStore: BenchmarkStore;
185
- readonly modelLimitsService: ModelLimitsService;
186
- readonly providerRegistry: ProviderRegistry;
187
- readonly toolLLM: ToolLLM;
188
- readonly distributedRuntime: DistributedRuntimeManager;
189
- readonly daemonHandlers: DaemonHandlerSurfaces;
190
- /** Elects the one node on this network that consumes inbound messages; hand it to the DaemonServer so its consumers share this leadership instead of holding a second election. */
191
- readonly clusterCoordinator: ClusterCoordinator;
192
- /** LAN group membership: identity, keys, roster, and the `cluster` verbs. */
193
- readonly clusterGroup: ClusterGroupComposition;
194
- /** Start the group layer and then the election, in that order. Idempotent. */
195
- readonly startCluster: () => Promise<void>;
196
- readonly remoteRunnerRegistry: RemoteRunnerRegistry;
197
- readonly remoteSupervisor: RemoteSupervisor;
198
- readonly sessionMemoryStore: SessionMemoryStore;
199
- readonly sessionLineageTracker: SessionLineageTracker;
200
- readonly sessionChangeTracker: SessionChangeTracker;
201
- readonly planManager: ExecutionPlanManager;
202
- readonly adaptivePlanner: AdaptivePlanner;
203
- readonly idempotencyStore: IdempotencyStore;
204
- readonly overflowHandler: OverflowHandler;
205
- readonly policyRuntimeState: PolicyRuntimeState;
206
- readonly archetypeLoader: ArchetypeLoader;
207
- readonly agentManager: AgentManager;
208
- readonly agentMessageBus: AgentMessageBus;
209
- readonly agentOrchestrator: AgentOrchestrator;
210
- readonly contextAccountingHolder: ContextAccountingHolder; // bound at bootstrap.ts; see context-accounting-source.ts
211
- readonly wrfcController: WrfcController;
212
- readonly processManager: ProcessManager;
213
- /** The phase/work-item orchestration engine — see runtime/workstream-services.ts. */
214
- readonly orchestrationEngine: OrchestrationEngine;
215
- readonly workstreamCommands: WorkstreamCommandService;
216
- /** The repo source-tree code index — see runtime/code-index-services.ts. */
217
- readonly codeIndexStore: CodeIndexStore;
218
- readonly codeIndexReindexScheduler: CodeIndexReindexScheduler; // tool-site reindex
219
- /** Daily snapshots of every SQLite store this runtime writes, with bounded retention; unref'd timers (mirrors the SDK composition — hosts that tear down a runtime stop() it themselves). */
220
- readonly storeSnapshotScheduler: StoreSnapshotScheduler;
221
- readonly appendOnlyRetentionScheduler: ReturnType<typeof createDurabilityServices>['appendOnlyRetentionScheduler']; // periodic append-only sweep; unref'd timers, stop() on teardown
222
- /** Stops the recurring crash-residue sweep; idempotent, unref'd timer (hosts that tear a runtime down call it). */
223
- readonly stopDurabilityHousekeeping: () => void;
224
- readonly memoryConsolidationScheduler: MemoryConsolidationScheduler;
225
- readonly powerManager: PowerManager;
226
- /** The daemon's memory governor (default ON). Backs ops.memory.get and defends the daemon's footprint by tier. */
227
- readonly memoryGovernor: MemoryGovernor;
228
- /** Registry of every retained cache the governor can shrink (knowledge stores + shared session broker). */
229
- readonly cacheRegistry: CacheRegistry;
230
- /** Controller the governor uses to pause/resume the deferrable background jobs under pressure. */
231
- readonly pauseController: PauseController;
232
- readonly sessionLiveTurnControls: SessionLiveTurnControlsHolder;
233
- /** 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. */
234
- readonly processRegistry: ArchivableProcessRegistry;
235
- readonly modeManager: ModeManager;
236
- readonly fileUndoManager: FileUndoManager;
237
- readonly workspaceCheckpointManager: WorkspaceCheckpointManager;
238
- /** Per-workspace trust gate — restricts write/execute/delegate tools until the workspace is trusted. */
239
- readonly workspaceTrustManager: WorkspaceTrustManager;
240
- readonly integrationHelpers: IntegrationHelperService;
241
- /** Re-root path-bound stores (MemoryStore, ProjectIndex) to a new working directory, called by WorkspaceSwapManager after verification; stores needing a process restart just warn-log and keep serving the old path until the daemon restarts with the new --working-dir. */
242
- rerootStores(newWorkingDir: string): Promise<void>;
243
- /**
244
- * Cancel the agent runs this graph is hosting, returning how many.
245
- *
246
- * Required by the SDK's RuntimePollerOwners: by dispose() time the fleet
247
- * registry, orchestration engine, process registry and bus these runs report
248
- * through are already down, so a run still described as "running" is orphaned
249
- * rather than preserved.
250
- */
251
- cancelHostedAgentRuns(): number;
252
- dispose(): void; // Stop every poller this graph started; best-effort, total, idempotent. This surface owns its graph — see disposal-wiring.ts.
253
- }
65
+ import type { RuntimeServicesOptions, RuntimeServices } from './runtime-services-types.ts';
66
+ export type { RuntimeServicesOptions, RuntimeServices } from './runtime-services-types.ts';
254
67
 
255
68
  export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeServices {
256
69
  const disposalScope = createDisposalScope('RuntimeServices'); const workingDirectory = options.workingDir; // disposal seam: see ./disposal-wiring.ts
@@ -350,8 +163,24 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
350
163
  join(surface.sessionsDir, 'task-graph.json'),
351
164
  );
352
165
  const hookActivityTracker = new HookActivityTracker();
166
+ // featureFlags is REQUIRED here in practice, even though the SDK types it
167
+ // optional. isFeatureGateEnabled(null, ...) is permissive by design — a
168
+ // narrow embed with no manager wired gets the capability rather than a
169
+ // silent off — so omitting it did not disable the watcher framework when
170
+ // watchers.enabled is turned off; it made the setting configure nothing.
171
+ // Threading it preserves current effective behaviour rather than changing
172
+ // it: watchers.enabled defaults true, the watcher-framework flag's own
173
+ // defaultState is 'enabled', and the flag declares no notOperable record —
174
+ // so with nothing configured the gate reads exactly as before, and the
175
+ // difference is only that turning it OFF now turns it off. This composes
176
+ // with bootstrap-core.ts's own `if (configManager.get('watchers.enabled'))`
177
+ // check around the built-in polling watcher registration: that check reads
178
+ // the same key, so the two never disagree, and this fix only reaches the
179
+ // watcher registry's OTHER entry points (registerWatcher/startWatcher/etc.)
180
+ // that bootstrap-core.ts's narrower check does not cover.
353
181
  const watcherRegistry = new WatcherRegistry({
354
182
  storePath: shellPaths.resolveProjectPath('tui', 'watchers.json'),
183
+ featureFlags,
355
184
  });
356
185
  watcherRegistry.attachRuntime({
357
186
  runtimeStore: options.runtimeStore,
@@ -414,6 +243,15 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
414
243
  // modal): bootstrap.ts activates the wire transport when a compatible
415
244
  // external daemon is adopted, same signal as the session spine.
416
245
  const memorySpine = new MemorySpineClient({ local: createLocalMemoryAccess(memoryRegistry) });
246
+ // featureFlags is REQUIRED here in practice, even though the SDK types it
247
+ // optional. isFeatureGateEnabled(null, ...) is permissive by design, so
248
+ // omitting it did not disable delivery tracking when
249
+ // integrations.deliveryTracking is turned off — it made the setting
250
+ // configure nothing: deliverText/deliverJobRun kept running either way.
251
+ // Threading it preserves current effective behaviour rather than changing
252
+ // it: the config default is true, the delivery-engine flag's own
253
+ // defaultState is 'enabled', and the flag declares no notOperable record —
254
+ // so with nothing configured the gate reads exactly as before.
417
255
  const deliveryManager = new AutomationDeliveryManager({
418
256
  configManager,
419
257
  // This manager builds the delivery router the daemon actually replies
@@ -427,7 +265,20 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
427
265
  runtimeStore: options.runtimeStore,
428
266
  routeBindings,
429
267
  artifactStore,
268
+ featureFlags,
430
269
  });
270
+ // Same shape as deliveryManager above: automation.enabled defaults true,
271
+ // the automation-domain flag's own defaultState is 'enabled', so threading
272
+ // featureFlags here does not change what a default install does — it only
273
+ // makes turning automation.enabled off actually turn AutomationManager's own
274
+ // create/update/run/list surface off. This composes with bootstrap.ts's own
275
+ // `if (configManager.get('automation.enabled'))` gate around scheduling
276
+ // automationManager.start(): that check reads the same key, and
277
+ // AutomationManager.start() re-checks the same gate internally (it no-ops
278
+ // and stops rather than starting when disabled), so the two can never
279
+ // disagree — this fix only reaches the manager's OTHER entry points
280
+ // (createJob/updateJob/runNow/etc.) that the bootstrap.ts gate does not
281
+ // cover, the same way watchers.enabled and the watcher registry above do.
431
282
  const automationManager = new AutomationManager({
432
283
  configManager,
433
284
  defaultSurfaceKind: 'tui',
@@ -439,6 +290,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
439
290
  // Same live registry: a bare model id on an automation job resolves through
440
291
  // the shared resolver instead of a format-only rejection.
441
292
  providerRegistry,
293
+ featureFlags,
442
294
  spawnTask: (input) => {
443
295
  const record = agentManager.spawn({
444
296
  mode: 'spawn',
@@ -500,6 +352,16 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
500
352
  approvalBridge: approvalBroker,
501
353
  automationBridge: automationManager,
502
354
  });
355
+ // The paired-phone feature for this host, on the SAME runtime phones pair onto
356
+ // and the SAME approval broker every other confirmation rides. Every `device.*`
357
+ // setting is read live through this; see device-posture-composition.ts.
358
+ const { devicePosture } = createDevicePostureServices({
359
+ configManager,
360
+ distributedRuntime,
361
+ approvals: approvalBroker,
362
+ stateDirectory: shellPaths.resolveProjectPath('tui', 'devices'),
363
+ gatewayMethods,
364
+ });
503
365
 
504
366
  // Which machines on this network are "us", and which of them reads the
505
367
  // shared inbox. Both inert until startCluster() — no socket, no key material
@@ -525,7 +387,18 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
525
387
  = createRemoteExecutionServices({
526
388
  agentManager, workingDirectory, hookDispatcher, configManager, runtimeBus: options.runtimeBus,
527
389
  });
528
- const tokenAuditor = new ApiTokenAuditor({ managed: false });
390
+ // featureFlags is REQUIRED here in practice, even though the SDK types it
391
+ // optional. isFeatureGateEnabled(null, ...) is permissive by design, so
392
+ // omitting it did not disable managed blocking when
393
+ // security.tokenAudit.enabled is turned off. Threading it preserves current
394
+ // effective behaviour rather than changing it: managed is hardcoded false
395
+ // here (advisory reporting only; excess-scope/overdue tokens are reported,
396
+ // never blocked, regardless of this flag — see isBlocked()'s
397
+ // `this._config.managed && this._managedBlockingEnabled()` guard), the
398
+ // config default for security.tokenAudit.enabled is true, and the
399
+ // token-scope-rotation-audit flag's own defaultState is 'enabled' — so with
400
+ // nothing configured the gate reads exactly as before either way.
401
+ const tokenAuditor = new ApiTokenAuditor({ managed: false, featureFlags });
529
402
  const componentHealthMonitor = new ComponentHealthMonitor();
530
403
  const worktreeRegistry = new WorktreeRegistry(workingDirectory);
531
404
  const webhookNotifier = new WebhookNotifier();
@@ -577,7 +450,15 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
577
450
  runtimeBus: options.runtimeBus,
578
451
  observeExternalAgents: options.observeExternalAgents, providerRegistry, // observeExternalAgents is daemon-side only
579
452
  });
580
- const modeManager = new ModeManager(); const fileUndoManager = new FileUndoManager();
453
+ // featureFlags is REQUIRED here in practice, even though the SDK types it
454
+ // optional. isFeatureGateEnabled(null, ...) is permissive by design, so
455
+ // omitting it did not disable the HITL UX mode system when
456
+ // behavior.hitlMode is set to 'off' — setHITLMode/setDomainVerbosity kept
457
+ // accepting writes either way. Threading it preserves current effective
458
+ // behaviour rather than changing it: behavior.hitlMode defaults to
459
+ // 'balanced' (not 'off'), and the hitl-ux-modes flag's own defaultState is
460
+ // 'enabled' — so with nothing configured the gate reads exactly as before.
461
+ const modeManager = new ModeManager({ featureFlags }); const fileUndoManager = new FileUndoManager();
581
462
  const workspaceCheckpointManager = createWorkspaceCheckpointing({ workspaceRoot: workingDirectory, surface, runtimeBus: options.runtimeBus, configManager });
582
463
  // memory-consolidation honors governor backpressure: it ticks only when idle
583
464
  // AND the 'memory-consolidation' job is not paused AND expensive work is
@@ -605,7 +486,11 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
605
486
 
606
487
  // Managed local-voice provisioning (voice.local.status/install) — single-flight
607
488
  // one-act install + no-network status; see voice-setup-services.ts.
608
- const { voiceSetup } = wireVoiceSetup({ configManager, shellPaths, voiceProviders, admitExpensiveWork });
489
+ const { voiceSetup, stopWakeHousekeeping } = wireVoiceSetup({ configManager, shellPaths, voiceProviders, admitExpensiveWork,
490
+ // Boot provisioning of the wake-word model + its recovery sweep, opted into
491
+ // by the real entrypoints only (same treatment as powerSeam) so a one-shot
492
+ // CLI command and a test composing this graph fetch nothing and start no timer.
493
+ provisionWakeModelsAtBoot: options.provisionWakeModelsAtBoot === true });
609
494
 
610
495
  // 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
496
  // calendar.*/email.* are platform-served; these two let it register (mail-composition.ts).
@@ -743,6 +628,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
743
628
  providerRegistry,
744
629
  toolLLM,
745
630
  distributedRuntime,
631
+ devicePosture,
746
632
  daemonHandlers,
747
633
  clusterCoordinator,
748
634
  clusterGroup,
@@ -768,7 +654,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
768
654
  workstreamCommands,
769
655
  codeIndexStore,
770
656
  codeIndexReindexScheduler,
771
- storeSnapshotScheduler, appendOnlyRetentionScheduler, stopDurabilityHousekeeping,
657
+ storeSnapshotScheduler, appendOnlyRetentionScheduler, stopDurabilityHousekeeping, stopWakeHousekeeping,
772
658
  memoryConsolidationScheduler,
773
659
  powerManager,
774
660
  memoryGovernor,
@@ -23,30 +23,76 @@ import {
23
23
  createVoiceSetupService,
24
24
  type VoiceSetupService,
25
25
  } from '@pellux/goodvibes-sdk/platform/runtime/voice-setup';
26
- import type { VoiceProviderRegistry } from '@pellux/goodvibes-sdk/platform/voice';
26
+ import {
27
+ startWakeBootProvisioning,
28
+ type VoiceProviderRegistry,
29
+ } from '@pellux/goodvibes-sdk/platform/voice';
27
30
  import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
31
+ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
28
32
  import type { ShellPathService } from '@/runtime/index.ts';
29
33
 
30
34
  export type { VoiceSetupService };
31
35
 
36
+ /**
37
+ * The recovery act this surface names when a boot provision degrades. The SDK
38
+ * would otherwise name the control-plane verb, which is correct everywhere and
39
+ * useless to someone sitting in a terminal.
40
+ */
41
+ export const WAKE_RECOVERY_COMMAND = '/voice wake setup';
42
+
32
43
  export interface VoiceSetupServicesDeps {
33
44
  readonly configManager: ConfigManager;
34
45
  readonly shellPaths: Pick<ShellPathService, 'resolveUserPath'>;
35
46
  readonly voiceProviders: VoiceProviderRegistry;
36
47
  /** Critical-tier admission gate — a provision run allocates archive + model buffers. */
37
48
  readonly admitExpensiveWork: (label: string) => { allowed: boolean; reason?: string | undefined };
49
+ /**
50
+ * Opt into boot-time wake-model provisioning and the recovery sweeper.
51
+ *
52
+ * Off unless a real entrypoint asks for it, matching how this graph treats the
53
+ * host power seam: `daemon/cli.ts` and `runtime/bootstrap-core.ts` opt in, the
54
+ * one-shot CLI commands do not, and a test composing the graph gets neither a
55
+ * network fetch nor a repeating timer it did not ask for.
56
+ */
57
+ readonly provisionWakeModelsAtBoot?: boolean | undefined;
58
+ /** Injected in tests: the boot starter, so no sweeper and no fetch are real. */
59
+ readonly startBootProvisioning?: typeof startWakeBootProvisioning | undefined;
60
+ }
61
+
62
+ export interface VoiceSetupServices {
63
+ readonly voiceSetup: VoiceSetupService;
64
+ /**
65
+ * Stop the wake-word recovery sweeper and cancel a pending boot provision.
66
+ * A no-op when boot provisioning was not opted into. Registered on the
67
+ * disposal scope (disposal-wiring.ts) — an hourly timer nothing stops is a
68
+ * poller this surface leaked, which is exactly what that scope exists for.
69
+ */
70
+ readonly stopWakeHousekeeping: () => void;
38
71
  }
39
72
 
40
73
  /** Compose the managed-voice single-flight install + no-network status read. */
41
- export function wireVoiceSetup(deps: VoiceSetupServicesDeps): { voiceSetup: VoiceSetupService } {
74
+ export function wireVoiceSetup(deps: VoiceSetupServicesDeps): VoiceSetupServices {
42
75
  const { configManager, voiceProviders } = deps;
76
+ const managedVoiceRoot = deps.shellPaths.resolveUserPath('voice');
43
77
  const voiceSetup = createVoiceSetupService({
44
- managedVoiceRoot: deps.shellPaths.resolveUserPath('voice'),
78
+ managedVoiceRoot,
45
79
  getConfig: (key) => String(configManager.get(key as Parameters<typeof configManager.get>[0]) ?? ''),
46
80
  setConfig: (key, value) => configManager.setDynamic(key as Parameters<typeof configManager.setDynamic>[0], value),
47
81
  // A successful (re-)install clears any tripped local-engine breaker.
48
82
  resetLocalEngineFailureState: () => voiceProviders.get('local')?.resetEngineFailureState?.(),
49
83
  admitExpensiveWork: deps.admitExpensiveWork,
50
84
  });
51
- return { voiceSetup };
85
+ if (deps.provisionWakeModelsAtBoot !== true) {
86
+ return { voiceSetup, stopWakeHousekeeping: () => {} };
87
+ }
88
+ // Sweep the wake tree, then fetch whatever the install could not. The SDK owns
89
+ // the policy — never throws, one plain message, reaps before it retries — and
90
+ // routes the attempt through the setup service's single flight so a boot
91
+ // attempt and a user typing /voice wake setup are one download.
92
+ const boot = (deps.startBootProvisioning ?? startWakeBootProvisioning)({
93
+ managedRoot: managedVoiceRoot,
94
+ ensureProvisioned: () => voiceSetup.wakeEnsureProvisioned({ recoveryHint: WAKE_RECOVERY_COMMAND }),
95
+ announce: (message) => { logger.info(message); },
96
+ });
97
+ return { voiceSetup, stopWakeHousekeeping: () => boot.stop() };
52
98
  }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * voice-capture-shell.ts — the shell's side of microphone capture.
3
+ *
4
+ * Extracted from main() for the same reason the process-lifecycle wiring was:
5
+ * the entrypoint is a wiring file held under a source-line gate, and new shell
6
+ * composition gets its own module and a single call there.
7
+ *
8
+ * What this owns is only the shell-facing half — where a transcript lands (the
9
+ * composer draft, through the same public `prompt`/`cursorPos` fields the
10
+ * external-editor path writes), how a turn is submitted, which keybinding seam
11
+ * the Alt+V action hangs off, and the teardown registration that guarantees a
12
+ * live recorder subprocess dies with the process. Everything about audio itself
13
+ * lives in src/audio/voice-capture-wiring.ts, which must not (and does not)
14
+ * import shell-UI.
15
+ */
16
+
17
+ import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
18
+ import type { CommandContext } from '../input/command-registry.ts';
19
+ import type { ShellPathService } from '@/runtime/index.ts';
20
+ import type { VoiceCaptureIndicatorState } from '../core/voice-capture-status.ts';
21
+ import { wireVoiceCapture } from '../audio/voice-capture-wiring.ts';
22
+
23
+ export interface VoiceCaptureShellDeps {
24
+ readonly configManager: ConfigManager;
25
+ readonly shellPaths: Pick<ShellPathService, 'resolveUserPath'>;
26
+ readonly homeDirectory: string | (() => string);
27
+ /** Names retained wake clips so the SDK's sweeper reaps them when this session ends. */
28
+ readonly sessionId: string;
29
+ /** The Alt+V seam is hung here (commandContext.toggleVoiceInput). */
30
+ readonly commandContext: CommandContext;
31
+ /** The shell's teardown registry; the device release is appended to it. */
32
+ readonly unsubs: Array<() => void>;
33
+ /** The live composer buffer — InputHandler exposes public `prompt`/`cursorPos`. */
34
+ readonly buffer: { prompt: string; cursorPos: number };
35
+ readonly submitInput: (text: string) => void;
36
+ readonly notify: (message: string) => void;
37
+ readonly render: () => void;
38
+ }
39
+
40
+ /**
41
+ * Compose voice capture and hand back the footer-row reader.
42
+ *
43
+ * Opens no device: wake detection consults `voice.wake.*` and refuses without
44
+ * touching the capture opener when it is off, and push-to-talk opens one only on
45
+ * a keypress.
46
+ */
47
+ export function installVoiceCapture(deps: VoiceCaptureShellDeps): () => VoiceCaptureIndicatorState | null {
48
+ const capture = wireVoiceCapture({
49
+ configManager: deps.configManager,
50
+ // The same managed root `/voice setup` uses; the wake tree is `<root>/wake`.
51
+ managedVoiceRoot: deps.shellPaths.resolveUserPath('voice'),
52
+ // Surface-scoped: the extracted onnxruntime assets belong to this surface's
53
+ // own directory, beside its other managed state.
54
+ assetDirectory: deps.shellPaths.resolveUserPath('tui', 'onnxruntime'),
55
+ homeDirectory: deps.homeDirectory,
56
+ sessionId: deps.sessionId,
57
+ writeDraft: (text) => {
58
+ deps.buffer.prompt = text;
59
+ deps.buffer.cursorPos = text.length;
60
+ deps.render();
61
+ },
62
+ submitTurn: (text) => deps.submitInput(text),
63
+ notify: deps.notify,
64
+ render: deps.render,
65
+ });
66
+ deps.commandContext.toggleVoiceInput = capture.toggleVoiceInput;
67
+ deps.unsubs.push(...capture.unsubs);
68
+ return capture.status;
69
+ }