diff --git a/src/proxy/__tests__/codex-api-headers.test.ts b/src/proxy/__tests__/codex-api-headers.test.ts index 263f88a..260bcb3 100644 --- a/src/proxy/__tests__/codex-api-headers.test.ts +++ b/src/proxy/__tests__/codex-api-headers.test.ts @@ -112,13 +112,27 @@ describe("codex-api headers", () => { expect(transport.lastHeaders!["x-codex-turn-state"]).toBeUndefined(); }); - it("excludes turnState and service_tier from JSON body", async () => { + it("excludes turnState from body and maps service_tier fast→priority", async () => { const api = await createApi(); await api.createResponse( makeRequest({ turnState: "abc", service_tier: "fast" }), ); const body = JSON.parse(transport.lastBody!) as Record; expect(body.turnState).toBeUndefined(); + expect(body.service_tier).toBe("priority"); + }); + + it("passes non-fast service_tier as-is", async () => { + const api = await createApi(); + await api.createResponse(makeRequest({ service_tier: "flex" })); + const body = JSON.parse(transport.lastBody!) as Record; + expect(body.service_tier).toBe("flex"); + }); + + it("omits service_tier from body when not set", async () => { + const api = await createApi(); + await api.createResponse(makeRequest({})); + const body = JSON.parse(transport.lastBody!) as Record; expect(body.service_tier).toBeUndefined(); }); }); diff --git a/src/proxy/codex-api.ts b/src/proxy/codex-api.ts index db89c7f..0da228d 100644 --- a/src/proxy/codex-api.ts +++ b/src/proxy/codex-api.ts @@ -43,6 +43,14 @@ import { type CodexUsageResponse, } from "./codex-types.js"; +/** + * Map user-facing service_tier to Codex backend value. + * Desktop codex-rs sends "fast" → "priority" (see codex-rs client.rs:740). + */ +function mapServiceTier(tier: string): string { + return tier === "fast" ? "priority" : tier; +} + export class CodexApi { private token: string; private accountId: string | null; @@ -212,7 +220,7 @@ export class CodexApi { if (request.tools?.length) wsRequest.tools = request.tools; if (request.tool_choice) wsRequest.tool_choice = request.tool_choice; if (request.text) wsRequest.text = request.text; - // service_tier is stripped — Codex backend rejects it ("Unsupported service_tier") + if (request.service_tier) wsRequest.service_tier = mapServiceTier(request.service_tier); if (request.prompt_cache_key) wsRequest.prompt_cache_key = request.prompt_cache_key; if (request.include?.length) wsRequest.include = request.include; @@ -240,8 +248,8 @@ export class CodexApi { headers["x-client-request-id"] = crypto.randomUUID(); if (request.turnState) headers["x-codex-turn-state"] = request.turnState; - const { previous_response_id: _pid, useWebSocket: _ws, turnState: _ts, service_tier: _st, ...bodyFields } = request; - const body = JSON.stringify(bodyFields); + const { previous_response_id: _pid, useWebSocket: _ws, turnState: _ts, service_tier: rawSt, ...bodyFields } = request; + const body = JSON.stringify(rawSt ? { ...bodyFields, service_tier: mapServiceTier(rawSt) } : bodyFields); let transportRes; try { diff --git a/src/proxy/ws-transport.ts b/src/proxy/ws-transport.ts index b602650..b8e06dd 100644 --- a/src/proxy/ws-transport.ts +++ b/src/proxy/ws-transport.ts @@ -49,6 +49,7 @@ export interface WsCreateRequest { strict?: boolean; }; }; + service_tier?: string; prompt_cache_key?: string; include?: string[]; // NOTE: `store` and `stream` are intentionally omitted.