From dcfe36c79702ec240b146c45f167ab75ecddd205 Mon Sep 17 00:00:00 2001 From: David Brailovsky Date: Tue, 14 Jul 2026 21:09:29 +0200 Subject: [PATCH] clamp session-id to 64 chars for openai-codex (#6653) fixes #6630 --- packages/ai/src/api/openai-codex-responses.ts | 5 +- packages/ai/test/openai-codex-stream.test.ts | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/packages/ai/src/api/openai-codex-responses.ts b/packages/ai/src/api/openai-codex-responses.ts index fc57ec33..de0d1792 100644 --- a/packages/ai/src/api/openai-codex-responses.ts +++ b/packages/ai/src/api/openai-codex-responses.ts @@ -258,8 +258,9 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons if (nextBody !== undefined) { body = nextBody as RequestBody; } - const websocketRequestId = options?.sessionId || createCodexRequestId(); - const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, options?.sessionId); + const codexSessionId = clampOpenAIPromptCacheKey(options?.sessionId); + const websocketRequestId = codexSessionId || createCodexRequestId(); + const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, codexSessionId); const websocketHeaders = buildWebSocketHeaders( model.headers, options?.headers, diff --git a/packages/ai/test/openai-codex-stream.test.ts b/packages/ai/test/openai-codex-stream.test.ts index fc47572a..793ccc20 100644 --- a/packages/ai/test/openai-codex-stream.test.ts +++ b/packages/ai/test/openai-codex-stream.test.ts @@ -640,6 +640,54 @@ describe("openai-codex streaming", () => { expect(capturedPayload?.prompt_cache_key).toBe("x".repeat(64)); }); + it("clamps Codex session-id header to 64 characters", async () => { + const token = mockToken(); + const sessionId = "x".repeat(67); + let capturedHeaders: Headers | undefined; + const encoder = new TextEncoder(); + vi.stubGlobal( + "fetch", + vi.fn(async (_input: string | URL, init?: RequestInit) => { + capturedHeaders = init?.headers instanceof Headers ? init.headers : undefined; + return new Response( + new ReadableStream({ + start(controller) { + controller.enqueue(encoder.encode(buildSSEPayload({ status: "completed" }))); + controller.close(); + }, + }), + { status: 200, headers: { "content-type": "text/event-stream" } }, + ); + }), + ); + + const model: Model<"openai-codex-responses"> = { + id: "gpt-5.1-codex", + name: "GPT-5.1 Codex", + api: "openai-codex-responses", + provider: "openai-codex", + baseUrl: "https://chatgpt.com/backend-api", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 400000, + maxTokens: 128000, + }; + const context: Context = { + systemPrompt: "You are a helpful assistant.", + messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }], + }; + + await streamOpenAICodexResponses(model, context, { + apiKey: token, + transport: "sse", + sessionId, + }).result(); + + expect(capturedHeaders?.get("session-id")).toBe("x".repeat(64)); + expect(capturedHeaders?.get("x-client-request-id")).toBe("x".repeat(64)); + }); + it("preserves gpt-5.5 xhigh reasoning effort from simple options", async () => { const tempDir = mkdtempSync(join(tmpdir(), "pi-codex-stream-")); process.env.PI_CODING_AGENT_DIR = tempDir;