@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.
- package/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +9605 -3119
- package/package.json +9 -7
- package/scripts/postinstall.js +57 -1
- package/src/audio/activation-sound.ts +117 -0
- package/src/audio/capture.ts +112 -0
- package/src/audio/onnxruntime-assets.d.ts +24 -0
- package/src/audio/voice-capture-wiring.ts +119 -0
- package/src/audio/voice-input-session.ts +181 -0
- package/src/audio/wake-inference.ts +182 -0
- package/src/audio/wake-runtime.ts +341 -0
- package/src/cli/bundle-command.ts +4 -1
- package/src/cli/help.ts +10 -0
- package/src/cli/management-utils.ts +4 -1
- package/src/cli/redaction.ts +68 -3
- package/src/config/goodvibes-home.ts +10 -0
- package/src/config/secret-config.ts +64 -8
- package/src/core/turn-event-wiring.ts +7 -16
- package/src/core/voice-capture-status.ts +53 -0
- package/src/core/voice-stt-gateway.ts +75 -0
- package/src/core/wake-provision-runner.ts +86 -0
- package/src/core/wake-provision-status.ts +170 -0
- package/src/core/workstream-notification.ts +69 -0
- package/src/daemon/cli.ts +86 -5
- package/src/daemon/handlers/credentials.ts +15 -5
- package/src/daemon/provision-wake-model.ts +81 -0
- package/src/daemon/send/channels.ts +200 -0
- package/src/daemon/send/command.ts +332 -0
- package/src/daemon/send/composition.ts +99 -0
- package/src/daemon/send/failure-text.ts +93 -0
- package/src/daemon/send/inert-text.ts +225 -0
- package/src/daemon/send/stdin.ts +24 -0
- package/src/input/command-registry.ts +6 -0
- package/src/input/commands/config.ts +68 -2
- package/src/input/commands/experience-runtime.ts +37 -3
- package/src/input/commands/local-provider-runtime.ts +9 -1
- package/src/input/commands/operator-runtime.ts +6 -1
- package/src/input/commands/payment-card-intake.ts +200 -0
- package/src/input/commands/profile-render.ts +250 -0
- package/src/input/commands/profile-runtime.ts +362 -0
- package/src/input/commands/profile-types.ts +381 -0
- package/src/input/commands.ts +7 -0
- package/src/input/composer-editor.ts +4 -0
- package/src/input/handler-shortcuts.ts +8 -0
- package/src/input/keybindings.ts +10 -1
- package/src/input/onboarding/onboarding-feature-units.ts +5 -5
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -1
- package/src/input/payments-config.ts +167 -0
- package/src/input/provider-key-intake.ts +10 -2
- package/src/input/settings-modal-commit.ts +146 -0
- package/src/input/settings-modal-data.ts +30 -0
- package/src/input/settings-modal-reset.ts +6 -2
- package/src/input/settings-modal-secrets.ts +9 -2
- package/src/input/settings-modal-types.ts +30 -2
- package/src/input/settings-modal.ts +27 -107
- package/src/main.ts +12 -14
- package/src/panels/modals/marketplace-modal.ts +5 -0
- package/src/panels/modals/skills-modal.ts +5 -0
- package/src/renderer/process-indicator.ts +66 -0
- package/src/renderer/settings-modal-helpers.ts +8 -2
- package/src/renderer/settings-modal.ts +16 -1
- package/src/renderer/shell-surface.ts +22 -3
- package/src/runtime/bootstrap-core.ts +7 -7
- package/src/runtime/bootstrap.ts +3 -3
- package/src/runtime/device-posture-composition.ts +110 -0
- package/src/runtime/disposal-wiring.ts +16 -0
- package/src/runtime/install-self-check-startup.ts +23 -0
- package/src/runtime/onboarding/types.ts +6 -1
- package/src/runtime/path-shadow-startup.ts +243 -0
- package/src/runtime/path-shadow.ts +418 -0
- package/src/runtime/reachability-notice.ts +100 -0
- package/src/runtime/runtime-services-types.ts +260 -0
- package/src/runtime/services.ts +95 -209
- package/src/runtime/voice-setup-services.ts +50 -4
- package/src/shell/voice-capture-shell.ts +69 -0
- package/src/verification/verification-ledger.ts +163 -24
- package/src/version.ts +1 -1
package/src/runtime/services.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { FocusTracker } from '../core/focus-tracker.ts';
|
|
3
|
-
import {
|
|
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
|
|
7
|
-
import { ApprovalBroker, GatewayMethodCatalog,
|
|
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 {
|
|
25
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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 {
|
|
77
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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):
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|