diff --git a/extensions/openai/realtime-quicksilver-bridge.test.ts b/extensions/openai/realtime-quicksilver-bridge.test.ts index 2c9e22de54ef..2e1708058d79 100644 --- a/extensions/openai/realtime-quicksilver-bridge.test.ts +++ b/extensions/openai/realtime-quicksilver-bridge.test.ts @@ -478,6 +478,29 @@ describe("OpenAIQuicksilverVoiceBridge", () => { expect(harness.onReady).toHaveBeenCalledOnce(); }); + it("keeps recoverable provider errors non-terminal before session readiness", async () => { + const harness = createHarness({ autoStart: false }); + const connecting = harness.bridge.connect(); + await vi.waitFor(() => expect(harness.socket.readyState).toBe(1)); + + harness.socket.serverEvent({ + type: "error", + error: { code: "missing_scope", message: "temporary provider rejection" }, + }); + + expect(harness.bridge.isConnected()).toBe(false); + expect(harness.onError).not.toHaveBeenCalled(); + expect(harness.onClose).not.toHaveBeenCalled(); + + harness.socket.serverEvent({ + type: "session.started", + session: { id: "live-1", expires_at: Math.floor(Date.now() / 1000) + 60 }, + }); + await connecting; + expect(harness.bridge.isConnected()).toBe(true); + expect(harness.onReady).toHaveBeenCalledOnce(); + }); + it.each([ [1000, "completed"], [1006, "error"], @@ -595,7 +618,7 @@ describe("OpenAIQuicksilverVoiceBridge", () => { harness.socket.serverEvent({ type: "error", - error: { message: "invalid live session" }, + error: { code: "authentication_error", message: "invalid live session" }, }); await expect(connecting).rejects.toThrow("OpenAI GPT-Live transport failed"); diff --git a/extensions/openai/realtime-quicksilver-bridge.ts b/extensions/openai/realtime-quicksilver-bridge.ts index 4036343dfc6b..938a80debfa3 100644 --- a/extensions/openai/realtime-quicksilver-bridge.ts +++ b/extensions/openai/realtime-quicksilver-bridge.ts @@ -21,6 +21,7 @@ import { closeOpenAILiveSocket, openAIQuicksilverToolResultText, } from "./realtime-quicksilver-protocol.js"; +import { handleOpenAIQuicksilverProviderError } from "./realtime-quicksilver-provider-error.js"; import { projectOpenAIQuicksilverErrorMessage } from "./realtime-quicksilver-redaction.js"; import { connectOpenAIQuicksilverSideband, @@ -562,18 +563,17 @@ export class OpenAIQuicksilverVoiceBridge implements RealtimeVoiceBridge { } const message = projectOpenAIQuicksilverErrorMessage("provider"); const error = new Error(message); - if (!this.lifecycle.isReady()) { - failStartup(error, "session start failed"); - return; - } const reportEvent = () => this.config.onEvent?.({ direction: "server", type: "error", detail: message }); - if (event.fatalAuth) { - this.fail(connection, error, "authentication failed", reportEvent); - } else { - reportEvent(); - this.config.onError?.(error); - } + handleOpenAIQuicksilverProviderError({ + fatalAuth: event.fatalAuth, + ready: this.lifecycle.isReady(), + failStartup: () => failStartup(error, "session start failed"), + failAuthentication: () => this.fail(connection, error, "authentication failed", reportEvent), + reportEvent, + reportError: () => this.config.onError?.(error), + logger: this.config.logger, + }); } private startDelegation( diff --git a/extensions/openai/realtime-quicksilver-provider-error.ts b/extensions/openai/realtime-quicksilver-provider-error.ts new file mode 100644 index 000000000000..92c5685afd71 --- /dev/null +++ b/extensions/openai/realtime-quicksilver-provider-error.ts @@ -0,0 +1,38 @@ +import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry"; + +type OpenAIQuicksilverProviderErrorHandling = { + fatalAuth: boolean; + ready: boolean; + failStartup: () => void; + failAuthentication: () => void; + reportEvent: () => void; + reportError: () => void; + logger?: Pick; +}; + +export function handleOpenAIQuicksilverProviderError({ + fatalAuth, + ready, + failStartup, + failAuthentication, + reportEvent, + reportError, + logger, +}: OpenAIQuicksilverProviderErrorHandling): void { + if (fatalAuth) { + if (!ready) { + failStartup(); + } else { + failAuthentication(); + } + return; + } + reportEvent(); + if (!ready) { + (logger?.warn ?? console.warn)( + "OpenAI GPT-Live provider error before session startup; continuing readiness", + ); + return; + } + reportError(); +}