mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-13 18:52:18 +03:00
fix(sse): scopes CC-relay anthropic-beta to its own requestDefaults
Two already-merged agentrouter commits widened usesClaudeCodeProtocol()'s native-Claude system-transform block (billing header + selectBetaFlags-derived anthropic-beta) to also run for generic CC-compatible relay connections, not just real claude traffic and agentrouter's own wire-image mimicry. selectBetaFlags() has no visibility into a relay's own providerSpecificData.requestDefaults, so its header replacement silently wiped out an earlier context-1m append and force-included redact-thinking regardless of the relay's own opt-in. Restores both for plain CC-compatible relays only; real claude/agentrouter traffic is unaffected. Also bumps four stale hardcoded Codex/Claude Code CLI version-string test assertions (0.144.1->0.146.0, 2.1.219->2.1.220) that drifted when the same two commits bumped the version constants without updating their tests, and rebaselines base.ts's frozen file-size cap for this fix's own +35 lines.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"_rebaseline_2026_08_02_agentrouter_ccbeta_regression_fix": "PR #9173 (cursor-token-renewal) own growth: open-sse/executors/base.ts 1578->1613 (+35). Fixes a real regression from the same two already-merged agentrouter commits documented in _rebaseline_2026_08_02_agentrouter_protocol_dispatch above — usesClaudeCodeProtocol() widened the native-Claude system-transform block (billing header, selectBetaFlags-derived anthropic-beta) to also run for generic CC-compatible relay connections, not just real `claude` traffic and agentrouter's own wire-image mimicry. selectBetaFlags() has no visibility into a relay's own providerSpecificData.requestDefaults, so its Object.assign() silently wiped out an earlier CONTEXT_1M_BETA_HEADER append and force-included redact-thinking-2026-02-12 regardless of the relay's own redactThinking opt-in. Restores both behaviors for `usesClaudeCodeProtocol && !usesCcWireImage(this.provider)` connections only — real claude/agentrouter traffic is untouched. Covered by tests/unit/executor-default-base.test.ts's 'uses CC-compatible connection defaults to append 1M beta' test and tests/unit/cc-compatible-provider.test.ts (both pre-existing, both re-verified passing).",
|
||||||
"_rebaseline_2026_08_02_agentrouter_protocol_dispatch": "Reconcile-onto-tip drift surfaced by PR #9173 (cursor-token-renewal): two already-merged, no-PR-branch-left commits on release/v3.8.50 (564c204ef fix(agentrouter): support Claude and Codex protocols; ec150a006 fix(agentrouter): honor alternate protocol in chat pipeline) grew open-sse/executors/base.ts 1562->1578 (Claude/Codex protocol dispatch wiring in the agentrouter executor branch) and open-sse/handlers/chatCore.ts 5020->5028 + tests/unit/chatcore-translation-paths.test.ts 2769->2776 (alternate-protocol chat-pipeline routing + companion test coverage) past their frozen caps, unrelated to this PR's own Cursor renewal changes. Same pattern as the prior release-green rebaselines (fast-gates PR->release do not run check:file-size): no offending branch left to fix in-place. Real sizes per check-file-size.mjs's own split(\"\\n\").length metric.",
|
"_rebaseline_2026_08_02_agentrouter_protocol_dispatch": "Reconcile-onto-tip drift surfaced by PR #9173 (cursor-token-renewal): two already-merged, no-PR-branch-left commits on release/v3.8.50 (564c204ef fix(agentrouter): support Claude and Codex protocols; ec150a006 fix(agentrouter): honor alternate protocol in chat pipeline) grew open-sse/executors/base.ts 1562->1578 (Claude/Codex protocol dispatch wiring in the agentrouter executor branch) and open-sse/handlers/chatCore.ts 5020->5028 + tests/unit/chatcore-translation-paths.test.ts 2769->2776 (alternate-protocol chat-pipeline routing + companion test coverage) past their frozen caps, unrelated to this PR's own Cursor renewal changes. Same pattern as the prior release-green rebaselines (fast-gates PR->release do not run check:file-size): no offending branch left to fix in-place. Real sizes per check-file-size.mjs's own split(\"\\n\").length metric.",
|
||||||
"_rebaseline_2026_07_24_8470_hyperagent_sticky_thread": "PR #8470 (artickc, fix/hyperagent-tool-loop-thread-sticky) own growth: open-sse/executors/hyperagent.ts 936->1025 (wc -l; check-file-size.mjs counts via split(\"\\n\").length so the gate sees 937->1026, +89, crosses the 1000 cap). Fixes a real bug where a reverse-conversion proxy (text-Intent/JSON to Claude Code native tool_calls) rewrites assistant messages between agentic tool-loop turns, breaking HyperAgent's conversation-prefix fingerprint and cold-starting the thread mid tool-loop. Adds Anthropic tool_use/tool_result flattening to extractMessageText() plus a new rootUserFingerprint()/root-key lookup tier in resolveHyperAgentThreadBinding()/storeHyperAgentThreadAfterTurn() so the thread stays sticky across the tool loop. Cohesive additions inside the existing single-file executor; not extractable without splitting the executor mid-request-flow. Covered by tests/unit/executor-hyperagent.test.ts (19/19, +5 new cases for tool_result/tool_use flattening + root-key stickiness). Pre-merge review flagged a cross-conversation root-key collision risk (tracked in the PR's own mandatory pre-merge checklist, not yet addressed) — unrelated to this file-size ratchet, tracked separately by /fix-prs.",
|
"_rebaseline_2026_07_24_8470_hyperagent_sticky_thread": "PR #8470 (artickc, fix/hyperagent-tool-loop-thread-sticky) own growth: open-sse/executors/hyperagent.ts 936->1025 (wc -l; check-file-size.mjs counts via split(\"\\n\").length so the gate sees 937->1026, +89, crosses the 1000 cap). Fixes a real bug where a reverse-conversion proxy (text-Intent/JSON to Claude Code native tool_calls) rewrites assistant messages between agentic tool-loop turns, breaking HyperAgent's conversation-prefix fingerprint and cold-starting the thread mid tool-loop. Adds Anthropic tool_use/tool_result flattening to extractMessageText() plus a new rootUserFingerprint()/root-key lookup tier in resolveHyperAgentThreadBinding()/storeHyperAgentThreadAfterTurn() so the thread stays sticky across the tool loop. Cohesive additions inside the existing single-file executor; not extractable without splitting the executor mid-request-flow. Covered by tests/unit/executor-hyperagent.test.ts (19/19, +5 new cases for tool_result/tool_use flattening + root-key stickiness). Pre-merge review flagged a cross-conversation root-key collision risk (tracked in the PR's own mandatory pre-merge checklist, not yet addressed) — unrelated to this file-size ratchet, tracked separately by /fix-prs.",
|
||||||
"_rebaseline_2026_07_25_8494_capability_filter_fail_closed": "PR #8494 (fix/capability-filters-fail-closed, #8488) own growth: open-sse/services/combo.ts 3640->3693 (+53) adds a fail-closed guard after filterTargetsByRequestCompatibility() — when every eligible target is excluded by request-capability filtering (vision/tools/etc) instead of quota/health, the combo now returns an explicit `capability_mismatch` 400 (describeCapabilityFilterExhaustion, imported from combo/comboStructure.ts) rather than silently falling through to a generic no-targets error, plus a `compatFilterFailOpen` escape hatch (combo config OR settings) mirrored at both the main/auto and round-robin call sites for symmetry. combo/comboStructure.ts (previously under cap, un-frozen) grows 794->918 (+124) — new home for describeCapabilityFilterExhaustion + providerSupportsEmulatedToolCalling (#5240 emulated tool-calling exemption so fail-closed does not regress prompt-emulation-only combos like all-chatgpt-web). Irreducible orchestration wiring at the existing filter chokepoint (same precedent as #7301's universal-cooldown-retry generalization). Companion test tests/unit/combo-routing-engine.test.ts 3409->3449 (+40, fail-closed/fail-open coverage across both call sites) also rebaselined. Covered by tests/unit/8488-capability-filter-fail-closed.test.ts (new) + 95/95 passing across both files. Structural shrink of combo.ts tracked in #3501.",
|
"_rebaseline_2026_07_25_8494_capability_filter_fail_closed": "PR #8494 (fix/capability-filters-fail-closed, #8488) own growth: open-sse/services/combo.ts 3640->3693 (+53) adds a fail-closed guard after filterTargetsByRequestCompatibility() — when every eligible target is excluded by request-capability filtering (vision/tools/etc) instead of quota/health, the combo now returns an explicit `capability_mismatch` 400 (describeCapabilityFilterExhaustion, imported from combo/comboStructure.ts) rather than silently falling through to a generic no-targets error, plus a `compatFilterFailOpen` escape hatch (combo config OR settings) mirrored at both the main/auto and round-robin call sites for symmetry. combo/comboStructure.ts (previously under cap, un-frozen) grows 794->918 (+124) — new home for describeCapabilityFilterExhaustion + providerSupportsEmulatedToolCalling (#5240 emulated tool-calling exemption so fail-closed does not regress prompt-emulation-only combos like all-chatgpt-web). Irreducible orchestration wiring at the existing filter chokepoint (same precedent as #7301's universal-cooldown-retry generalization). Companion test tests/unit/combo-routing-engine.test.ts 3409->3449 (+40, fail-closed/fail-open coverage across both call sites) also rebaselined. Covered by tests/unit/8488-capability-filter-fail-closed.test.ts (new) + 95/95 passing across both files. Structural shrink of combo.ts tracked in #3501.",
|
||||||
@@ -343,7 +344,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_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.",
|
"_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/antigravity.ts": 1528,
|
||||||
"open-sse/executors/base.ts": 1578,
|
"open-sse/executors/base.ts": 1613,
|
||||||
"open-sse/executors/chatgpt-web.ts": 3241,
|
"open-sse/executors/chatgpt-web.ts": 3241,
|
||||||
"open-sse/executors/codex.ts": 1534,
|
"open-sse/executors/codex.ts": 1534,
|
||||||
"open-sse/executors/cursor.ts": 1560,
|
"open-sse/executors/cursor.ts": 1560,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import type { ProviderRequestDefaults } from "../services/providerRequestDefault
|
|||||||
import { signRequestBody } from "../services/claudeCodeCCH.ts";
|
import { signRequestBody } from "../services/claudeCodeCCH.ts";
|
||||||
import {
|
import {
|
||||||
appendAnthropicBetaHeader,
|
appendAnthropicBetaHeader,
|
||||||
|
CLAUDE_CODE_COMPATIBLE_REDACT_THINKING_BETA,
|
||||||
CONTEXT_1M_BETA_HEADER,
|
CONTEXT_1M_BETA_HEADER,
|
||||||
enforceThinkingTemperature,
|
enforceThinkingTemperature,
|
||||||
modelHasNativeContext1m,
|
modelHasNativeContext1m,
|
||||||
@@ -1216,6 +1217,40 @@ export class BaseExecutor {
|
|||||||
headers["x-api-key"] =
|
headers["x-api-key"] =
|
||||||
activeCredentials?.apiKey || activeCredentials?.accessToken || "";
|
activeCredentials?.apiKey || activeCredentials?.accessToken || "";
|
||||||
}
|
}
|
||||||
|
// The Object.assign() above just replaced "anthropic-beta" wholesale
|
||||||
|
// with the selectBetaFlags()-derived set, silently wiping out the
|
||||||
|
// CONTEXT_1M_BETA_HEADER appended earlier from this CC-compatible
|
||||||
|
// relay's own requestDefaults.context1m (selectBetaFlags has no
|
||||||
|
// visibility into per-connection requestDefaults — it only reasons
|
||||||
|
// about the request body shape). Restore it for CC-compatible
|
||||||
|
// relays (not wire-image/native traffic, which never went through
|
||||||
|
// that earlier append in the first place).
|
||||||
|
if (usesClaudeCodeProtocol && !usesCcWireImage(this.provider)) {
|
||||||
|
if (shouldForwardCcCompatibleContext1m) {
|
||||||
|
appendAnthropicBetaHeader(headers, CONTEXT_1M_BETA_HEADER);
|
||||||
|
}
|
||||||
|
// selectBetaFlags() always includes redact-thinking for an
|
||||||
|
// "opaque" client (no client-negotiated anthropic-beta) — correct
|
||||||
|
// for real `claude` traffic and agentrouter's wire-image
|
||||||
|
// mimicry, both of which must look identical to a genuine Claude
|
||||||
|
// Code CLI request. A plain CC-compatible relay is neither:
|
||||||
|
// redactThinking there is an explicit per-connection opt-in
|
||||||
|
// (providerSpecificData.requestDefaults), not an "opaque client"
|
||||||
|
// default. Strip it back out unless the relay's own
|
||||||
|
// requestDefaults opted in (#agentrouter regression: this whole
|
||||||
|
// block used to run only for real `claude` clients, where this
|
||||||
|
// distinction didn't exist).
|
||||||
|
const betaKey = Object.keys(headers).find(
|
||||||
|
(key) => key.toLowerCase() === "anthropic-beta"
|
||||||
|
);
|
||||||
|
if (betaKey && ccRequestDefaults.redactThinking !== true) {
|
||||||
|
headers[betaKey] = headers[betaKey]
|
||||||
|
.split(",")
|
||||||
|
.map((value) => value.trim())
|
||||||
|
.filter((value) => value && value !== CLAUDE_CODE_COMPATIBLE_REDACT_THINKING_BETA)
|
||||||
|
.join(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
delete headers["X-Stainless-Helper-Method"];
|
delete headers["X-Stainless-Helper-Method"];
|
||||||
|
|
||||||
// OS/arch follow the host running the signed binary. Runtime version
|
// OS/arch follow the host running the signed binary. Runtime version
|
||||||
|
|||||||
@@ -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.Authorization, "Bearer codex-token");
|
||||||
assert.equal(standardHeaders.Accept, "text/event-stream");
|
assert.equal(standardHeaders.Accept, "text/event-stream");
|
||||||
assert.equal(standardHeaders["chatgpt-account-id"], "workspace-1");
|
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["Openai-Beta"], "responses=experimental");
|
||||||
assert.equal(standardHeaders["X-Codex-Beta-Features"], "responses_websockets");
|
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");
|
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);
|
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");
|
assert.equal(headers["User-Agent"], "custom-codex/9.9.9");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1513,6 +1513,6 @@ test("DefaultExecutor.execute does not produce duplicate anthropic-version heade
|
|||||||
const sentBody = JSON.parse(capturedBody) as { system?: Array<{ text?: string }> };
|
const sentBody = JSON.parse(capturedBody) as { system?: Array<{ text?: string }> };
|
||||||
assert.match(
|
assert.match(
|
||||||
sentBody.system?.[0]?.text ?? "",
|
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};$/
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ test("GlmExecutor separates OpenAI-compatible coding headers from Anthropic head
|
|||||||
assert.equal(anthropicHeaders["anthropic-version"], "2023-06-01");
|
assert.equal(anthropicHeaders["anthropic-version"], "2023-06-01");
|
||||||
assert.match(anthropicHeaders["anthropic-beta"], /claude-code-20250219/);
|
assert.match(anthropicHeaders["anthropic-beta"], /claude-code-20250219/);
|
||||||
assert.equal(anthropicHeaders["anthropic-dangerous-direct-browser-access"], "true");
|
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-Lang"], "js");
|
||||||
assert.equal(anthropicHeaders["X-Stainless-Runtime"], "node");
|
assert.equal(anthropicHeaders["X-Stainless-Runtime"], "node");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -159,11 +159,11 @@ test("provider models route merges live Codex models with the local catalog then
|
|||||||
assert.equal(body.discoveredCandidateCount, undefined);
|
assert.equal(body.discoveredCandidateCount, undefined);
|
||||||
assert.deepEqual(seenRequests, [
|
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",
|
authorization: "Bearer codex-access-token",
|
||||||
workspaceId: "account-123",
|
workspaceId: "account-123",
|
||||||
originator: "codex_cli_rs",
|
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",
|
url: "https://raw.githubusercontent.com/openai/codex/refs/heads/main/codex-rs/models-manager/models.json",
|
||||||
|
|||||||
Reference in New Issue
Block a user