Files
oh-my-pi/packages/coding-agent/src/live/transport.ts
T
roboomp 25c4ec07ed fix(live): honored standard proxies for sideband websocket
Centralized URL-aware proxy resolution so Codex WebSocket transports share standard proxy environment fallbacks and NO_PROXY handling.

Fixes #6770
2026-07-27 06:24:28 +00:00

423 lines
14 KiB
TypeScript

import { type AuthStorage, isAuthRetryableError, type OAuthAccess, withOAuthAccess } from "@oh-my-pi/pi-ai";
import { getProxyForUrl, wrapFetchForProxy } from "@oh-my-pi/pi-ai/utils/proxy";
import {
CODEX_BASE_URL,
CODEX_CLIENT_VERSION,
getCodexAccountId,
OPENAI_HEADERS,
} from "@oh-my-pi/pi-catalog/wire/codex";
import { LiveWebRtcPeer } from "@oh-my-pi/pi-natives";
import { generateCodexAttestation } from "./attestation";
import {
buildLiveSessionPayload,
type LiveClientMessage,
type LiveServerEvent,
parseLiveServerEvent,
} from "./protocol";
const SIGNALING_URL = `${CODEX_BASE_URL}/codex/realtime/calls?intent=quicksilver&architecture=avas`;
const MAX_ERROR_BODY_LENGTH = 2_048;
const SIDEBAND_CONNECT_ATTEMPTS = 5;
const SIDEBAND_CONNECT_TIMEOUT_MS = 15_000;
const LIVE_PROVIDER = "openai-codex";
const LIVE_ORIGINATOR = "Codex Desktop";
const LIVE_CALL_ID_PATTERN = /^rtc_[\w-]+$/;
type Lifecycle = "idle" | "connecting" | "connected" | "closing" | "closed";
interface LiveSignalingResult {
answer: string;
callId: string;
access: OAuthAccess;
attestation: string | undefined;
}
class LiveSignalingError extends Error {
status: number;
errorMessage: string;
constructor(status: number, message: string) {
super(message);
this.name = "LiveSignalingError";
this.status = status;
this.errorMessage = message;
}
}
/** Callbacks emitted by the live WebRTC transport. */
export interface LiveTransportCallbacks {
onEvent(event: LiveServerEvent): void;
onOutputLevel(level: number): void;
}
/** Configuration required to establish a Codex live call. */
export interface LiveTransportOptions {
authStorage: AuthStorage;
sessionId: string;
instructions: string;
voice: string;
callbacks: LiveTransportCallbacks;
signal?: AbortSignal;
}
/** Extracts the server-assigned `rtc_*` call ID from a signaling Location header. */
export function parseLiveCallId(location: string | null): string | undefined {
if (!location) return undefined;
return location
.split("?", 1)[0]
?.split("/")
.find(segment => LIVE_CALL_ID_PATTERN.test(segment));
}
/** Builds the Frameless Bidi sideband WebSocket URL for an accepted Codex call. */
export function buildLiveSidebandUrl(callId: string): string {
const url = new URL(`https://api.openai.com/v1/live/${encodeURIComponent(callId)}`);
url.protocol = "wss:";
return url.toString();
}
function liveSessionHeaders(
access: OAuthAccess,
sessionId: string,
realtimeSessionId: string,
attestation: string | undefined,
): Record<string, string> {
const headers: Record<string, string> = {
Authorization: `Bearer ${access.accessToken}`,
"OpenAI-Alpha": "quicksilver=v2",
"User-Agent": `Codex Desktop/${CODEX_CLIENT_VERSION}`,
"x-session-id": realtimeSessionId,
[OPENAI_HEADERS.ORIGINATOR]: LIVE_ORIGINATOR,
[OPENAI_HEADERS.VERSION]: CODEX_CLIENT_VERSION,
[OPENAI_HEADERS.SCOPED_SESSION_ID]: sessionId,
[OPENAI_HEADERS.THREAD_ID]: sessionId,
};
const accountId = access.accountId ?? getCodexAccountId(access.accessToken);
if (accountId) headers[OPENAI_HEADERS.ACCOUNT_ID] = accountId;
if (attestation) headers["x-oai-attestation"] = attestation;
return headers;
}
function boundedErrorBody(body: string, statusText: string): string {
const normalized = body.trim().replaceAll(/\s+/g, " ");
if (!normalized) return statusText || "empty response body";
if (normalized.length <= MAX_ERROR_BODY_LENGTH) return normalized;
return `${normalized.slice(0, MAX_ERROR_BODY_LENGTH)}…`;
}
function isAuthError(error: unknown): boolean {
return isAuthRetryableError(error);
}
function abortReason(signal: AbortSignal | undefined): Error {
if (signal?.reason instanceof Error) return signal.reason;
return new DOMException("Live connection aborted", "AbortError");
}
/** Native WebRTC transport for a Codex Frameless Bidi live session. */
export class CodexLiveTransport {
readonly #options: LiveTransportOptions;
#peer: LiveWebRtcPeer | undefined;
readonly #realtimeSessionId = crypto.randomUUID();
#sideband: Bun.WebSocket | undefined;
#state: Lifecycle = "idle";
#connectPromise: Promise<void> | undefined;
#closePromise: Promise<void> | undefined;
#sendTail: Promise<void> = Promise.resolve();
#muted = false;
#unexpectedFailureReported = false;
readonly #abortListener: () => void;
constructor(options: LiveTransportOptions) {
this.#options = options;
this.#abortListener = () => {
void this.close();
};
if (!options.signal?.aborted) options.signal?.addEventListener("abort", this.#abortListener, { once: true });
}
/** Establish the native peer, perform Codex signaling, and wait for the data channel. */
connect(): Promise<void> {
if (this.#state === "connected") return Promise.resolve();
if (this.#connectPromise) return this.#connectPromise;
if (this.#state === "closing" || this.#state === "closed")
return Promise.reject(new Error("Live transport is closed"));
if (this.#options.signal?.aborted) return Promise.reject(abortReason(this.#options.signal));
this.#state = "connecting";
const operation = this.#connect().catch(async error => {
await this.close();
throw error;
});
this.#connectPromise = operation;
return operation;
}
async #connect(): Promise<void> {
const peer = new LiveWebRtcPeer(
(error, payload) => {
if (error) {
this.#handlePeerFailure(error.message);
} else {
this.#handleServerEvent(payload);
}
},
(error, level) => {
if (error) {
this.#handlePeerFailure(error.message);
} else {
this.#handleOutputLevel(level);
}
},
(error, message) => this.#handlePeerFailure(error?.message ?? message),
);
this.#peer = peer;
const offer = await peer.createOffer();
if (this.#state !== "connecting") throw abortReason(this.#options.signal);
const signaling = await this.#signal(offer);
await peer.acceptAnswer(signaling.answer);
peer.setMuted(this.#muted);
await peer.waitForOpen();
if (this.#state !== "connecting") throw abortReason(this.#options.signal);
await this.#connectSideband(signaling.callId, signaling.access, signaling.attestation);
if (this.#state !== "connecting") throw abortReason(this.#options.signal);
this.#state = "connected";
}
async #signal(offer: string): Promise<LiveSignalingResult> {
const attestation = await generateCodexAttestation();
return await withOAuthAccess(
this.#options.authStorage,
LIVE_PROVIDER,
access => this.#signalWithAccess(offer, access, attestation),
{
sessionId: this.#options.sessionId,
signal: this.#options.signal,
isAuthError,
missingAccessMessage: "No Codex OAuth credential is available for a live call.",
},
);
}
async #signalWithAccess(
offer: string,
access: OAuthAccess,
attestation: string | undefined,
): Promise<LiveSignalingResult> {
const headers = new Headers({
...liveSessionHeaders(access, this.#options.sessionId, this.#realtimeSessionId, attestation),
Accept: "*/*",
"Content-Type": "application/json",
});
const fetchImpl = wrapFetchForProxy(fetch, LIVE_PROVIDER);
const response = await fetchImpl(SIGNALING_URL, {
method: "POST",
headers,
body: JSON.stringify({
sdp: offer,
session: buildLiveSessionPayload(this.#options.instructions, this.#options.voice),
}),
signal: this.#options.signal,
});
const responseBody = await response.text();
if (!response.ok) {
const detail = boundedErrorBody(responseBody, response.statusText);
throw new LiveSignalingError(response.status, `Codex live signaling failed (${response.status}): ${detail}`);
}
const answer = responseBody;
if (!answer.trim())
throw new LiveSignalingError(response.status, "Codex live signaling returned an empty SDP answer");
const callId = parseLiveCallId(response.headers.get("location"));
if (!callId) {
throw new LiveSignalingError(response.status, "Codex live signaling returned no valid call ID");
}
return { answer, callId, access, attestation };
}
async #connectSideband(callId: string, access: OAuthAccess, attestation: string | undefined): Promise<void> {
let failure = new Error("Codex live sideband connection failed");
for (let attempt = 0; attempt < SIDEBAND_CONNECT_ATTEMPTS; attempt++) {
try {
await this.#openSideband(callId, access, attestation);
return;
} catch (cause) {
failure = cause instanceof Error ? cause : new Error(String(cause));
if (this.#options.signal?.aborted) throw abortReason(this.#options.signal);
if (attempt + 1 < SIDEBAND_CONNECT_ATTEMPTS) await Bun.sleep(200 * 2 ** attempt);
}
}
throw failure;
}
async #openSideband(callId: string, access: OAuthAccess, attestation: string | undefined): Promise<void> {
const url = buildLiveSidebandUrl(callId);
const options = {
headers: liveSessionHeaders(access, this.#options.sessionId, this.#realtimeSessionId, attestation),
proxy: getProxyForUrl(LIVE_PROVIDER, new URL(url)),
} satisfies Bun.WebSocketOptions;
const socket: Bun.WebSocket = Reflect.construct(WebSocket, [url, options]);
socket.binaryType = "nodebuffer";
const { promise, resolve, reject } = Promise.withResolvers<void>();
let opened = false;
let settled = false;
let timeout: NodeJS.Timeout | undefined;
const cleanup = (): void => {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
this.#options.signal?.removeEventListener("abort", onAbort);
};
const rejectConnect = (error: Error): void => {
if (settled) return;
settled = true;
cleanup();
reject(error);
};
const onAbort = (): void => {
socket.close(1000, "aborted");
rejectConnect(abortReason(this.#options.signal));
};
socket.onopen = () => {
if (settled) {
socket.close(1000, "stale");
return;
}
opened = true;
settled = true;
cleanup();
this.#sideband = socket;
resolve();
};
socket.onmessage = event => {
if (typeof event.data !== "string") {
this.#reportFailure("Codex live sideband returned an unexpected binary frame.");
return;
}
this.#handleSidebandEvent(event.data);
};
socket.onerror = event => {
const detail = event instanceof ErrorEvent && event.message ? `: ${event.message}` : "";
if (!opened) {
rejectConnect(new Error(`Codex live sideband connection failed${detail}`));
socket.close(1011, "connection failed");
return;
}
this.#reportFailure(`Codex live sideband failed${detail}`);
};
socket.onclose = event => {
if (!opened) {
rejectConnect(new Error(`Codex live sideband closed before connecting (${event.code})`));
return;
}
if (this.#sideband !== socket) return;
this.#sideband = undefined;
if (this.#state === "connecting" || this.#state === "connected") {
const detail = event.reason ? `: ${event.reason}` : "";
this.#reportFailure(`Codex live sideband closed (${event.code})${detail}`);
}
};
if (this.#options.signal?.aborted) {
onAbort();
} else {
this.#options.signal?.addEventListener("abort", onAbort, { once: true });
timeout = setTimeout(() => {
socket.close(1000, "connect timeout");
rejectConnect(new Error("Codex live sideband connection timed out"));
}, SIDEBAND_CONNECT_TIMEOUT_MS);
timeout.unref?.();
}
await promise;
}
#handleSidebandEvent(payload: string): void {
if (this.#state === "closing" || this.#state === "closed") return;
const event = parseLiveServerEvent(payload);
if (!event) return;
try {
this.#options.callbacks.onEvent(event);
} catch {}
}
#handleServerEvent(payload: string): void {
if (this.#state === "closing" || this.#state === "closed") return;
const event = parseLiveServerEvent(payload);
if (!event || (this.#sideband?.readyState === WebSocket.OPEN && event.type !== "error")) return;
try {
this.#options.callbacks.onEvent(event);
} catch {}
}
#handleOutputLevel(level: number): void {
if (this.#state !== "connected" || !Number.isFinite(level)) return;
try {
this.#options.callbacks.onOutputLevel(Math.min(1, Math.max(0, level)));
} catch {}
}
#handlePeerFailure(message: string): void {
this.#reportFailure(message);
}
#reportFailure(message: string): void {
if ((this.#state !== "connecting" && this.#state !== "connected") || this.#unexpectedFailureReported) {
return;
}
this.#unexpectedFailureReported = true;
try {
this.#options.callbacks.onEvent({ type: "error", message });
} catch {}
}
/** Serialize one Frameless Bidi control message onto the call's sideband WebSocket. */
send(message: LiveClientMessage): Promise<void> {
const operation = this.#sendTail.then(() => {
if (this.#state !== "connected") throw new Error("Live transport is not connected");
const sideband = this.#sideband;
if (!sideband || sideband.readyState !== WebSocket.OPEN) {
throw new Error("Codex live sideband is not connected");
}
sideband.send(JSON.stringify(message));
});
this.#sendTail = operation.catch(() => {});
return operation;
}
/** Queue 16 kHz mono Float32 PCM for native Opus transmission. */
pushAudio(samples: Float32Array): void {
if (this.#state !== "connected" || this.#muted || samples.length === 0) return;
this.#peer?.pushAudio(samples);
}
/** Enable or disable the native audio source and discard partial input when muted. */
async setMuted(muted: boolean): Promise<void> {
this.#muted = muted;
if (this.#state === "connected") this.#peer?.setMuted(muted);
}
/** Stop sideband signaling and the native WebRTC media peer. Safe to call repeatedly. */
close(): Promise<void> {
if (this.#closePromise) return this.#closePromise;
this.#state = "closing";
const operation = this.#close();
this.#closePromise = operation;
return operation;
}
async #close(): Promise<void> {
this.#options.signal?.removeEventListener("abort", this.#abortListener);
const sideband = this.#sideband;
const peer = this.#peer;
this.#sideband = undefined;
this.#peer = undefined;
if (sideband && (sideband.readyState === WebSocket.OPEN || sideband.readyState === WebSocket.CONNECTING)) {
sideband.close(1000, "done");
}
if (peer) {
try {
await peer.close();
} catch {}
}
this.#state = "closed";
}
}