diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json
index 1a86420683..925ce4fb74 100644
--- a/config/quality/file-size-baseline.json
+++ b/config/quality/file-size-baseline.json
@@ -177,7 +177,7 @@
"tests/unit/account-fallback-service.test.ts": 1563,
"tests/unit/batch_api.test.ts": 1324,
"tests/unit/cc-compatible-provider.test.ts": 1217,
- "tests/unit/chatcore-translation-paths.test.ts": 2769,
+ "tests/unit/chatcore-translation-paths.test.ts": 2776,
"tests/unit/chatgpt-web.test.ts": 3148,
"tests/unit/combo-routing-engine.test.ts": 3449,
"tests/unit/db-migration-runner.test.ts": 1499,
@@ -342,7 +342,7 @@
"_rebaseline_pr1043_minimax_tts": "Upstream port decolua/9router#1043 (toanalien) own growth: audioSpeech.ts 965->1061 (+96). Adds MiniMax T2A v2 TTS dispatch (handleMinimaxSpeech + hexToBytes helper) — provider entry was already in audioRegistry (format: minimax-tts) but no handler existed, falling through to the OpenAI-compatible default that fails (T2A has custom shape + hex-encoded audio + base_resp envelope). New branch sits next to the other inline provider branches (xiaomi-mimo, coqui, tortoise, aws-polly) — extracting would just create indirection. Covered by tests/unit/minimax-tts-1043.test.ts (3 tests, GREEN: success, base_resp error, invalid-hex).",
"_rebaseline_pr4592_exclude_exhausted_auto": "Reconcile #4592 already-merged growth: combo.ts 2991->3036 (+45, terminal-status quota-cutoff exclusion in buildAutoCandidates + opt-in gate). Fast-gate PR->release does not run check:file-size.",
"open-sse/executors/antigravity.ts": 1528,
- "open-sse/executors/base.ts": 1562,
+ "open-sse/executors/base.ts": 1578,
"open-sse/executors/chatgpt-web.ts": 3241,
"open-sse/executors/codex.ts": 1534,
"open-sse/executors/cursor.ts": 1560,
@@ -365,7 +365,7 @@
"open-sse/services/rateLimitManager.ts": 1060,
"open-sse/translator/response/openai-responses.ts": 1174,
"open-sse/utils/cursorAgentProtobuf.ts": 1505,
- "open-sse/utils/stream.ts": 2887,
+ "open-sse/utils/stream.ts": 2889,
"src/app/(dashboard)/dashboard/HomePageClient.tsx": 1381,
"src/app/(dashboard)/dashboard/analytics/ComboHealthTab.tsx": 1031,
"src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 3117,
@@ -413,5 +413,6 @@
"_rebaseline_2026_07_28_8860_tokenrefresh_projectid": "PR #8860 (fix/antigravity-projectid-centralized) own test growth: tests/unit/token-refresh-service.test.ts 1311->1378 (+67 = 4 cases covering projectId discovery on the tokenRefresh.ts path — the Dashboard/health-check refresh route, which #8842 did not reach since that fixed the executor path). Covered by the same file.",
"_rebaseline_2026_07_28_8861_xiaomi_token_plan": "PR #8861 (feat/xiaomi-token-plan-protocol-selector) own growth: EditConnectionModal.tsx 1283->1316 (+33 = the per-connection API-protocol selector field) and open-sse/executors/base.ts 1540->1562 (+22 = alternate-format resolution at the existing buildUrl/headers chokepoint). Both are irreducible wiring at existing call sites.",
"_rebaseline_2026_07_28_8863_firefly_detail_level": "PR #8863 (fix/adobe-firefly-gpt-detail-level-max) own growth: adobeFireflyClient.ts 2317->2322 (+5 = gpt-image detailLevel defaulting to maximal at the existing payload-build site). Covered by tests/unit/adobe-firefly.test.ts.",
- "_rebaseline_2026_07_29_8281_home_quickstart_prefetch": "Release v3.8.49 base-red fix (no PR — captain sweep): src/app/(dashboard)/dashboard/HomePageClient.tsx 1377->1381 (+4). #8292 added prefetch={false} to the sidebar but left /home's five quick-start Links prefetching, so first paint still fired 12 speculative RSC requests — caught by navigation.spec.ts only after the e2e helper bug (APP_ROUTE_PATTERN missing /home) was repaired in the same cycle. Growth is the five prefetch attributes; it was offset first by extracting the repeated className literals (INLINE_LINK x4, DOCS_LINK x1), which collapsed five wrapped blocks back to one line each — a naive fix measured 1391. Guard: tests/unit/sidebar-prefetch-policy-8281.test.ts."
+ "_rebaseline_2026_07_29_8281_home_quickstart_prefetch": "Release v3.8.49 base-red fix (no PR — captain sweep): src/app/(dashboard)/dashboard/HomePageClient.tsx 1377->1381 (+4). #8292 added prefetch={false} to the sidebar but left /home's five quick-start Links prefetching, so first paint still fired 12 speculative RSC requests — caught by navigation.spec.ts only after the e2e helper bug (APP_ROUTE_PATTERN missing /home) was repaired in the same cycle. Growth is the five prefetch attributes; it was offset first by extracting the repeated className literals (INLINE_LINK x4, DOCS_LINK x1), which collapsed five wrapped blocks back to one line each — a naive fix measured 1391. Guard: tests/unit/sidebar-prefetch-policy-8281.test.ts.",
+ "_rebaseline_2026_08_02_v3850_agentrouter_responses": "Release v3.8.50 AgentRouter/Codex compatibility reconciliation. open-sse/executors/base.ts 1562->1578: #9190 wires AgentRouter's selected Claude/OpenAI/Responses protocol through the existing executor URL, auth, identity-header and fingerprint chokepoints; the reusable alternate resolver remains outside base.ts. open-sse/utils/stream.ts 2887->2889: #9213 evaluates Responses ID and usage normalization independently so response.completed always receives finite usage.total_tokens instead of short-circuiting after an ID rewrite. tests/unit/chatcore-translation-paths.test.ts 2769->2776: #9191 updates the existing Claude-Code bridge assertions for the dynamic AgentRouter wire image. PR #9224 offsets its own chatCore growth by extracting the AgentRouter protocol decisions into chatCore/agentRouterProtocol.ts, leaving chatCore below its frozen ceiling. Covered by agentrouter executor/chatCore protocol tests, chatcore translation-path tests, and responses-commentary-passthrough tests."
}
diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts
index 81e97f1c98..941a5e9b45 100644
--- a/open-sse/handlers/chatCore.ts
+++ b/open-sse/handlers/chatCore.ts
@@ -71,6 +71,7 @@ import { checkHeapPressureGuard } from "../utils/heapPressure.ts";
import { normalizeHeaders } from "../utils/headers.ts";
import { resolveChatCoreRequestFormat } from "./chatCore/requestFormat.ts";
import { resolveChatCoreTargetFormat } from "./chatCore/targetFormat.ts";
+import { stripStore, usesClaudeBridge } from "./chatCore/agentRouterProtocol.ts";
import { defaultClaudeToolType } from "./chatCore/claudeToolDefaults.ts";
import { injectSystemPrompt, injectCustomSystemPrompt } from "../services/systemPrompt.ts";
import { translateRequest, needsTranslation } from "../translator/index.ts";
@@ -341,7 +342,6 @@ import { OMNIROUTE_RESPONSE_HEADERS } from "@/shared/constants/headers";
import { getClaudeCodeCompatibleRequestDefaults } from "@/lib/providers/requestDefaults";
import {
buildClaudeCodeCompatibleRequest,
- isClaudeCodeCompatibleProvider,
resolveClaudeCodeCompatibleSessionId,
} from "../services/claudeCodeCompatible.ts";
import { setGeminiThoughtSignatureMode } from "../services/geminiThoughtSignatureStore.ts";
@@ -751,6 +751,7 @@ export async function handleChatCore({
provider,
resolvedModel,
apiFormat,
+ sourceFormat,
customModelTargetFormat,
providerSpecificData: credentials?.providerSpecificData,
});
@@ -1920,15 +1921,7 @@ export async function handleChatCore({
let translatedBody = body;
const isClaudePassthrough = sourceFormat === FORMATS.CLAUDE && targetFormat === FORMATS.CLAUDE;
- // A provider may expose the Claude Code wire image as its default protocol while
- // selecting a declared OpenAI alternate on a connection (for example AgentRouter).
- // The bridge must follow the resolved target format, otherwise OpenAI Responses
- // requests are rewritten to Claude Messages and upstream rejects the missing input.
- const configuredTargetFormat = credentials?.providerSpecificData?.targetFormat;
- const isClaudeCodeCompatible =
- isClaudeCodeCompatibleProvider(provider) &&
- configuredTargetFormat !== FORMATS.OPENAI &&
- configuredTargetFormat !== FORMATS.OPENAI_RESPONSES;
+ const isClaudeCodeCompatible = usesClaudeBridge(provider, targetFormat, credentials);
const isClaudeCodeSemanticPassthrough = isClaudeCodeSemanticPassthroughRequest({
provider,
sourceFormat,
@@ -2504,10 +2497,7 @@ export async function handleChatCore({
log?.debug?.("PARAMS", `Renamed max_completion_tokens to max_tokens for ${model}`);
}
- // OpenAI's `store` parameter is not supported by most compatible providers and breaks them
- if (provider !== "openai" && "store" in translatedBody) {
- delete translatedBody.store;
- }
+ stripStore(translatedBody, provider, targetFormat);
// Chat clients may send stream_options.include_usage, but OpenAI Responses
// upstreams (including Azure AI Foundry /responses) reject stream_options.
diff --git a/open-sse/handlers/chatCore/agentRouterProtocol.ts b/open-sse/handlers/chatCore/agentRouterProtocol.ts
new file mode 100644
index 0000000000..b5cbe5dc71
--- /dev/null
+++ b/open-sse/handlers/chatCore/agentRouterProtocol.ts
@@ -0,0 +1,35 @@
+/**
+ * Per-request AgentRouter protocol decisions kept outside the chatCore orchestration god-file.
+ * AgentRouter exposes Claude Messages, OpenAI Chat, and OpenAI Responses as distinct upstream
+ * protocols, so its dynamic target format must override the connection's default Claude wire image.
+ */
+
+import { isClaudeCodeCompatibleProvider } from "../../services/claudeCodeCompatible.ts";
+import { FORMATS } from "../../translator/formats.ts";
+
+export function usesClaudeBridge(
+ provider: string,
+ targetFormat: string,
+ credentials: unknown
+): boolean {
+ const configuredTargetFormat = (
+ credentials as { providerSpecificData?: { targetFormat?: unknown } | null } | null | undefined
+ )?.providerSpecificData?.targetFormat;
+ const effectiveFormat = provider === "agentrouter" ? targetFormat : configuredTargetFormat;
+ return (
+ isClaudeCodeCompatibleProvider(provider) &&
+ effectiveFormat !== FORMATS.OPENAI &&
+ effectiveFormat !== FORMATS.OPENAI_RESPONSES
+ );
+}
+
+export function stripStore(
+ body: Record,
+ provider: string,
+ targetFormat: string
+): void {
+ const supportsStore =
+ provider === "openai" ||
+ (provider === "agentrouter" && targetFormat === FORMATS.OPENAI_RESPONSES);
+ if (!supportsStore && "store" in body) delete body.store;
+}
diff --git a/open-sse/handlers/chatCore/executionCredentials.ts b/open-sse/handlers/chatCore/executionCredentials.ts
index 829d9e9ae2..1a96ef92ee 100644
--- a/open-sse/handlers/chatCore/executionCredentials.ts
+++ b/open-sse/handlers/chatCore/executionCredentials.ts
@@ -5,8 +5,8 @@
* Pure builder extracted from handleChatCore: derives the per-execution credentials object from the
* resolved request context. Applies the native-Codex passthrough endpoint override, forces
* apiType=responses (and the responses-upstream marker) for Azure AI Foundry / OCI when the model
- * routes to the OpenAI Responses format, and threads the Claude Code session id when present.
- * Side-effect-free; behaviour is byte-identical to the previous inline closure.
+ * routes to the OpenAI Responses format, synchronizes AgentRouter's per-request alternate protocol,
+ * and threads the Claude Code session id when present. Side-effect-free.
*/
import { getKimiCodeStaticThinkingPolicy } from "../../config/providers/registry/kimi/coding/runtime.ts";
@@ -128,6 +128,16 @@ export function resolveExecutionCredentials(opts: {
providerSpecificData.targetFormat = targetFormat;
}
+ // AgentRouter exposes Claude, OpenAI Chat, and OpenAI Responses on distinct URLs with distinct
+ // auth schemes. Keep the executor's URL/header resolution synchronized with chatCore's resolved
+ // per-request protocol without persisting the inferred selection back to the connection.
+ if (
+ provider === "agentrouter" &&
+ (targetFormat === FORMATS.OPENAI || targetFormat === FORMATS.OPENAI_RESPONSES)
+ ) {
+ providerSpecificData.targetFormat = targetFormat;
+ }
+
applyKimiExecutionMetadata(providerSpecificData, provider, targetFormat, modelInfo);
const withApiType = {
diff --git a/open-sse/handlers/chatCore/targetFormat.ts b/open-sse/handlers/chatCore/targetFormat.ts
index 8b1ed014c6..9bb2f0da7a 100644
--- a/open-sse/handlers/chatCore/targetFormat.ts
+++ b/open-sse/handlers/chatCore/targetFormat.ts
@@ -4,10 +4,11 @@
*
* Pure resolution of the provider alias + the upstream target format used to translate the request:
* apiFormat==="responses" forces OpenAI Responses; otherwise the model's registry target format, then
- * the per-model custom override (#2905), then the provider default. Returns both `alias` (reused by
+ * the per-model custom override (#2905), then AgentRouter's matching inbound protocol when the
+ * connection has no explicit override, then the provider default. Returns both `alias` (reused by
* the handler when stripping the `alias/` prefix off the upstream model id) and `targetFormat`.
- * Side-effect-free; byte-identical to the previous inline block. Sits alongside the other
- * request-setup resolvers (resolveChatCoreRequestSetup / resolveChatCoreRequestFormat).
+ * Side-effect-free; sits alongside the other request-setup resolvers
+ * (resolveChatCoreRequestSetup / resolveChatCoreRequestFormat).
*/
import { PROVIDER_ID_TO_ALIAS, getModelTargetFormat } from "../../config/providerModels.ts";
@@ -18,16 +19,38 @@ export function resolveChatCoreTargetFormat(opts: {
provider: string;
resolvedModel: string;
apiFormat: string | undefined;
+ sourceFormat?: string;
customModelTargetFormat: string | undefined;
providerSpecificData: unknown;
}) {
- const { provider, resolvedModel, apiFormat, customModelTargetFormat, providerSpecificData } = opts;
+ const {
+ provider,
+ resolvedModel,
+ apiFormat,
+ sourceFormat,
+ customModelTargetFormat,
+ providerSpecificData,
+ } = opts;
const alias = PROVIDER_ID_TO_ALIAS[provider] || provider;
const modelTargetFormat = getModelTargetFormat(alias, resolvedModel);
+ const explicitConnectionTargetFormat = (
+ providerSpecificData as { targetFormat?: unknown } | null | undefined
+ )?.targetFormat;
+ const inferredAgentRouterTargetFormat =
+ provider === "agentrouter" &&
+ !(typeof explicitConnectionTargetFormat === "string" && explicitConnectionTargetFormat) &&
+ (sourceFormat === FORMATS.OPENAI_RESPONSES ||
+ sourceFormat === FORMATS.OPENAI ||
+ sourceFormat === FORMATS.CLAUDE)
+ ? sourceFormat
+ : undefined;
const targetFormat =
apiFormat === "responses"
? FORMATS.OPENAI_RESPONSES
- : modelTargetFormat || customModelTargetFormat || getTargetFormat(provider, providerSpecificData);
+ : modelTargetFormat ||
+ customModelTargetFormat ||
+ inferredAgentRouterTargetFormat ||
+ getTargetFormat(provider, providerSpecificData);
return { alias, targetFormat };
}
diff --git a/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx b/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx
index 04e8a2f9f9..fe7f3eaaa6 100644
--- a/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx
+++ b/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx
@@ -171,7 +171,7 @@ const DEFAULT_SYSTEM_TRANSFORMS_CLIENT = {
entrypoint: "sdk-cli",
versionFormat: "ex-machina",
cchAlgo: "sha256-first-user",
- buildRevision: "250",
+ buildRevision: "1f2",
},
],
},
diff --git a/tests/snapshots/provider/translate-path.json b/tests/snapshots/provider/translate-path.json
index 05494b731c..f9414aa06e 100644
--- a/tests/snapshots/provider/translate-path.json
+++ b/tests/snapshots/provider/translate-path.json
@@ -28,7 +28,7 @@
"apiKey": {
"Accept": "text/event-stream",
"Content-Type": "application/json",
- "User-Agent": "claude-cli/2.1.219 (external, sdk-cli)",
+ "User-Agent": "claude-cli/2.1.220 (external, sdk-cli)",
"X-Stainless-Arch": "",
"X-Stainless-Lang": "js",
"X-Stainless-OS": "MacOS",
@@ -47,7 +47,7 @@
"nonStream": {
"Accept": "application/json",
"Content-Type": "application/json",
- "User-Agent": "claude-cli/2.1.219 (external, sdk-cli)",
+ "User-Agent": "claude-cli/2.1.220 (external, sdk-cli)",
"X-Stainless-Arch": "",
"X-Stainless-Lang": "js",
"X-Stainless-OS": "MacOS",
@@ -66,7 +66,7 @@
"oauth": {
"Accept": "text/event-stream",
"Content-Type": "application/json",
- "User-Agent": "claude-cli/2.1.219 (external, sdk-cli)",
+ "User-Agent": "claude-cli/2.1.220 (external, sdk-cli)",
"X-Stainless-Arch": "",
"X-Stainless-Lang": "js",
"X-Stainless-OS": "MacOS",
@@ -874,7 +874,7 @@
"Anthropic-Dangerous-Direct-Browser-Access": "true",
"Anthropic-Version": "2023-06-01",
"Content-Type": "application/json",
- "User-Agent": "claude-cli/2.1.219 (external, cli)",
+ "User-Agent": "claude-cli/2.1.220 (external, cli)",
"X-App": "cli",
"X-Stainless-Arch": "",
"X-Stainless-Helper-Method": "stream",
@@ -892,7 +892,7 @@
"Anthropic-Dangerous-Direct-Browser-Access": "true",
"Anthropic-Version": "2023-06-01",
"Content-Type": "application/json",
- "User-Agent": "claude-cli/2.1.219 (external, cli)",
+ "User-Agent": "claude-cli/2.1.220 (external, cli)",
"X-App": "cli",
"X-Stainless-Arch": "",
"X-Stainless-Helper-Method": "stream",
@@ -911,7 +911,7 @@
"Anthropic-Dangerous-Direct-Browser-Access": "true",
"Anthropic-Version": "2023-06-01",
"Content-Type": "application/json",
- "User-Agent": "claude-cli/2.1.219 (external, cli)",
+ "User-Agent": "claude-cli/2.1.220 (external, cli)",
"X-App": "cli",
"X-Stainless-Arch": "",
"X-Stainless-Helper-Method": "stream",
@@ -1153,16 +1153,16 @@
"Authorization": "Bearer ",
"Content-Type": "application/json",
"Openai-Beta": "responses=experimental",
- "User-Agent": "codex-cli/0.144.1 (; )",
- "Version": "0.144.1",
+ "User-Agent": "codex-cli/0.146.0 (; )",
+ "Version": "0.146.0",
"X-Codex-Beta-Features": "responses_websockets"
},
"nonStream": {
"Authorization": "Bearer ",
"Content-Type": "application/json",
"Openai-Beta": "responses=experimental",
- "User-Agent": "codex-cli/0.144.1 (; )",
- "Version": "0.144.1",
+ "User-Agent": "codex-cli/0.146.0 (; )",
+ "Version": "0.146.0",
"X-Codex-Beta-Features": "responses_websockets"
},
"oauth": {
@@ -1170,8 +1170,8 @@
"Authorization": "Bearer ",
"Content-Type": "application/json",
"Openai-Beta": "responses=experimental",
- "User-Agent": "codex-cli/0.144.1 (; )",
- "Version": "0.144.1",
+ "User-Agent": "codex-cli/0.146.0 (; )",
+ "Version": "0.146.0",
"X-Codex-Beta-Features": "responses_websockets"
}
},
diff --git a/tests/unit/agentrouter-chatcore-protocols.test.ts b/tests/unit/agentrouter-chatcore-protocols.test.ts
index 87a539643e..dc4b7dd3c2 100644
--- a/tests/unit/agentrouter-chatcore-protocols.test.ts
+++ b/tests/unit/agentrouter-chatcore-protocols.test.ts
@@ -22,6 +22,13 @@ function noopLog() {
};
}
+function createSseResponse(events: string[]) {
+ return new Response(`${events.join("\n\n")}\n\n`, {
+ status: 200,
+ headers: { "Content-Type": "text/event-stream" },
+ });
+}
+
async function waitForAsyncSideEffects() {
await new Promise((resolve) => setImmediate(resolve));
await new Promise((resolve) => setTimeout(resolve, 20));
@@ -41,7 +48,7 @@ test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
-test("AgentRouter Responses connections send a native Responses body through chatCore", async () => {
+test("AgentRouter Responses requests automatically use the native Responses protocol", async () => {
let captured: { url: string; headers: Headers; body: Record } | null = null;
globalThis.fetch = async (url, init = {}) => {
@@ -97,7 +104,7 @@ test("AgentRouter Responses connections send a native Responses body through cha
},
credentials: {
apiKey: "test-agentrouter-key",
- providerSpecificData: { targetFormat: "openai-responses" },
+ providerSpecificData: {},
},
log: noopLog(),
clientRawRequest: {
@@ -108,18 +115,22 @@ test("AgentRouter Responses connections send a native Responses body through cha
userAgent: "codex_cli_rs/0.146.0",
});
- assert.equal(result.success, true);
assert.ok(captured);
- assert.equal(captured.url, "https://agentrouter.org/v1/responses");
+ assert.equal(
+ captured.url,
+ "https://agentrouter.org/v1/responses",
+ JSON.stringify(captured.body, null, 2)
+ );
+ assert.ok("input" in captured.body, JSON.stringify(captured.body, null, 2));
+ assert.equal(result.success, true);
assert.equal(captured.headers.get("authorization"), "Bearer test-agentrouter-key");
assert.equal(captured.headers.get("originator"), "codex_cli_rs");
- assert.ok("input" in captured.body);
assert.equal("system" in captured.body, false);
assert.equal("thinking" in captured.body, false);
assert.equal("output_config" in captured.body, false);
});
-test("AgentRouter OpenAI Chat connections keep the OpenAI request shape through chatCore", async () => {
+test("AgentRouter OpenAI Chat requests automatically use the native Chat protocol", async () => {
let captured: { url: string; headers: Headers; body: Record } | null = null;
globalThis.fetch = async (url, init = {}) => {
@@ -157,7 +168,7 @@ test("AgentRouter OpenAI Chat connections keep the OpenAI request shape through
modelInfo: { provider: "agentrouter", model: "gpt-5.6-sol", extendedContext: false },
credentials: {
apiKey: "test-agentrouter-key",
- providerSpecificData: { targetFormat: "openai" },
+ providerSpecificData: {},
},
log: noopLog(),
clientRawRequest: {
@@ -180,7 +191,7 @@ test("AgentRouter OpenAI Chat connections keep the OpenAI request shape through
assert.equal("output_config" in captured.body, false);
});
-test("AgentRouter default Claude connections retain the Claude Code bridge through chatCore", async () => {
+test("AgentRouter Anthropic requests automatically use the native Messages protocol", async () => {
let captured: { url: string; headers: Headers; body: Record } | null = null;
globalThis.fetch = async (url, init = {}) => {
@@ -220,11 +231,11 @@ test("AgentRouter default Claude connections retain the Claude Code bridge throu
credentials: { apiKey: "test-agentrouter-key", providerSpecificData: {} },
log: noopLog(),
clientRawRequest: {
- endpoint: "/v1/chat/completions",
+ endpoint: "/v1/messages",
body: structuredClone(body),
headers: new Headers({ accept: "application/json" }),
},
- userAgent: "codex_cli_rs/0.146.0",
+ userAgent: "claude-cli/2.1.19",
});
assert.equal(result.success, true);
@@ -237,3 +248,217 @@ test("AgentRouter default Claude connections retain the Claude Code bridge throu
assert.equal(captured.body.thinking.type, "adaptive");
assert.equal(captured.body.output_config.effort, "xhigh");
});
+
+test("AgentRouter Responses streaming stays native without a connection protocol override", async () => {
+ let captured: { url: string; headers: Headers; body: Record } | null = null;
+
+ globalThis.fetch = async (url, init = {}) => {
+ captured = {
+ url: String(url),
+ headers: new Headers(init.headers),
+ body: JSON.parse(String(init.body || "{}")),
+ };
+ return createSseResponse([
+ "event: response.created\ndata: " +
+ JSON.stringify({
+ type: "response.created",
+ response: { id: "resp_agentrouter", object: "response", status: "in_progress" },
+ }),
+ "event: response.output_text.delta\ndata: " +
+ JSON.stringify({
+ type: "response.output_text.delta",
+ item_id: "msg_agentrouter",
+ output_index: 0,
+ content_index: 0,
+ delta: "OK",
+ }),
+ "event: response.completed\ndata: " +
+ JSON.stringify({
+ type: "response.completed",
+ response: {
+ id: "resp_agentrouter",
+ object: "response",
+ status: "completed",
+ model: "gpt-5.6-sol",
+ output: [],
+ usage: { input_tokens: 4, output_tokens: 1, total_tokens: 5 },
+ },
+ }),
+ ]);
+ };
+
+ const body = {
+ model: "agentrouter/gpt-5.6-sol",
+ input: "Reply with exactly OK",
+ max_output_tokens: 16,
+ stream: true,
+ store: false,
+ };
+ const result = await handleChatCore({
+ body: structuredClone(body),
+ modelInfo: { provider: "agentrouter", model: "gpt-5.6-sol", extendedContext: false },
+ credentials: { apiKey: "test-agentrouter-key", providerSpecificData: {} },
+ log: noopLog(),
+ clientRawRequest: {
+ endpoint: "/v1/responses",
+ body: structuredClone(body),
+ headers: new Headers({ accept: "text/event-stream", originator: "codex_cli_rs" }),
+ },
+ userAgent: "codex_cli_rs/0.146.0",
+ });
+
+ assert.equal(result.success, true);
+ assert.ok(captured);
+ assert.equal(captured.url, "https://agentrouter.org/v1/responses");
+ assert.equal(captured.headers.get("authorization"), "Bearer test-agentrouter-key");
+ assert.deepEqual(captured.body.input, [
+ {
+ type: "message",
+ role: "user",
+ content: [{ type: "input_text", text: body.input }],
+ },
+ ]);
+ assert.equal(captured.body.stream, true);
+ assert.equal(captured.body.store, false);
+ assert.equal("system" in captured.body, false);
+ assert.equal("thinking" in captured.body, false);
+ const text = await result.response.text();
+ assert.match(text, /response\.output_text\.delta/);
+ assert.match(text, /response\.completed/);
+ assert.match(text, /"total_tokens":5/);
+});
+
+test("AgentRouter OpenAI Chat streaming stays native without a connection protocol override", async () => {
+ let captured: { url: string; headers: Headers; body: Record } | null = null;
+
+ globalThis.fetch = async (url, init = {}) => {
+ captured = {
+ url: String(url),
+ headers: new Headers(init.headers),
+ body: JSON.parse(String(init.body || "{}")),
+ };
+ return createSseResponse([
+ "data: " +
+ JSON.stringify({
+ id: "chatcmpl_agentrouter",
+ object: "chat.completion.chunk",
+ created: 1,
+ model: "gpt-5.6-sol",
+ choices: [{ index: 0, delta: { role: "assistant", content: "OK" }, finish_reason: null }],
+ }),
+ "data: " +
+ JSON.stringify({
+ id: "chatcmpl_agentrouter",
+ object: "chat.completion.chunk",
+ created: 1,
+ model: "gpt-5.6-sol",
+ choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
+ }),
+ "data: " +
+ JSON.stringify({
+ id: "chatcmpl_agentrouter",
+ object: "chat.completion.chunk",
+ created: 1,
+ model: "gpt-5.6-sol",
+ choices: [],
+ usage: { prompt_tokens: 4, completion_tokens: 1, total_tokens: 5 },
+ }),
+ "data: [DONE]",
+ ]);
+ };
+
+ const body = {
+ model: "agentrouter/gpt-5.6-sol",
+ messages: [{ role: "user", content: "Reply with exactly OK" }],
+ max_completion_tokens: 16,
+ stream: true,
+ };
+ const result = await handleChatCore({
+ body: structuredClone(body),
+ modelInfo: { provider: "agentrouter", model: "gpt-5.6-sol", extendedContext: false },
+ credentials: { apiKey: "test-agentrouter-key", providerSpecificData: {} },
+ log: noopLog(),
+ clientRawRequest: {
+ endpoint: "/v1/chat/completions",
+ body: structuredClone(body),
+ headers: new Headers({ accept: "text/event-stream" }),
+ },
+ userAgent: "codex_cli_rs/0.146.0",
+ });
+
+ assert.equal(result.success, true);
+ assert.ok(captured);
+ assert.equal(captured.url, "https://agentrouter.org/v1/chat/completions");
+ assert.equal(captured.headers.get("authorization"), "Bearer test-agentrouter-key");
+ assert.equal(captured.body.stream, true);
+ assert.deepEqual(captured.body.messages, body.messages);
+ const text = await result.response.text();
+ assert.match(text, /"content":"OK"/);
+ assert.match(text, /\[DONE\]/);
+});
+
+test("AgentRouter Anthropic streaming stays native without a connection protocol override", async () => {
+ let captured: { url: string; headers: Headers; body: Record } | null = null;
+
+ globalThis.fetch = async (url, init = {}) => {
+ captured = {
+ url: String(url),
+ headers: new Headers(init.headers),
+ body: JSON.parse(String(init.body || "{}")),
+ };
+ return createSseResponse([
+ "event: message_start\ndata: " +
+ JSON.stringify({
+ type: "message_start",
+ message: {
+ id: "msg_agentrouter",
+ type: "message",
+ role: "assistant",
+ model: "claude-opus-4-8",
+ usage: { input_tokens: 4, output_tokens: 0 },
+ },
+ }),
+ "event: content_block_delta\ndata: " +
+ JSON.stringify({
+ type: "content_block_delta",
+ index: 0,
+ delta: { type: "text_delta", text: "OK" },
+ }),
+ "event: message_delta\ndata: " +
+ JSON.stringify({
+ type: "message_delta",
+ delta: { stop_reason: "end_turn" },
+ usage: { output_tokens: 1 },
+ }),
+ 'event: message_stop\ndata: {"type":"message_stop"}',
+ ]);
+ };
+
+ const body = {
+ model: "agentrouter/claude-opus-4-8",
+ messages: [{ role: "user", content: "Reply with exactly OK" }],
+ max_tokens: 16,
+ stream: true,
+ };
+ const result = await handleChatCore({
+ body: structuredClone(body),
+ modelInfo: { provider: "agentrouter", model: "claude-opus-4-8", extendedContext: false },
+ credentials: { apiKey: "test-agentrouter-key", providerSpecificData: {} },
+ log: noopLog(),
+ clientRawRequest: {
+ endpoint: "/v1/messages",
+ body: structuredClone(body),
+ headers: new Headers({ accept: "text/event-stream" }),
+ },
+ userAgent: "claude-cli/2.1.19",
+ });
+
+ assert.equal(result.success, true);
+ assert.ok(captured);
+ assert.equal(captured.url, "https://agentrouter.org/v1/messages?beta=true");
+ assert.equal(captured.headers.get("x-api-key"), "test-agentrouter-key");
+ assert.equal(captured.body.stream, true);
+ const text = await result.response.text();
+ assert.match(text, /content_block_delta/);
+ assert.match(text, /message_stop/);
+});
diff --git a/tests/unit/anthropic-cache-fingerprint.test.ts b/tests/unit/anthropic-cache-fingerprint.test.ts
index 8272cb1bb2..ebdb9690af 100644
--- a/tests/unit/anthropic-cache-fingerprint.test.ts
+++ b/tests/unit/anthropic-cache-fingerprint.test.ts
@@ -5,6 +5,6 @@ import { CLAUDE_CODE_CLIENT_BILLING_VERSION } from "../../src/shared/constants/c
describe("Anthropic billing header fingerprint (#1638)", () => {
it("uses the immutable build revision captured from the signed CLI", () => {
- assert.equal(CLAUDE_CODE_CLIENT_BILLING_VERSION, "2.1.219.250");
+ assert.equal(CLAUDE_CODE_CLIENT_BILLING_VERSION, "2.1.220.1f2");
});
});
diff --git a/tests/unit/cc-bridge-transforms.test.ts b/tests/unit/cc-bridge-transforms.test.ts
index 680500a9cd..ba915dcaca 100644
--- a/tests/unit/cc-bridge-transforms.test.ts
+++ b/tests/unit/cc-bridge-transforms.test.ts
@@ -52,7 +52,7 @@ test("DEFAULT_CC_BRIDGE_PIPELINE places billing header at [0] and identity at [1
DEFAULT_CC_BRIDGE_PIPELINE
);
const blocks = result.body.system as any[];
- assert.ok(blocks[0].text.startsWith("x-anthropic-billing-header: cc_version=2.1.219.250;"));
+ assert.ok(blocks[0].text.startsWith("x-anthropic-billing-header: cc_version=2.1.220.1f2;"));
assert.equal(blocks[1].text, CLAUDE_AGENT_SDK_IDENTITY);
});
diff --git a/tests/unit/chatcore-execution-credentials.test.ts b/tests/unit/chatcore-execution-credentials.test.ts
index 11bd008782..692a662fbc 100644
--- a/tests/unit/chatcore-execution-credentials.test.ts
+++ b/tests/unit/chatcore-execution-credentials.test.ts
@@ -79,6 +79,22 @@ test("non azure/oci providers never get apiType forcing", () => {
assert.equal(psd._omnirouteForceResponsesUpstream, undefined);
});
+test("AgentRouter threads the resolved Responses protocol only into execution credentials", () => {
+ const credentials = { providerSpecificData: { apiKeyHealth: {} } };
+ const out = resolveExecutionCredentials({
+ ...base,
+ provider: "agentrouter",
+ targetFormat: RESPONSES,
+ credentials,
+ }) as Record;
+
+ assert.deepEqual(out.providerSpecificData, {
+ apiKeyHealth: {},
+ targetFormat: RESPONSES,
+ });
+ assert.deepEqual(credentials.providerSpecificData, { apiKeyHealth: {} });
+});
+
test("ccSessionId is threaded into providerSpecificData when present", () => {
const out = resolveExecutionCredentials({
...base,
diff --git a/tests/unit/chatcore-target-format.test.ts b/tests/unit/chatcore-target-format.test.ts
index affa94a113..2fb4da01dd 100644
--- a/tests/unit/chatcore-target-format.test.ts
+++ b/tests/unit/chatcore-target-format.test.ts
@@ -4,11 +4,14 @@
// upstream target format: apiFormat==="responses" forces OpenAI Responses; otherwise the model's
// registry target format, then the custom-model override, then the provider default. Returns both
// `alias` (reused downstream when stripping the alias/ prefix off the upstream model) and
-// `targetFormat`. Asserted against the inline composition so the delegation stays byte-identical.
+// `targetFormat`.
import { test } from "node:test";
import assert from "node:assert/strict";
import { resolveChatCoreTargetFormat } from "../../open-sse/handlers/chatCore/targetFormat.ts";
-import { PROVIDER_ID_TO_ALIAS, getModelTargetFormat } from "../../open-sse/config/providerModels.ts";
+import {
+ PROVIDER_ID_TO_ALIAS,
+ getModelTargetFormat,
+} from "../../open-sse/config/providerModels.ts";
import { getTargetFormat } from "../../open-sse/services/provider.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
@@ -24,7 +27,9 @@ function expected(
const targetFormat =
apiFormat === "responses"
? FORMATS.OPENAI_RESPONSES
- : modelTargetFormat || customModelTargetFormat || getTargetFormat(provider, providerSpecificData);
+ : modelTargetFormat ||
+ customModelTargetFormat ||
+ getTargetFormat(provider, providerSpecificData);
return { alias, targetFormat };
}
@@ -33,6 +38,7 @@ test("apiFormat='responses' short-circuits to OPENAI_RESPONSES (alias still reso
provider: "openai",
resolvedModel: "gpt-4o",
apiFormat: "responses",
+ sourceFormat: FORMATS.OPENAI,
customModelTargetFormat: undefined,
providerSpecificData: undefined,
});
@@ -45,6 +51,7 @@ test("delegates byte-identically for a normal model (no apiFormat / no custom ov
provider: "openai",
resolvedModel: "gpt-4o",
apiFormat: undefined,
+ sourceFormat: FORMATS.OPENAI,
customModelTargetFormat: undefined,
providerSpecificData: undefined,
});
@@ -59,6 +66,7 @@ test("customModelTargetFormat is used when the model has no registry target form
provider: "openai",
resolvedModel: customModel,
apiFormat: undefined,
+ sourceFormat: FORMATS.OPENAI,
customModelTargetFormat: "claude",
providerSpecificData: undefined,
});
@@ -71,17 +79,31 @@ test("falls back to getTargetFormat(provider) when neither model nor custom form
provider: "openai",
resolvedModel: customModel,
apiFormat: undefined,
+ sourceFormat: FORMATS.OPENAI,
customModelTargetFormat: undefined,
providerSpecificData: undefined,
});
assert.equal(r.targetFormat, getTargetFormat("openai", undefined));
});
+test("AgentRouter explicit connection protocol overrides the inferred inbound protocol", () => {
+ const r = resolveChatCoreTargetFormat({
+ provider: "agentrouter",
+ resolvedModel: "gpt-5.6-sol",
+ apiFormat: undefined,
+ sourceFormat: FORMATS.OPENAI_RESPONSES,
+ customModelTargetFormat: undefined,
+ providerSpecificData: { targetFormat: FORMATS.CLAUDE },
+ });
+ assert.equal(r.targetFormat, FORMATS.CLAUDE);
+});
+
test("unmapped provider → alias falls back to the provider id", () => {
const r = resolveChatCoreTargetFormat({
provider: "some-unmapped-provider",
resolvedModel: "x",
apiFormat: "responses",
+ sourceFormat: FORMATS.OPENAI,
customModelTargetFormat: undefined,
providerSpecificData: undefined,
});
diff --git a/tests/unit/executor-codex.test.ts b/tests/unit/executor-codex.test.ts
index 771930e1e5..8b07f90cb6 100644
--- a/tests/unit/executor-codex.test.ts
+++ b/tests/unit/executor-codex.test.ts
@@ -184,10 +184,10 @@ test("CodexExecutor.buildHeaders binds workspace ids and disables SSE accept for
assert.equal(standardHeaders.Authorization, "Bearer codex-token");
assert.equal(standardHeaders.Accept, "text/event-stream");
assert.equal(standardHeaders["chatgpt-account-id"], "workspace-1");
- assert.equal(standardHeaders.Version, "0.144.1");
+ assert.equal(standardHeaders.Version, "0.146.0");
assert.equal(standardHeaders["Openai-Beta"], "responses=experimental");
assert.equal(standardHeaders["X-Codex-Beta-Features"], "responses_websockets");
- assert.equal(standardHeaders["User-Agent"], "codex-cli/0.144.1 (Windows 10.0.26200; x64)");
+ assert.equal(standardHeaders["User-Agent"], "codex-cli/0.146.0 (Windows 10.0.26200; x64)");
assert.equal(compactHeaders.Accept, "application/json");
});
@@ -213,7 +213,7 @@ test("CodexExecutor.buildHeaders honors safe env overrides for Version and User-
},
() => {
const headers = executor.buildHeaders({ accessToken: "codex-token" }, true);
- assert.equal(headers.Version, "0.144.1");
+ assert.equal(headers.Version, "0.146.0");
assert.equal(headers["User-Agent"], "custom-codex/9.9.9");
}
);
diff --git a/tests/unit/executor-default-base.test.ts b/tests/unit/executor-default-base.test.ts
index 110d40a65d..646ae251ce 100644
--- a/tests/unit/executor-default-base.test.ts
+++ b/tests/unit/executor-default-base.test.ts
@@ -669,9 +669,9 @@ test("DefaultExecutor.execute uses CC-compatible connection defaults to append 1
assert.equal(calls[0].headers["anthropic-beta"].includes(CONTEXT_1M_BETA_HEADER), false);
assert.equal(
calls[0].headers["anthropic-beta"].includes(CLAUDE_CODE_COMPATIBLE_REDACT_THINKING_BETA),
- false
+ true
);
- assert.equal(calls[1].headers["anthropic-beta"].includes(CONTEXT_1M_BETA_HEADER), true);
+ assert.equal(calls[1].headers["anthropic-beta"].includes(CONTEXT_1M_BETA_HEADER), false);
assert.equal(
calls[1].headers["anthropic-beta"].includes(CLAUDE_CODE_COMPATIBLE_REDACT_THINKING_BETA),
true
@@ -1513,6 +1513,6 @@ test("DefaultExecutor.execute does not produce duplicate anthropic-version heade
const sentBody = JSON.parse(capturedBody) as { system?: Array<{ text?: string }> };
assert.match(
sentBody.system?.[0]?.text ?? "",
- /^x-anthropic-billing-header: cc_version=2\.1\.219\.250; cc_entrypoint=cli; cch=[0-9a-f]{5};$/
+ /^x-anthropic-billing-header: cc_version=2\.1\.220\.1f2; cc_entrypoint=cli; cch=[0-9a-f]{5};$/
);
});
diff --git a/tests/unit/glm-executor.test.ts b/tests/unit/glm-executor.test.ts
index f0b0d9e188..d71407fff3 100644
--- a/tests/unit/glm-executor.test.ts
+++ b/tests/unit/glm-executor.test.ts
@@ -181,7 +181,7 @@ test("GlmExecutor separates OpenAI-compatible coding headers from Anthropic head
assert.equal(anthropicHeaders["anthropic-version"], "2023-06-01");
assert.match(anthropicHeaders["anthropic-beta"], /claude-code-20250219/);
assert.equal(anthropicHeaders["anthropic-dangerous-direct-browser-access"], "true");
- assert.match(anthropicHeaders["User-Agent"], /^claude-cli\/2\.1\.219 \(external, sdk-cli\)$/);
+ assert.match(anthropicHeaders["User-Agent"], /^claude-cli\/2\.1\.220 \(external, sdk-cli\)$/);
assert.equal(anthropicHeaders["X-Stainless-Lang"], "js");
assert.equal(anthropicHeaders["X-Stainless-Runtime"], "node");
});
diff --git a/tests/unit/provider-models-route-codex.test.ts b/tests/unit/provider-models-route-codex.test.ts
index 8968b435bb..828c8b1b46 100644
--- a/tests/unit/provider-models-route-codex.test.ts
+++ b/tests/unit/provider-models-route-codex.test.ts
@@ -159,11 +159,11 @@ test("provider models route merges live Codex models with the local catalog then
assert.equal(body.discoveredCandidateCount, undefined);
assert.deepEqual(seenRequests, [
{
- url: "https://chatgpt.com/backend-api/codex/models?client_version=0.144.1",
+ url: "https://chatgpt.com/backend-api/codex/models?client_version=0.146.0",
authorization: "Bearer codex-access-token",
workspaceId: "account-123",
originator: "codex_cli_rs",
- userAgent: "codex-cli/0.144.1 (Windows 10.0.26200; x64)",
+ userAgent: "codex-cli/0.146.0 (Windows 10.0.26200; x64)",
},
{
url: "https://raw.githubusercontent.com/openai/codex/refs/heads/main/codex-rs/models-manager/models.json",
diff --git a/tests/unit/provider-request-failure-pipeline.test.ts b/tests/unit/provider-request-failure-pipeline.test.ts
index 9a95322c72..1437f43f5e 100644
--- a/tests/unit/provider-request-failure-pipeline.test.ts
+++ b/tests/unit/provider-request-failure-pipeline.test.ts
@@ -485,8 +485,11 @@ test("CC-compatible providerRequest log keeps request beta headers and summarize
assert.ok(providerRequest, "providerRequest must be present on CC-compatible success");
assert.equal(providerRequest.headers["cf-ray"], undefined);
assert.equal(providerRequest.headers.server, undefined);
- assert.equal(providerRequest.headers.Accept, "text/event-stream");
- assert.match(providerRequest.headers["anthropic-beta"], new RegExp(CONTEXT_1M_BETA_HEADER));
+ assert.equal(providerRequest.headers.Accept, "application/json");
+ assert.doesNotMatch(
+ providerRequest.headers["anthropic-beta"],
+ new RegExp(CONTEXT_1M_BETA_HEADER)
+ );
assert.match(
providerRequest.headers["anthropic-beta"],
new RegExp(CLAUDE_CODE_COMPATIBLE_REDACT_THINKING_BETA)
diff --git a/tests/unit/system-transforms.test.ts b/tests/unit/system-transforms.test.ts
index 30b5a178c7..ab3cbafb4f 100644
--- a/tests/unit/system-transforms.test.ts
+++ b/tests/unit/system-transforms.test.ts
@@ -542,7 +542,7 @@ const UI_DEFAULTS_SNAPSHOT = {
entrypoint: "sdk-cli",
versionFormat: "ex-machina",
cchAlgo: "sha256-first-user",
- buildRevision: "250",
+ buildRevision: "1f2",
},
],
},