From 5cbf7828f051cea4ef2ce2c86952ff080f4000e0 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 5 Mar 2026 10:36:20 +0900 Subject: [PATCH 1/3] fix(task): avoid pending sessionId metadata in background delegate output --- .../delegate-task/background-task.test.ts | 108 ++++++++++++++++++ src/tools/delegate-task/background-task.ts | 34 +++--- 2 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 src/tools/delegate-task/background-task.test.ts diff --git a/src/tools/delegate-task/background-task.test.ts b/src/tools/delegate-task/background-task.test.ts new file mode 100644 index 00000000..cc87586f --- /dev/null +++ b/src/tools/delegate-task/background-task.test.ts @@ -0,0 +1,108 @@ +const bunTest = require("bun:test") +const describeFn = bunTest.describe +const testFn = bunTest.test +const expectFn = bunTest.expect +const beforeEachFn = bunTest.beforeEach +const afterEachFn = bunTest.afterEach + +const { executeBackgroundTask } = require("./background-task") +const { __setTimingConfig, __resetTimingConfig } = require("./timing") + +describeFn("executeBackgroundTask output/session metadata compatibility", () => { + beforeEachFn(() => { + //#given - reduce waiting to keep tests fast + __setTimingConfig({ + WAIT_FOR_SESSION_INTERVAL_MS: 1, + WAIT_FOR_SESSION_TIMEOUT_MS: 2, + }) + }) + + afterEachFn(() => { + __resetTimingConfig() + }) + + testFn("does not emit synthetic pending session metadata when session id is unresolved", async () => { + //#given - launched task without resolved subagent session id + const metadataCalls: any[] = [] + const manager = { + launch: async () => ({ + id: "bg_unresolved", + sessionID: undefined, + description: "Unresolved session", + agent: "explore", + status: "running", + }), + getTask: () => undefined, + } + + const result = await executeBackgroundTask( + { + description: "Unresolved session", + prompt: "check", + run_in_background: true, + load_skills: [], + }, + { + sessionID: "ses_parent", + callID: "call_1", + metadata: async (value: any) => metadataCalls.push(value), + abort: new AbortController().signal, + }, + { manager }, + { sessionID: "ses_parent", messageID: "msg_1" }, + "explore", + undefined, + undefined, + undefined, + ) + + //#then - output and metadata should avoid fake session markers + expectFn(result).not.toContain("") + expectFn(result).not.toContain("session_id: undefined") + expectFn(result).not.toContain("session_id: pending") + expectFn(metadataCalls).toHaveLength(1) + expectFn("sessionId" in metadataCalls[0].metadata).toBe(false) + }) + + testFn("emits task metadata session_id when real session id is available", async () => { + //#given - launched task with resolved subagent session id + const metadataCalls: any[] = [] + const manager = { + launch: async () => ({ + id: "bg_resolved", + sessionID: "ses_sub_123", + description: "Resolved session", + agent: "explore", + status: "running", + }), + getTask: () => ({ sessionID: "ses_sub_123" }), + } + + const result = await executeBackgroundTask( + { + description: "Resolved session", + prompt: "check", + run_in_background: true, + load_skills: [], + }, + { + sessionID: "ses_parent", + callID: "call_2", + metadata: async (value: any) => metadataCalls.push(value), + abort: new AbortController().signal, + }, + { manager }, + { sessionID: "ses_parent", messageID: "msg_2" }, + "explore", + undefined, + undefined, + undefined, + ) + + //#then - output and metadata should include canonical session linkage + expectFn(result).toContain("") + expectFn(result).toContain("session_id: ses_sub_123") + expectFn(metadataCalls).toHaveLength(1) + expectFn(metadataCalls[0].metadata.sessionId).toBe("ses_sub_123") + }) +}) diff --git a/src/tools/delegate-task/background-task.ts b/src/tools/delegate-task/background-task.ts index c01538a5..6aad94ea 100644 --- a/src/tools/delegate-task/background-task.ts +++ b/src/tools/delegate-task/background-task.ts @@ -56,25 +56,31 @@ export async function executeBackgroundTask( SessionCategoryRegistry.register(sessionId, args.category) } + const metadata = { + prompt: args.prompt, + agent: task.agent, + category: args.category, + load_skills: args.load_skills, + description: args.description, + run_in_background: args.run_in_background, + command: args.command, + ...(sessionId ? { sessionId } : {}), + ...(categoryModel ? { model: { providerID: categoryModel.providerID, modelID: categoryModel.modelID } } : {}), + } + const unstableMeta = { title: args.description, - metadata: { - prompt: args.prompt, - agent: task.agent, - category: args.category, - load_skills: args.load_skills, - description: args.description, - run_in_background: args.run_in_background, - sessionId: sessionId ?? "pending", - command: args.command, - model: categoryModel ? { providerID: categoryModel.providerID, modelID: categoryModel.modelID } : undefined, - }, + metadata, } await ctx.metadata?.(unstableMeta) if (ctx.callID) { storeToolMetadata(ctx.sessionID, ctx.callID, unstableMeta) } + const taskMetadataBlock = sessionId + ? `\n\n\nsession_id: ${sessionId}\n` + : "" + return `Background task launched. Task ID: ${task.id} @@ -82,11 +88,7 @@ Description: ${task.description} Agent: ${task.agent}${args.category ? ` (category: ${args.category})` : ""} Status: ${task.status} -System notifies on completion. Use \`background_output\` with task_id="${task.id}" to check. - - -session_id: ${sessionId} -` +System notifies on completion. Use \`background_output\` with task_id="${task.id}" to check.${taskMetadataBlock}` } catch (error) { return formatDetailedError(error, { operation: "Launch background task", From acf4c46439dca5bc7c9f9d3a7d1f54b8f86267f7 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 5 Mar 2026 10:43:45 +0900 Subject: [PATCH 2/3] fix(task): align background output task_id with opencode contract --- .../delegate-task/background-task.test.ts | 48 +++++++++++++++++++ src/tools/delegate-task/background-task.ts | 4 +- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/tools/delegate-task/background-task.test.ts b/src/tools/delegate-task/background-task.test.ts index cc87586f..25f8f2c2 100644 --- a/src/tools/delegate-task/background-task.test.ts +++ b/src/tools/delegate-task/background-task.test.ts @@ -102,7 +102,55 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () => //#then - output and metadata should include canonical session linkage expectFn(result).toContain("") expectFn(result).toContain("session_id: ses_sub_123") + expectFn(result).toContain("task_id: ses_sub_123") + expectFn(result).toContain("Background Task ID: bg_resolved") expectFn(metadataCalls).toHaveLength(1) expectFn(metadataCalls[0].metadata.sessionId).toBe("ses_sub_123") }) + + testFn("captures late-resolved session id and emits synced metadata", async () => { + //#given - background task session id appears after launch via manager polling + const metadataCalls: any[] = [] + let reads = 0 + const manager = { + launch: async () => ({ + id: "bg_late", + sessionID: undefined, + description: "Late session", + agent: "explore", + status: "running", + }), + getTask: () => { + reads += 1 + return reads >= 2 ? { sessionID: "ses_late_123" } : undefined + }, + } + + const result = await executeBackgroundTask( + { + description: "Late session", + prompt: "check", + run_in_background: true, + load_skills: [], + }, + { + sessionID: "ses_parent", + callID: "call_3", + metadata: async (value: any) => metadataCalls.push(value), + abort: new AbortController().signal, + }, + { manager }, + { sessionID: "ses_parent", messageID: "msg_3" }, + "explore", + undefined, + undefined, + undefined, + ) + + //#then - late session id still propagates to task metadata contract + expectFn(result).toContain("session_id: ses_late_123") + expectFn(result).toContain("task_id: ses_late_123") + expectFn(metadataCalls).toHaveLength(1) + expectFn(metadataCalls[0].metadata.sessionId).toBe("ses_late_123") + }) }) diff --git a/src/tools/delegate-task/background-task.ts b/src/tools/delegate-task/background-task.ts index 6aad94ea..2b385b26 100644 --- a/src/tools/delegate-task/background-task.ts +++ b/src/tools/delegate-task/background-task.ts @@ -78,12 +78,12 @@ export async function executeBackgroundTask( } const taskMetadataBlock = sessionId - ? `\n\n\nsession_id: ${sessionId}\n` + ? `\n\n\nsession_id: ${sessionId}\ntask_id: ${sessionId}\n` : "" return `Background task launched. -Task ID: ${task.id} +Background Task ID: ${task.id} Description: ${task.description} Agent: ${task.agent}${args.category ? ` (category: ${args.category})` : ""} Status: ${task.status} From 39d94a4af67b7f00e8a82afcdf642b57b746abcc Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 5 Mar 2026 10:50:44 +0900 Subject: [PATCH 3/3] fix(task): disambiguate background task_id metadata --- src/tools/delegate-task/background-task.test.ts | 2 ++ src/tools/delegate-task/background-task.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/delegate-task/background-task.test.ts b/src/tools/delegate-task/background-task.test.ts index 25f8f2c2..e244f7cc 100644 --- a/src/tools/delegate-task/background-task.test.ts +++ b/src/tools/delegate-task/background-task.test.ts @@ -103,6 +103,7 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () => expectFn(result).toContain("") expectFn(result).toContain("session_id: ses_sub_123") expectFn(result).toContain("task_id: ses_sub_123") + expectFn(result).toContain("background_task_id: bg_resolved") expectFn(result).toContain("Background Task ID: bg_resolved") expectFn(metadataCalls).toHaveLength(1) expectFn(metadataCalls[0].metadata.sessionId).toBe("ses_sub_123") @@ -150,6 +151,7 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () => //#then - late session id still propagates to task metadata contract expectFn(result).toContain("session_id: ses_late_123") expectFn(result).toContain("task_id: ses_late_123") + expectFn(result).toContain("background_task_id: bg_late") expectFn(metadataCalls).toHaveLength(1) expectFn(metadataCalls[0].metadata.sessionId).toBe("ses_late_123") }) diff --git a/src/tools/delegate-task/background-task.ts b/src/tools/delegate-task/background-task.ts index 2b385b26..aedd0b3e 100644 --- a/src/tools/delegate-task/background-task.ts +++ b/src/tools/delegate-task/background-task.ts @@ -78,7 +78,7 @@ export async function executeBackgroundTask( } const taskMetadataBlock = sessionId - ? `\n\n\nsession_id: ${sessionId}\ntask_id: ${sessionId}\n` + ? `\n\n\nsession_id: ${sessionId}\ntask_id: ${sessionId}\nbackground_task_id: ${task.id}\n` : "" return `Background task launched.