From 6a66bfccec96fe922139a1df89b33dbdf7f18e65 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 4 Feb 2026 11:25:37 +0900 Subject: [PATCH] fix(doctor): respect user-configured agent variant (#1464) * fix(doctor): respect user-configured agent variant * fix(doctor): align variant resolution with agent-variant.ts - Add case-insensitive agent key lookup (matches canonical logic) - Support category-based variant inheritance (agent.category -> categories[cat].variant) - Separate getCategoryEffectiveVariant for category-specific resolution - Addresses Oracle review feedback --- bun.lock | 28 +++++----- src/cli/doctor/checks/model-resolution.ts | 67 ++++++++++++++++++++--- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/bun.lock b/bun.lock index 50747e1e..ef900aa8 100644 --- a/bun.lock +++ b/bun.lock @@ -28,13 +28,13 @@ "typescript": "^5.7.3", }, "optionalDependencies": { - "oh-my-opencode-darwin-arm64": "3.2.1", - "oh-my-opencode-darwin-x64": "3.2.1", - "oh-my-opencode-linux-arm64": "3.2.1", - "oh-my-opencode-linux-arm64-musl": "3.2.1", - "oh-my-opencode-linux-x64": "3.2.1", - "oh-my-opencode-linux-x64-musl": "3.2.1", - "oh-my-opencode-windows-x64": "3.2.1", + "oh-my-opencode-darwin-arm64": "3.2.2", + "oh-my-opencode-darwin-x64": "3.2.2", + "oh-my-opencode-linux-arm64": "3.2.2", + "oh-my-opencode-linux-arm64-musl": "3.2.2", + "oh-my-opencode-linux-x64": "3.2.2", + "oh-my-opencode-linux-x64-musl": "3.2.2", + "oh-my-opencode-windows-x64": "3.2.2", }, }, }, @@ -226,19 +226,19 @@ "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - "oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.2.1", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-IvhHRUXTr/g/hJlkKTU2oCdgRl2BDl/Qre31Rukhs4NumlvME6iDmdnm8mM7bTxugfCBkfUUr7QJLxxLhzjdLA=="], + "oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.2.2", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-KyfoWcANfcvpfanrrX+Wc8vH8vr9mvr7dJMHBe2bkvuhdtHnLHOG18hQwLg6jk4HhdoZAeBEmkolOsK2k4XajA=="], - "oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.2.1", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-V2JbAdThAVfhBOcb+wBPZrAI0vBxPPRBdvmAixAxBOFC49CIJUrEFIRBUYFKhSQGHYWrNy8z0zJYoNQm4oQPog=="], + "oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.2.2", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-ajZ1E36Ixwdz6rvSUKUI08M2xOaNIl1ZsdVjknZTrPRtct9xgS+BEFCoSCov9bnV/9DrZD3mlZtO/+FFDbseUg=="], - "oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.2.1", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-SeT8P7Icq5YH/AIaEF28J4q+ifUnOqO2UgMFtdFusr8JLadYFy+6dTdeAuD2uGGToDQ3ZNKuaG+lo84KzEhA5w=="], + "oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.2.2", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-ItJsYfigXcOa8/ejTjopC4qk5BCeYioMQ693kPTpeYHK3ByugTjJk8aamE7bHlVnmrdgWldz91QFzaP82yOAdg=="], - "oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.2.1", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-wJUEVVUn1gyVIFNV4mxWg9cYo1rQdTKUXdGLfiqPiyQhWhZLRfPJ+9qpghvIVv7Dne6rzkbhYWdwdk/tew5RtQ=="], + "oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.2.2", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-/TvjYe/Kb//ZSHnJzgRj0QPKpS5Y2nermVTSaMTGS2btObXQyQWzuphDhsVRu60SVrNLbflHzfuTdqb3avDjyA=="], - "oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.2.1", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-p/XValXi1RRTZV8mEsdStXwZBkyQpgZjB41HLf0VfizPMAKRr6/bhuFZ9BDZFIhcDnLYcGV54MAVEsWms5yC2A=="], + "oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.2.2", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-Ka5j+tjuQkNnpESVzcTzW5tZMlBhOfP9F12+UaR72cIcwFpSoLMBp84rV6R0vXM0zUcrrN7mPeW66DvQ6A0XQQ=="], - "oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.2.1", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-G7aNMqAMO2P+wUUaaAV8sXymm59cX4G9aVNXKAd/PM6RgFWh2F4HkXkOhOdHKYZzCl1QRhjh672mNillYsvebg=="], + "oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.2.2", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-ISl0sTNShKCgPFO+rsDqEDsvVHQAMfOSAxO0KuWbHFKaH+KaRV4d3N/ihgxZ2M94CZjJLzZEuln+6kLZ93cvzQ=="], - "oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.2.1", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-pyqTGlNxirKxQgXx9YJBq2y8KN/1oIygVupClmws7dDPj9etI1l8fs/SBEnMsYzMqTlGbLVeJ5+kj9p+yg7YDA=="], + "oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.2.2", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-KeiJLQvJuZ+UYf/+eMsQXvCiHDRPk6tD15lL+qruLvU19va62JqMNvTuOv97732uF19iG0ZMiiVhqIMbSyVPqQ=="], "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], diff --git a/src/cli/doctor/checks/model-resolution.ts b/src/cli/doctor/checks/model-resolution.ts index 9c6c7956..b6ee904b 100644 --- a/src/cli/doctor/checks/model-resolution.ts +++ b/src/cli/doctor/checks/model-resolution.ts @@ -69,8 +69,8 @@ export interface ModelResolutionInfo { } interface OmoConfig { - agents?: Record - categories?: Record + agents?: Record + categories?: Record } function loadConfig(): OmoConfig | null { @@ -182,7 +182,44 @@ function formatModelWithVariant(model: string, variant?: string): string { return variant ? `${model} (${variant})` : model } -function getEffectiveVariant(requirement: ModelRequirement): string | undefined { +function getAgentOverride( + agentName: string, + config: OmoConfig, +): { variant?: string; category?: string } | undefined { + const agentOverrides = config.agents + if (!agentOverrides) return undefined + + // Direct lookup first, then case-insensitive lookup (matches agent-variant.ts) + return ( + agentOverrides[agentName] ?? + Object.entries(agentOverrides).find( + ([key]) => key.toLowerCase() === agentName.toLowerCase() + )?.[1] + ) +} + +function getEffectiveVariant( + name: string, + requirement: ModelRequirement, + config: OmoConfig, +): string | undefined { + const agentOverride = getAgentOverride(name, config) + + // Priority 1: Agent's direct variant override + if (agentOverride?.variant) { + return agentOverride.variant + } + + // Priority 2: Agent's category -> category's variant (matches agent-variant.ts) + const categoryName = agentOverride?.category + if (categoryName) { + const categoryVariant = config.categories?.[categoryName]?.variant + if (categoryVariant) { + return categoryVariant + } + } + + // Priority 3: Fall back to requirement's fallback chain const firstEntry = requirement.fallbackChain[0] return firstEntry?.variant ?? requirement.variant } @@ -193,7 +230,20 @@ interface AvailableModelsInfo { cacheExists: boolean } -function buildDetailsArray(info: ModelResolutionInfo, available: AvailableModelsInfo): string[] { +function getCategoryEffectiveVariant( + categoryName: string, + requirement: ModelRequirement, + config: OmoConfig, +): string | undefined { + const categoryVariant = config.categories?.[categoryName]?.variant + if (categoryVariant) { + return categoryVariant + } + const firstEntry = requirement.fallbackChain[0] + return firstEntry?.variant ?? requirement.variant +} + +function buildDetailsArray(info: ModelResolutionInfo, available: AvailableModelsInfo, config: OmoConfig): string[] { const details: string[] = [] details.push("═══ Available Models (from cache) ═══") @@ -215,14 +265,17 @@ function buildDetailsArray(info: ModelResolutionInfo, available: AvailableModels details.push("Agents:") for (const agent of info.agents) { const marker = agent.userOverride ? "●" : "○" - const display = formatModelWithVariant(agent.effectiveModel, getEffectiveVariant(agent.requirement)) + const display = formatModelWithVariant(agent.effectiveModel, getEffectiveVariant(agent.name, agent.requirement, config)) details.push(` ${marker} ${agent.name}: ${display}`) } details.push("") details.push("Categories:") for (const category of info.categories) { const marker = category.userOverride ? "●" : "○" - const display = formatModelWithVariant(category.effectiveModel, getEffectiveVariant(category.requirement)) + const display = formatModelWithVariant( + category.effectiveModel, + getCategoryEffectiveVariant(category.name, category.requirement, config) + ) details.push(` ${marker} ${category.name}: ${display}`) } details.push("") @@ -249,7 +302,7 @@ export async function checkModelResolution(): Promise { name: CHECK_NAMES[CHECK_IDS.MODEL_RESOLUTION], status: available.cacheExists ? "pass" : "warn", message: `${agentCount} agents, ${categoryCount} categories${overrideNote}${cacheNote}`, - details: buildDetailsArray(info, available), + details: buildDetailsArray(info, available, config), } }