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)
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
@@ -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.`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user