1e0823a0fc
Keep background task metadata aligned with the background_output contract so callers do not pass a session id where the task manager expects a background task id. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
205 lines
6.1 KiB
TypeScript
205 lines
6.1 KiB
TypeScript
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: 50,
|
|
})
|
|
})
|
|
|
|
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("<task_metadata>")
|
|
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("<task_metadata>")
|
|
expectFn(result).toContain("session_id: ses_sub_123")
|
|
expectFn(result).toContain("task_id: bg_resolved")
|
|
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")
|
|
})
|
|
|
|
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: bg_late")
|
|
expectFn(result).toContain("background_task_id: bg_late")
|
|
expectFn(metadataCalls).toHaveLength(1)
|
|
expectFn(metadataCalls[0].metadata.sessionId).toBe("ses_late_123")
|
|
})
|
|
|
|
testFn("passes question-deny session permission when launching delegate task", async () => {
|
|
//#given - delegate task background launch should deny question at session creation time
|
|
const launchCalls: any[] = []
|
|
const manager = {
|
|
launch: async (input: any) => {
|
|
launchCalls.push(input)
|
|
return {
|
|
id: "bg_permission",
|
|
sessionID: "ses_permission_123",
|
|
description: "Permission session",
|
|
agent: "explore",
|
|
status: "running",
|
|
}
|
|
},
|
|
getTask: () => ({ sessionID: "ses_permission_123" }),
|
|
}
|
|
|
|
//#when
|
|
await executeBackgroundTask(
|
|
{
|
|
description: "Permission session",
|
|
prompt: "check",
|
|
run_in_background: true,
|
|
load_skills: [],
|
|
},
|
|
{
|
|
sessionID: "ses_parent",
|
|
callID: "call_4",
|
|
metadata: async () => {},
|
|
abort: new AbortController().signal,
|
|
},
|
|
{ manager },
|
|
{ sessionID: "ses_parent", messageID: "msg_4" },
|
|
"explore",
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
)
|
|
|
|
//#then
|
|
expectFn(launchCalls).toHaveLength(1)
|
|
expectFn(launchCalls[0].sessionPermission).toEqual([
|
|
{ permission: "question", action: "deny", pattern: "*" },
|
|
])
|
|
})
|
|
})
|