diff --git a/packages/app/src/runtime/i18n/en.ts b/packages/app/src/runtime/i18n/en.ts index 93d87277128..ba473a64529 100644 --- a/packages/app/src/runtime/i18n/en.ts +++ b/packages/app/src/runtime/i18n/en.ts @@ -11,7 +11,7 @@ export const dict = { "voice.connecting": "Connecting…", "voice.you": "You:", "voice.assistant": "OpenCode:", - "voice.failure.unavailable": "Console voice is unavailable on this server.", + "voice.failure.unavailable": "Voice calls need an OpenAI API key on this server.", "voice.failure.microphone": "Microphone access is required to call.", "voice.failure.dropped": "The call disconnected.", "voice.failure.connection": "Couldn’t connect the call.", diff --git a/packages/app/src/voice/transport.ts b/packages/app/src/voice/transport.ts index 3d7ac840197..7ad543c5fc4 100644 --- a/packages/app/src/voice/transport.ts +++ b/packages/app/src/voice/transport.ts @@ -15,7 +15,6 @@ export class VoiceTransport { private audio = new Audio() private context = new AudioContext() private stream?: MediaStream - private hostedID?: string private timer?: ReturnType private ended = false private closing?: Promise @@ -85,7 +84,6 @@ export class VoiceTransport { !offered.models.some( (model) => model.model === "gpt-live-1" && - model.integration === "opencode" && model.available && model.transports.includes("webrtc"), ) @@ -131,7 +129,6 @@ export class VoiceTransport { ) if (response.transport.type !== "webrtc") throw new VoiceFailure("connection") - this.hostedID = response.session?.id await this.peer.setRemoteDescription({ type: "answer", sdp: response.transport.sdp }) signal.throwIfAborted() const timedOut = () => this.ready.reject(new VoiceFailure("connection")) @@ -168,12 +165,9 @@ export class VoiceTransport { this.send({ type: muted ? "session.input_audio.mute" : "session.input_audio.unmute" }) } - async instructions(instructions: string) { - if (!this.hostedID) throw new VoiceFailure("connection") - await this.inputOptions.api.voice.instructions( - { sessionID: this.hostedID, instructions }, - { signal: this.abort.signal }, - ) + /** The call runs on the server's own OpenAI key, so its data channel can update the backend's instructions. */ + instructions(instructions: string) { + this.send({ type: "session.update", session: { delegation: { type: "responses", responses: { instructions } } } }) } close() {