From a5363b83b59b65a38944a9e670bb5f33882bac0a Mon Sep 17 00:00:00 2001 From: Edison42 Date: Fri, 3 Jul 2026 10:52:22 +0700 Subject: [PATCH] fix(kiro): add Claude Sonnet 5 model support (#2264) Co-authored-by: Cursor --- cli/src/cli/menus/providers.js | 1 + open-sse/providers/capabilities.js | 6 +++++- open-sse/providers/registry/kiro.js | 4 ++++ src/shared/constants/cliTools.js | 2 +- tests/__baseline__/known-fails.txt | 3 +-- tests/unit/capabilities.test.js | 17 ++++++++++++++++ tests/unit/kiro-model-slots.test.js | 30 +++++++++++++++++++---------- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/cli/src/cli/menus/providers.js b/cli/src/cli/menus/providers.js index d98e9d64..57013466 100644 --- a/cli/src/cli/menus/providers.js +++ b/cli/src/cli/menus/providers.js @@ -78,6 +78,7 @@ const PROVIDER_MODELS = { { id: "grok-code-fast-1" }, ], kr: [ + { id: "claude-sonnet-5" }, { id: "claude-sonnet-4.5" }, { id: "claude-haiku-4.5" }, ], diff --git a/open-sse/providers/capabilities.js b/open-sse/providers/capabilities.js index 6d5c7815..98f4ed47 100644 --- a/open-sse/providers/capabilities.js +++ b/open-sse/providers/capabilities.js @@ -71,7 +71,7 @@ export function capabilitiesFromServiceKind(kind) { * otherwise mis-match. Only declare deltas vs DEFAULT. */ export const MODEL_CAPABILITIES = { - // Claude 4.6/4.7/4.8 have 1M context + adaptive thinking (override generic claude pattern) + // Claude 4.6/4.7/4.8 and Kiro Sonnet 5 have 1M context + adaptive thinking (override generic claude pattern) "claude-opus-4.6": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, "claude-opus-4.7": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, "claude-opus-4-7": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, @@ -82,6 +82,10 @@ export const MODEL_CAPABILITIES = { "claude-opus-4-8-thinking": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, "claude-sonnet-4.6": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, "claude-sonnet-4-6": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, + "claude-sonnet-5": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, + "claude-sonnet-5-thinking": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, + "claude-sonnet-5-agentic": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, + "claude-sonnet-5-thinking-agentic": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 }, // Gemini image-gen / OpenAI image / xai image variants "gpt-image-1": { imageOutput: true, tools: false }, diff --git a/open-sse/providers/registry/kiro.js b/open-sse/providers/registry/kiro.js index fb78a227..12015643 100644 --- a/open-sse/providers/registry/kiro.js +++ b/open-sse/providers/registry/kiro.js @@ -42,16 +42,20 @@ export default { }, }, models: [ + { id: "claude-sonnet-5", name: "Claude Sonnet 5" }, { id: "claude-sonnet-4.5", name: "Claude Sonnet 4.5" }, { id: "claude-haiku-4.5", name: "Claude Haiku 4.5" }, { id: "deepseek-3.2", name: "DeepSeek 3.2", strip: ["image","audio"] }, { id: "qwen3-coder-next", name: "Qwen3 Coder Next", strip: ["image","audio"] }, { id: "glm-5", name: "GLM 5" }, { id: "MiniMax-M2.5", name: "MiniMax M2.5" }, + { id: "claude-sonnet-5-thinking", name: "Claude Sonnet 5 (Thinking)" }, { id: "claude-sonnet-4.5-thinking", name: "Claude Sonnet 4.5 (Thinking)" }, { id: "claude-haiku-4.5-thinking", name: "Claude Haiku 4.5 (Thinking)" }, + { id: "claude-sonnet-5-agentic", name: "Claude Sonnet 5 (Agentic)" }, { id: "claude-sonnet-4.5-agentic", name: "Claude Sonnet 4.5 (Agentic)" }, { id: "claude-haiku-4.5-agentic", name: "Claude Haiku 4.5 (Agentic)" }, + { id: "claude-sonnet-5-thinking-agentic", name: "Claude Sonnet 5 (Thinking + Agentic)" }, { id: "claude-sonnet-4.5-thinking-agentic", name: "Claude Sonnet 4.5 (Thinking + Agentic)" }, { id: "claude-haiku-4.5-thinking-agentic", name: "Claude Haiku 4.5 (Thinking + Agentic)" }, ], diff --git a/src/shared/constants/cliTools.js b/src/shared/constants/cliTools.js index 18ecab72..a593da92 100644 --- a/src/shared/constants/cliTools.js +++ b/src/shared/constants/cliTools.js @@ -56,6 +56,7 @@ export const MITM_TOOLS = { configType: "mitm", mitmDomain: "q.us-east-1.amazonaws.com", defaultModels: [ + { id: "claude-sonnet-5", name: "Claude Sonnet 5", alias: "claude-sonnet-5" }, { id: "claude-sonnet-4.5", name: "Claude Sonnet 4.5", alias: "claude-sonnet-4.5" }, { id: "claude-sonnet-4", name: "Claude Sonnet 4", alias: "claude-sonnet-4" }, { id: "claude-haiku-4.5", name: "Claude Haiku 4.5", alias: "claude-haiku-4.5" }, @@ -391,4 +392,3 @@ export const getProviderModelsForMapping = (providers) => { }); return result; }; - diff --git a/tests/__baseline__/known-fails.txt b/tests/__baseline__/known-fails.txt index 78e0e76d..9ffd63f9 100644 --- a/tests/__baseline__/known-fails.txt +++ b/tests/__baseline__/known-fails.txt @@ -1,6 +1,5 @@ tests/unit/antigravity-mitm.test.js :: Antigravity MITM model handling flags the out-of-box agent/Default model mandatory tests/unit/claude-header-forwarding.test.js :: proxyAwareFetch — api.anthropic.com routing routes api.anthropic.com to gotScraping (non-streaming) and returns ok response -tests/unit/kiro-model-slots.test.js :: Kiro MITM model slots offers a mappable slot for the agent default model id 'auto' tests/unit/oauth-cursor-auto-import.test.js :: GET /api/oauth/cursor/auto-import extracts tokens using exact keys tests/unit/oauth-cursor-auto-import.test.js :: GET /api/oauth/cursor/auto-import falls back to fuzzy key matching on macOS when exact keys are missing tests/unit/oauth-cursor-auto-import.test.js :: GET /api/oauth/cursor/auto-import linux uses single hardcoded path and original error message @@ -23,4 +22,4 @@ tests/unit/rtk.test.js :: compressMessages (enabled) skips when body has no mess tests/unit/translator-request-normalization.test.js :: request normalization claudeToOpenAIRequest flattens text-only content arrays into string tests/unit/translator-request-normalization.test.js :: request normalization filterToOpenAIFormat flattens text-only arrays to string tests/unit/translator-request-normalization.test.js :: request normalization parseSSELine supports provider raw NDJSON stream lines -tests/unit/translator-request-normalization.test.js :: request normalization translateRequest keeps /v1/messages Claude->OpenAI text payloads string-safe \ No newline at end of file +tests/unit/translator-request-normalization.test.js :: request normalization translateRequest keeps /v1/messages Claude->OpenAI text payloads string-safe diff --git a/tests/unit/capabilities.test.js b/tests/unit/capabilities.test.js index 3839809b..31512e9a 100644 --- a/tests/unit/capabilities.test.js +++ b/tests/unit/capabilities.test.js @@ -2,6 +2,15 @@ import { describe, expect, it } from "vitest"; import { getCapabilitiesForModel } from "../../open-sse/providers/capabilities.js"; describe("getCapabilitiesForModel", () => { + const claudeSonnet5Expected = { + contextWindow: 1000000, + maxOutput: 128000, + thinkingFormat: "claude-adaptive", + reasoning: true, + vision: true, + search: true, + }; + it("reports Kiro Claude Opus 4.8 as a 1M context model", () => { expect(getCapabilitiesForModel("kiro", "claude-opus-4.8").contextWindow).toBe(1000000); expect(getCapabilitiesForModel("kiro", "anthropic/claude-opus-4.8").contextWindow).toBe(1000000); @@ -9,4 +18,12 @@ describe("getCapabilitiesForModel", () => { expect(getCapabilitiesForModel("kiro", "claude-opus-4.8-thinking").contextWindow).toBe(1000000); expect(getCapabilitiesForModel("kiro", "claude-opus-4-8-thinking").contextWindow).toBe(1000000); }); + + it("reports Kiro Claude Sonnet 5 as a 1M adaptive-thinking model", () => { + expect(getCapabilitiesForModel("kiro", "claude-sonnet-5")).toMatchObject(claudeSonnet5Expected); + expect(getCapabilitiesForModel("kiro", "anthropic/claude-sonnet-5")).toMatchObject(claudeSonnet5Expected); + expect(getCapabilitiesForModel("kiro", "claude-sonnet-5-thinking")).toMatchObject(claudeSonnet5Expected); + expect(getCapabilitiesForModel("kiro", "claude-sonnet-5-agentic")).toMatchObject(claudeSonnet5Expected); + expect(getCapabilitiesForModel("kiro", "claude-sonnet-5-thinking-agentic")).toMatchObject(claudeSonnet5Expected); + }); }); diff --git a/tests/unit/kiro-model-slots.test.js b/tests/unit/kiro-model-slots.test.js index 3eb9c7b8..ffc3e382 100644 --- a/tests/unit/kiro-model-slots.test.js +++ b/tests/unit/kiro-model-slots.test.js @@ -1,12 +1,10 @@ import { describe, expect, it } from "vitest"; +import { PROVIDER_MODELS } from "../../open-sse/config/providerModels.js"; import { MITM_TOOLS } from "../../src/shared/constants/cliTools.js"; -// Guards the fix in commit 356607c: Kiro's agent/"vibe" mode sends modelId -// "auto" for the main turn and "simple-task" for background sub-tasks. Both -// need a mappable defaultModels slot — otherwise getMappedModel (src/mitm/server.js) -// returns null and the /generateAssistantResponse call is passed through to AWS -// instead of being routed to the user's chosen provider (surfacing as Kiro's -// "monthly usage limit" once the AWS quota is gone). +// Guards Kiro model ids that still need mappable defaultModels slots. Without +// a slot, getMappedModel (src/mitm/server.js) returns null and the request is +// passed through to AWS instead of being routed to the user's chosen provider. describe("Kiro MITM model slots", () => { const kiro = MITM_TOOLS.kiro; @@ -16,10 +14,10 @@ describe("Kiro MITM model slots", () => { expect(Array.isArray(kiro.defaultModels)).toBe(true); }); - it("offers a mappable slot for the agent default model id 'auto'", () => { - const auto = kiro.defaultModels.find((m) => m.id === "auto"); - expect(auto).toBeTruthy(); - expect(auto.alias).toBe("auto"); + it("offers a mappable slot for Claude Sonnet 5", () => { + const sonnet5 = kiro.defaultModels.find((m) => m.id === "claude-sonnet-5"); + expect(sonnet5).toBeTruthy(); + expect(sonnet5.alias).toBe("claude-sonnet-5"); }); it("offers a mappable slot for the background sub-task model id 'simple-task'", () => { @@ -28,3 +26,15 @@ describe("Kiro MITM model slots", () => { expect(simpleTask.alias).toBe("simple-task"); }); }); + +describe("Kiro static provider models", () => { + it("includes Claude Sonnet 5 and its synthetic Kiro variants", () => { + const ids = (PROVIDER_MODELS.kr || []).map((model) => model.id); + expect(ids).toEqual(expect.arrayContaining([ + "claude-sonnet-5", + "claude-sonnet-5-thinking", + "claude-sonnet-5-agentic", + "claude-sonnet-5-thinking-agentic", + ])); + }); +});