From 3e32afe64625cc3670dfe542728b2c13066ce671 Mon Sep 17 00:00:00 2001 From: Moha Abdi Date: Wed, 28 Jan 2026 10:26:31 +0300 Subject: [PATCH] fix(agent-variant): resolve variant based on current model, not static config (#1179) --- src/index.ts | 15 +++- src/shared/agent-variant.test.ts | 116 ++++++++++++++++++++++++++++++- src/shared/agent-variant.ts | 38 ++++++++++ 3 files changed, 165 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index c8b8b6862..7319ec34e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,7 +40,7 @@ import { contextCollector, createContextInjectorMessagesTransformHook, } from "./features/context-injector"; -import { applyAgentVariant, resolveAgentVariant } from "./shared/agent-variant"; +import { applyAgentVariant, resolveAgentVariant, resolveVariantForModel } from "./shared/agent-variant"; import { createFirstMessageVariantGate } from "./shared/first-message-variant"; import { discoverUserClaudeSkills, @@ -384,13 +384,22 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { const message = (output as { message: { variant?: string } }).message if (firstMessageVariantGate.shouldOverride(input.sessionID)) { - const variant = resolveAgentVariant(pluginConfig, input.agent) + const variant = input.model && input.agent + ? resolveVariantForModel(pluginConfig, input.agent, input.model) + : resolveAgentVariant(pluginConfig, input.agent) if (variant !== undefined) { message.variant = variant } firstMessageVariantGate.markApplied(input.sessionID) } else { - applyAgentVariant(pluginConfig, input.agent, message) + if (input.model && input.agent && message.variant === undefined) { + const variant = resolveVariantForModel(pluginConfig, input.agent, input.model) + if (variant !== undefined) { + message.variant = variant + } + } else { + applyAgentVariant(pluginConfig, input.agent, message) + } } await keywordDetector?.["chat.message"]?.(input, output); diff --git a/src/shared/agent-variant.test.ts b/src/shared/agent-variant.test.ts index a23ea735a..4b12647e6 100644 --- a/src/shared/agent-variant.test.ts +++ b/src/shared/agent-variant.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test" import type { OhMyOpenCodeConfig } from "../config" -import { applyAgentVariant, resolveAgentVariant } from "./agent-variant" +import { applyAgentVariant, resolveAgentVariant, resolveVariantForModel } from "./agent-variant" describe("resolveAgentVariant", () => { test("returns undefined when agent name missing", () => { @@ -81,3 +81,117 @@ describe("applyAgentVariant", () => { expect(message.variant).toBe("max") }) }) + +describe("resolveVariantForModel", () => { + test("returns correct variant for anthropic provider", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "anthropic", modelID: "claude-opus-4-5" } + + // #when + const variant = resolveVariantForModel(config, "sisyphus", model) + + // #then + expect(variant).toBe("max") + }) + + test("returns correct variant for openai provider", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "openai", modelID: "gpt-5.2" } + + // #when + const variant = resolveVariantForModel(config, "sisyphus", model) + + // #then + expect(variant).toBe("medium") + }) + + test("returns undefined for provider with no variant in chain", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "google", modelID: "gemini-3-pro" } + + // #when + const variant = resolveVariantForModel(config, "sisyphus", model) + + // #then + expect(variant).toBeUndefined() + }) + + test("returns undefined for provider not in chain", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "unknown-provider", modelID: "some-model" } + + // #when + const variant = resolveVariantForModel(config, "sisyphus", model) + + // #then + expect(variant).toBeUndefined() + }) + + test("returns undefined for unknown agent", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "anthropic", modelID: "claude-opus-4-5" } + + // #when + const variant = resolveVariantForModel(config, "nonexistent-agent", model) + + // #then + expect(variant).toBeUndefined() + }) + + test("returns variant for zai-coding-plan provider without variant", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "zai-coding-plan", modelID: "glm-4.7" } + + // #when + const variant = resolveVariantForModel(config, "sisyphus", model) + + // #then + expect(variant).toBeUndefined() + }) + + test("falls back to category chain when agent has no requirement", () => { + // #given + const config = { + agents: { + "custom-agent": { category: "ultrabrain" }, + }, + } as OhMyOpenCodeConfig + const model = { providerID: "openai", modelID: "gpt-5.2-codex" } + + // #when + const variant = resolveVariantForModel(config, "custom-agent", model) + + // #then + expect(variant).toBe("xhigh") + }) + + test("returns correct variant for oracle agent with openai", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "openai", modelID: "gpt-5.2" } + + // #when + const variant = resolveVariantForModel(config, "oracle", model) + + // #then + expect(variant).toBe("high") + }) + + test("returns correct variant for oracle agent with anthropic", () => { + // #given + const config = {} as OhMyOpenCodeConfig + const model = { providerID: "anthropic", modelID: "claude-opus-4-5" } + + // #when + const variant = resolveVariantForModel(config, "oracle", model) + + // #then + expect(variant).toBe("max") + }) +}) diff --git a/src/shared/agent-variant.ts b/src/shared/agent-variant.ts index 463e220a6..65c27c3d7 100644 --- a/src/shared/agent-variant.ts +++ b/src/shared/agent-variant.ts @@ -1,5 +1,6 @@ import type { OhMyOpenCodeConfig } from "../config" import { findCaseInsensitive } from "./case-insensitive" +import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "./model-requirements" export function resolveAgentVariant( config: OhMyOpenCodeConfig, @@ -29,6 +30,43 @@ export function resolveAgentVariant( return config.categories?.[categoryName]?.variant } +export function resolveVariantForModel( + config: OhMyOpenCodeConfig, + agentName: string, + currentModel: { providerID: string; modelID: string }, +): string | undefined { + const agentRequirement = AGENT_MODEL_REQUIREMENTS[agentName] + if (agentRequirement) { + return findVariantInChain(agentRequirement.fallbackChain, currentModel.providerID) + } + + const agentOverrides = config.agents as + | Record + | undefined + const agentOverride = agentOverrides ? findCaseInsensitive(agentOverrides, agentName) : undefined + const categoryName = agentOverride?.category + if (categoryName) { + const categoryRequirement = CATEGORY_MODEL_REQUIREMENTS[categoryName] + if (categoryRequirement) { + return findVariantInChain(categoryRequirement.fallbackChain, currentModel.providerID) + } + } + + return undefined +} + +function findVariantInChain( + fallbackChain: { providers: string[]; model: string; variant?: string }[], + providerID: string, +): string | undefined { + for (const entry of fallbackChain) { + if (entry.providers.includes(providerID)) { + return entry.variant + } + } + return undefined +} + export function applyAgentVariant( config: OhMyOpenCodeConfig, agentName: string | undefined,