diff --git a/CHANGELOG.md b/CHANGELOG.md index 10467a5204..e370b1bbaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### 🔧 Bug Fixes + +- **fix(kiro):** bound the Claude model-id dash→dot normalization to a 1–2 digit minor so date-suffixed ids (e.g. claude-opus-4-20250514) are no longer corrupted. (thanks @voravitl) + --- ## [3.8.43] — TBD diff --git a/open-sse/translator/request/openai-to-kiro.ts b/open-sse/translator/request/openai-to-kiro.ts index 194484ded8..eef193a622 100644 --- a/open-sse/translator/request/openai-to-kiro.ts +++ b/open-sse/translator/request/openai-to-kiro.ts @@ -669,8 +669,11 @@ export function buildKiroPayload(model, body, stream, credentials) { // Normalize model name: Claude Code sends dashes (claude-sonnet-4-6), // Kiro API expects dots (claude-sonnet-4.6). Convert trailing version segment. + // The minor group is bounded to 1-2 digits so date-suffixed ids (e.g. + // claude-opus-4-20250514) are never mistaken for a dash-separated minor + // version and corrupted into claude-opus-4.20250514 (upstream 9router #2270). const normalizedModel = model.replace( - /^(claude-(?:opus|sonnet|haiku|3-\d+)-\d+)-(\d+)$/, + /^(claude-(?:opus|sonnet|haiku|3-\d+)-\d+)-(\d{1,2})$/, "$1.$2" ); const messages = body.messages || []; diff --git a/tests/unit/translator-openai-to-kiro.test.ts b/tests/unit/translator-openai-to-kiro.test.ts index cfd68f3591..8523d433c7 100644 --- a/tests/unit/translator-openai-to-kiro.test.ts +++ b/tests/unit/translator-openai-to-kiro.test.ts @@ -996,3 +996,48 @@ test("buildKiroPayload accepts kr/* model ids without the [1m] suffix", () => { "model ids without [1m] must continue to build normally" ); }); + +// Regression for upstream decolua/9router PR #2270: the dash->dot normalization's +// trailing minor-version group must be bounded (1-2 digits), otherwise a +// date-suffixed Claude model id (e.g. claude-opus-4-20250514) gets corrupted into +// "claude-opus-4.20250514" because the unbounded `-(\d+)$` group swallows the +// 8-digit date as if it were a minor version. +test("buildKiroPayload normalizes short dash-suffixed minor versions to dots", () => { + const body = { messages: [{ role: "user", content: "Hello" }] }; + + const opus = buildKiroPayload("claude-opus-4-8", body, false, null); + assert.equal( + opus.conversationState.currentMessage.userInputMessage.modelId, + "claude-opus-4.8", + "1-digit minor version should normalize dash to dot" + ); + + const sonnet = buildKiroPayload("claude-sonnet-4-6", body, false, null); + assert.equal( + sonnet.conversationState.currentMessage.userInputMessage.modelId, + "claude-sonnet-4.6", + "1-digit minor version should normalize dash to dot (sonnet)" + ); +}); + +test("buildKiroPayload does not corrupt date-suffixed Claude model ids (#2270)", () => { + const body = { messages: [{ role: "user", content: "Hello" }] }; + + const result = buildKiroPayload("claude-opus-4-20250514", body, false, null); + assert.equal( + result.conversationState.currentMessage.userInputMessage.modelId, + "claude-opus-4-20250514", + "date-suffixed model ids (3+ digit trailing group) must NOT be dash->dot normalized" + ); +}); + +test("buildKiroPayload leaves already-two-dash Claude ids unchanged (#2270)", () => { + const body = { messages: [{ role: "user", content: "Hello" }] }; + + const result = buildKiroPayload("claude-opus-4-1-20250805", body, false, null); + assert.equal( + result.conversationState.currentMessage.userInputMessage.modelId, + "claude-opus-4-1-20250805", + "two-dash form (patch + date) must remain unchanged" + ); +});