From bd37e6676ac69aa47d95bbda84e3177527eb96d4 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 7 Apr 2026 09:30:22 +0900 Subject: [PATCH] fix(delegate-task): make description optional with auto-generation from prompt (#3162) When weaker models (GLM-5, MiniMax) omit the description parameter on delegate_task, the tool now auto-generates it from the first 4 words of the prompt instead of throwing an error. Changes: - Schema: description is now optional (tool.schema.string().optional()) - Runtime: auto-generates from prompt when missing/empty/whitespace - DelegateTaskArgs.description type stays as string (guaranteed by auto-gen) - Tests: 3 new cases - missing/empty/explicit description handling - Metadata title set after description resolution (correct ordering) --- src/tools/delegate-task/tools.test.ts | 119 +++++++++++++++++++++++--- src/tools/delegate-task/tools.ts | 11 +-- 2 files changed, 115 insertions(+), 15 deletions(-) diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index 33e338c1b..c605fc043 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -1366,9 +1366,10 @@ describe("sisyphus-task", () => { )).rejects.toThrow("Invalid arguments: 'run_in_background' parameter is REQUIRED") }) - test("#given category without description #when executing #then throws required parameter error", async () => { + test("#given category without description #when executing #then auto-generates description from prompt", async () => { // given const { createDelegateTask } = require("./tools") + let capturedTitle: string | undefined const mockManager = { launch: async () => ({}) } const mockClient = { app: { agents: async () => ({ data: [] }) }, @@ -1383,16 +1384,114 @@ describe("sisyphus-task", () => { const tool = createDelegateTask({ manager: mockManager, client: mockClient }) // when - // then - await expect(tool.execute( - { - prompt: "Do something", - category: "quick", - run_in_background: false, - load_skills: [], + try { + await tool.execute( + { + prompt: "Fix the broken unit tests in parser module", + category: "quick", + run_in_background: false, + load_skills: [], + }, + { + sessionID: "parent-session", + messageID: "parent-message", + agent: "sisyphus", + abort: new AbortController().signal, + metadata: async (meta: { title?: string }) => { capturedTitle = meta.title }, + } + ) + } catch { + // execution may fail due to incomplete mocks — we only care about the title + } + + // then — description auto-generated from first 4 words of prompt + expect(capturedTitle).toBe("Fix the broken unit") + }) + + test("#given empty description #when executing #then auto-generates description from prompt", async () => { + // given + const { createDelegateTask } = require("./tools") + let capturedTitle: string | undefined + const mockManager = { launch: async () => ({}) } + const mockClient = { + app: { agents: async () => ({ data: [] }) }, + config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, + session: { + create: async () => ({ data: { id: "test-session" } }), + prompt: async () => ({ data: {} }), + promptAsync: async () => ({ data: {} }), + messages: async () => ({ data: [] }), }, - { sessionID: "parent-session", messageID: "parent-message", agent: "sisyphus", abort: new AbortController().signal } - )).rejects.toThrow("Invalid arguments: 'description' parameter is REQUIRED") + } + const tool = createDelegateTask({ manager: mockManager, client: mockClient }) + + // when + try { + await tool.execute( + { + description: " ", + prompt: "Refactor authentication module completely", + category: "quick", + run_in_background: false, + load_skills: [], + }, + { + sessionID: "parent-session", + messageID: "parent-message", + agent: "sisyphus", + abort: new AbortController().signal, + metadata: async (meta: { title?: string }) => { capturedTitle = meta.title }, + } + ) + } catch { + // execution may fail due to incomplete mocks + } + + // then + expect(capturedTitle).toBe("Refactor authentication module completely") + }) + + test("#given explicit description #when executing #then preserves provided description", async () => { + // given + const { createDelegateTask } = require("./tools") + let capturedTitle: string | undefined + const mockManager = { launch: async () => ({}) } + const mockClient = { + app: { agents: async () => ({ data: [] }) }, + config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, + session: { + create: async () => ({ data: { id: "test-session" } }), + prompt: async () => ({ data: {} }), + promptAsync: async () => ({ data: {} }), + messages: async () => ({ data: [] }), + }, + } + const tool = createDelegateTask({ manager: mockManager, client: mockClient }) + + // when + try { + await tool.execute( + { + description: "My custom task name", + prompt: "Do something else entirely", + category: "quick", + run_in_background: false, + load_skills: [], + }, + { + sessionID: "parent-session", + messageID: "parent-message", + agent: "sisyphus", + abort: new AbortController().signal, + metadata: async (meta: { title?: string }) => { capturedTitle = meta.title }, + } + ) + } catch { + // execution may fail due to incomplete mocks + } + + // then — explicit description preserved + expect(capturedTitle).toBe("My custom task name") }) test("#given explicit run_in_background=false #when executing #then sync execution succeeds", async () => { diff --git a/src/tools/delegate-task/tools.ts b/src/tools/delegate-task/tools.ts index 46822bfcd..ab55e1789 100644 --- a/src/tools/delegate-task/tools.ts +++ b/src/tools/delegate-task/tools.ts @@ -98,7 +98,7 @@ export function createDelegateTask(options: DelegateTaskToolOptions): ToolDefini description, args: { load_skills: tool.schema.array(tool.schema.string()).describe("Skill names to inject. REQUIRED - pass [] if no skills needed."), - description: tool.schema.string().describe("Short task description (3-5 words)"), + description: tool.schema.string().optional().describe("Short task description (3-5 words). Auto-generated from prompt if omitted."), prompt: tool.schema.string().describe("Full detailed prompt for the agent"), run_in_background: tool.schema.boolean().describe("REQUIRED. true=async (returns task_id), false=sync (waits). Use false for task delegation, true ONLY for parallel exploration."), category: tool.schema.string().optional().describe(`REQUIRED if subagent_type not provided. Do NOT provide both category and subagent_type.`), @@ -118,13 +118,14 @@ export function createDelegateTask(options: DelegateTaskToolOptions): ToolDefini } args.subagent_type = SISYPHUS_JUNIOR_AGENT } + // Auto-generate description from prompt when missing or empty + if (!args.description || typeof args.description !== "string" || args.description.trim() === "") { + const words = (args.prompt || "").trim().split(/\s+/) + args.description = words.slice(0, 4).join(" ") || "Delegated task" + } await ctx.metadata?.({ title: args.description, }) - - if (!args.description || typeof args.description !== "string") { - throw new Error(`Invalid arguments: 'description' parameter is REQUIRED. Provide a short (3-5 words) task description.`) - } if (args.run_in_background === undefined) { throw new Error(`Invalid arguments: 'run_in_background' parameter is REQUIRED. Specify run_in_background=false for task delegation, or run_in_background=true for parallel exploration.`) }