From 567f5075c3105424d188a96eb35b6c419982bdec Mon Sep 17 00:00:00 2001 From: Jeon Suyeol Date: Fri, 6 Mar 2026 12:06:57 +0900 Subject: [PATCH] handle Claude Code official model aliases (sonnet, opus, haiku, inherit) --- .../claude-model-mapper.test.ts | 32 +++++++++++++++---- .../claude-model-mapper.ts | 15 +++++++-- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/features/claude-code-agent-loader/claude-model-mapper.test.ts b/src/features/claude-code-agent-loader/claude-model-mapper.test.ts index df2ab8d25..b3da88759 100644 --- a/src/features/claude-code-agent-loader/claude-model-mapper.test.ts +++ b/src/features/claude-code-agent-loader/claude-model-mapper.test.ts @@ -16,6 +16,30 @@ describe("mapClaudeModelToOpenCode", () => { }) }) + describe("#given Claude Code alias", () => { + it("#when called with sonnet #then maps to anthropic/claude-sonnet-4-6", () => { + expect(mapClaudeModelToOpenCode("sonnet")).toBe("anthropic/claude-sonnet-4-6") + }) + + it("#when called with opus #then maps to anthropic/claude-opus-4-6", () => { + expect(mapClaudeModelToOpenCode("opus")).toBe("anthropic/claude-opus-4-6") + }) + + it("#when called with haiku #then maps to anthropic/claude-haiku-4-5", () => { + expect(mapClaudeModelToOpenCode("haiku")).toBe("anthropic/claude-haiku-4-5") + }) + + it("#when called with Sonnet (capitalized) #then maps case-insensitively", () => { + expect(mapClaudeModelToOpenCode("Sonnet")).toBe("anthropic/claude-sonnet-4-6") + }) + }) + + describe("#given inherit", () => { + it("#when called with inherit #then returns undefined", () => { + expect(mapClaudeModelToOpenCode("inherit")).toBeUndefined() + }) + }) + describe("#given bare Claude model name", () => { it("#when called with claude-sonnet-4-5-20250514 #then adds anthropic prefix", () => { expect(mapClaudeModelToOpenCode("claude-sonnet-4-5-20250514")).toBe("anthropic/claude-sonnet-4-5-20250514") @@ -25,8 +49,8 @@ describe("mapClaudeModelToOpenCode", () => { expect(mapClaudeModelToOpenCode("claude-opus-4-6")).toBe("anthropic/claude-opus-4-6") }) - it("#when called with claude-haiku-4-5 #then adds anthropic prefix", () => { - expect(mapClaudeModelToOpenCode("claude-haiku-4-5")).toBe("anthropic/claude-haiku-4-5") + it("#when called with claude-haiku-4-5-20251001 #then adds anthropic prefix", () => { + expect(mapClaudeModelToOpenCode("claude-haiku-4-5-20251001")).toBe("anthropic/claude-haiku-4-5-20251001") }) it("#when called with claude-3-5-sonnet-20241022 #then adds anthropic prefix", () => { @@ -62,10 +86,6 @@ describe("mapClaudeModelToOpenCode", () => { it("#when called with gemini-3-flash #then returns unchanged", () => { expect(mapClaudeModelToOpenCode("gemini-3-flash")).toBe("gemini-3-flash") }) - - it("#when called with a custom model name #then returns unchanged", () => { - expect(mapClaudeModelToOpenCode("my-custom-model")).toBe("my-custom-model") - }) }) describe("#given model with leading/trailing whitespace", () => { diff --git a/src/features/claude-code-agent-loader/claude-model-mapper.ts b/src/features/claude-code-agent-loader/claude-model-mapper.ts index 7d1c5dc5f..6d9b120d2 100644 --- a/src/features/claude-code-agent-loader/claude-model-mapper.ts +++ b/src/features/claude-code-agent-loader/claude-model-mapper.ts @@ -2,15 +2,24 @@ import { normalizeModelID } from "../../shared/model-normalization" const ANTHROPIC_PREFIX = "anthropic/" +const CLAUDE_CODE_ALIAS_MAP: Record = { + sonnet: `${ANTHROPIC_PREFIX}claude-sonnet-4-6`, + opus: `${ANTHROPIC_PREFIX}claude-opus-4-6`, + haiku: `${ANTHROPIC_PREFIX}claude-haiku-4-5`, +} + export function mapClaudeModelToOpenCode(model: string | undefined): string | undefined { if (!model) return undefined const trimmed = model.trim() if (trimmed.length === 0) return undefined - if (trimmed.includes("/")) { - return trimmed - } + if (trimmed === "inherit") return undefined + + const aliasResult = CLAUDE_CODE_ALIAS_MAP[trimmed.toLowerCase()] + if (aliasResult) return aliasResult + + if (trimmed.includes("/")) return trimmed const normalized = normalizeModelID(trimmed)