From 1411ca255af7af64fda3ad7d193c89a8362032b1 Mon Sep 17 00:00:00 2001 From: Lynricsy Date: Wed, 4 Feb 2026 11:51:20 +0800 Subject: [PATCH] fix(delegate-task): honor explicit category model over sisyphus-junior --- src/tools/delegate-task/executor.ts | 10 +++-- src/tools/delegate-task/tools.test.ts | 62 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/tools/delegate-task/executor.ts b/src/tools/delegate-task/executor.ts index df761c08d..2b9396e2e 100644 --- a/src/tools/delegate-task/executor.ts +++ b/src/tools/delegate-task/executor.ts @@ -794,18 +794,22 @@ export async function resolveCategoryExecution( let categoryModel: { providerID: string; modelID: string; variant?: string } | undefined const overrideModel = sisyphusJuniorModel + const explicitCategoryModel = userCategories?.[args.category!]?.model if (!requirement) { - actualModel = overrideModel ?? resolved.model + // Precedence: explicit category model > sisyphus-junior default > category resolved model + // This keeps `sisyphus-junior.model` useful as a global default while allowing + // per-category overrides via `categories[category].model`. + actualModel = explicitCategoryModel ?? overrideModel ?? resolved.model if (actualModel) { - modelInfo = overrideModel + modelInfo = explicitCategoryModel || overrideModel ? { model: actualModel, type: "user-defined", source: "override" } : { model: actualModel, type: "system-default", source: "system-default" } } } else { const resolution = resolveModelPipeline({ intent: { - userModel: overrideModel ?? userCategories?.[args.category!]?.model, + userModel: explicitCategoryModel ?? overrideModel, categoryDefaultModel: resolved.model, }, constraints: { availableModels }, diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index bfbe3e8cb..84ee612b5 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -1770,6 +1770,68 @@ describe("sisyphus-task", () => { expect(launchInput.model.providerID).toBe("anthropic") expect(launchInput.model.modelID).toBe("claude-sonnet-4-5") }) + + test("explicit category model takes precedence over sisyphus-junior model", async () => { + // given - explicit category model differs from sisyphus-junior override + const { createDelegateTask } = require("./tools") + let launchInput: any + + const mockManager = { + launch: async (input: any) => { + launchInput = input + return { + id: "task-category-precedence", + sessionID: "ses_category_precedence_test", + description: "Category precedence test", + agent: "sisyphus-junior", + status: "running", + } + }, + } + + const mockClient = { + app: { agents: async () => ({ data: [] }) }, + config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, + model: { list: async () => [] }, + session: { + create: async () => ({ data: { id: "test-session" } }), + prompt: async () => ({ data: {} }), + messages: async () => ({ data: [] }), + }, + } + + const tool = createDelegateTask({ + manager: mockManager, + client: mockClient, + sisyphusJuniorModel: "anthropic/claude-sonnet-4-5", + userCategories: { + ultrabrain: { model: "openai/gpt-5.2-codex" }, + }, + }) + + const toolContext = { + sessionID: "parent-session", + messageID: "parent-message", + agent: "sisyphus", + abort: new AbortController().signal, + } + + // when - using ultrabrain category with explicit model override + await tool.execute( + { + description: "Category precedence test", + prompt: "Do something", + category: "ultrabrain", + run_in_background: true, + load_skills: [], + }, + toolContext + ) + + // then - explicit category model should win + expect(launchInput.model.providerID).toBe("openai") + expect(launchInput.model.modelID).toBe("gpt-5.2-codex") + }) }) describe("browserProvider propagation", () => {