diff --git a/apps/desktop/src/hermes.ts b/apps/desktop/src/hermes.ts index 645d0c6fa3a3..f94f0d2d741f 100644 --- a/apps/desktop/src/hermes.ts +++ b/apps/desktop/src/hermes.ts @@ -249,6 +249,13 @@ function profileScoped(profile?: null | string): { profile?: string } { return selected ? { profile: selected } : {} } +/** Profile that profile-scoped REST/WS calls should target (null → primary). + * Read-only twin of setApiRequestProfile for modules (e.g. voice playback) + * that build their own connection URLs and must stay on the same backend. */ +export function getApiRequestProfile(): null | string { + return _apiProfile +} + /** Options for a plugin REST call — mirrors the app's own `hermesDesktop.api` * shape, minus the path (which is namespace-derived). */ export interface PluginRestOptions { diff --git a/apps/desktop/src/lib/voice-playback.ts b/apps/desktop/src/lib/voice-playback.ts index 80d9a3f22a2e..a40948931629 100644 --- a/apps/desktop/src/lib/voice-playback.ts +++ b/apps/desktop/src/lib/voice-playback.ts @@ -1,6 +1,6 @@ import { resolveGatewayWsUrl } from '@hermes/shared' -import { speakText } from '@/hermes' +import { getApiRequestProfile, speakText } from '@/hermes' import { $voicePlayback, setVoicePlaybackState, @@ -74,9 +74,11 @@ async function resolveSpeakStreamUrl(): Promise { } try { - // Mint a fresh credential (single-use ticket in OAuth mode), then swap the - // gateway endpoint for the PCM one — auth is shared across WS routes. - const wsUrl = await resolveGatewayWsUrl(desktop, await desktop.getConnection()) + // Mint a fresh credential (single-use ticket in OAuth mode) for the + // ACTIVE profile's backend, then swap the gateway endpoint for the PCM + // one — auth is shared across WS routes. + const profile = getApiRequestProfile() + const wsUrl = await resolveGatewayWsUrl(desktop, await desktop.getConnection(profile)) const url = new URL(wsUrl) if (!url.pathname.endsWith('/api/ws')) { @@ -85,6 +87,12 @@ async function resolveSpeakStreamUrl(): Promise { url.pathname = url.pathname.replace(/\/api\/ws$/, '/api/audio/speak-stream') + // The backend resolves the TTS provider chain from this profile's + // config/.env (same seam as /api/pty?profile=). + if (profile) { + url.searchParams.set('profile', profile) + } + return url.toString() } catch { return null