Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/web-search/executor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { OcxProviderConfig } from "../types";
import { mergeCodexSelectedHeaders } from "../codex/forward-transport-headers";
import { CODEX_RESPONSES_LITE_HEADER, mergeCodexSelectedHeaders } from "../codex/forward-transport-headers";
import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
import { redactSecretString } from "../lib/redact";
import { sidecarEnter } from "../lib/sidecar-tracker";
Expand Down Expand Up @@ -84,9 +84,11 @@ export async function runWebSearch(
if (settings.reserveCompatibility && settings.model === NATIVE_RESERVE_MODEL) {
return { text: "", sources: [], error: "Luna Reserve compatibility is only available as a conversation model, not a search helper. Choose another search helper model." };
}
const headers = Object.fromEntries(mergeCodexSelectedHeaders(
const mergedHeaders = mergeCodexSelectedHeaders(
{ "Content-Type": "application/json", ...forwardProvider.headers }, selectedForwardHeaders,
));
);
mergedHeaders.delete(CODEX_RESPONSES_LITE_HEADER);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the owning structure docs for the header exception

The ownership rule in src/AGENTS.md requires that “every doc listed for an area is updated in the same change that changes the area.” structure/INDEX.md:522 assigns src/web-search/ to structure/runtime.md, structure/providers-and-adapters.md, and structure/providers/anthropic-progress.md, but none is updated here. This leaves the new exception to selected-header forwarding undocumented in the architecture source of truth. Update the mapped documents to record that OpenAI search helpers strip the Responses Lite marker after merging configured and selected headers, with a reference to the regression coverage.

AGENTS.md reference: src/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

const headers = Object.fromEntries(mergedHeaders);
const body = {
model: settings.model,
instructions: settings.describeImages ? BASE_INSTRUCTION + IMAGE_INSTRUCTION : BASE_INSTRUCTION,
Expand Down
37 changes: 37 additions & 0 deletions tests/web-search/web-search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,43 @@ test("OpenAI web-search execution uses the pinned canonical URL and selected cre
expect(observedProtocol).toBe("http1.1");
});

test("issue #8160 - OpenAI search helper strips responses-lite marker from merged headers", async () => {
const originalFetch = globalThis.fetch;
let capturedHeaders: Headers | undefined;
globalThis.fetch = (async (_url: any, init: any) => {
capturedHeaders = new Headers(init?.headers);
return new Response(
'data: ' + JSON.stringify({ type: "response.completed", response: { status: "completed", output: [] } }) + '\n\n',
{ headers: { "content-type": "text/event-stream" } },
);
}) as typeof fetch;

try {
await runOpenAiWebSearch(
"synthetic query",
{ type: "web_search" },
{
adapter: "openai-responses",
authMode: "forward",
baseUrl: "https://chatgpt.com/backend-api/codex",
headers: { "x-openai-internal-codex-responses-lite": "true", "x-custom-test": "forwarded" },
} as any,
new Headers({
"x-openai-internal-codex-responses-lite": "true",
"chatgpt-account-id": "test-account",
}),
{ model: "gpt-6.1-sol", reasoning: "low", timeoutMs: 1_000 },
);

expect(capturedHeaders).toBeDefined();
expect(capturedHeaders!.has("x-openai-internal-codex-responses-lite")).toBe(false);
expect(capturedHeaders!.get("chatgpt-account-id")).toBe("test-account");
expect(capturedHeaders!.get("x-custom-test")).toBe("forwarded");
} finally {
globalThis.fetch = originalFetch;
}
});

async function collectSse(stream: ReadableStream<Uint8Array>): Promise<{ event?: string; data: Record<string, unknown> }[]> {
const reader = stream.getReader();
const decoder = new TextDecoder();
Expand Down
Loading