refactor(background-agent): optimize lifecycle and simplify tools (#1411)

* refactor(background-agent): optimize cache timer lifecycle and result handling

Ultraworked with Sisyphus

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* refactor(background-task): simplify tool implementation and expand test coverage

Ultraworked with Sisyphus

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(background-task): fix BackgroundCancel tool parameter handling

Correct parameter names and types in BackgroundCancel tool to match actual usage patterns. Add comprehensive test coverage for parameter validation.

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-02-03 12:11:45 +09:00
committed by GitHub
parent b62519b401
commit e36dde6e64
4 changed files with 209 additions and 67 deletions
@@ -184,6 +184,10 @@ function getTaskMap(manager: BackgroundManager): Map<string, BackgroundTask> {
return (manager as unknown as { tasks: Map<string, BackgroundTask> }).tasks
}
function getPendingByParent(manager: BackgroundManager): Map<string, Set<string>> {
return (manager as unknown as { pendingByParent: Map<string, Set<string>> }).pendingByParent
}
async function tryCompleteTaskForTest(manager: BackgroundManager, task: BackgroundTask): Promise<boolean> {
return (manager as unknown as { tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean> })
.tryCompleteTask(task, "test")
@@ -1454,6 +1458,44 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
})
})
describe("cancelTask", () => {
test("should cancel running task and release concurrency", async () => {
// given
const manager = createBackgroundManager()
stubNotifyParentSession(manager)
const concurrencyManager = getConcurrencyManager(manager)
const concurrencyKey = "test-provider/test-model"
await concurrencyManager.acquire(concurrencyKey)
const task = createMockTask({
id: "task-cancel-running",
sessionID: "session-cancel-running",
parentSessionID: "parent-cancel",
status: "running",
concurrencyKey,
})
getTaskMap(manager).set(task.id, task)
const pendingByParent = getPendingByParent(manager)
pendingByParent.set(task.parentSessionID, new Set([task.id]))
// when
const cancelled = await manager.cancelTask(task.id, { source: "test" })
// then
const updatedTask = manager.getTask(task.id)
expect(cancelled).toBe(true)
expect(updatedTask?.status).toBe("cancelled")
expect(updatedTask?.completedAt).toBeInstanceOf(Date)
expect(updatedTask?.concurrencyKey).toBeUndefined()
expect(concurrencyManager.getCount(concurrencyKey)).toBe(0)
const pendingSet = pendingByParent.get(task.parentSessionID)
expect(pendingSet?.has(task.id) ?? false).toBe(false)
})
})
describe("multiple keys process in parallel", () => {
test("should process different concurrency keys in parallel", async () => {
// given