test(delegate-task): cover aborted concurrent background launches

This commit is contained in:
YeonGyu-Kim
2026-03-31 15:11:12 -07:00
parent 56cf16c4c5
commit 9a0f2ff9a7
3 changed files with 363 additions and 3 deletions
@@ -7,6 +7,7 @@ const afterEachFn = bunTest.afterEach
const { executeBackgroundTask } = require("./background-task")
const { __setTimingConfig, __resetTimingConfig } = require("./timing")
const { SessionCategoryRegistry } = require("../../shared/session-category-registry")
describeFn("executeBackgroundTask output/session metadata compatibility", () => {
beforeEachFn(() => {
@@ -19,6 +20,7 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
afterEachFn(() => {
__resetTimingConfig()
SessionCategoryRegistry.clear()
})
testFn("does not emit synthetic pending session metadata when session id is unresolved", async () => {
@@ -201,4 +203,226 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
{ permission: "question", action: "deny", pattern: "*" },
])
})
testFn("keeps launched background task alive when parent aborts before session id resolves", async () => {
//#given - parallel tool execution can abort the parent call after launch succeeds
const metadataCalls: any[] = []
const abortController = new AbortController()
const manager = {
launch: async () => ({
id: "bg_abort_after_launch",
sessionID: undefined,
description: "Abort after launch",
agent: "explore",
status: "pending",
}),
getTask: () => {
abortController.abort()
return { sessionID: undefined, status: "pending" }
},
}
//#when
const result = await executeBackgroundTask(
{
description: "Abort after launch",
prompt: "check",
run_in_background: true,
load_skills: [],
},
{
sessionID: "ses_parent",
callID: "call_abort_after_launch",
metadata: async (value: any) => metadataCalls.push(value),
abort: abortController.signal,
},
{ manager },
{ sessionID: "ses_parent", messageID: "msg_abort_after_launch" },
"explore",
undefined,
undefined,
undefined,
)
//#then - background launch should still succeed without fake abort failure
expectFn(result).toContain("Background task launched")
expectFn(result).toContain("Background Task ID: bg_abort_after_launch")
expectFn(result).not.toContain("Task aborted while waiting for session to start")
expectFn(metadataCalls).toHaveLength(1)
expectFn("sessionId" in metadataCalls[0].metadata).toBe(false)
})
testFn("registers late session category even when parent aborts before session id resolves", async () => {
//#given - session wiring should continue after returning early on parent abort
const abortController = new AbortController()
abortController.abort()
let reads = 0
const manager = {
launch: async () => ({
id: "bg_abort_category",
sessionID: undefined,
description: "Abort category",
agent: "explore",
status: "pending",
}),
getTask: () => {
reads += 1
return reads >= 2
? { sessionID: "ses_abort_category", status: "running" }
: { sessionID: undefined, status: "pending" }
},
}
//#when
const result = await executeBackgroundTask(
{
description: "Abort category",
prompt: "check",
run_in_background: true,
load_skills: [],
category: "quick",
},
{
sessionID: "ses_parent",
callID: "call_abort_category",
metadata: async () => {},
abort: abortController.signal,
},
{ manager },
{ sessionID: "ses_parent", messageID: "msg_abort_category" },
"explore",
undefined,
undefined,
[{ providers: ["openai"], model: "gpt-5.4" }],
)
await new Promise(resolve => setTimeout(resolve, 5))
//#then - late session setup should still register category for runtime fallback
expectFn(result).toContain("Background task launched")
expectFn(SessionCategoryRegistry.get("ses_abort_category")).toBe("quick")
})
testFn("prefers child terminal status over parent abort while waiting for session id", async () => {
//#given - failed child launch should not be misreported as a successful background launch
const abortController = new AbortController()
abortController.abort()
const manager = {
launch: async () => ({
id: "bg_abort_terminal",
sessionID: undefined,
description: "Abort terminal",
agent: "explore",
status: "pending",
}),
getTask: () => ({ sessionID: undefined, status: "interrupt" }),
}
//#when
const result = await executeBackgroundTask(
{
description: "Abort terminal",
prompt: "check",
run_in_background: true,
load_skills: [],
},
{
sessionID: "ses_parent",
callID: "call_abort_terminal",
metadata: async () => {},
abort: abortController.signal,
},
{ manager },
{ sessionID: "ses_parent", messageID: "msg_abort_terminal" },
"explore",
undefined,
undefined,
undefined,
)
//#then - terminal child status should win over abort and surface the failure
expectFn(result).toContain("Task failed to start")
expectFn(result).toContain("interrupt")
})
testFn("keeps sibling background launch alive when two tasks start concurrently", async () => {
//#given - one aborted parent call should not interrupt a sibling launch from the same parent session
const firstAbortController = new AbortController()
const secondAbortController = new AbortController()
const states = new Map([
["bg_first", { reads: 0, abortOnFirstRead: true, sessionID: "ses_first" }],
["bg_second", { reads: 0, abortOnFirstRead: false, sessionID: "ses_second" }],
])
let launchCount = 0
const manager = {
launch: async () => {
launchCount += 1
return launchCount === 1
? { id: "bg_first", sessionID: undefined, description: "First", agent: "explore", status: "pending" }
: { id: "bg_second", sessionID: undefined, description: "Second", agent: "explore", status: "pending" }
},
getTask: (taskID: string) => {
const state = states.get(taskID)
if (!state) return undefined
state.reads += 1
if (state.abortOnFirstRead && state.reads === 1) {
firstAbortController.abort()
}
return state.reads >= 2
? { sessionID: state.sessionID, status: "running" }
: { sessionID: undefined, status: "pending" }
},
}
//#when
const [firstResult, secondResult] = await Promise.all([
executeBackgroundTask(
{
description: "First",
prompt: "check",
run_in_background: true,
load_skills: [],
},
{
sessionID: "ses_parent",
callID: "call_first",
metadata: async () => {},
abort: firstAbortController.signal,
},
{ manager },
{ sessionID: "ses_parent", messageID: "msg_first" },
"explore",
undefined,
undefined,
undefined,
),
executeBackgroundTask(
{
description: "Second",
prompt: "check",
run_in_background: true,
load_skills: [],
},
{
sessionID: "ses_parent",
callID: "call_second",
metadata: async () => {},
abort: secondAbortController.signal,
},
{ manager },
{ sessionID: "ses_parent", messageID: "msg_second" },
"explore",
undefined,
undefined,
undefined,
),
])
//#then - both tasks still launch and the sibling is not reported as interrupted
expectFn(firstResult).toContain("Background task launched")
expectFn(firstResult).not.toContain("Task failed to start")
expectFn(secondResult).toContain("Background task launched")
expectFn(secondResult).toContain("session_id: ses_second")
expectFn(secondResult).not.toContain("interrupt")
})
})