From 1c0bc9748ee950172f3db9881a98c7f26dfaced7 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 12 Mar 2026 10:30:23 +0900 Subject: [PATCH] fix: make run_in_background optional with default false in task tool (#2375) --- src/tools/delegate-task/tools.test.ts | 59 +++++++++++++++++++++++++++ src/tools/delegate-task/tools.ts | 22 +++++----- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index 652c48c45..2d2194eaf 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -27,6 +27,10 @@ type DelegateTaskArgsWithSerializedSkills = Omit & { + run_in_background?: boolean +} + function createTestAvailableModels(): Set { return new Set(TEST_AVAILABLE_MODELS) } @@ -401,6 +405,61 @@ describe("sisyphus-task", () => { }, { timeout: 10000 }) }) + describe("run_in_background parameter", () => { + test("defaults to sync mode when run_in_background is omitted", async () => { + // given + const { createDelegateTask } = require("./tools") + const executeSyncTaskSpy = spyOn(executor, "executeSyncTask").mockResolvedValue("sync result") + const executeBackgroundTaskSpy = spyOn(executor, "executeBackgroundTask") + spyOn(executor, "resolveSkillContent").mockResolvedValue({ + content: undefined, + contents: [], + error: null, + }) + spyOn(executor, "resolveParentContext").mockResolvedValue({ + sessionID: "parent-session", + agent: "sisyphus", + }) + spyOn(executor, "resolveCategoryExecution").mockResolvedValue({ + agentToUse: "Sisyphus-Junior", + categoryModel: undefined, + categoryPromptAppend: undefined, + modelInfo: undefined, + actualModel: undefined, + isUnstableAgent: false, + fallbackChain: undefined, + maxPromptTokens: undefined, + }) + + const tool = createDelegateTask({ + manager: { launch: async () => ({}) }, + client: { config: { get: async () => ({}) } }, + }) + + const toolContext = { + sessionID: "parent-session", + messageID: "parent-message", + agent: "sisyphus", + abort: new AbortController().signal, + } + + const args: DelegateTaskArgsWithOptionalBackground = { + description: "Default background flag", + prompt: "Handle this task synchronously", + category: "quick", + load_skills: [], + } + + // when + const result = await tool.execute(args as DelegateTaskArgs, toolContext) + + // then + expect(result).toBe("sync result") + expect(executeSyncTaskSpy).toHaveBeenCalled() + expect(executeBackgroundTaskSpy).not.toHaveBeenCalled() + }) + }) + describe("category delegation config validation", () => { test("fills subagent_type as sisyphus-junior when category is provided without subagent_type", async () => { // given diff --git a/src/tools/delegate-task/tools.ts b/src/tools/delegate-task/tools.ts index 68c7dc4af..03c37bd91 100644 --- a/src/tools/delegate-task/tools.ts +++ b/src/tools/delegate-task/tools.ts @@ -100,7 +100,7 @@ export function createDelegateTask(options: DelegateTaskToolOptions): ToolDefini 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)"), prompt: tool.schema.string().describe("Full detailed prompt for the agent"), - run_in_background: tool.schema.boolean().describe("true=async (returns task_id), false=sync (waits). Default: false"), + run_in_background: tool.schema.boolean().optional().default(false).describe("true=async (returns task_id), false=sync (waits). Default: false"), category: tool.schema.string().optional().describe(`REQUIRED if subagent_type not provided. Do NOT provide both category and subagent_type.`), subagent_type: tool.schema.string().optional().describe("REQUIRED if category not provided. Do NOT provide both category and subagent_type."), session_id: tool.schema.string().optional().describe("Existing Task session to continue"), @@ -122,9 +122,6 @@ export function createDelegateTask(options: DelegateTaskToolOptions): ToolDefini title: args.description, }) - if (args.run_in_background === undefined) { - throw new Error(`Invalid arguments: 'run_in_background' parameter is REQUIRED. Use run_in_background=false for task delegation, run_in_background=true only for parallel exploration.`) - } if (typeof args.load_skills === "string") { try { const parsed = JSON.parse(args.load_skills) @@ -140,7 +137,8 @@ export function createDelegateTask(options: DelegateTaskToolOptions): ToolDefini throw new Error(`Invalid arguments: load_skills=null is not allowed. Pass [] if no skills needed.`) } - const runInBackground = args.run_in_background === true + const runInBackgroundValue = args.run_in_background + const runInBackground = runInBackgroundValue === true const { content: skillContent, contents: skillContents, error: skillError } = await resolveSkillContent(args.load_skills, { gitMasterConfig: options.gitMasterConfig, @@ -200,19 +198,21 @@ export function createDelegateTask(options: DelegateTaskToolOptions): ToolDefini fallbackChain = resolution.fallbackChain maxPromptTokens = resolution.maxPromptTokens - const isRunInBackgroundExplicitlyFalse = args.run_in_background === false || args.run_in_background === "false" as unknown as boolean + const isRunInBackgroundFalse = runInBackgroundValue === false + || runInBackgroundValue === "false" as unknown as boolean + || runInBackgroundValue === undefined log("[task] unstable agent detection", { category: args.category, actualModel, isUnstableAgent, - run_in_background_value: args.run_in_background, - run_in_background_type: typeof args.run_in_background, - isRunInBackgroundExplicitlyFalse, - willForceBackground: isUnstableAgent && isRunInBackgroundExplicitlyFalse, + run_in_background_value: runInBackgroundValue, + run_in_background_type: typeof runInBackgroundValue, + isRunInBackgroundFalse, + willForceBackground: isUnstableAgent && isRunInBackgroundFalse, }) - if (isUnstableAgent && isRunInBackgroundExplicitlyFalse) { + if (isUnstableAgent && isRunInBackgroundFalse) { const systemContent = buildSystemContent({ skillContent, skillContents,