From 8f449e1627e0844d0c690aaa90079e9aa12f1fd7 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 3 Apr 2026 18:27:26 +0900 Subject: [PATCH] fix: respect user-configured category model over fallbackChain defaults When a user configures a custom model for a category (e.g. quick.model), the hardcoded CATEGORY_MODEL_REQUIREMENTS fallbackChain was overriding it. This caused the user's model to be ignored and replaced with the default (e.g. openai/gpt-5.4-mini). Fix: - Use userModelOverride directly instead of potentially stale actualModel - Suppress hardcoded fallbackChain when explicitCategoryModel is provided - Add regression test verifying user category model takes precedence Closes #3040 --- .../delegate-task/category-resolver.test.ts | 32 +++++++++++++++++++ src/tools/delegate-task/category-resolver.ts | 6 ++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/tools/delegate-task/category-resolver.test.ts b/src/tools/delegate-task/category-resolver.test.ts index d48407dba..1b2ca185f 100644 --- a/src/tools/delegate-task/category-resolver.test.ts +++ b/src/tools/delegate-task/category-resolver.test.ts @@ -452,4 +452,36 @@ describe("resolveCategoryExecution", () => { cacheSpy.mockRestore() agentsSpy.mockRestore() }) + + test("does not inherit hardcoded fallbackChain when user configures a category model [regression #3040]", async () => { + //#given + const args = { + category: "quick", + prompt: "test prompt", + description: "Test task", + run_in_background: false, + load_skills: [], + blockedBy: undefined, + enableSkillTools: false, + } + const executorCtx = createMockExecutorContext() + executorCtx.userCategories = { + quick: { + model: "animal-gateway-xai/grok-4-fast-non-reasoning", + }, + } + + //#when + const result = await resolveCategoryExecution(args, executorCtx, undefined, "anthropic/claude-sonnet-4-6") + + //#then + expect(result.error).toBeUndefined() + expect(result.actualModel).toBe("animal-gateway-xai/grok-4-fast-non-reasoning") + expect(result.categoryModel).toEqual({ + providerID: "animal-gateway-xai", + modelID: "grok-4-fast-non-reasoning", + variant: undefined, + }) + expect(result.fallbackChain).toBeUndefined() + }) }) diff --git a/src/tools/delegate-task/category-resolver.ts b/src/tools/delegate-task/category-resolver.ts index 5651f509d..ffcfd1881 100644 --- a/src/tools/delegate-task/category-resolver.ts +++ b/src/tools/delegate-task/category-resolver.ts @@ -150,12 +150,12 @@ Available categories: ${allCategoryNames}`, const userModelOverride = explicitCategoryModel ?? overrideModel if (userModelOverride) { actualModel = userModelOverride - const parsedModel = parseModelString(actualModel) + const parsedModel = parseModelString(userModelOverride) const variantToUse = userCategories?.[args.category!]?.variant ?? resolved.config.variant categoryModel = parsedModel ? applyCategoryParams({ ...parsedModel, variant: variantToUse ?? parsedModel.variant }, resolved.config) : undefined - modelInfo = { model: actualModel, type: "user-defined", source: "override" } + modelInfo = { model: userModelOverride, type: "user-defined", source: "override" } } } else if (resolution) { const { @@ -275,6 +275,6 @@ Available categories: ${categoryNames.join(", ")}`, actualModel, isUnstableAgent, // Don't use hardcoded fallback chain when resolution was skipped (cold cache) - fallbackChain: configuredFallbackChain ?? (isModelResolutionSkipped ? undefined : requirement?.fallbackChain), + fallbackChain: configuredFallbackChain ?? ((isModelResolutionSkipped || explicitCategoryModel) ? undefined : requirement?.fallbackChain), } }