fix(executor): strip provider prefix from versioned built-in tool model field (#3532)

Integrated into release/v3.8.20
This commit is contained in:
Giorgos Giakoumettis
2026-06-10 15:12:02 +03:00
committed by GitHub
parent 40d9c54516
commit 99281545fc
2 changed files with 72 additions and 0 deletions

View File

@@ -317,6 +317,27 @@ export function sanitizeReasoningEffortForProvider(
return body;
}
/**
* Strip the OmniRoute provider prefix from versioned built-in tool model
* fields (e.g. `cc/claude-opus-4-8` → `claude-opus-4-8`). Versioned built-in
* tool types carry an 8-digit date suffix (`advisor_20260301`, `bash_20250124`);
* the real Claude CLI sends a bare model id there, never a prefixed one, so a
* leaked OmniRoute prefix makes Anthropic reject the request. Mutates in place.
*/
export function stripVersionedToolModelPrefix(tools: unknown): void {
if (!Array.isArray(tools)) return;
for (const t of tools as Array<Record<string, unknown>>) {
if (
typeof t.type === "string" &&
/^[a-z][a-z0-9_]*_\d{8}$/.test(t.type) &&
typeof t.model === "string" &&
t.model.includes("/")
) {
t.model = t.model.split("/").pop();
}
}
}
/**
* BaseExecutor - Base class for provider executors.
* Implements the Strategy pattern: subclasses override specific methods
@@ -824,6 +845,9 @@ export class BaseExecutor {
for (const t of tb.tools as Array<Record<string, unknown>>) {
delete t.cache_control;
}
// Also strip OmniRoute provider prefix from versioned built-in tool
// model fields (e.g. cc/claude-opus-4-8 → claude-opus-4-8).
stripVersionedToolModelPrefix(tb.tools);
}
// Per-request behavior overrides via custom client headers.

View File

@@ -18,6 +18,7 @@ import {
sanitizeClaudeToolSchema,
sanitizeClaudeToolSchemas,
} from "../../open-sse/translator/helpers/schemaCoercion.ts";
import { stripVersionedToolModelPrefix } from "../../open-sse/executors/base.ts";
type AnyRecord = Record<string, unknown>;
const schemaOf = (tools: unknown, i = 0): AnyRecord =>
@@ -270,3 +271,50 @@ describe("review fixes — established aliases + kill-switch", () => {
}
});
});
describe("native claude OAuth path — versioned built-in tool model prefix stripping", () => {
// Exercises the REAL production helper from base.ts (not a re-implementation),
// so reverting the fix in base.ts breaks these assertions.
it("strips cc/ prefix from advisor_20260301 model field", () => {
const tools: AnyRecord[] = [
{ type: "advisor_20260301", name: "advisor", model: "cc/claude-opus-4-8" },
{ type: "bash_20250124", name: "Bash", model: "kiro/claude-opus-4.8" },
{ name: "Read", input_schema: { type: "object", properties: {} } },
];
stripVersionedToolModelPrefix(tools);
assert.equal(tools[0].model, "claude-opus-4-8", "cc/ prefix stripped from advisor model");
assert.equal(tools[1].model, "claude-opus-4.8", "kiro/ prefix stripped from bash model");
assert.equal("model" in tools[2], false, "regular tool untouched");
});
it("strips multi-segment prefix (claude/claude-sonnet-4-6) from versioned tool model", () => {
const tools: AnyRecord[] = [
{ type: "bash_20250124", name: "Bash", model: "claude/claude-sonnet-4-6" },
];
stripVersionedToolModelPrefix(tools);
assert.equal(tools[0].model, "claude-sonnet-4-6");
});
it("leaves bare model on versioned tool unchanged", () => {
const tools: AnyRecord[] = [
{ type: "advisor_20260301", name: "advisor", model: "claude-opus-4-8" },
];
stripVersionedToolModelPrefix(tools);
assert.equal(tools[0].model, "claude-opus-4-8");
});
it("leaves non-versioned tool types untouched even with a prefixed model", () => {
const tools: AnyRecord[] = [
{ type: "custom", name: "x", model: "cc/claude-opus-4-8" },
{ type: "advisor_2026", name: "y", model: "cc/claude-opus-4-8" }, // not 8 digits
];
stripVersionedToolModelPrefix(tools);
assert.equal(tools[0].model, "cc/claude-opus-4-8", "non-versioned type untouched");
assert.equal(tools[1].model, "cc/claude-opus-4-8", "short date suffix untouched");
});
it("is a no-op for non-array input", () => {
assert.doesNotThrow(() => stripVersionedToolModelPrefix(undefined));
assert.doesNotThrow(() => stripVersionedToolModelPrefix({ tools: [] }));
});
});