import type { AudioCaptureBackend, AudioCaptureNoiseSuppression } from '../capture/types.js'; import type { WakeSupervisorPolicy } from './supervisor.js'; import type { WakeDetectorTuning } from './types.js'; /** The surfaces `voice.wake.surfaces.*` enumerates. */ export type WakeSurface = 'tui' | 'agent' | 'webui' | 'app'; /** What a surface can actually do, so a row is refused rather than faked. */ export interface WakeSurfaceCapabilities { /** * Whether speex suppression can be applied to captured audio on this surface. * * Left out, it is ANSWERED rather than assumed: the platform carries SpeexDSP's * preprocessor as a WebAssembly module, so the answer is whether this runtime * has WebAssembly, see `noiseSuppressionSupport()`. Both shipped surfaces do, * and both apply the stage, which is why `speex` runs rather than refusing. * * A surface that genuinely cannot, a JavaScript runtime with no WebAssembly, * passes `false` and gets a blocker with that reason, because the wrong reading * of this flag is the exact lie the row exists to prevent: audio captured * unfiltered while the setting claims a filter is running. */ readonly speexAvailable?: boolean | undefined; /** * The speech gate is loaded on this surface and will actually screen frames. * * A head IS pinned now ({@link WAKE_VAD_MODEL}) and provisions with the wake * models, but loading an inference session is the host's job on every surface, * exactly as it is for the classifier and the embedding, so this stays * host-declared. A host sets it once it has passed the gate's session to the * engine; until then `voice.wake.vadThreshold` above 0 is refused rather than * silently skipped, because a frame reaching the classifier unscreened while * the row says it was screened is the lie the refusal exists to prevent. */ readonly vadAvailable?: boolean | undefined; /** Audio can be written to disk, for `retainAudio: session-temp`. */ readonly canRetainAudio?: boolean | undefined; /** A local audio file path can be played, for `activationSound: custom`. */ readonly canPlayLocalFile?: boolean | undefined; } /** A row that prevents the detector from starting, with the reason to show. */ export interface WakeSettingBlocker { readonly key: string; readonly detail: string; } /** A row not in force on this surface, while the detector still runs. */ export interface WakeSettingLimitation { readonly key: string; readonly detail: string; } /** The activation sound to play the moment a wake confirms. */ export interface WakeActivationSound { readonly kind: 'none' | 'chime' | 'custom'; /** Only meaningful when `kind` is `custom`. */ readonly path: string; } /** Capture settings, shared by wake detection and push-to-talk voice input. */ export interface WakeCaptureSettings { readonly device: string; readonly backend: AudioCaptureBackend; readonly noiseSuppression: AudioCaptureNoiseSuppression; /** Samples per frame; fixed by what the classifier was trained at. */ readonly frameSamples: number; } /** Every `voice.wake.*` row, resolved for one surface. */ export interface WakeRuntimeSettings { readonly surface: WakeSurface; /** `voice.wake.enabled`. */ readonly enabled: boolean; /** `voice.wake.surfaces.`. */ readonly surfaceEnabled: boolean; /** * True only when the feature is on, this surface is one of its delivery * surfaces, and no row blocks it. A surface must consult THIS and nothing * else before opening a device. */ readonly active: boolean; readonly modelIds: readonly string[]; readonly tuning: WakeDetectorTuning; readonly vadThreshold: number; readonly capture: WakeCaptureSettings; readonly activationSound: WakeActivationSound; readonly indicator: 'off' | 'statusline' | 'banner'; readonly preRollMs: number; readonly captureMaxSeconds: number; readonly silenceStopMs: number; /** * `voice.wake.silenceFloorRms`. 0 means measure the room per utterance and * keep following it; any other value pins the floor and freezes it. */ readonly silenceFloorRms: number; /** * `voice.wake.speechRetriggerMs`. How long a loud run must last to count as * speech, so a breath or a lip tick does not restart the silence wait. */ readonly speechRetriggerMs: number; readonly autoSubmit: boolean; readonly retainAudio: 'none' | 'session-temp'; readonly customModelDir: string; readonly supervisor: WakeSupervisorPolicy; readonly browserBackend: 'wasm' | 'webgpu'; readonly blockers: readonly WakeSettingBlocker[]; readonly limitations: readonly WakeSettingLimitation[]; } /** Reads one config key. Returns undefined for a key the source does not hold. */ export type WakeSettingReader = (key: string) => unknown; /** * Every key {@link resolveWakeRuntimeSettings} reads. Exported so a test can * assert it against the schema's `voice.wake.*` rows in both directions, a row * the resolver ignores is a row that configures nothing. */ export declare const WAKE_SETTING_KEYS: readonly string[]; /** The `voice.wake.surfaces.*` key for a surface. */ export declare function wakeSurfaceKey(surface: WakeSurface): string; /** A second key that must move with the one just written, and why. */ export interface WakeEnablementCompanion { readonly key: string; readonly value: boolean; /** Plain words for the reply: what was also set, and what it would have done. */ readonly message: string; } /** * The companion write that keeps `voice.wake.enabled` from configuring nothing. * * TWO FLAGS GATE ONE FEATURE, AND ONE OF THEM IS INVISIBLE. * * Turning `voice.wake.enabled` on while `voice.wake.surfaces.` is off * is a silent no-op: the setting reports success, the value really is stored, * and no microphone ever opens. That cost the owner an entire session, they * enabled the feature, were told it was enabled, and nothing listened. * * A master switch whose delivery row is off is not a configuration the user * meant. So enabling the master on an opted-out surface SAYS so and sets the * surface row in the same act, rather than leaving a correct-looking setting * that does nothing. Returns null when no companion is needed. * * The reverse is deliberately NOT symmetric: turning the master off is a * complete instruction on its own, and enabling a single surface row while the * feature is off is how someone stages a surface before switching it on. */ export declare function resolveWakeEnablementCompanion(key: string, value: unknown, read: WakeSettingReader, surface: WakeSurface): WakeEnablementCompanion | null; /** * Resolve every `voice.wake.*` row for one surface. * * Reads defaults for anything the source does not hold, so a partial config tree * (a browser tab that fetched only part of it, a fixture) resolves to the shipped * behaviour rather than to zeroes. */ export declare function resolveWakeRuntimeSettings(read: WakeSettingReader, surface: WakeSurface, capabilities?: WakeSurfaceCapabilities): WakeRuntimeSettings; //# sourceMappingURL=settings.d.ts.map