From cb6f1c9f755591de0b11e3ecac93d5714624aed3 Mon Sep 17 00:00:00 2001 From: justsisyphus Date: Fri, 16 Jan 2026 18:45:19 +0900 Subject: [PATCH] fix(delegate-task): category default model takes precedence over parent model Previously, parent model string would override category default model, causing categories like 'ultrabrain' to use the parent's model (e.g., sonnet) instead of the intended category default (e.g., gpt-5.2). Model priority is now: 1. userConfig.model (oh-my-opencode.json override) 2. defaultConfig.model (category default) 3. parentModelString (fallback) 4. systemDefaultModel (last resort) --- src/tools/delegate-task/tools.test.ts | 30 ++++++++++++++++++++------- src/tools/delegate-task/tools.ts | 5 ++--- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index ff96f6aa..bae7a6cc 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -19,7 +19,7 @@ function resolveCategoryConfig( return null } - const model = userConfig?.model ?? parentModelString ?? defaultConfig?.model ?? systemDefaultModel + const model = userConfig?.model ?? defaultConfig?.model ?? parentModelString ?? systemDefaultModel const config: CategoryConfig = { ...defaultConfig, ...userConfig, @@ -212,15 +212,29 @@ describe("sisyphus-task", () => { expect(result!.config.temperature).toBe(0.3) }) - test("parentModelString is used when no user model and takes precedence over default", () => { - // #given + test("category default model takes precedence over parentModelString", () => { + // #given - builtin category has default model, parent model should NOT override it const categoryName = "visual-engineering" const parentModelString = "cliproxy/claude-opus-4-5" // #when const result = resolveCategoryConfig(categoryName, { parentModelString }) - // #then + // #then - category default model wins, parent model is ignored for builtin categories + expect(result).not.toBeNull() + expect(result!.config.model).toBe("google/gemini-3-pro-preview") + }) + + test("parentModelString is used as fallback when category has no default model", () => { + // #given - custom category with no model defined, only parentModelString as fallback + const categoryName = "my-custom-no-model" + const userCategories = { "my-custom-no-model": { temperature: 0.5 } } as unknown as Record + const parentModelString = "cliproxy/claude-opus-4-5" + + // #when + const result = resolveCategoryConfig(categoryName, { userCategories, parentModelString }) + + // #then - parent model is used as fallback since custom category has no default expect(result).not.toBeNull() expect(result!.config.model).toBe("cliproxy/claude-opus-4-5") }) @@ -888,18 +902,18 @@ describe("sisyphus-task", () => { expect(actualModel).toBe("openai/gpt-5.2") }) - test("when parentModelString is used - modelInfo should report inherited", () => { - // #given + test("category default model takes precedence over parentModelString for builtin category", () => { + // #given - builtin ultrabrain category has default model gpt-5.2 const categoryName = "ultrabrain" const parentModelString = "cliproxy/claude-opus-4-5" // #when const resolved = resolveCategoryConfig(categoryName, { parentModelString }) - // #then - actualModel should be parentModelString, type should be "inherited" + // #then - category default model wins, not the parent model expect(resolved).not.toBeNull() const actualModel = resolved!.config.model - expect(actualModel).toBe(parentModelString) + expect(actualModel).toBe("openai/gpt-5.2") }) test("when user defines model - modelInfo should report user-defined regardless of parentModelString", () => { diff --git a/src/tools/delegate-task/tools.ts b/src/tools/delegate-task/tools.ts index 6a7f861b..371fc9cb 100644 --- a/src/tools/delegate-task/tools.ts +++ b/src/tools/delegate-task/tools.ts @@ -124,9 +124,8 @@ function resolveCategoryConfig( return null } - // Model priority: user override > parent model (inherit) > category default > system default - // Parent model takes precedence over category default so custom providers work out-of-box - const model = userConfig?.model ?? parentModelString ?? defaultConfig?.model ?? systemDefaultModel + // Model priority: user override > category default > parent model (fallback) > system default + const model = userConfig?.model ?? defaultConfig?.model ?? parentModelString ?? systemDefaultModel const config: CategoryConfig = { ...defaultConfig, ...userConfig,