Skip to content
Merged
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: 4 additions & 4 deletions src/server/chat-completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,9 @@ async function handleChatCompletionsWithBudget(
const value = req.headers.get(name);
if (value) headers.set(name, value);
}
// Never enrich a caller-auth transport with a credential from another domain.
// Later shadow/thread rewrites strip credentials at the actual Responses boundary.
if (!callerAuthorizationRoute) {
// A noncanonical caller-auth route can use stored main auth only through a sidecar snapshot.
// Later shadow/thread rewrites strip primary credentials at the actual Responses boundary.
if (!callerAuthorizationRoute || (settledRoute && !isCanonicalOpenAiForwardProvider(settledRoute.provider))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid claiming native main when no sidecar is needed

For every unchanged keyless Cursor Chat request, including ordinary text requests with sidecars disabled or no usable OpenAI candidate, this condition now calls tryClaimNativeMainProfileForTurn before sidecar need is evaluated. That claim remains attached to the turn even when getMainAccountToken() returns null, so a long-running Cursor request that never accesses OpenAI can make /api/native-main-profiles/switch wait for its deadline and return MAIN_REQUESTS_ACTIVE, while also fencing new native-main traffic. Gate the claim and credential snapshot on the request actually requiring an OpenAI vision/search sidecar.

Useful? React with 👍 / 👎.

// This enrichment is optional for routed/non-main providers. If native main
// is fenced, omit it and let auth-context reject only a final physical-main
// selection while healthy pool/provider routes continue.
Expand All @@ -271,7 +271,7 @@ async function handleChatCompletionsWithBudget(
if (token) {
const mainHeaders = new Headers({ authorization: `Bearer ${token.accessToken}`, "chatgpt-account-id": token.chatgptAccountId });
openAiSidecarAuth ??= captureExplicitOpenAiCallerAuth(mainHeaders, config);
if (!routeMayChangeCredentialDomain) {
if (!callerAuthorizationRoute && !routeMayChangeCredentialDomain) {
headers.set("authorization", `Bearer ${token.accessToken}`);
headers.set("chatgpt-account-id", token.chatgptAccountId);
}
Expand Down
58 changes: 58 additions & 0 deletions tests/codex-integration/bearer-admission-routed-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type { OcxConfig } from "../../src/types";
import { ownedServiceHomeInspection } from "../helpers/owned-service-home-inspection";
import { removeTreeWithRetry } from "../helpers/remove-tree";
import { fakeChatGptJwt } from "../helpers/fake-chatgpt-jwt";
import { resetVisionDescriptionCache } from "../../src/vision";

/**
* Issue #2132: bearer admission must not require a stored ChatGPT credential.
Expand Down Expand Up @@ -151,6 +152,7 @@ async function withCursorCaptureServer<T>(
}

beforeEach(() => {
resetVisionDescriptionCache();
clearComboTargetCooldowns();
resetSubagentModelFallbackStateForTests();
delete process.env.OPENCODEX_CURSOR_TEST_TOKEN;
Expand Down Expand Up @@ -186,6 +188,7 @@ beforeEach(() => {
});

afterEach(() => {
resetVisionDescriptionCache();
closeRequestHistoryIndex();
clearComboTargetCooldowns();
resetSubagentModelFallbackStateForTests();
Expand Down Expand Up @@ -429,6 +432,61 @@ describe("bearer admission is not reused as a Cursor upstream credential", () =>
});
});

test.each(["owned", "fenced"])("Chat Cursor keeps stored vision auth off its primary wire (%s)", async ownership => {
await withCursorCaptureServer(async (baseUrl, capturedAuth) => {
const config = cursorForwardConfig(baseUrl);
config.providers.cursorcustom!.noVisionModels = ["auto"];
config.providers.openai = {
adapter: "openai-responses", baseUrl: "https://chatgpt.com/backend-api/codex",
authMode: "forward", codexAccountMode: "direct",
};
// Keep this auth fixture independent of the legacy sidecar model migration.
config.visionSidecar = { enabled: true, backend: "openai", model: "gpt-5.6-luna" };
saveConfig(config);
const stored = fakeChatGptJwt({ chatgpt_account_id: "stored_main_acc", exp: Math.floor(Date.now() / 1000) + 3600 });
writeFileSync(join(codexHome, "auth.json"), JSON.stringify({
tokens: { access_token: stored, account_id: "stored_main_acc" },
}));
const sidecar: Array<{ authorization: string | null; account: string | null; claimed: boolean }> = [];
globalThis.fetch = (async (input, init) => {
const url = new URL(input instanceof Request ? input.url : String(input));
if (url.hostname === "chatgpt.com") {
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
sidecar.push({ authorization: headers.get("authorization"), account: headers.get("chatgpt-account-id"),
claimed: getNativeMainProfileRequestCount() > 0 });
return new Response(`data: ${JSON.stringify({ type: "response.output_text.delta", delta: "A red square." })}\n\ndata: [DONE]\n\n`, {
headers: { "content-type": "text/event-stream" },
});
}
return originalFetch(input, init);
}) as typeof fetch;
const server = ownership === "owned" ? await startOwnedServer() : startServer(0, {
inspectNativeCodexOwnership: () => ({ ownership: "foreign", reason: "fixture owned by another service" }),
});
try {
if (ownership === "fenced") expect(await waitForNativeMainStartupGate()).toMatchObject({ status: "blocked" });
const response = await originalFetch(new URL("/v1/chat/completions", server.url), {
method: "POST",
headers: { "content-type": "application/json", "x-opencodex-api-key": ADMISSION_SECRET,
authorization: "Bearer cursor-upstream-token" },
body: JSON.stringify({ model: "cursorcustom/auto", stream: false, messages: [{ role: "user", content: [
{ type: "text", text: "Describe this image" },
{ type: "image_url", image_url: { url: "data:image/png;base64,aGVsbG8taW1hZ2UtYnl0ZXM=" } },
] }] }),
});
await response.text();
// The capture-only Cursor fixture ends without a completion frame.
expect(response.status).toBe(502);
expect(sidecar).toEqual(ownership === "owned"
? [{ authorization: `Bearer ${stored}`, account: "stored_main_acc", claimed: true }] : []);
expect(capturedAuth).toEqual(["Bearer cursor-upstream-token"]);
} finally {
await server.stop(true);
}
expect(getNativeMainProfileRequestCount()).toBe(0);
});
});

test("Chat never falls back from missing Cursor auth to stored main auth", async () => {
await withCursorCaptureServer(async (baseUrl, capturedAuth) => {
saveConfig(cursorForwardConfig(baseUrl));
Expand Down
Loading